From: Sherry Sun <sherry....@nxp.com> Convert the ci_udc driver to driver model by using the uclass UCLASS_USB_GADGET_GENERIC. The clk and power of USB controller and USB PHY both are initialized by parsing the device tree nodes.
If CONFIG_DM_USB_GADGET is defined, we use the ci_udc driver in DM way, if it does not defined, we can use ci_udc driver in its original Non-DM way. Signed-off-by: Sherry Sun <sherry....@nxp.com> Reviewed-by: Ye Li <ye...@nxp.com> [Peng: need to extract common code from host and gadget driver(TODO)] Signed-off-by: Peng Fan <peng....@nxp.com> --- drivers/usb/gadget/ci_udc.c | 305 +++++++++++++++++++++++++++++++++++- drivers/usb/host/ehci-mx6.c | 15 +- include/usb/ci_udc.h | 3 + 3 files changed, 305 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 226a9e6d67..6f2e38ffd8 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -13,16 +13,25 @@ #include <cpu_func.h> #include <net.h> #include <malloc.h> +#include <dm.h> +#include <clk.h> +#include <power-domain.h> #include <asm/byteorder.h> #include <asm/cache.h> #include <linux/delay.h> #include <linux/errno.h> #include <asm/io.h> #include <asm/unaligned.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/clock.h> +#include <asm/mach-imx/regs-usbphy.h> #include <linux/types.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <dm/pinctrl.h> #include <usb/ci_udc.h> +#include <usb/ehci-ci.h> #include "../host/ehci.h" #include "ci_udc.h" @@ -93,9 +102,18 @@ static int ci_ep_dequeue(struct usb_ep *ep, struct usb_request *req); static struct usb_request * ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags); static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req); +#if CONFIG_IS_ENABLED(DM_USB_GADGET) +static int ci_udc_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int ci_udc_gadget_stop(struct usb_gadget *g); +#endif static struct usb_gadget_ops ci_udc_ops = { .pullup = ci_pullup, +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + .udc_start = ci_udc_gadget_start, + .udc_stop = ci_udc_gadget_stop, +#endif }; static struct usb_ep_ops ci_ep_ops = { @@ -866,7 +884,7 @@ void udc_irq(void) } } -int usb_gadget_handle_interrupts(int index) +int ci_udc_handle_interrupts(void) { u32 value; struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; @@ -1010,6 +1028,19 @@ static int ci_udc_probe(void) return 0; } +bool dfu_usb_get_reset(void) +{ + struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + + return !!(readl(&udc->usbsts) & STS_URI); +} + +#if !CONFIG_IS_ENABLED(DM_USB_GADGET) +int usb_gadget_handle_interrupts(int index) +{ + return ci_udc_handle_interrupts(); +} + int usb_gadget_register_driver(struct usb_gadget_driver *driver) { int ret; @@ -1063,10 +1094,276 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } +#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ -bool dfu_usb_get_reset(void) +static int ci_udc_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; + if (!driver) + return -EINVAL; + if (!driver->bind || !driver->setup || !driver->disconnect) + return -EINVAL; - return !!(readl(&udc->usbsts) & STS_URI); + controller.driver = driver; + return 0; +} + +static int ci_udc_gadget_stop(struct usb_gadget *g) +{ + controller.driver = NULL; + + ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req); + free(controller.items_mem); + free(controller.epts); + return 0; +} + +struct ci_udc_priv_data { + struct ehci_ctrl ctrl; + struct udevice otgdev; + struct clk_bulk clks; + int phy_off; + struct power_domain otg_pd; + struct clk phy_clk; + struct power_domain phy_pd; +}; + +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + return ci_udc_handle_interrupts(); +} + +static int ci_udc_phy_setup(struct udevice *dev, struct ci_udc_priv_data *priv) +{ + struct udevice __maybe_unused phy_dev; + priv->phy_off = fdtdec_lookup_phandle(gd->fdt_blob, + dev_of_offset(dev), + "fsl,usbphy"); + if (priv->phy_off < 0) + return -EINVAL; + + phy_dev.node = offset_to_ofnode(priv->phy_off); + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + /* Need to power on the PHY before access it */ + if (!power_domain_get(&phy_dev, &priv->phy_pd)) { + if (power_domain_on(&priv->phy_pd)) + return -EINVAL; + } +#endif + +#if CONFIG_IS_ENABLED(CLK) + int ret; + + ret = clk_get_by_index(&phy_dev, 0, &priv->phy_clk); + if (ret) { + printf("Failed to get phy_clk\n"); + return ret; + } + + ret = clk_enable(&priv->phy_clk); + if (ret) { + printf("Failed to enable phy_clk\n"); + return ret; + } +#endif + + return 0; +} + +static int ci_udc_phy_shutdown(struct ci_udc_priv_data *priv) +{ + int ret = 0; + +#if CONFIG_IS_ENABLED(CLK) + if (priv->phy_clk.dev) { + ret = clk_disable(&priv->phy_clk); + if (ret) + return ret; + + ret = clk_free(&priv->phy_clk); + if (ret) + return ret; + } +#endif + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + ret = power_domain_off(&priv->phy_pd); + if (ret) + printf("Power down USB PHY failed! (error = %d)\n", ret); +#endif + return ret; +} + +static int ci_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; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable_bulk(clks); + if (ret) { + clk_release_bulk(clks); + return ret; + } +#endif + + return 0; +} + +static int ci_udc_otg_phy_mode(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + + void *__iomem phy_ctrl, *__iomem phy_status; + void *__iomem phy_base = (void *__iomem)devfdt_get_addr(&priv->otgdev); + u32 val; + + if (is_mx6() || is_mx7ulp() || is_imx8()) { + phy_base = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, + priv->phy_off, + "reg"); + if ((fdt_addr_t)phy_base == FDT_ADDR_T_NONE) + return -EINVAL; + + phy_ctrl = (void __iomem *)(phy_base + USBPHY_CTRL); + val = readl(phy_ctrl); + if (val & USBPHY_CTRL_OTG_ID) + return USB_INIT_DEVICE; + else + return USB_INIT_HOST; + } else if (is_mx7() || is_imx8mm() || is_imx8mn()) { + phy_status = (void __iomem *)(phy_base + + USBNC_PHY_STATUS_OFFSET); + val = readl(phy_status); + if (val & USBNC_PHYSTATUS_ID_DIG) + return USB_INIT_DEVICE; + else + return USB_INIT_HOST; + } else { + return -EINVAL; + } +} + +static int ci_udc_otg_ofdata_to_platdata(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + int node = dev_of_offset(dev); + int usbotg_off; + + if (usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_PERIPHERAL) { + dev_dbg(dev, "Invalid mode\n"); + return -ENODEV; + } + + usbotg_off = fdtdec_lookup_phandle(gd->fdt_blob, + node, + "chipidea,usb"); + if (usbotg_off < 0) + return -EINVAL; + priv->otgdev.node = offset_to_ofnode(usbotg_off); + priv->otgdev.parent = dev->parent; + + return 0; +} + +static int ci_udc_otg_probe(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + struct usb_ehci *ehci; + int ret; + + ehci = (struct usb_ehci *)devfdt_get_addr(&priv->otgdev); + + pinctrl_select_state(&priv->otgdev, "default"); + +#if defined(CONFIG_MX6) + if (mx6_usb_fused((u32)ehci)) { + printf("USB@0x%x is fused, disable it\n", (u32)ehci); + return -ENODEV; + } +#endif + + ret = board_usb_init(dev->seq, USB_INIT_DEVICE); + if (ret) { + printf("Failed to initialize board for USB\n"); + return ret; + } + +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + if (!power_domain_get(&priv->otgdev, &priv->otg_pd)) { + if (power_domain_on(&priv->otg_pd)) + return -EINVAL; + } +#endif + + ret = ci_udc_phy_setup(&priv->otgdev, priv); + if (ret) + return ret; + + ret = ci_udc_otg_clk_init(&priv->otgdev, &priv->clks); + if (ret) + return ret; + + if (ci_udc_otg_phy_mode(dev) != USB_INIT_DEVICE) + return -ENODEV; + + priv->ctrl.hccr = (struct ehci_hccr *)((ulong)&ehci->caplength); + priv->ctrl.hcor = (struct ehci_hcor *)((ulong)priv->ctrl.hccr + + HC_LENGTH(ehci_readl(&(priv->ctrl.hccr)->cr_capbase))); + controller.ctrl = &priv->ctrl; + + ret = ci_udc_probe(); + if (ret) { + DBG("udc probe failed, returned %d\n", ret); + return ret; + } + + ret = usb_add_gadget_udc((struct device *)dev, &controller.gadget); + + return ret; +} + +static int ci_udc_otg_remove(struct udevice *dev) +{ + struct ci_udc_priv_data *priv = dev_get_priv(dev); + + usb_del_gadget_udc(&controller.gadget); + + clk_release_bulk(&priv->clks); + ci_udc_phy_shutdown(priv); +#if CONFIG_IS_ENABLED(POWER_DOMAIN) + if (power_domain_off(&priv->otg_pd)) { + printf("Power down USB controller failed!\n"); + return -EINVAL; + } +#endif + board_usb_cleanup(dev->seq, USB_INIT_DEVICE); + + controller.ctrl = NULL; + return 0; } + +static const struct udevice_id ci_udc_otg_ids[] = { + { .compatible = "fsl,imx27-usb-gadget" }, + { } +}; + +U_BOOT_DRIVER(ci_udc_otg) = { + .name = "ci-udc-otg", + .id = UCLASS_USB_GADGET_GENERIC, + .of_match = ci_udc_otg_ids, + .ofdata_to_platdata = ci_udc_otg_ofdata_to_platdata, + .probe = ci_udc_otg_probe, + .remove = ci_udc_otg_remove, + .priv_auto_alloc_size = sizeof(struct ci_udc_priv_data), +}; + +#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */ diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 20617850f3..2bfae1f661 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -17,6 +17,7 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> #include <asm/mach-imx/iomux-v3.h> +#include <asm/mach-imx/regs-usbphy.h> #include <asm/mach-imx/sys_proto.h> #include <dm.h> #include <asm/mach-types.h> @@ -37,18 +38,6 @@ DECLARE_GLOBAL_DATA_PTR; #define USB_H1_CTRL_OFFSET 0x04 -#define USBPHY_CTRL 0x00000030 -#define USBPHY_CTRL_SET 0x00000034 -#define USBPHY_CTRL_CLR 0x00000038 -#define USBPHY_CTRL_TOG 0x0000003c - -#define USBPHY_PWD 0x00000000 -#define USBPHY_CTRL_SFTRST 0x80000000 -#define USBPHY_CTRL_CLKGATE 0x40000000 -#define USBPHY_CTRL_ENUTMILEVEL3 0x00008000 -#define USBPHY_CTRL_ENUTMILEVEL2 0x00004000 -#define USBPHY_CTRL_OTG_ID 0x08000000 - #define ANADIG_USB2_CHRG_DETECT_EN_B 0x00100000 #define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B 0x00080000 @@ -58,8 +47,6 @@ DECLARE_GLOBAL_DATA_PTR; #define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS 0x00000040 #define USBNC_OFFSET 0x200 -#define USBNC_PHY_STATUS_OFFSET 0x23C -#define USBNC_PHYSTATUS_ID_DIG (1 << 4) /* otg_id status */ #define USBNC_PHYCFG2_ACAENB (1 << 4) /* otg_id detection enable */ #define UCTRL_PWR_POL (1 << 9) /* OTG Polarity of Power Pin */ #define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */ diff --git a/include/usb/ci_udc.h b/include/usb/ci_udc.h index 06adb2bb4d..ddae8e178b 100644 --- a/include/usb/ci_udc.h +++ b/include/usb/ci_udc.h @@ -7,7 +7,10 @@ #ifndef __CI_UDC_H__ #define __CI_UDC_H__ +#include <usb/ehci-ci.h> #define EP_MAX_PACKET_SIZE 0x200 #define EP0_MAX_PACKET_SIZE 64 + +int ehci_mx6_common_init(struct usb_ehci *ehci, int index); #endif /* __CI_UDC_H__ */ -- 2.28.0