/* * Copyright 2017 NXP * * FSL i.MX8M USB HOST xHCI Controller * * Author: Jun Li * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include "xhci.h" /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; #define USBMIX_PHY_OFFSET 0xF0040 #define PHY_CTRL0_REF_SSP_EN BIT(2) #define PHY_CTRL1_RESET BIT(0) #define PHY_CTRL1_ATERESET BIT(3) #define PHY_CTRL1_VDATSRCENB0 BIT(19) #define PHY_CTRL1_VDATDETENB0 BIT(20) #define PHY_CTRL2_TXENABLEN0 BIT(8) struct imx8m_usbmix { u32 phy_ctrl0; u32 phy_ctrl1; u32 phy_ctrl2; u32 phy_ctrl3; }; struct imx8m_xhci { struct xhci_hccr *hcd; struct dwc3 *dwc3_reg; struct imx8m_usbmix *usbmix_reg; }; struct imx8m_usbctrl_data { u32 usb_id; unsigned long ctr_addr; }; static struct imx8m_xhci imx8m_xhci; static struct imx8m_usbctrl_data ctr_data[] = { {0, USB1_BASE_ADDR}, {1, USB2_BASE_ADDR}, }; static void imx8m_usb_phy_init(struct imx8m_usbmix *usbmix_reg) { u32 reg; reg = readl(&usbmix_reg->phy_ctrl1); reg &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); reg |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; writel(reg, &usbmix_reg->phy_ctrl1); reg = readl(&usbmix_reg->phy_ctrl0); reg |= PHY_CTRL0_REF_SSP_EN; writel(reg, &usbmix_reg->phy_ctrl0); reg = readl(&usbmix_reg->phy_ctrl2); reg |= PHY_CTRL2_TXENABLEN0; writel(reg, &usbmix_reg->phy_ctrl2); reg = readl(&usbmix_reg->phy_ctrl1); reg &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); writel(reg, &usbmix_reg->phy_ctrl1); } static void imx8m_xhci_set_suspend_clk(struct dwc3 *dwc3_reg) { u32 reg; /* Set suspend_clk to be 32KHz */ reg = readl(&dwc3_reg->g_ctl); reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK); reg |= DWC3_GCTL_PWRDNSCALE(2); writel(reg, &dwc3_reg->g_ctl); } static int imx8m_xhci_core_init(struct imx8m_xhci *imx8m_xhci) { int ret = 0; imx8m_usb_phy_init(imx8m_xhci->usbmix_reg); ret = dwc3_core_init(imx8m_xhci->dwc3_reg); if (ret) { debug("%s:failed to initialize core\n", __func__); return ret; } imx8m_xhci_set_suspend_clk(imx8m_xhci->dwc3_reg); /* We are hard-coding DWC3 core to Host Mode */ dwc3_set_mode(imx8m_xhci->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); /* Set GFLADJ_30MHZ as 20h as per XHCI spec default value */ dwc3_set_fladj(imx8m_xhci->dwc3_reg, GFLADJ_30MHZ_DEFAULT); return ret; } #ifdef CONFIG_DM_USB static int xhci_imx8m_probe(struct udevice *dev) { struct xhci_hccr *hccr; struct xhci_hcor *hcor; struct imx8m_xhci *ctx = &imx8m_xhci; int ret = 0; ctx->hcd = (struct xhci_hccr *)(ctr_data[dev->seq].ctr_addr); ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); ctx->usbmix_reg = (struct imx8m_usbmix *)((char *)(ctx->hcd) + USBMIX_PHY_OFFSET); ret = board_usb_init(ctr_data[dev->seq].usb_id, USB_INIT_HOST); if (ret != 0) { imx8m_usb_power(ctr_data[dev->seq].usb_id, false); puts("Failed to initialize board for imx8m USB\n"); return ret; } ret = imx8m_xhci_core_init(ctx); if (ret < 0) { puts("Failed to initialize imx8m xhci\n"); return ret; } hccr = (struct xhci_hccr *)ctx->hcd; hcor = (struct xhci_hcor *)((uintptr_t) hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); debug("imx8m-xhci: init hccr %lx and hcor %lx hc_length %lx\n", (uintptr_t)hccr, (uintptr_t)hcor, (uintptr_t)HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); return xhci_register(dev, hccr, hcor); } static int xhci_imx8m_remove(struct udevice *dev) { int ret = xhci_deregister(dev); board_usb_cleanup(dev->seq, USB_INIT_HOST); return ret; } static const struct udevice_id xhci_usb_ids[] = { { .compatible = "fsl, imx8mq-dwc3", }, { } }; U_BOOT_DRIVER(xhci_imx8m) = { .name = "xhci_imx8m", .id = UCLASS_USB, .of_match = xhci_usb_ids, .probe = xhci_imx8m_probe, .remove = xhci_imx8m_remove, .ops = &xhci_usb_ops, .platdata_auto_alloc_size = sizeof(struct usb_platdata), .priv_auto_alloc_size = sizeof(struct xhci_ctrl), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; #else int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) { struct imx8m_xhci *ctx = &imx8m_xhci; int ret = 0; ctx->hcd = (struct xhci_hccr *)(ctr_data[index].ctr_addr); ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); ctx->usbmix_reg = (struct imx8m_usbmix *)((char *)(ctx->hcd) + USBMIX_PHY_OFFSET); ret = board_usb_init(ctr_data[index].usb_id, USB_INIT_HOST); if (ret != 0) { imx8m_usb_power(ctr_data[index].usb_id, false); puts("Failed to initialize board for imx8m USB\n"); return ret; } ret = imx8m_xhci_core_init(ctx); if (ret < 0) { puts("Failed to initialize imx8m xhci\n"); return ret; } *hccr = (struct xhci_hccr *)ctx->hcd; *hcor = (struct xhci_hcor *)((uintptr_t) *hccr + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); debug("imx8m-xhci: init hccr %lx and hcor %lx hc_length %lx\n", (uintptr_t)*hccr, (uintptr_t)*hcor, (uintptr_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); return ret; } void xhci_hcd_stop(int index) { board_usb_cleanup(ctr_data[index].usb_id, USB_INIT_HOST); } #endif