On Wed, Nov 12, 2014 at 08:56:33AM +0100, Tomeu Vizoso wrote:
[...]
> diff --git a/drivers/memory/tegra/tegra124-emc.c 
> b/drivers/memory/tegra/tegra124-emc.c
[...]
> +static int t124_emc_burst_regs[] = {

The t124 prefix seems rather redundant in a Tegra124-specific file. Also
these should really be unsigned.

> +struct emc_timing {
> +     unsigned long rate;
> +
> +     /*
> +      * Store EMC burst data in a union to minimize mistakes. This allows
> +      * us to use the same burst data lists as used by the downstream and
> +      * ChromeOS kernels.

I don't understand what this means. Can you elaborate?

> +      */
> +     union {
> +             u32 emc_burst_data[ARRAY_SIZE(t124_emc_burst_regs)];
> +             struct {
> +                     u32 __pad0[121];
> +                     u32 emc_xm2dqspadctrl2;
> +                     u32 __pad1[15];
> +                     u32 emc_zcal_interval;
> +                     u32 __pad2[1];
> +                     u32 emc_mrs_wait_cnt;
> +             };
> +     };
[...]
> +struct tegra_emc {
> +     struct platform_device *pdev;

I don't see this ever used other than for accessing pdev->dev, in which
case it would be much simpler and save a lot of characters if this was
simply:

        struct device *dev;

> +     struct tegra_mc *mc;
> +
> +     void __iomem *emc_regs;

There is no second set of registers, so the emc_ prefix is useless.

> +
> +     enum emc_dram_type dram_type;
> +     u8 dram_num;

Should be an unsized type and match what's returned by the MC accessor.

> +
> +     struct emc_timing last_timing;

struct emc_timing is rather big, can this be a pointer to an entry in
the timings array instead?

> +     struct emc_timing *timings;
> +     unsigned int num_timings;
> +};
> +
> +/* Timing change sequence functions */
> +
> +static void emc_ccfifo_writel(struct tegra_emc *tegra, u32 val,
> +                           unsigned long offs)
> +{
> +     writel(val, tegra->emc_regs + EMC_CCFIFO_DATA);
> +     writel(offs, tegra->emc_regs + EMC_CCFIFO_ADDR);
> +}
> +
> +static void emc_seq_update_timing(struct tegra_emc *tegra)
> +{
> +     int i;

unsigned int, please.

> +
> +     writel(1, tegra->emc_regs + EMC_TIMING_CONTROL);
> +
> +     for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; ++i) {
> +             if (!(readl(tegra->emc_regs + EMC_STATUS) &
> +                 EMC_STATUS_TIMING_UPDATE_STALLED))
> +                     return;
> +     }

You're busy-looping 1000 times here. What are the real criteria here?
How long is this allowed to take? Should this not be a timed loop where
the number of iterations doesn't matter?

> +
> +     dev_err(&tegra->pdev->dev, "timing update failed\n");
> +}

Should this return an error that can be propagated?

> +static void emc_seq_disable_auto_cal(struct tegra_emc *tegra)
> +{
> +     int i;
> +
> +     writel(0, tegra->emc_regs + EMC_AUTO_CAL_INTERVAL);
> +
> +     for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; ++i) {
> +             if (!(readl(tegra->emc_regs + EMC_AUTO_CAL_STATUS) &
> +                 EMC_AUTO_CAL_STATUS_ACTIVE))
> +                     return;
> +     }
> +
> +     dev_err(&tegra->pdev->dev, "auto cal disable failed\n");
> +}

Same comments as for emc_seq_update_timing().

> +
> +static void emc_seq_wait_clkchange(struct tegra_emc *tegra)
> +{
> +     int i;
> +
> +     for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; ++i) {
> +             if (readl(tegra->emc_regs + EMC_INTSTATUS) &
> +                 EMC_INTSTATUS_CLKCHANGE_COMPLETE)
> +                     return;
> +     }
> +
> +     dev_err(&tegra->pdev->dev, "clkchange failed\n");
> +}

And again.

