blob: ff336082e3ab84a50584be79eca22a2c2dc89f78 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Mateusz Kulikowskidc381172016-03-31 23:12:26 +02002/*
3 * Qualcomm EHCI driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 *
7 * Based on Linux driver
Mateusz Kulikowskidc381172016-03-31 23:12:26 +02008 */
9
Sam Dayc0421462024-05-06 10:26:58 +000010#include <clk.h>
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020011#include <dm.h>
Sam Dayc0421462024-05-06 10:26:58 +000012#include <dm/device_compat.h>
Caleb Connolly154ed1d2024-02-26 17:26:21 +000013#include <dm/lists.h>
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020014#include <errno.h>
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020015#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
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020024struct 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 */
Ramon Fried7e365962018-09-21 13:35:50 +030028 struct phy phy;
Sam Dayc0421462024-05-06 10:26:58 +000029 struct clk iface_clk;
30 struct clk core_clk;
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020031};
32
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020033static 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
Ramon Fried7e365962018-09-21 13:35:50 +030038 generic_phy_reset(&p->phy);
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020039
40 /* set mode to host controller */
41 writel(CM_HOST, &ehci->usbmode);
42
43 return 0;
44}
45
46static const struct ehci_ops msm_ehci_ops = {
47 .init_after_reset = msm_init_after_reset
48};
49
50static 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;
Simon Glassb75b15b2020-12-03 16:55:23 -070054 struct usb_plat *plat = dev_get_plat(dev);
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020055 struct ehci_hccr *hccr;
56 struct ehci_hcor *hcor;
57 int ret;
58
Sam Dayc0421462024-05-06 10:26:58 +000059 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
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020079 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
Patrice Chotard343ff752022-09-06 08:15:28 +020083 ret = generic_setup_phy(dev, &p->phy, 0);
Ramon Fried7e365962018-09-21 13:35:50 +030084 if (ret)
Sam Dayc0421462024-05-06 10:26:58 +000085 goto cleanup_iface;
Ramon Fried7e365962018-09-21 13:35:50 +030086
Ramon Fried7b0334f2018-09-21 13:35:51 +030087 ret = board_usb_init(0, plat->init_type);
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020088 if (ret < 0)
Sam Dayc0421462024-05-06 10:26:58 +000089 goto cleanup_iface;
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020090
Ramon Fried7b0334f2018-09-21 13:35:51 +030091 return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0,
92 plat->init_type);
Sam Dayc0421462024-05-06 10:26:58 +000093
94cleanup_iface:
95 clk_disable_unprepare(&p->iface_clk);
96cleanup_core:
97 clk_disable_unprepare(&p->core_clk);
98 return ret;
Mateusz Kulikowskidc381172016-03-31 23:12:26 +020099}
100
101static 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
Sam Dayc0421462024-05-06 10:26:58 +0000114 clk_disable_unprepare(&p->iface_clk);
115 clk_disable_unprepare(&p->core_clk);
116
Patrice Chotard343ff752022-09-06 08:15:28 +0200117 ret = generic_shutdown_phy(&p->phy);
Ramon Fried7e365962018-09-21 13:35:50 +0300118 if (ret)
119 return ret;
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200120
Ramon Friedbc866cc82018-09-21 13:35:43 +0300121 ret = board_usb_init(0, USB_INIT_DEVICE); /* Board specific hook */
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200122 if (ret < 0)
123 return ret;
124
125 /* Reset controller */
126 setbits_le32(&ehci->usbcmd, CMD_RESET);
127
128 /* Wait for reset */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100129 if (wait_for_bit_le32(&ehci->usbcmd, CMD_RESET, false, 30, false)) {
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200130 printf("Stuck on USB reset.\n");
131 return -ETIMEDOUT;
132 }
133
134 return 0;
135}
136
Simon Glassaad29ae2020-12-03 16:55:21 -0700137static int ehci_usb_of_to_plat(struct udevice *dev)
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200138{
139 struct msm_ehci_priv *priv = dev_get_priv(dev);
140
141 priv->ulpi_vp.port_num = 0;
Kever Yangd24b1542020-03-04 08:59:49 +0800142 priv->ehci = dev_read_addr_ptr(dev);
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200143
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
Caleb Connolly154ed1d2024-02-26 17:26:21 +0000155static 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
Ramon Friedcbee9082018-09-21 13:35:53 +0300173#if defined(CONFIG_CI_UDC)
174/* Little quirk that MSM needs with Chipidea controller
175 * Must reinit phy after reset
176 */
177void 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
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200185static const struct udevice_id ehci_usb_ids[] = {
Caleb Connolly154ed1d2024-02-26 17:26:21 +0000186 { .compatible = "qcom,ci-hdrc", },
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200187 { }
188};
189
190U_BOOT_DRIVER(usb_ehci) = {
191 .name = "ehci_msm",
192 .id = UCLASS_USB,
193 .of_match = ehci_usb_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700194 .of_to_plat = ehci_usb_of_to_plat,
Caleb Connolly154ed1d2024-02-26 17:26:21 +0000195 .bind = ehci_usb_of_bind,
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200196 .probe = ehci_usb_probe,
197 .remove = ehci_usb_remove,
198 .ops = &ehci_usb_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700199 .priv_auto = sizeof(struct msm_ehci_priv),
Simon Glassb75b15b2020-12-03 16:55:23 -0700200 .plat_auto = sizeof(struct usb_plat),
Mateusz Kulikowskidc381172016-03-31 23:12:26 +0200201 .flags = DM_FLAG_ALLOC_PRIV_DMA,
202};