From c791c8431c34da803fdac4a308999879a6a33834 Mon Sep 17 00:00:00 2001 From: Patrick Delaunay Date: Fri, 29 Mar 2019 15:42:15 +0100 Subject: [PATCH] usb: dwc2: convert driver to DM_USB_GADGET Minimal conversion to driver model by using the uclass UCLASS_USB_GADGET_GENERIC based on: - reset uclass - clock uclass - generic uclass. Signed-off-by: Patrick Delaunay Reviewed-by: Lukasz Majewski --- doc/device-tree-bindings/usb/dwc2.txt | 54 +++++ drivers/usb/gadget/dwc2_udc_otg.c | 292 +++++++++++++++++++++++++- 2 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 doc/device-tree-bindings/usb/dwc2.txt diff --git a/doc/device-tree-bindings/usb/dwc2.txt b/doc/device-tree-bindings/usb/dwc2.txt new file mode 100644 index 0000000000..6dc3c4a344 --- /dev/null +++ b/doc/device-tree-bindings/usb/dwc2.txt @@ -0,0 +1,54 @@ +Platform DesignWare HS OTG USB 2.0 controller +----------------------------------------------------- + +Required properties: +- compatible : One of: + - brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC. + - hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC. + - rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc; + - "rockchip,px30-usb", "rockchip,rk3066-usb", "snps,dwc2": for px30 Soc; + - "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc; + - "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc; + - "lantiq,arx100-usb": The DWC2 USB controller instance in Lantiq ARX SoCs; + - "lantiq,xrx200-usb": The DWC2 USB controller instance in Lantiq XRX SoCs; + - "amlogic,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs; + - "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b SoCs; + - "amlogic,meson-gxbb-usb": The DWC2 USB controller instance in Amlogic S905 SoCs; + - "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs; + - snps,dwc2: A generic DWC2 USB controller with default parameters. + - "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs + configured in FS mode; + - "st,stm32f4x9-hsotg": The DWC2 USB HS controller instance in STM32F4x9 SoCs + configured in HS mode; + - "st,stm32f7-hsotg": The DWC2 USB HS controller instance in STM32F7 SoCs + configured in HS mode; +- reg : Should contain 1 register range (address and length) +- interrupts : Should contain 1 interrupt +- clocks: clock provider specifier +- clock-names: shall be "otg" +Refer to clk/clock-bindings.txt for generic clock consumer properties + +Optional properties: +- phys: phy provider specifier +- phy-names: shall be "usb2-phy" +Refer to phy/phy-bindings.txt for generic phy consumer properties +- dr_mode: shall be one of "host", "peripheral" and "otg" + Refer to usb/generic.txt +- g-rx-fifo-size: size of rx fifo size in gadget mode. +- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. +- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode. + +Deprecated properties: +- g-use-dma: gadget DMA mode is automatically detected + +Example: + + usb@101c0000 { + compatible = "ralink,rt3050-usb, snps,dwc2"; + reg = <0x101c0000 40000>; + interrupts = <18>; + clocks = <&usb_otg_ahb_clk>; + clock-names = "otg"; + phys = <&usbphy>; + phy-names = "usb2-phy"; + }; diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index edca05d009..af16fc1ed4 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -18,11 +18,17 @@ */ #undef DEBUG #include +#include +#include +#include +#include +#include + #include #include -#include #include +#include #include #include @@ -31,6 +37,8 @@ #include +#include + #include "dwc2_udc_otg_regs.h" #include "dwc2_udc_otg_priv.h" @@ -222,6 +230,7 @@ static int udc_enable(struct dwc2_udc *dev) return 0; } +#if !CONFIG_IS_ENABLED(DM_USB_GADGET) /* Register entry point for the peripheral controller driver. */ @@ -296,6 +305,54 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) udc_disable(dev); return 0; } +#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ + +static int dwc2_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc2_udc *dev = the_controller; + + debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name"); + + if (!driver || + (driver->speed != USB_SPEED_FULL && + driver->speed != USB_SPEED_HIGH) || + !driver->bind || !driver->disconnect || !driver->setup) + return -EINVAL; + + if (!dev) + return -ENODEV; + + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + + debug_cond(DEBUG_SETUP != 0, + "Registered gadget driver %s\n", dev->gadget.name); + return udc_enable(dev); +} + +static int dwc2_gadget_stop(struct usb_gadget *g) +{ + struct dwc2_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + + if (!dev->driver) + return -EINVAL; + + dev->driver = 0; + stop_activity(dev, dev->driver); + + udc_disable(dev); + + return 0; +} + +#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ /* * done - retire a request; caller blocked irqs @@ -730,6 +787,10 @@ static void dwc2_fifo_flush(struct usb_ep *_ep) static const struct usb_gadget_ops dwc2_udc_ops = { /* current versions must always be self-powered */ +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + .udc_start = dwc2_gadget_start, + .udc_stop = dwc2_gadget_stop, +#endif }; static struct dwc2_udc memory = { @@ -841,12 +902,239 @@ int dwc2_udc_probe(struct dwc2_plat_otg_data *pdata) return retval; } -int usb_gadget_handle_interrupts(int index) +int dwc2_udc_handle_interrupt(void) { u32 intr_status = readl(®->gintsts); u32 gintmsk = readl(®->gintmsk); if (intr_status & gintmsk) return dwc2_udc_irq(1, (void *)the_controller); + + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_USB_GADGET) + +int usb_gadget_handle_interrupts(int index) +{ + return dwc2_udc_handle_interrupt(); +} + +#else /* CONFIG_IS_ENABLED(DM_USB_GADGET) */ + +struct dwc2_priv_data { + struct clk_bulk clks; + struct reset_ctl_bulk resets; + struct phy *phys; + int num_phys; +}; + +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + return dwc2_udc_handle_interrupt(); +} + +int dwc2_phy_setup(struct udevice *dev, struct phy **array, int *num_phys) +{ + int i, ret, count; + struct phy *usb_phys; + + /* Return if no phy declared */ + if (!dev_read_prop(dev, "phys", NULL)) + return 0; + + count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (count <= 0) + return count; + + usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), + GFP_KERNEL); + if (!usb_phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); + if (ret && ret != -ENOENT) { + dev_err(dev, "Failed to get USB PHY%d for %s\n", + i, dev->name); + return ret; + } + } + + for (i = 0; i < count; i++) { + ret = generic_phy_init(&usb_phys[i]); + if (ret) { + dev_err(dev, "Can't init USB PHY%d for %s\n", + i, dev->name); + goto phys_init_err; + } + } + + for (i = 0; i < count; i++) { + ret = generic_phy_power_on(&usb_phys[i]); + if (ret) { + dev_err(dev, "Can't power USB PHY%d for %s\n", + i, dev->name); + goto phys_poweron_err; + } + } + + *array = usb_phys; + *num_phys = count; + return 0; + +phys_poweron_err: + for (i = count - 1; i >= 0; i--) + generic_phy_power_off(&usb_phys[i]); + + for (i = 0; i < count; i++) + generic_phy_exit(&usb_phys[i]); + + return ret; + +phys_init_err: + for (; i >= 0; i--) + generic_phy_exit(&usb_phys[i]); + + return ret; } + +void dwc2_phy_shutdown(struct udevice *dev, struct phy *usb_phys, int num_phys) +{ + int i, ret; + + for (i = 0; i < num_phys; i++) { + if (!generic_phy_valid(&usb_phys[i])) + continue; + + ret = generic_phy_power_off(&usb_phys[i]); + ret |= generic_phy_exit(&usb_phys[i]); + if (ret) { + dev_err(dev, "Can't shutdown USB PHY%d for %s\n", + i, dev->name); + } + } +} + +static int dwc2_udc_otg_ofdata_to_platdata(struct udevice *dev) +{ + struct dwc2_plat_otg_data *platdata = dev_get_platdata(dev); + int node = dev_of_offset(dev); + + if (usb_get_dr_mode(node) != USB_DR_MODE_PERIPHERAL) { + dev_dbg(dev, "Invalid mode\n"); + return -ENODEV; + } + + platdata->regs_otg = dev_read_addr(dev); + + platdata->rx_fifo_sz = dev_read_u32_default(dev, "g-rx-fifo-size", 0); + platdata->np_tx_fifo_sz = dev_read_u32_default(dev, + "g-np-tx-fifo-size", 0); + platdata->tx_fifo_sz = dev_read_u32_default(dev, "g-tx-fifo-size", 0); + + return 0; +} + +static int dwc2_udc_otg_reset_init(struct udevice *dev, + struct reset_ctl_bulk *resets) +{ + int ret; + + ret = reset_get_bulk(dev, resets); + if (ret == -ENOTSUPP) + return 0; + + if (ret) + return ret; + + ret = reset_deassert_bulk(resets); + if (ret) { + reset_release_bulk(resets); + return ret; + } + + return 0; +} + +static int dwc2_udc_otg_clk_init(struct udevice *dev, + struct clk_bulk *clks) +{ + int ret; + + ret = clk_get_bulk(dev, clks); + if (ret == -ENOSYS) + return 0; + + if (ret) + return ret; + + ret = clk_enable_bulk(clks); + if (ret) { + clk_release_bulk(clks); + return ret; + } + + return 0; +} + +static int dwc2_udc_otg_probe(struct udevice *dev) +{ + struct dwc2_plat_otg_data *platdata = dev_get_platdata(dev); + struct dwc2_priv_data *priv = dev_get_priv(dev); + int ret; + + ret = dwc2_udc_otg_clk_init(dev, &priv->clks); + if (ret) + return ret; + + ret = dwc2_udc_otg_reset_init(dev, &priv->resets); + if (ret) + return ret; + + ret = dwc2_phy_setup(dev, &priv->phys, &priv->num_phys); + if (ret) + return ret; + + ret = dwc2_udc_probe(platdata); + if (ret) + return ret; + + the_controller->driver = 0; + + ret = usb_add_gadget_udc((struct device *)dev, &the_controller->gadget); + + return ret; +} + +static int dwc2_udc_otg_remove(struct udevice *dev) +{ + struct dwc2_priv_data *priv = dev_get_priv(dev); + + usb_del_gadget_udc(&the_controller->gadget); + + reset_release_bulk(&priv->resets); + + clk_release_bulk(&priv->clks); + + dwc2_phy_shutdown(dev, priv->phys, priv->num_phys); + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc2_udc_otg_ids[] = { + { .compatible = "snps,dwc2" }, +}; + +U_BOOT_DRIVER(dwc2_udc_otg) = { + .name = "dwc2-udc-otg", + .id = UCLASS_USB_GADGET_GENERIC, + .of_match = dwc2_udc_otg_ids, + .ofdata_to_platdata = dwc2_udc_otg_ofdata_to_platdata, + .probe = dwc2_udc_otg_probe, + .remove = dwc2_udc_otg_remove, + .platdata_auto_alloc_size = sizeof(struct dwc2_plat_otg_data), + .priv_auto_alloc_size = sizeof(struct dwc2_priv_data), +}; +#endif /* CONFIG_IS_ENABLED(DM_USB_GADGET) */ -- 2.39.5