> +
> +static void emc_prepare_timing_change(struct tegra_emc *tegra,
> +                                   const struct emc_timing *timing)
> +{
> +     u32 val, val2;
> +     bool update = false;
> +     int pre_wait = 0;
> +     int i;

Both of these can be unsigned.

> +     enum emc_dll_change dll_change;
> +
> +     if ((tegra->last_timing.emc_mode_1 & 0x1) == (timing->emc_mode_1 & 1))
> +             dll_change = DLL_CHANGE_NONE;
> +     else if (timing->emc_mode_1 & 1)
> +             dll_change = DLL_CHANGE_ON;
> +     else
> +             dll_change = DLL_CHANGE_OFF;
> +
> +     /* Clear CLKCHANGE_COMPLETE interrupts */
> +
> +     writel(EMC_INTSTATUS_CLKCHANGE_COMPLETE,
> +            tegra->emc_regs + EMC_INTSTATUS);
> +
> +     /* Disable dynamic self-refresh */
> +
> +     val = readl(tegra->emc_regs + EMC_CFG);
> +     if (val & EMC_CFG_PWR_MASK) {
> +             val &= ~EMC_CFG_POWER_FEATURES_MASK;
> +             writel(val, tegra->emc_regs + EMC_CFG);
> +
> +             pre_wait = 5;
> +     }
> +
> +     /* Disable SEL_DPD_CTRL for clock change */
> +
> +     val = readl(tegra->emc_regs + EMC_SEL_DPD_CTRL);
> +     if (val & (tegra->dram_type == DRAM_TYPE_DDR3 ?
> +         EMC_SEL_DPD_CTRL_DDR3_MASK : EMC_SEL_DPD_CTRL_MASK)) {

This is really hard to read. Perhaps something like:

        if (tegra->dram_type == DRAM_TYPE_DDR3)
                mask = EMC_SEL_DPD_CTRL_DDR3_MASK;
        else
                mask = EMC_SEL_DPD_CTRL_MASK;

?

> +             val &= ~(EMC_SEL_DPD_CTRL_DATA_SEL_DPD |
> +                             EMC_SEL_DPD_CTRL_ODT_SEL_DPD |
> +                             EMC_SEL_DPD_CTRL_CA_SEL_DPD |
> +                             EMC_SEL_DPD_CTRL_CLK_SEL_DPD);
> +             if (tegra->dram_type == DRAM_TYPE_DDR3)
> +                     val &= ~EMC_SEL_DPD_CTRL_RESET_SEL_DPD;

These seem to be all the fields already present in the respective masks,
so why not just:

        val &= ~mask;

?

> +             writel(val, tegra->emc_regs + EMC_SEL_DPD_CTRL);
> +     }
> +
> +     /* Prepare DQ/DQS for clock change */
> +
> +     val = readl(tegra->emc_regs + EMC_BGBIAS_CTL0);
> +     val2 = tegra->last_timing.emc_bgbias_ctl0;
> +     if (!(timing->emc_bgbias_ctl0 &
> +           EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_RX) &&
> +         (val & EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_RX)) {
> +             val2 &= ~EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_RX;
> +             update = true;
> +     }
> +
> +     if ((val & EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD) ||
> +         (val & EMC_BGBIAS_CTL0_BIAS0_DSC_E_PWRD_IBIAS_VTTGEN)) {
> +             update = true;
> +     }
> +
> +     if (update) {
> +             writel(val2, tegra->emc_regs + EMC_BGBIAS_CTL0);
> +             if (pre_wait < 5)
> +                     pre_wait = 5;
> +     }
> +
> +     update = false;
> +     val = readl(tegra->emc_regs + EMC_XM2DQSPADCTRL2);
> +     if (timing->emc_xm2dqspadctrl2 & EMC_XM2DQSPADCTRL2_VREF_ENABLE &&
> +         !(val & EMC_XM2DQSPADCTRL2_VREF_ENABLE)) {
> +             val |= EMC_XM2DQSPADCTRL2_VREF_ENABLE;
> +             update = true;
> +     }
> +
> +     if (timing->emc_xm2dqspadctrl2 & EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE &&
> +         !(val & EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE)) {
> +             val |= EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE;
> +             update = true;
> +     }
> +
> +     if (update) {
> +             writel(val, tegra->emc_regs + EMC_XM2DQSPADCTRL2);
> +             if (pre_wait < 30)
> +                     pre_wait = 30;
> +     }
> +
> +     /* Wait to settle */
> +
> +     if (pre_wait) {
> +             emc_seq_update_timing(tegra);
> +             udelay(pre_wait);
> +     }
> +
> +     /* Program CTT_TERM control */
> +
> +     if (tegra->last_timing.emc_ctt_term_ctrl != timing->emc_ctt_term_ctrl) {

There are a whole lot of very long lines caused by tegra->last_timing.
How about introducing a temporary variable:

        struct emc_timing *last = &tegra->last_timing;

or simply:

        struct emc_timing *last = tegra->last_timing;

after a change I suggested earlier.

> +             emc_seq_disable_auto_cal(tegra);
> +             writel(timing->emc_ctt_term_ctrl,
> +                     tegra->emc_regs + EMC_CTT_TERM_CTRL);
> +             emc_seq_update_timing(tegra);
> +     }
> +
> +     /* Program burst shadow registers */
> +
> +     for (i = 0; i < ARRAY_SIZE(timing->emc_burst_data); ++i)
> +             __raw_writel(timing->emc_burst_data[i],
> +                          tegra->emc_regs + t124_emc_burst_regs[i]);
> +
> +     tegra_mc_write_emem_configuration(tegra->mc, timing->rate);
> +
> +     val = timing->emc_cfg & ~EMC_CFG_POWER_FEATURES_MASK;
> +     emc_ccfifo_writel(tegra, val, EMC_CFG);
> +
> +     /* Program AUTO_CAL_CONFIG */
> +
> +     if (timing->emc_auto_cal_config2 !=
> +         tegra->last_timing.emc_auto_cal_config2)
> +             emc_ccfifo_writel(tegra, timing->emc_auto_cal_config2,
> +                               EMC_AUTO_CAL_CONFIG2);
> +     if (timing->emc_auto_cal_config3 !=
> +         tegra->last_timing.emc_auto_cal_config3)
> +             emc_ccfifo_writel(tegra, timing->emc_auto_cal_config3,
> +                               EMC_AUTO_CAL_CONFIG3);
> +     if (timing->emc_auto_cal_config !=
> +         tegra->last_timing.emc_auto_cal_config) {
> +             val = timing->emc_auto_cal_config;
> +             val &= EMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
> +             emc_ccfifo_writel(tegra, val, EMC_AUTO_CAL_CONFIG);
> +     }
> +
> +     /* DDR3: predict MRS long wait count */
> +
> +     if (tegra->dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_ON) {
> +             u32 cnt = 32;
> +
> +             if (timing->emc_zcal_interval != 0 &&
> +                 tegra->last_timing.emc_zcal_interval == 0)
> +                     cnt -= tegra->dram_num * 256;

Hmm... I don't understand this: cnt == 32 at the beginning, now you're
subtracting something that's >= 256 if dram_num > 0. Is that really
intended?

> +
> +             val = timing->emc_mrs_wait_cnt
> +                     & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK
> +                     >> EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;

I think >> has higher precedence than &, so you probably want
parentheses here.

> +             if (cnt < val)
> +                     cnt = val;
> +
> +             val = timing->emc_mrs_wait_cnt
> +                     & ~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
> +             val |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
> +                     & EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
> +
> +             writel(val, tegra->emc_regs + EMC_MRS_WAIT_CNT);
> +     }
> +
> +     val = timing->emc_cfg_2;
> +     val &= ~EMC_CFG_2_DIS_STP_OB_CLK_DURING_NON_WR;
> +     emc_ccfifo_writel(tegra, val, EMC_CFG_2);
> +
> +     /* DDR3: Turn off DLL and enter self-refresh */
> +
> +     if (tegra->dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_OFF)
> +             emc_ccfifo_writel(tegra, timing->emc_mode_1, EMC_EMRS);
> +
> +     /* Disable refresh controller */
> +
> +     emc_ccfifo_writel(tegra, EMC_REFCTRL_DEV_SEL(tegra->dram_num),
> +                       EMC_REFCTRL);
> +     if (tegra->dram_type == DRAM_TYPE_DDR3)
> +             emc_ccfifo_writel(tegra,
> +                               EMC_DRAM_DEV_SEL(tegra->dram_num)
> +                                     | EMC_SELF_REF_CMD_ENABLED,
> +                               EMC_SELF_REF);
> +
> +     /* Flow control marker */
> +
> +     emc_ccfifo_writel(tegra, 1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE);
> +
> +     /* DDR3: Exit self-refresh */
> +
> +     if (tegra->dram_type == DRAM_TYPE_DDR3)
> +             emc_ccfifo_writel(tegra,
> +                               EMC_DRAM_DEV_SEL(tegra->dram_num),
> +                               EMC_SELF_REF);
> +     emc_ccfifo_writel(tegra,
> +                       EMC_REFCTRL_DEV_SEL(tegra->dram_num)
> +                             | EMC_REFCTRL_ENABLE,
> +                       EMC_REFCTRL);
> +
> +     /* Set DRAM mode registers */
> +
> +     if (tegra->dram_type == DRAM_TYPE_DDR3) {
> +             if (timing->emc_mode_1 != tegra->last_timing.emc_mode_1)
> +                     emc_ccfifo_writel(tegra, timing->emc_mode_1, EMC_EMRS);
> +             if (timing->emc_mode_2 != tegra->last_timing.emc_mode_2)
> +                     emc_ccfifo_writel(tegra, timing->emc_mode_2, EMC_EMRS2);

These patterns repeat a lot, perhaps some helpers would be useful.
Something like:

#define emc_ccfifo_writel_if_changed(emc, old, new, field, offset)      \
        if ((new)->field != (old)->field)                               \
                emc_ccfifo_writel(emc, (new)->field, offset);

...

                        emc_ccfifo_writel_if_changed(tegra, last, timing,
                                                     emc_mode_1, EMC_EMRS);

That's not as much of an improvement as I had hoped... so feel free to
ignore.

> +static int load_timings_from_dt(struct tegra_emc *tegra,
> +                             struct device_node *node)
> +{
> +     struct device_node *child;
> +     int child_count = of_get_child_count(node);
> +     int i = 0, err;

unsigned int for i.

> +
> +     tegra->timings = devm_kzalloc(&tegra->pdev->dev,
> +                                   sizeof(struct emc_timing) * child_count,
> +                                   GFP_KERNEL);

devm_kcalloc()/

> +     if (!tegra->timings)
> +             return -ENOMEM;
> +
> +     tegra->num_timings = child_count;
> +
> +     for_each_child_of_node(node, child) {
> +             struct emc_timing *timing = tegra->timings + (i++);

timing = &tegra->timings[i++];?

> +
> +             err = load_one_timing_from_dt(tegra, timing, child);
> +             if (err)
> +                     return err;
> +     }
> +
> +     sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing),
> +          cmp_timings, NULL);

sizeof(*timing)?

> +
> +     return 0;
> +}
> +
> +static const struct of_device_id tegra_emc_of_match[] = {
> +     { .compatible = "nvidia,tegra124-emc" },
> +     {}
> +};
> +MODULE_DEVICE_TABLE(of, tegra_emc_of_match);
> +
> +static struct tegra_emc *emc_instance;

