[PATCH v4 4/4] memory: emif: add device tree support to emif driver

Shilimkar, Santosh santosh.shilimkar at ti.com
Sat Jun 30 00:08:41 EDT 2012


(+ Greg, By mistake the your name got dropped cc from list)

On Fri, Jun 29, 2012 at 7:13 PM, Santosh Shilimkar
<santosh.shilimkar at ti.com> wrote:
> From: Aneesh V <aneesh at ti.com>
>
> Device tree support for the EMIF driver.
>
> Reviewed-by: Benoit Cousson <b-cousson at ti.com>
> Reviewed-by: Grant Likely <grant.likely at secretlab.ca>
> Tested-by: Lokesh Vutla <lokeshvutla at ti.com>
> Signed-off-by: Aneesh V <aneesh at ti.com>
> [santosh.shilimkar at ti.com: Rebased against 3.5-rc]
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar at ti.com>
> ---
>  drivers/memory/emif.c |  291 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 290 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
> index 33a4396..101997b 100644
> --- a/drivers/memory/emif.c
> +++ b/drivers/memory/emif.c
> @@ -18,6 +18,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/interrupt.h>
>  #include <linux/slab.h>
> +#include <linux/of.h>
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
>  #include <linux/module.h>
> @@ -49,6 +50,7 @@
>  *                             frequency in effect at the moment)
>  * @plat_data:                 Pointer to saved platform data.
>  * @debugfs_root:              dentry to the root folder for EMIF in debugfs
> + * @np_ddr:                    Pointer to ddr device tree node
>  */
>  struct emif_data {
>        u8                              duplicate;
> @@ -63,6 +65,7 @@ struct emif_data {
>        struct emif_regs                *curr_regs;
>        struct emif_platform_data       *plat_data;
>        struct dentry                   *debugfs_root;
> +       struct device_node              *np_ddr;
>  };
>
>  static struct emif_data *emif1;
> @@ -1148,6 +1151,278 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
>        return valid;
>  }
>
> +#if defined(CONFIG_OF)
> +static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
> +               struct emif_data *emif)
> +{
> +       struct emif_custom_configs      *cust_cfgs = NULL;
> +       int                             len;
> +       const int                       *lpmode, *poll_intvl;
> +
> +       lpmode = of_get_property(np_emif, "low-power-mode", &len);
> +       poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
> +
> +       if (lpmode || poll_intvl)
> +               cust_cfgs = devm_kzalloc(emif->dev, sizeof(*cust_cfgs),
> +                       GFP_KERNEL);
> +
> +       if (!cust_cfgs)
> +               return;
> +
> +       if (lpmode) {
> +               cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
> +               cust_cfgs->lpmode = *lpmode;
> +               of_property_read_u32(np_emif,
> +                               "low-power-mode-timeout-performance",
> +                               &cust_cfgs->lpmode_timeout_performance);
> +               of_property_read_u32(np_emif,
> +                               "low-power-mode-timeout-power",
> +                               &cust_cfgs->lpmode_timeout_power);
> +               of_property_read_u32(np_emif,
> +                               "low-power-mode-freq-threshold",
> +                               &cust_cfgs->lpmode_freq_threshold);
> +       }
> +
> +       if (poll_intvl) {
> +               cust_cfgs->mask |=
> +                               EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
> +               cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl;
> +       }
> +
> +       if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
> +               devm_kfree(emif->dev, cust_cfgs);
> +               return;
> +       }
> +
> +       emif->plat_data->custom_configs = cust_cfgs;
> +}
> +
> +static void __init_or_module of_get_min_tck(struct device_node *np,
> +               struct emif_data *emif)
> +{
> +       int                     ret = 0;
> +       struct lpddr2_min_tck   *min;
> +
> +       min = devm_kzalloc(emif->dev, sizeof(*min), GFP_KERNEL);
> +       if (!min)
> +               goto default_min_tck;
> +
> +       ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
> +       ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
> +       ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
> +       ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
> +       ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
> +       ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
> +       ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
> +       ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
> +       ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
> +       ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
> +       ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
> +
> +       if (ret) {
> +               devm_kfree(emif->dev, min);
> +               goto default_min_tck;
> +       }
> +
> +       emif->plat_data->min_tck = min;
> +       return;
> +
> +default_min_tck:
> +       dev_warn(emif->dev, "%s: using default min-tck values\n", __func__);
> +       emif->plat_data->min_tck = &lpddr2_jedec_min_tck;
> +}
> +
> +static int __init_or_module of_do_get_timings(struct device_node *np,
> +               struct lpddr2_timings *tim)
> +{
> +       int ret;
> +
> +       ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
> +       ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
> +       ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
> +       ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
> +       ret |= of_property_read_u32(np, "tWR", &tim->tWR);
> +       ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
> +       ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
> +       ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
> +       ret |= of_property_read_u32(np, "tXP", &tim->tXP);
> +       ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
> +       ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
> +       ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
> +       ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
> +       ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
> +       ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
> +       ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
> +       ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
> +       ret |= of_property_read_u32(np, "tDQSCK-max-derated",
> +               &tim->tDQSCK_max_derated);
> +
> +       return ret;
> +}
> +
> +static void __init_or_module of_get_ddr_timings(struct device_node *np_ddr,
> +               struct emif_data *emif)
> +{
> +       struct lpddr2_timings   *timings = NULL;
> +       u32                     arr_sz = 0, i = 0;
> +       struct device_node      *np_tim;
> +       char                    *tim_compat;
> +
> +       switch (emif->plat_data->device_info->type) {
> +       case DDR_TYPE_LPDDR2_S2:
> +       case DDR_TYPE_LPDDR2_S4:
> +               tim_compat = "jedec,lpddr2-timings";
> +               break;
> +       default:
> +               dev_warn(emif->dev, "%s: un-supported memory type\n", __func__);
> +       }
> +
> +       for_each_child_of_node(np_ddr, np_tim)
> +               if (of_device_is_compatible(np_tim, tim_compat))
> +                       arr_sz++;
> +
> +       if (arr_sz)
> +               timings = devm_kzalloc(emif->dev, sizeof(*timings) * arr_sz,
> +                       GFP_KERNEL);
> +
> +       if (!timings)
> +               goto default_timings;
> +
> +       for_each_child_of_node(np_ddr, np_tim) {
> +               if (of_device_is_compatible(np_tim, tim_compat)) {
> +                       if (of_do_get_timings(np_tim, &timings[i])) {
> +                               devm_kfree(emif->dev, timings);
> +                               goto default_timings;
> +                       }
> +                       i++;
> +               }
> +       }
> +
> +       emif->plat_data->timings = timings;
> +       emif->plat_data->timings_arr_size = arr_sz;
> +
> +       return;
> +
> +default_timings:
> +       get_default_timings(emif);
> +
> +       return;
> +}
> +
> +static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
> +               struct device_node *np_ddr,
> +               struct ddr_device_info *dev_info)
> +{
> +       u32 density = 0, io_width = 0;
> +       int len;
> +
> +       if (of_find_property(np_emif, "cs1-used", &len))
> +               dev_info->cs1_used = true;
> +
> +       if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
> +               dev_info->cal_resistors_per_cs = true;
> +
> +       if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4"))
> +               dev_info->type = DDR_TYPE_LPDDR2_S4;
> +       else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2"))
> +               dev_info->type = DDR_TYPE_LPDDR2_S2;
> +
> +       of_property_read_u32(np_ddr, "density", &density);
> +       of_property_read_u32(np_ddr, "io-width", &io_width);
> +
> +       /* Convert from density in Mb to the density encoding in jedc_ddr.h */
> +       if (density & (density - 1))
> +               dev_info->density = 0;
> +       else
> +               dev_info->density = __fls(density) - 5;
> +
> +       /* Convert from io_width in bits to io_width encoding in jedc_ddr.h */
> +       if (io_width & (io_width - 1))
> +               dev_info->io_width = 0;
> +       else
> +               dev_info->io_width = __fls(io_width) - 1;
> +}
> +
> +static struct emif_data * __init_or_module of_get_device_details(
> +               struct device_node *np_emif, struct device *dev)
> +{
> +       struct emif_data                *emif = NULL;
> +       struct ddr_device_info          *dev_info = NULL;
> +       struct emif_platform_data       *pd = NULL;
> +       struct device_node              *np_ddr;
> +       int                             len;
> +
> +       np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
> +       if (!np_ddr)
> +               goto error;
> +       emif    = devm_kzalloc(dev, sizeof(struct emif_data), GFP_KERNEL);
> +       pd      = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
> +       dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
> +
> +       if (!emif || !pd || !dev_info) {
> +               dev_err(dev, "%s: of_get_device_details() failure!!\n",
> +                       __func__);
> +               goto error;
> +       }
> +
> +       emif->plat_data         = pd;
> +       pd->device_info         = dev_info;
> +       emif->dev               = dev;
> +       emif->np_ddr            = np_ddr;
> +       emif->temperature_level = SDRAM_TEMP_NOMINAL;
> +
> +       if (of_device_is_compatible(np_emif, "ti,emif-4d"))
> +               emif->plat_data->ip_rev = EMIF_4D;
> +       else if (of_device_is_compatible(np_emif, "ti,emif-4d5"))
> +               emif->plat_data->ip_rev = EMIF_4D5;
> +
> +       of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
> +
> +       if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
> +               pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
> +
> +       of_get_ddr_info(np_emif, np_ddr, dev_info);
> +       if (!is_dev_data_valid(pd->device_info->type, pd->device_info->density,
> +                       pd->device_info->io_width, pd->phy_type, pd->ip_rev,
> +                       emif->dev)) {
> +               dev_err(dev, "%s: invalid device data!!\n", __func__);
> +               goto error;
> +       }
> +       /*
> +        * For EMIF instances other than EMIF1 see if the devices connected
> +        * are exactly same as on EMIF1(which is typically the case). If so,
> +        * mark it as a duplicate of EMIF1. This will save some memory and
> +        * computation.
> +        */
> +       if (emif1 && emif1->np_ddr == np_ddr) {
> +               emif->duplicate = true;
> +               goto out;
> +       } else if (emif1) {
> +               dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
> +                       __func__);
> +       }
> +
> +
> +       of_get_custom_configs(np_emif, emif);
> +       of_get_ddr_timings(np_ddr, emif);
> +       of_get_min_tck(np_ddr, emif);
> +       goto out;
> +
> +error:
> +       return NULL;
> +out:
> +       return emif;
> +}
> +
> +#else
> +
> +static struct emif_data * __init_or_module of_get_device_details(
> +               struct device_node *np_emif, struct device *dev)
> +{
> +       return NULL;
> +}
> +#endif
> +
>  static struct emif_data *__init_or_module get_device_details(
>                struct platform_device *pdev)
>  {
> @@ -1267,7 +1542,11 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
>        struct resource         *res;
>        int                     irq;
>
> -       emif = get_device_details(pdev);
> +       if (pdev->dev.of_node)
> +               emif = of_get_device_details(pdev->dev.of_node, &pdev->dev);
> +       else
> +               emif = get_device_details(pdev);
> +
>        if (!emif) {
>                pr_err("%s: error getting device data\n", __func__);
>                goto error;
> @@ -1644,11 +1923,21 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
>        spin_unlock_irqrestore(&emif_lock, irq_state);
>  }
>
> +#if defined(CONFIG_OF)
> +static const struct of_device_id emif_of_match[] = {
> +               { .compatible = "ti,emif-4d" },
> +               { .compatible = "ti,emif-4d5" },
> +               {},
> +};
> +MODULE_DEVICE_TABLE(of, emif_of_match);
> +#endif
> +
>  static struct platform_driver emif_driver = {
>        .remove         = __exit_p(emif_remove),
>        .shutdown       = emif_shutdown,
>        .driver = {
>                .name = "emif",
> +               .of_match_table = of_match_ptr(emif_of_match),
>        },
>  };
>
> --
> 1.7.9.5
>



More information about the linux-arm-kernel mailing list