1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Qualcomm EHCI driver 4 * 5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> 6 * 7 * Based on Linux driver 8 */ 9 10 #include <common.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <fdtdec.h> 14 #include <linux/libfdt.h> 15 #include <usb.h> 16 #include <usb/ehci-ci.h> 17 #include <usb/ulpi.h> 18 #include <wait_bit.h> 19 #include <asm/gpio.h> 20 #include <asm/io.h> 21 #include <linux/compat.h> 22 #include "ehci.h" 23 24 struct msm_ehci_priv { 25 struct ehci_ctrl ctrl; /* Needed by EHCI */ 26 struct usb_ehci *ehci; /* Start of IP core*/ 27 struct ulpi_viewport ulpi_vp; /* ULPI Viewport */ 28 struct phy phy; 29 }; 30 31 static int msm_init_after_reset(struct ehci_ctrl *dev) 32 { 33 struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl); 34 struct usb_ehci *ehci = p->ehci; 35 36 generic_phy_reset(&p->phy); 37 38 /* set mode to host controller */ 39 writel(CM_HOST, &ehci->usbmode); 40 41 return 0; 42 } 43 44 static const struct ehci_ops msm_ehci_ops = { 45 .init_after_reset = msm_init_after_reset 46 }; 47 48 static int ehci_usb_probe(struct udevice *dev) 49 { 50 struct msm_ehci_priv *p = dev_get_priv(dev); 51 struct usb_ehci *ehci = p->ehci; 52 struct usb_platdata *plat = dev_get_platdata(dev); 53 struct ehci_hccr *hccr; 54 struct ehci_hcor *hcor; 55 int ret; 56 57 hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength); 58 hcor = (struct ehci_hcor *)((phys_addr_t)hccr + 59 HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); 60 61 ret = ehci_setup_phy(dev, &p->phy, 0); 62 if (ret) 63 return ret; 64 65 ret = board_usb_init(0, plat->init_type); 66 if (ret < 0) 67 return ret; 68 69 return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, 70 plat->init_type); 71 } 72 73 static int ehci_usb_remove(struct udevice *dev) 74 { 75 struct msm_ehci_priv *p = dev_get_priv(dev); 76 struct usb_ehci *ehci = p->ehci; 77 int ret; 78 79 ret = ehci_deregister(dev); 80 if (ret) 81 return ret; 82 83 /* Stop controller. */ 84 clrbits_le32(&ehci->usbcmd, CMD_RUN); 85 86 ret = ehci_shutdown_phy(dev, &p->phy); 87 if (ret) 88 return ret; 89 90 ret = board_usb_init(0, USB_INIT_DEVICE); /* Board specific hook */ 91 if (ret < 0) 92 return ret; 93 94 /* Reset controller */ 95 setbits_le32(&ehci->usbcmd, CMD_RESET); 96 97 /* Wait for reset */ 98 if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) { 99 printf("Stuck on USB reset.\n"); 100 return -ETIMEDOUT; 101 } 102 103 return 0; 104 } 105 106 static int ehci_usb_ofdata_to_platdata(struct udevice *dev) 107 { 108 struct msm_ehci_priv *priv = dev_get_priv(dev); 109 110 priv->ulpi_vp.port_num = 0; 111 priv->ehci = (void *)devfdt_get_addr(dev); 112 113 if (priv->ehci == (void *)FDT_ADDR_T_NONE) 114 return -EINVAL; 115 116 /* Warning: this will not work if viewport address is > 64 bit due to 117 * ULPI design. 118 */ 119 priv->ulpi_vp.viewport_addr = (phys_addr_t)&priv->ehci->ulpi_viewpoint; 120 121 return 0; 122 } 123 124 #if defined(CONFIG_CI_UDC) 125 /* Little quirk that MSM needs with Chipidea controller 126 * Must reinit phy after reset 127 */ 128 void ci_init_after_reset(struct ehci_ctrl *ctrl) 129 { 130 struct msm_ehci_priv *p = ctrl->priv; 131 132 generic_phy_reset(&p->phy); 133 } 134 #endif 135 136 static const struct udevice_id ehci_usb_ids[] = { 137 { .compatible = "qcom,ehci-host", }, 138 { } 139 }; 140 141 U_BOOT_DRIVER(usb_ehci) = { 142 .name = "ehci_msm", 143 .id = UCLASS_USB, 144 .of_match = ehci_usb_ids, 145 .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, 146 .probe = ehci_usb_probe, 147 .remove = ehci_usb_remove, 148 .ops = &ehci_usb_ops, 149 .priv_auto_alloc_size = sizeof(struct msm_ehci_priv), 150 .platdata_auto_alloc_size = sizeof(struct usb_platdata), 151 .flags = DM_FLAG_ALLOC_PRIV_DMA, 152 }; 153