Can we find a way around this global variable? I suppose that somebody
is going to call the tegra_emc_{prepare,complete}_timing_change() below,
so perhaps the caller needs to get a reference to the EMC and pass that
in?

> +static struct emc_timing *tegra_emc_find_timing(unsigned long rate)
> +{
> +     int i;

unsigned

> +
> +     if (!emc_instance)
> +             return NULL;
> +
> +     for (i = 0; i < emc_instance->num_timings; ++i) {
> +             if (emc_instance->timings[i].rate == rate)
> +                     break;
> +     }
> +
> +     if (i == emc_instance->num_timings) {
> +             dev_err(&emc_instance->pdev->dev, "no timing for rate %lu\n", 
> rate);
> +             return NULL;
> +     }
> +
> +     return emc_instance->timings + i;

This could be rewritten in a similar way than I suggested for the MC
changes earlier.

> +}
> +
> +int tegra_emc_prepare_timing_change(unsigned long rate)
> +{
> +     struct emc_timing *timing = tegra_emc_find_timing(rate);
> +
> +     if (!timing)
> +             return -ENOENT;
> +
> +     emc_prepare_timing_change(emc_instance, timing);
> +
> +     return 0;
> +}

It seems like this really ought to return an error if
emc_prepare_timing_change() fails.

