Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / usb / host / xhci-dwc3.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2015 Freescale Semiconductor, Inc.
4  *
5  * DWC3 controller driver
6  *
7  * Author: Ramneek Mehresh<ramneek.mehresh@freescale.com>
8  */
9
10 #include <clk.h>
11 #include <common.h>
12 #include <dm.h>
13 #include <generic-phy.h>
14 #include <log.h>
15 #include <reset.h>
16 #include <usb.h>
17 #include <dwc3-uboot.h>
18 #include <linux/delay.h>
19
20 #include <usb/xhci.h>
21 #include <asm/io.h>
22 #include <linux/usb/dwc3.h>
23 #include <linux/usb/otg.h>
24
25 struct xhci_dwc3_plat {
26         struct clk_bulk clks;
27         struct phy_bulk phys;
28         struct reset_ctl_bulk resets;
29 };
30
31 void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode)
32 {
33         clrsetbits_le32(&dwc3_reg->g_ctl,
34                         DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
35                         DWC3_GCTL_PRTCAPDIR(mode));
36 }
37
38 static void dwc3_phy_reset(struct dwc3 *dwc3_reg)
39 {
40         /* Assert USB3 PHY reset */
41         setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
42
43         /* Assert USB2 PHY reset */
44         setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
45
46         mdelay(100);
47
48         /* Clear USB3 PHY reset */
49         clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
50
51         /* Clear USB2 PHY reset */
52         clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
53 }
54
55 void dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
56 {
57         /* Before Resetting PHY, put Core in Reset */
58         setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
59
60         /* reset USB3 phy - if required */
61         dwc3_phy_reset(dwc3_reg);
62
63         mdelay(100);
64
65         /* After PHYs are stable we can take Core out of reset state */
66         clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
67 }
68
69 int dwc3_core_init(struct dwc3 *dwc3_reg)
70 {
71         u32 reg;
72         u32 revision;
73         unsigned int dwc3_hwparams1;
74
75         revision = readl(&dwc3_reg->g_snpsid);
76         /* This should read as U3 followed by revision number */
77         if ((revision & DWC3_GSNPSID_MASK) != 0x55330000 &&
78             (revision & DWC3_GSNPSID_MASK) != 0x33310000) {
79                 puts("this is not a DesignWare USB3 DRD Core\n");
80                 return -1;
81         }
82
83         dwc3_core_soft_reset(dwc3_reg);
84
85         dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1);
86
87         reg = readl(&dwc3_reg->g_ctl);
88         reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
89         reg &= ~DWC3_GCTL_DISSCRAMBLE;
90         switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) {
91         case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
92                 reg &= ~DWC3_GCTL_DSBLCLKGTNG;
93                 break;
94         default:
95                 debug("No power optimization available\n");
96         }
97
98         /*
99          * WORKAROUND: DWC3 revisions <1.90a have a bug
100          * where the device can fail to connect at SuperSpeed
101          * and falls back to high-speed mode which causes
102          * the device to enter a Connect/Disconnect loop
103          */
104         if ((revision & DWC3_REVISION_MASK) < 0x190a)
105                 reg |= DWC3_GCTL_U2RSTECN;
106
107         writel(reg, &dwc3_reg->g_ctl);
108
109         return 0;
110 }
111
112 void dwc3_set_fladj(struct dwc3 *dwc3_reg, u32 val)
113 {
114         setbits_le32(&dwc3_reg->g_fladj, GFLADJ_30MHZ_REG_SEL |
115                         GFLADJ_30MHZ(val));
116 }
117
118 #if CONFIG_IS_ENABLED(DM_USB)
119 static int xhci_dwc3_reset_init(struct udevice *dev,
120                                 struct xhci_dwc3_plat *plat)
121 {
122         int ret;
123
124         ret = reset_get_bulk(dev, &plat->resets);
125         if (ret == -ENOTSUPP || ret == -ENOENT)
126                 return 0;
127         else if (ret)
128                 return ret;
129
130         ret = reset_deassert_bulk(&plat->resets);
131         if (ret) {
132                 reset_release_bulk(&plat->resets);
133                 return ret;
134         }
135
136         return 0;
137 }
138
139 static int xhci_dwc3_clk_init(struct udevice *dev,
140                               struct xhci_dwc3_plat *plat)
141 {
142         int ret;
143
144         ret = clk_get_bulk(dev, &plat->clks);
145         if (ret == -ENOSYS || ret == -ENOENT)
146                 return 0;
147         if (ret)
148                 return ret;
149
150         ret = clk_enable_bulk(&plat->clks);
151         if (ret) {
152                 clk_release_bulk(&plat->clks);
153                 return ret;
154         }
155
156         return 0;
157 }
158
159 static int xhci_dwc3_probe(struct udevice *dev)
160 {
161         struct xhci_hcor *hcor;
162         struct xhci_hccr *hccr;
163         struct dwc3 *dwc3_reg;
164         enum usb_dr_mode dr_mode;
165         struct xhci_dwc3_plat *plat = dev_get_plat(dev);
166         const char *phy;
167         u32 reg;
168         int ret;
169
170         ret = xhci_dwc3_reset_init(dev, plat);
171         if (ret)
172                 return ret;
173
174         ret = xhci_dwc3_clk_init(dev, plat);
175         if (ret)
176                 return ret;
177
178         hccr = (struct xhci_hccr *)((uintptr_t)dev_remap_addr(dev));
179         hcor = (struct xhci_hcor *)((uintptr_t)hccr +
180                         HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
181
182         ret = dwc3_setup_phy(dev, &plat->phys);
183         if (ret && (ret != -ENOTSUPP))
184                 return ret;
185
186         dwc3_reg = (struct dwc3 *)((char *)(hccr) + DWC3_REG_OFFSET);
187
188         dwc3_core_init(dwc3_reg);
189
190         /* Set dwc3 usb2 phy config */
191         reg = readl(&dwc3_reg->g_usb2phycfg[0]);
192
193         phy = dev_read_string(dev, "phy_type");
194         if (phy && strcmp(phy, "utmi_wide") == 0) {
195                 reg |= DWC3_GUSB2PHYCFG_PHYIF;
196                 reg &= ~DWC3_GUSB2PHYCFG_USBTRDTIM_MASK;
197                 reg |= DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT;
198         }
199
200         if (dev_read_bool(dev, "snps,dis_enblslpm-quirk"))
201                 reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
202
203         if (dev_read_bool(dev, "snps,dis-u2-freeclk-exists-quirk"))
204                 reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
205
206         if (dev_read_bool(dev, "snps,dis_u2_susphy_quirk"))
207                 reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
208
209         writel(reg, &dwc3_reg->g_usb2phycfg[0]);
210
211         dr_mode = usb_get_dr_mode(dev_ofnode(dev));
212         if (dr_mode == USB_DR_MODE_OTG &&
213             dev_read_bool(dev, "usb-role-switch")) {
214                 dr_mode = usb_get_role_switch_default_mode(dev_ofnode(dev));
215                 if (dr_mode == USB_DR_MODE_UNKNOWN)
216                         dr_mode = USB_DR_MODE_OTG;
217         }
218         if (dr_mode == USB_DR_MODE_UNKNOWN)
219                 /* by default set dual role mode to HOST */
220                 dr_mode = USB_DR_MODE_HOST;
221
222         dwc3_set_mode(dwc3_reg, dr_mode);
223
224         return xhci_register(dev, hccr, hcor);
225 }
226
227 static int xhci_dwc3_remove(struct udevice *dev)
228 {
229         struct xhci_dwc3_plat *plat = dev_get_plat(dev);
230
231         dwc3_shutdown_phy(dev, &plat->phys);
232
233         clk_release_bulk(&plat->clks);
234
235         reset_release_bulk(&plat->resets);
236
237         return xhci_deregister(dev);
238 }
239
240 static const struct udevice_id xhci_dwc3_ids[] = {
241         { .compatible = "snps,dwc3" },
242         { }
243 };
244
245 U_BOOT_DRIVER(xhci_dwc3) = {
246         .name = "xhci-dwc3",
247         .id = UCLASS_USB,
248         .of_match = xhci_dwc3_ids,
249         .probe = xhci_dwc3_probe,
250         .remove = xhci_dwc3_remove,
251         .ops = &xhci_usb_ops,
252         .priv_auto      = sizeof(struct xhci_ctrl),
253         .plat_auto      = sizeof(struct xhci_dwc3_plat),
254         .flags = DM_FLAG_ALLOC_PRIV_DMA,
255 };
256 #endif