[V5 PATCH 24/26] usb: gadget: mv_udc: add device tree support

Mark Rutland mark.rutland at arm.com
Fri Jan 25 04:51:52 EST 2013


On Fri, Jan 25, 2013 at 02:13:54AM +0000, chao xie wrote:
> 2013/1/24 Mark Rutland <mark.rutland at arm.com>:
> > Hello,
> >
> > On Thu, Jan 24, 2013 at 06:38:48AM +0000, Chao Xie wrote:
> >> In original driver, we have callbacks in platform data, and some
> >> dynamically allocations variables in platform data.
> >> Now, these blocks are removed, the device tree support is easier
> >> now.
> >
> > Please could you also add a binding document? See
> > Documentation/devicetree/bindings/usb for examples of existing bindings.
> >
> > Also, please Cc devicetree-discuss when introducing a new binding as you are
> > doing here.
> >
> >>
> >> Signed-off-by: Chao Xie <chao.xie at marvell.com>
> >> ---
> >>  drivers/usb/gadget/mv_udc.h      |    5 +-
> >>  drivers/usb/gadget/mv_udc_core.c |  106 ++++++++++++++++++++++++++++++--------
> >>  2 files changed, 86 insertions(+), 25 deletions(-)
> >>
> >> diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h
> >> index 50ae7c7..de84722 100644
> >> --- a/drivers/usb/gadget/mv_udc.h
> >> +++ b/drivers/usb/gadget/mv_udc.h
> >> @@ -179,6 +179,7 @@ struct mv_udc {
> >>       int                             irq;
> >>
> >>       unsigned int                    extern_attr;
> >> +     unsigned int                    mode;
> >>       struct notifier_block           notifier;
> >>
> >>       struct mv_cap_regs __iomem      *cap_regs;
> >> @@ -222,11 +223,9 @@ struct mv_udc {
> >>       struct mv_usb2_phy      *phy;
> >>       struct usb_phy          *transceiver;
> >>
> >> -     struct mv_usb_platform_data     *pdata;
> >> -
> >>       /* some SOC has mutiple clock sources for USB*/
> >>       unsigned int    clknum;
> >> -     struct clk      *clk[0];
> >> +     struct clk      **clk;
> >>  };
> >>
> >>  /* endpoint data structure */
> >> diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
> >> index 2e5907f..a4ee9a1 100644
> >> --- a/drivers/usb/gadget/mv_udc_core.c
> >> +++ b/drivers/usb/gadget/mv_udc_core.c
> >> @@ -34,6 +34,7 @@
> >>  #include <linux/irq.h>
> >>  #include <linux/platform_device.h>
> >>  #include <linux/clk.h>
> >> +#include <linux/of.h>
> >>  #include <linux/platform_data/mv_usb.h>
> >>  #include <linux/usb/mv_usb2.h>
> >>  #include <asm/unaligned.h>
> >> @@ -2153,21 +2154,57 @@ static int mv_udc_remove(struct platform_device *pdev)
> >>       return 0;
> >>  }
> >>
> >> +static int mv_udc_parse_dt(struct platform_device *pdev,
> >> +                             struct mv_udc *udc)
> >> +{
> >> +     struct device_node *np = pdev->dev.of_node;
> >> +     unsigned int clks_num;
> >> +     int i, ret;
> >> +     const char *clk_name;
> >> +
> >> +     if (!np)
> >> +             return 1;
> >> +
> >> +     clks_num = of_property_count_strings(np, "clocks");
> >> +     if (clks_num < 0)
> >> +             return clks_num;
> >> +
> >> +     udc->clk = devm_kzalloc(&pdev->dev,
> >> +             sizeof(struct clk *) * clks_num, GFP_KERNEL);
> >> +     if (udc->clk == NULL)
> >> +             return -ENOMEM;
> >> +
> >> +     for (i = 0; i < clks_num; i++) {
> >> +             ret = of_property_read_string_index(np, "clocks", i,
> >> +                     &clk_name);
> >> +             if (ret)
> >> +                     return ret;
> >> +             udc->clk[i] = devm_clk_get(&pdev->dev, clk_name);
> >> +             if (IS_ERR(udc->clk[i]))
> >> +                     return PTR_ERR(udc->clk[i]);
> >
> > I was going to ask if you couldn't use of_clk_get, but I see you want to use
> > the devm_* functions to handle cleanup. It seems a shame there's not a standard
> > devm_of_clk_get.
> >
> It is nice to have someone to review the device tree part patches.
> In fact main target of the modification is to remove the
> pointers/callbacks in the platform_data, so
> i can easly to add device tree support.
> 
> of_clk_get is is based on CONFIG_COMMON_CLK. of_clk_get is not simple.
> The orginal way we use to
> get a clock used two arguments: dev_id and con_id. The dev_id is the
> name of the device.
> of_clk_get need clock framework changes. It means that clock driver
> need add device tree support. Now
> we are doing the clock tree support for Marvell MMP SOCes, but it will
> not be done in a short time.
> As i think, of_clk_get still have some problems. It parses the
> property's name as "clocks", but it does not support
> mutipile clocks. If the device driver original has two clocks, the old
> way can do it as
> clk_get(dev, "clock 1");
> clk_get(dev, "clock 2");
> of_clk_get can not do it. I have not asked this question in the
> mailist, and i will do it soon.

I may have misunderstood here, but as far as I can see, of_clk_get supports
multiple clocks -- it even takes an index parameter:

struct clk *of_clk_get(struct device_node *np, int index)

I can see several dts files which have a clocks property with multiple clocks.
In arch/arm/boot/dts/vexpress-v2m.dtsi there's a "arm,sp810" node with
multiple clocks, which I believe is dealt with by vexpress_clk_of_init in
drivers/clk/versatile/clk-vexpress.c (though this uses of_clk_get_by_name).

This also raises the issue that you expect the clocks property to be a list of
strings, while other bindings expect the clocks property to be list of
phandle/clock-specifier pairs.

It's worth taking a look at:
Documentation/devicetree/bindings/clock/clock-bindings.txt

Going against the common convention here is a very bad idea.

> 
> >> +     }
> >> +
> >> +     udc->clknum = clks_num;
> >> +
> >> +     ret = of_property_read_u32(np, "extern_attr", &udc->extern_attr);
> >> +     if (ret)
> >> +             return ret;
> >
> > This looks like a *very* bad idea. The udc::extern_attr field seems to be a set
> > of flags, which this could reads in directly (without any sanity checking),
> > effectively making it an undocumented ABI. This might be better as a set of
> > valueless properties.
> >
> > Will unsigned int be the same as u32 on all platforms this could be used on?
> > If not, you're passing the wrong type into of_property_read_u32.
> >
> > Additionally, in devicetree, '-' is used in preference of '_' in compatible
> > and property names.
> >
> I see. So what you mean is if the extern_attr has two flags, FLAG_A and FLAG_B,
> i need add two boolean properties Property_FLAG_A and Property_FLAG_B?

Something like that. The properties might not map directly to flags - it's
better to describe the hardware reason these flags are required rather than
what these falgs do to linux.

> 
> >> +
> >> +     ret = of_property_read_u32(np, "mode", &udc->mode);
> >> +     if (ret)
> >> +             return ret;
> >
> > If I've understood correctly, this property will either be MV_USB_MODE_OTG (0)
> > or MV_USB_MODE_OTG (1). Again, I don't think this is good to be exported as an
> > ABI, especially as nothing in the enum definition points out that this affects
> > anything outside the kernel.
> >
> > If this isn't something that wants to be changed at runtime, this should
> > probably be a string property, with "host" and "otg" as valid values. Looking
> > in Documentation/devicetree, the nvidia,tegra20-ehci binding uses similar
> > strings in its dr_mode property. There may be other (undocumented) precedents.
> >
> Thanks. I will check the examples, and change it.

:)