> +
> +void tegra_emc_complete_timing_change(unsigned long rate)
> +{
> +     struct emc_timing *timing = tegra_emc_find_timing(rate);
> +
> +     if (!timing)
> +             return;
> +
> +     emc_complete_timing_change(emc_instance, timing);
> +}
> +
> +static int tegra_emc_probe(struct platform_device *pdev)
> +{
> +     struct tegra_emc *tegra;
> +     struct device_node *node;
> +     struct platform_device *mc_pdev;

Perhaps simply "mc"?

> +     struct resource *res;
> +     u32 ram_code, node_ram_code;

You only need node_ram_code in the loop, so perhaps move it there and
make the name something less cumbersome like "value"?

> +     int err;
> +
> +     tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
> +     if (!tegra)
> +             return -ENOMEM;
> +
> +     tegra->pdev = pdev;
> +
> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     tegra->emc_regs = devm_ioremap_resource(&pdev->dev, res);
> +     if (IS_ERR(tegra->emc_regs)) {
> +             dev_err(&pdev->dev, "failed to map EMC regs\n");

No need for this error message, devm_ioremap_resource() prints one of
you.

> +             return PTR_ERR(tegra->emc_regs);
> +     }
> +
> +     node = of_parse_phandle(pdev->dev.of_node,
> +                             "nvidia,memory-controller", 0);
> +     if (!node) {
> +             dev_err(&pdev->dev, "could not get memory controller\n");
> +             return -ENOENT;
> +     }
> +
> +     mc_pdev = of_find_device_by_node(node);
> +     if (!mc_pdev)
> +             return -ENOENT;
> +
> +     tegra->mc = platform_get_drvdata(mc_pdev);
> +     if (!tegra->mc)
> +             return -ENOENT;

Perhaps this ought to be -EPROBE_DEFER to handle that case?

> +
> +     of_node_put(node);

I think that should go right after the call to of_find_device_by_node(),
otherwise it'll leak in case of errors earlier.

> +
> +     ram_code = tegra_read_ram_code();
> +
> +     tegra->num_timings = 0;
> +
> +     for_each_child_of_node(pdev->dev.of_node, node) {
> +             err = of_property_read_u32(node, "nvidia,ram-code", 
> &node_ram_code);
> +             if (err || (node_ram_code != ram_code))
> +                     continue;
> +
> +             err = load_timings_from_dt(tegra, node);
> +             if (err)
> +                     return err;
> +             break;
> +     }
> +
> +     if (tegra->num_timings == 0)
> +             dev_warn(&pdev->dev, "no memory timings for ram code %u 
> registered\n", ram_code);

Isn't this an error? What's the use of proceeding if this happens?

This is slightly more complicated than I think it should be. Perhaps
split this into a helper function that finds a node matching the RAM
code, then:

        node = tegra_emc_find_node_by_ram_code(pdev->dev.of_node, ram_code);
        if (!node)
                return -ENOENT;

        err = load_timings_from_dt(tegra, node);
        if (err)
                return err;

Also I think you need to of_node_put() the node when you're done with
it.

> +
> +     err = emc_init(tegra);
> +     if (err) {
> +             dev_err(&pdev->dev, "initialization failed: %d\n", err);
> +             return err;
> +     }
> +
> +     platform_set_drvdata(pdev, tegra);
> +
> +     emc_instance = tegra;
> +
> +     return 0;
> +};
> +
> +static struct platform_driver tegra_emc_driver = {
> +     .probe = tegra_emc_probe,
> +     .driver = {
> +             .name = "tegra-emc",
> +             .of_match_table = tegra_emc_of_match,

Perhaps you also want to add a ".suppress_bind_attrs = true;" here to
avoid people from causing oopses by unbinding via sysfs.

Thierry

Attachment: pgpMs_oBXDRJn.pgp
Description: PGP signature

Reply via email to