X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fxhci-dwc3.c;h=dd0d156027efe3d8b6992d1d135d43f53fc243da;hb=9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1;hp=c722c504adaac3a969d61bae6b3426b38eabc26a;hpb=667f4dd90f0f40f8d4fde7ef280550ef5f7946f8;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index c722c50..dd0d156 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -1,16 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2015 Freescale Semiconductor, Inc. * * DWC3 controller driver * * Author: Ramneek Mehresh - * - * SPDX-License-Identifier: GPL-2.0+ */ #include +#include +#include +#include +#include + +#include "xhci.h" #include #include +#include + +struct xhci_dwc3_platdata { + struct phy *usb_phys; + int num_phys; +}; void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) { @@ -19,7 +30,7 @@ void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) DWC3_GCTL_PRTCAPDIR(mode)); } -void dwc3_phy_reset(struct dwc3 *dwc3_reg) +static void dwc3_phy_reset(struct dwc3 *dwc3_reg) { /* Assert USB3 PHY reset */ setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); @@ -44,6 +55,8 @@ void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) /* reset USB3 phy - if required */ dwc3_phy_reset(dwc3_reg); + mdelay(100); + /* After PHYs are stable we can take Core out of reset state */ clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); } @@ -95,3 +108,144 @@ void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val) setbits_le32(&dwc3_reg->g_fladj, GFLADJ_30MHZ_REG_SEL | GFLADJ_30MHZ(val)); } + +#if CONFIG_IS_ENABLED(DM_USB) +static int xhci_dwc3_setup_phy(struct udevice *dev) +{ + struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); + int i, ret, count; + + /* 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; + + plat->usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), + GFP_KERNEL); + if (!plat->usb_phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &plat->usb_phys[i]); + if (ret && ret != -ENOENT) { + pr_err("Failed to get USB PHY%d for %s\n", + i, dev->name); + return ret; + } + + ++plat->num_phys; + } + + for (i = 0; i < plat->num_phys; i++) { + ret = generic_phy_init(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't init USB PHY%d for %s\n", + i, dev->name); + goto phys_init_err; + } + } + + for (i = 0; i < plat->num_phys; i++) { + ret = generic_phy_power_on(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't power USB PHY%d for %s\n", + i, dev->name); + goto phys_poweron_err; + } + } + + return 0; + +phys_poweron_err: + for (; i >= 0; i--) + generic_phy_power_off(&plat->usb_phys[i]); + + for (i = 0; i < plat->num_phys; i++) + generic_phy_exit(&plat->usb_phys[i]); + + return ret; + +phys_init_err: + for (; i >= 0; i--) + generic_phy_exit(&plat->usb_phys[i]); + + return ret; +} + +static int xhci_dwc3_shutdown_phy(struct udevice *dev) +{ + struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); + int i, ret; + + for (i = 0; i < plat->num_phys; i++) { + if (!generic_phy_valid(&plat->usb_phys[i])) + continue; + + ret = generic_phy_power_off(&plat->usb_phys[i]); + ret |= generic_phy_exit(&plat->usb_phys[i]); + if (ret) { + pr_err("Can't shutdown USB PHY%d for %s\n", + i, dev->name); + } + } + + return 0; +} + +static int xhci_dwc3_probe(struct udevice *dev) +{ + struct xhci_hcor *hcor; + struct xhci_hccr *hccr; + struct dwc3 *dwc3_reg; + enum usb_dr_mode dr_mode; + int ret; + + hccr = (struct xhci_hccr *)((uintptr_t)dev_read_addr(dev)); + hcor = (struct xhci_hcor *)((uintptr_t)hccr + + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); + + ret = xhci_dwc3_setup_phy(dev); + if (ret) + return ret; + + dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET); + + dwc3_core_init(dwc3_reg); + + dr_mode = usb_get_dr_mode(dev_of_offset(dev)); + if (dr_mode == USB_DR_MODE_UNKNOWN) + /* by default set dual role mode to HOST */ + dr_mode = USB_DR_MODE_HOST; + + dwc3_set_mode(dwc3_reg, dr_mode); + + return xhci_register(dev, hccr, hcor); +} + +static int xhci_dwc3_remove(struct udevice *dev) +{ + xhci_dwc3_shutdown_phy(dev); + + return xhci_deregister(dev); +} + +static const struct udevice_id xhci_dwc3_ids[] = { + { .compatible = "snps,dwc3" }, + { } +}; + +U_BOOT_DRIVER(xhci_dwc3) = { + .name = "xhci-dwc3", + .id = UCLASS_USB, + .of_match = xhci_dwc3_ids, + .probe = xhci_dwc3_probe, + .remove = xhci_dwc3_remove, + .ops = &xhci_usb_ops, + .priv_auto_alloc_size = sizeof(struct xhci_ctrl), + .platdata_auto_alloc_size = sizeof(struct xhci_dwc3_platdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif