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 <clk.h>
11 #include <dm.h>
12 #include <dm/device_compat.h>
13 #include <dm/lists.h>
14 #include <errno.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 struct clk iface_clk;
30 struct clk core_clk;
31 };
32
msm_init_after_reset(struct ehci_ctrl * dev)33 static int msm_init_after_reset(struct ehci_ctrl *dev)
34 {
35 struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl);
36 struct usb_ehci *ehci = p->ehci;
37
38 generic_phy_reset(&p->phy);
39
40 /* set mode to host controller */
41 writel(CM_HOST, &ehci->usbmode);
42
43 return 0;
44 }
45
46 static const struct ehci_ops msm_ehci_ops = {
47 .init_after_reset = msm_init_after_reset
48 };
49
ehci_usb_probe(struct udevice * dev)50 static int ehci_usb_probe(struct udevice *dev)
51 {
52 struct msm_ehci_priv *p = dev_get_priv(dev);
53 struct usb_ehci *ehci = p->ehci;
54 struct usb_plat *plat = dev_get_plat(dev);
55 struct ehci_hccr *hccr;
56 struct ehci_hcor *hcor;
57 int ret;
58
59 ret = clk_get_by_name(dev, "core", &p->core_clk);
60 if (ret) {
61 dev_err(dev, "Failed to get core clock: %d\n", ret);
62 return ret;
63 }
64
65 ret = clk_get_by_name(dev, "iface", &p->iface_clk);
66 if (ret) {
67 dev_err(dev, "Failed to get iface clock: %d\n", ret);
68 return ret;
69 }
70
71 ret = clk_prepare_enable(&p->core_clk);
72 if (ret)
73 return ret;
74
75 ret = clk_prepare_enable(&p->iface_clk);
76 if (ret)
77 goto cleanup_core;
78
79 hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength);
80 hcor = (struct ehci_hcor *)((phys_addr_t)hccr +
81 HC_LENGTH(ehci_readl(&(hccr)->cr_capbase)));
82
83 ret = generic_setup_phy(dev, &p->phy, 0, PHY_MODE_USB_HOST, 0);
84 if (ret)
85 goto cleanup_iface;
86
87 ret = board_usb_init(0, plat->init_type);
88 if (ret < 0)
89 goto cleanup_iface;
90
91 return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0,
92 plat->init_type);
93
94 cleanup_iface:
95 clk_disable_unprepare(&p->iface_clk);
96 cleanup_core:
97 clk_disable_unprepare(&p->core_clk);
98 return ret;
99 }
100
ehci_usb_remove(struct udevice * dev)101 static int ehci_usb_remove(struct udevice *dev)
102 {
103 struct msm_ehci_priv *p = dev_get_priv(dev);
104 struct usb_ehci *ehci = p->ehci;
105 int ret;
106
107 ret = ehci_deregister(dev);
108 if (ret)
109 return ret;
110
111 /* Stop controller. */
112 clrbits_le32(&ehci->usbcmd, CMD_RUN);
113
114 clk_disable_unprepare(&p->iface_clk);
115 clk_disable_unprepare(&p->core_clk);
116
117 ret = generic_shutdown_phy(&p->phy);
118 if (ret)
119 return ret;
120
121 ret = board_usb_init(0, USB_INIT_DEVICE); /* Board specific hook */
122 if (ret < 0)
123 return ret;
124
125 /* Reset controller */
126 setbits_le32(&ehci->usbcmd, CMD_RESET);
127
128 /* Wait for reset */
129 if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) {
130 printf("Stuck on USB reset.\n");
131 return -ETIMEDOUT;
132 }
133
134 return 0;
135 }
136
ehci_usb_of_to_plat(struct udevice * dev)137 static int ehci_usb_of_to_plat(struct udevice *dev)
138 {
139 struct msm_ehci_priv *priv = dev_get_priv(dev);
140
141 priv->ulpi_vp.port_num = 0;
142 priv->ehci = dev_read_addr_ptr(dev);
143
144 if (priv->ehci == (void *)FDT_ADDR_T_NONE)
145 return -EINVAL;
146
147 /* Warning: this will not work if viewport address is > 64 bit due to
148 * ULPI design.
149 */
150 priv->ulpi_vp.viewport_addr = (phys_addr_t)&priv->ehci->ulpi_viewpoint;
151
152 return 0;
153 }
154
ehci_usb_of_bind(struct udevice * dev)155 static int ehci_usb_of_bind(struct udevice *dev)
156 {
157 ofnode ulpi_node = ofnode_first_subnode(dev_ofnode(dev));
158 ofnode phy_node;
159
160 if (!ofnode_valid(ulpi_node))
161 return 0;
162
163 phy_node = ofnode_first_subnode(ulpi_node);
164 if (!ofnode_valid(phy_node)) {
165 printf("%s: ulpi subnode with no phy\n", __func__);
166 return -ENOENT;
167 }
168
169 return device_bind_driver_to_node(dev, "msm8916_usbphy", "msm8916_usbphy",
170 phy_node, NULL);
171 }
172
173 #if defined(CONFIG_CI_UDC)
174 /* Little quirk that MSM needs with Chipidea controller
175 * Must reinit phy after reset
176 */
ci_init_after_reset(struct ehci_ctrl * ctrl)177 void ci_init_after_reset(struct ehci_ctrl *ctrl)
178 {
179 struct msm_ehci_priv *p = ctrl->priv;
180
181 generic_phy_reset(&p->phy);
182 }
183 #endif
184
185 static const struct udevice_id ehci_usb_ids[] = {
186 { .compatible = "qcom,ci-hdrc", },
187 { }
188 };
189
190 U_BOOT_DRIVER(usb_ehci) = {
191 .name = "ehci_msm",
192 .id = UCLASS_USB,
193 .of_match = ehci_usb_ids,
194 .of_to_plat = ehci_usb_of_to_plat,
195 .bind = ehci_usb_of_bind,
196 .probe = ehci_usb_probe,
197 .remove = ehci_usb_remove,
198 .ops = &ehci_usb_ops,
199 .priv_auto = sizeof(struct msm_ehci_priv),
200 .plat_auto = sizeof(struct usb_plat),
201 .flags = DM_FLAG_ALLOC_PRIV_DMA,
202 };
203