[RFC 6/6] bus: Add support for Tegra NOR controller

Mirza Krak mirza.krak at gmail.com
Mon Jul 25 05:17:11 PDT 2016


2016-07-25 13:14 GMT+02:00 Thierry Reding <thierry.reding at gmail.com>:
> On Tue, Jul 19, 2016 at 03:36:37PM +0200, Mirza Krak wrote:
>> +config TEGRA_NOR
>> +     tristate "Nvidia Tegra NOR flash bus driver a.k.a GMI/SNOR"
>> +             depends on ARCH_TEGRA_2x_SOC || ARCH_TEGRA_3x_SOC
>
> I think an ARCH_TEGRA dependency is enough here.

ACK.

>
>> +             depends on OF
>
> You can drop this because Tegra has been OF-only for a long time now.

ACK.

>
>> +             help
>> +                     Driver for NOR flash bus found on Tegra30/20 SOC`s.
>
> Maybe make this "Driver for GMI controller found on NVIDIA Tegra SoCs."
> or similar in light of the name change.

ACK.

>> --- /dev/null
>> +++ b/drivers/bus/tegra-nor.c
>> @@ -0,0 +1,118 @@
>> +/*
>> + * Driver for Nvidia NOR Flash bus a.k.a SNOR/GMI.
>
> Nit: I encourage the use of "NVIDIA" as spelling for consistency.

ACK.


>> +static int __init nor_parse_dt(struct platform_device *pdev,
>> +                             void __iomem *base)
>> +{
>> +     struct device_node *of_node = pdev->dev.of_node;
>> +     u32 config, timing[TEGRA_NOR_TIMING_REG_COUNT];
>> +     int ret;
>> +
>> +     ret = of_property_read_u32_array(of_node, "nvidia,cs-timing",
>> +                                      timing, TEGRA_NOR_TIMING_REG_COUNT);
>> +     if (!ret) {
>> +             writel(timing[0], base + TEGRA_NOR_TIMING0);
>> +             writel(timing[1], base + TEGRA_NOR_TIMING1);
>> +     }
>> +
>> +     ret = of_property_read_u32(of_node, "nvidia,config", &config);
>> +     if (ret)
>> +             return ret;
>> +
>> +     config |= TEGRA_NOR_CONFIG_GO;
>> +     writel(config, base + TEGRA_NOR_CONFIG);
>
> My preference would be for the tegra_gmi_parse_dt() function not to do
> any register programming. Instead I think it would be better to store
> any of the parameters inside a struct tegra_gmi and do the programming
> from within tegra_gmi_probe() (or via an other function such as
> tegra_gmi_initialize(), called from tegra_gmi_probe()).
>
> The reason is that you'll most likely want to do this programming when
> you resume from suspend, and you could reuse tegra_gmi_initialize() to
> do that.

ACK.

>
>> +
>> +     if (of_get_child_count(of_node))
>> +             ret = of_platform_populate(of_node,
>> +                                of_default_bus_match_table,
>> +                                NULL, &pdev->dev);
>
> Why the extra check? of_platform_populate() is almost a no-op if there
> aren't any children anyway. What it will do is set the OF_POPULATED_BUS
> flag, but I think we want that irrespective of whether or not there are
> any children.
>
> Was there any problem with calling it unconditionally that made you opt
> for the extra check?

I have not tested calling it unconditionally. Used another driver as
reference and they had that condition (drivers/bus/imx-weim.c), that
driver does not mention why the condition is there. But will test
removing the condition and see what happens.

>
> Also, I think you can call of_platform_default_populate(), which is a
> little shorter than the above because you can omit the match table.

Will look in to this.

>
>> +
>> +     return ret;
>> +}
>> +
>> +static int __init nor_probe(struct platform_device *pdev)
>> +{
>> +     struct resource *res;
>> +     struct clk *clk;
>> +     void __iomem *base;
>> +     int ret;
>> +
>> +     /* get the resource */
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     base = devm_ioremap_resource(&pdev->dev, res);
>> +     if (IS_ERR(base))
>> +             return PTR_ERR(base);
>> +
>> +     /* get the clock */
>> +     clk = devm_clk_get(&pdev->dev, NULL);
>
> Let's use a consumer name here. The problem with a NULL consumer name is
> that it effectively restricts the DT binding. It means that whatever the
> clock is its name needs to be the first in a clock-names property. We'll
> likely get away with this because there's only one clock, but should we
> ever need to add another we'd need to add wording to the device tree
> bindings that the "gmi" entry needs to be first.
>
> I'm not sure if that's clear, so I'll try to explain in other words: If
> you pass a NULL consumer name to clk_get() it will simply use the first
> clock in the clocks property. If you want to later extend the DT binding
> by adding a clock in a backwards-compatible way, you'll need to add the
> restriction that the "gmi" clock (the one that was previously unnamed)
> must be the first in the clock-names and clocks properties.
>
> That's all a little confusing, so let's avoid this by giving it a proper
> consumer name to begin with.

Got it. Will add a proper consumer name.

>
>> +     if (IS_ERR(clk))
>> +             return PTR_ERR(clk);
>> +
>> +     ret = clk_prepare_enable(clk);
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* parse the device node */
>> +     ret = nor_parse_dt(pdev, base);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "%s fail to create devices.\n",
>> +                     pdev->dev.of_node->full_name);
>> +             clk_disable_unprepare(clk);
>> +             return ret;
>> +     }
>
> The good thing if you don't do any programming in tegra_gmi_parse_dt(),
> is that you can easily move the clk_prepare_enable() call to here where
> things can't fail anymore, so you don't have to add cleanup code.

ACK.

>
>> +
>> +     dev_info(&pdev->dev, "Driver registered.\n");
>
> Please avoid this kind of output. Users expect that everything will work
> so you want to let them know when something goes wrong, and be quiet
> when all goes as expected.

Will remove that.

>> +static struct platform_driver nor_driver = {
>> +     .driver = {
>> +             .name           = "tegra-nor",
>> +             .of_match_table = nor_id_table,
>> +     },
>> +};
>> +module_platform_driver_probe(nor_driver, nor_probe);
>> +
>> +MODULE_AUTHOR("Mirza Krak <mirza.krak at gmail.com");
>> +MODULE_DESCRIPTION("NVIDIA Tegra NOR Bus Driver");
>> +MODULE_LICENSE("GPL");
>
> You're header comment says GPL version 2, which means that the
> MODULE_LICENSE needs to be "GPL v2". "GPL" means "version 2 or later".

Ok, I do not really mind it being GPL version 2 or later so will
change the header comment instead if that is ok.


Best regards,
Mirza



More information about the linux-arm-kernel mailing list