> 
> >> +
> >> +     return 0;
> >> +}
> >> +
> >>  static int mv_udc_probe(struct platform_device *pdev)
> >>  {
> >> -     struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
> >>       struct mv_udc *udc;
> >>       int retval = 0;
> >> -     int clk_i = 0;
> >>       struct resource *r;
> >>       size_t size;
> >>
> >> -     if (pdata == NULL) {
> >> -             dev_err(&pdev->dev, "missing platform_data\n");
> >> -             return -ENODEV;
> >> -     }
> >> -
> >> -     size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
> >> +     size = sizeof(*udc);
> >>       udc = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> >>       if (udc == NULL) {
> >>               dev_err(&pdev->dev, "failed to allocate memory for udc\n");
> >> @@ -2175,14 +2212,43 @@ static int mv_udc_probe(struct platform_device *pdev)
> >>       }
> >>
> >>       udc->done = &release_done;
> >> -     udc->extern_attr = pdata->extern_attr;
> >> -     udc->pdata = pdev->dev.platform_data;
> >>       spin_lock_init(&udc->lock);
> >>
> >>       udc->dev = pdev;
> >>
> >> +     retval = mv_udc_parse_dt(pdev, udc);
> >> +     if (retval > 0) {
> >> +             /* no CONFIG_OF */
> >> +             struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
> >> +             int clk_i = 0;
> >> +
> >> +             if (pdata == NULL) {
> >> +                     dev_err(&pdev->dev, "missing platform_data\n");
> >> +                     return -ENODEV;
> >> +             }
> >> +             udc->extern_attr = pdata->extern_attr;
> >> +             udc->mode = pdata->mode;
> >> +             udc->clknum = pdata->clknum;
> >> +
> >> +             size = sizeof(struct clk *) * udc->clknum;
> >> +             udc->clk = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> >> +             if (udc->clk == NULL)
> >> +                     return -ENOMEM;
> >> +             for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
> >> +                     udc->clk[clk_i] = devm_clk_get(&pdev->dev,
> >> +                                             pdata->clkname[clk_i]);
> >> +                     if (IS_ERR(udc->clk[clk_i])) {
> >> +                             retval = PTR_ERR(udc->clk[clk_i]);
> >> +                             return retval;
> >
> > Why not just return PTR_ERR(udc->clk[clk_i]); ?
> >
> sure. i will change it.
> 
> >> +                     }
> >> +             }
> >> +     } else if (retval < 0) {
> >> +             dev_err(&pdev->dev, "error parse dt\n");
> >> +             return retval;
> >> +     }
> >> +
> >>  #ifdef CONFIG_USB_OTG_UTILS
> >> -     if (pdata->mode == MV_USB_MODE_OTG) {
> >> +     if (udc->mode == MV_USB_MODE_OTG) {
> >>               udc->transceiver = devm_usb_get_phy(&pdev->dev,
> >>                                       USB_PHY_TYPE_USB2);
> >>               if (IS_ERR_OR_NULL(udc->transceiver)) {
> >> @@ -2191,17 +2257,6 @@ static int mv_udc_probe(struct platform_device *pdev)
> >>               }
> >>       }
> >>  #endif
> >> -
> >> -     udc->clknum = pdata->clknum;
> >> -     for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
> >> -             udc->clk[clk_i] = devm_clk_get(&pdev->dev,
> >> -                                     pdata->clkname[clk_i]);
> >> -             if (IS_ERR(udc->clk[clk_i])) {
> >> -                     retval = PTR_ERR(udc->clk[clk_i]);
> >> -                     return retval;
> >> -             }
> >> -     }
> >> -
> >>       r = platform_get_resource(udc->dev, IORESOURCE_MEM, 0);
> >>       if (r == NULL) {
> >>               dev_err(&pdev->dev, "no I/O memory resource defined\n");
> >> @@ -2466,6 +2521,12 @@ static void mv_udc_shutdown(struct platform_device *pdev)
> >>       mv_udc_disable(udc);
> >>  }
> >>
> >> +static struct of_device_id mv_udc_dt_ids[] = {
> >> +     { .compatible = "mrvl,mv-udc" },
> >
> > This compatible string should be listed in the binding document you generate to
> > accompany this.
> >
> I see. i will add the documents at the device tree directory.
> I will seperate the device tree part patches from the other patches. Thanks.

Great!

Thanks,
Mark.




More information about the linux-arm-kernel mailing list