blob: 3acffb40b0b2c53da323a1b64c923a1fdaf40df4 [file] [log] [blame]
Jagan Tekid3c38282018-05-07 13:03:26 +05301/*
2 * Allwinner sun4i USB PHY driver
3 *
4 * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
5 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
6 * Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com>
7 *
8 * Modelled arch/arm/mach-sunxi/usb_phy.c to compatible with generic-phy.
9 *
10 * SPDX-License-Identifier: GPL-2.0+
11 */
12
Jagan Teki0dc33332018-08-06 12:16:39 +053013#include <clk.h>
Jagan Tekid3c38282018-05-07 13:03:26 +053014#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Jagan Tekid3c38282018-05-07 13:03:26 +053016#include <dm/device.h>
17#include <generic-phy.h>
Jagan Teki21fc42d2018-05-07 13:03:27 +053018#include <phy-sun4i-usb.h>
Jagan Teki0dc33332018-08-06 12:16:39 +053019#include <reset.h>
Jagan Tekid3c38282018-05-07 13:03:26 +053020#include <asm/gpio.h>
21#include <asm/io.h>
Simon Glass9bc15642020-02-03 07:36:16 -070022#include <dm/device_compat.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060023#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060024#include <linux/delay.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070025#include <linux/err.h>
Samuel Hollandc70137c2021-09-12 09:22:42 -050026#include <power/regulator.h>
Jagan Tekid3c38282018-05-07 13:03:26 +053027
28#define REG_ISCR 0x00
29#define REG_PHYCTL_A10 0x04
30#define REG_PHYBIST 0x08
31#define REG_PHYTUNE 0x0c
32#define REG_PHYCTL_A33 0x10
33#define REG_PHY_OTGCTL 0x20
Andre Przywara8662e7e2022-07-14 23:09:21 -050034
35#define REG_HCI_PHY_CTL 0x10
Jagan Tekid3c38282018-05-07 13:03:26 +053036
37/* Common Control Bits for Both PHYs */
38#define PHY_PLL_BW 0x03
39#define PHY_RES45_CAL_EN 0x0c
40
41/* Private Control Bits for Each PHY */
42#define PHY_TX_AMPLITUDE_TUNE 0x20
43#define PHY_TX_SLEWRATE_TUNE 0x22
44#define PHY_DISCON_TH_SEL 0x2a
Jagan Teki37671e12018-05-07 13:03:37 +053045#define PHY_SQUELCH_DETECT 0x3c
Jagan Tekid3c38282018-05-07 13:03:26 +053046
47#define PHYCTL_DATA BIT(7)
48#define OTGCTL_ROUTE_MUSB BIT(0)
49
50#define PHY_TX_RATE BIT(4)
51#define PHY_TX_MAGNITUDE BIT(2)
52#define PHY_TX_AMPLITUDE_LEN 5
53
54#define PHY_RES45_CAL_DATA BIT(0)
55#define PHY_RES45_CAL_LEN 1
56#define PHY_DISCON_TH_LEN 2
57
58#define SUNXI_AHB_ICHR8_EN BIT(10)
59#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
60#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
61#define SUNXI_ULPI_BYPASS_EN BIT(0)
62
Jagan Teki05a7b9f2018-05-07 13:03:30 +053063/* A83T specific control bits for PHY0 */
64#define PHY_CTL_VBUSVLDEXT BIT(5)
65#define PHY_CTL_SIDDQ BIT(3)
Andre Przywara8662e7e2022-07-14 23:09:21 -050066#define PHY_CTL_H3_SIDDQ BIT(1)
Jagan Teki05a7b9f2018-05-07 13:03:30 +053067
68/* A83T specific control bits for PHY2 HSIC */
69#define SUNXI_EHCI_HS_FORCE BIT(20)
70#define SUNXI_HSIC_CONNECT_INT BIT(16)
71#define SUNXI_HSIC BIT(1)
72
Jagan Tekid3c38282018-05-07 13:03:26 +053073#define MAX_PHYS 4
74
Jagan Tekid3c38282018-05-07 13:03:26 +053075struct sun4i_usb_phy_cfg {
76 int num_phys;
Andre Przywarab06b90f2023-06-12 00:32:38 +010077 int hsic_index;
Jagan Tekid3c38282018-05-07 13:03:26 +053078 u32 disc_thresh;
Andre Przywara8662e7e2022-07-14 23:09:21 -050079 u32 hci_phy_ctl_clear;
Jagan Tekid3c38282018-05-07 13:03:26 +053080 u8 phyctl_offset;
Jagan Teki0dc33332018-08-06 12:16:39 +053081 bool dedicated_clocks;
Jagan Tekid3c38282018-05-07 13:03:26 +053082 bool phy0_dual_route;
Andre Przywarab06b90f2023-06-12 00:32:38 +010083 bool siddq_in_base;
Andre Przywara720f4e42023-06-12 00:32:39 +010084 bool needs_phy2_siddq;
Andre Przywarab2f0f312019-06-23 15:09:49 +010085 int missing_phys;
Jagan Tekid3c38282018-05-07 13:03:26 +053086};
87
Jagan Tekid3c38282018-05-07 13:03:26 +053088struct sun4i_usb_phy_plat {
89 void __iomem *pmu;
Andre Przywara3331d222022-06-07 23:36:18 +010090 struct gpio_desc gpio_vbus_det;
91 struct gpio_desc gpio_id_det;
Jagan Teki0dc33332018-08-06 12:16:39 +053092 struct clk clocks;
Andre Przywara720f4e42023-06-12 00:32:39 +010093 struct clk clk2;
Jagan Teki0dc33332018-08-06 12:16:39 +053094 struct reset_ctl resets;
Samuel Holland9a361ef2023-10-31 01:39:53 -050095 struct udevice *vbus;
Jagan Tekid3c38282018-05-07 13:03:26 +053096 int id;
97};
98
99struct sun4i_usb_phy_data {
100 void __iomem *base;
Jagan Tekid3c38282018-05-07 13:03:26 +0530101 const struct sun4i_usb_phy_cfg *cfg;
102 struct sun4i_usb_phy_plat *usb_phy;
Samuel Hollandc70137c2021-09-12 09:22:42 -0500103 struct udevice *vbus_power_supply;
Jagan Tekid3c38282018-05-07 13:03:26 +0530104};
105
106static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY;
107
108static void sun4i_usb_phy_write(struct phy *phy, u32 addr, u32 data, int len)
109{
110 struct sun4i_usb_phy_data *phy_data = dev_get_priv(phy->dev);
111 struct sun4i_usb_phy_plat *usb_phy = &phy_data->usb_phy[phy->id];
112 u32 temp, usbc_bit = BIT(usb_phy->id * 2);
113 void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
114 int i;
115
116 if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) {
117 /* SoCs newer than A33 need us to set phyctl to 0 explicitly */
118 writel(0, phyctl);
119 }
120
121 for (i = 0; i < len; i++) {
122 temp = readl(phyctl);
123
124 /* clear the address portion */
125 temp &= ~(0xff << 8);
126
127 /* set the address */
128 temp |= ((addr + i) << 8);
129 writel(temp, phyctl);
130
131 /* set the data bit and clear usbc bit*/
132 temp = readb(phyctl);
133 if (data & 0x1)
134 temp |= PHYCTL_DATA;
135 else
136 temp &= ~PHYCTL_DATA;
137 temp &= ~usbc_bit;
138 writeb(temp, phyctl);
139
140 /* pulse usbc_bit */
141 temp = readb(phyctl);
142 temp |= usbc_bit;
143 writeb(temp, phyctl);
144
145 temp = readb(phyctl);
146 temp &= ~usbc_bit;
147 writeb(temp, phyctl);
148
149 data >>= 1;
150 }
151}
152
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530153static void sun4i_usb_phy_passby(struct phy *phy, bool enable)
Jagan Tekid3c38282018-05-07 13:03:26 +0530154{
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530155 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
156 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
Jagan Tekid3c38282018-05-07 13:03:26 +0530157 u32 bits, reg_value;
158
159 if (!usb_phy->pmu)
160 return;
161
162 bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
163 SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530164
165 /* A83T USB2 is HSIC */
Andre Przywarab06b90f2023-06-12 00:32:38 +0100166 if (data->cfg->hsic_index && usb_phy->id == data->cfg->hsic_index)
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530167 bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT |
168 SUNXI_HSIC;
169
Jagan Tekid3c38282018-05-07 13:03:26 +0530170 reg_value = readl(usb_phy->pmu);
171
172 if (enable)
173 reg_value |= bits;
174 else
175 reg_value &= ~bits;
176
177 writel(reg_value, usb_phy->pmu);
178}
179
180static int sun4i_usb_phy_power_on(struct phy *phy)
181{
182 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
183 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
Samuel Holland9a361ef2023-10-31 01:39:53 -0500184 int ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530185
186 if (initial_usb_scan_delay) {
187 mdelay(initial_usb_scan_delay);
188 initial_usb_scan_delay = 0;
189 }
190
Samuel Hollandad991492022-07-14 22:34:53 -0500191 /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
192 if (phy->id == 0 && sun4i_usb_phy_vbus_detect(phy)) {
193 dev_warn(phy->dev, "External vbus detected, not enabling our own vbus\n");
194 return 0;
195 }
196
Samuel Holland9a361ef2023-10-31 01:39:53 -0500197 ret = regulator_set_enable_if_allowed(usb_phy->vbus, true);
198 if (ret)
199 return ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530200
201 return 0;
202}
203
204static int sun4i_usb_phy_power_off(struct phy *phy)
205{
206 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
207 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
Samuel Holland9a361ef2023-10-31 01:39:53 -0500208 int ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530209
Samuel Holland9a361ef2023-10-31 01:39:53 -0500210 ret = regulator_set_enable_if_allowed(usb_phy->vbus, false);
211 if (ret)
212 return ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530213
214 return 0;
215}
216
217static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, bool id_det)
218{
219 u32 regval;
220
221 regval = readl(data->base + REG_PHY_OTGCTL);
222 if (!id_det) {
223 /* Host mode. Route phy0 to EHCI/OHCI */
224 regval &= ~OTGCTL_ROUTE_MUSB;
225 } else {
226 /* Peripheral mode. Route phy0 to MUSB */
227 regval |= OTGCTL_ROUTE_MUSB;
228 }
229 writel(regval, data->base + REG_PHY_OTGCTL);
230}
231
232static int sun4i_usb_phy_init(struct phy *phy)
233{
234 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
235 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
236 u32 val;
Jagan Teki0dc33332018-08-06 12:16:39 +0530237 int ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530238
Jagan Teki0dc33332018-08-06 12:16:39 +0530239 ret = clk_enable(&usb_phy->clocks);
240 if (ret) {
Sean Andersone4d3c972020-09-15 10:45:04 -0400241 dev_err(phy->dev, "failed to enable usb_%ldphy clock\n",
242 phy->id);
Jagan Teki0dc33332018-08-06 12:16:39 +0530243 return ret;
244 }
245
246 ret = reset_deassert(&usb_phy->resets);
247 if (ret) {
Sean Andersone4d3c972020-09-15 10:45:04 -0400248 dev_err(phy->dev, "failed to deassert usb_%ldreset reset\n",
249 phy->id);
Jagan Teki0dc33332018-08-06 12:16:39 +0530250 return ret;
251 }
Jagan Tekid3c38282018-05-07 13:03:26 +0530252
Andre Przywara720f4e42023-06-12 00:32:39 +0100253 /* Some PHYs on some SoCs (the H616) need the help of PHY2 to work. */
254 if (data->cfg->needs_phy2_siddq && phy->id != 2) {
255 struct sun4i_usb_phy_plat *phy2 = &data->usb_phy[2];
256
257 ret = clk_enable(&phy2->clocks);
258 if (ret) {
259 dev_err(phy->dev, "failed to enable aux clock\n");
260 return ret;
261 }
262
263 ret = reset_deassert(&phy2->resets);
264 if (ret) {
265 dev_err(phy->dev, "failed to deassert aux reset\n");
266 return ret;
267 }
268
269 /*
270 * This extra clock is just needed to access the
271 * REG_HCI_PHY_CTL PMU register for PHY2.
272 */
273 ret = clk_enable(&phy2->clk2);
274 if (ret) {
275 dev_err(phy->dev, "failed to enable PHY2 clock\n");
276 return ret;
277 }
278
279 if (phy2->pmu && data->cfg->hci_phy_ctl_clear) {
280 val = readl(phy2->pmu + REG_HCI_PHY_CTL);
281 val &= ~data->cfg->hci_phy_ctl_clear;
282 writel(val, phy2->pmu + REG_HCI_PHY_CTL);
283 }
284
285 clk_disable(&phy2->clk2);
286 }
287
Andre Przywara8662e7e2022-07-14 23:09:21 -0500288 if (usb_phy->pmu && data->cfg->hci_phy_ctl_clear) {
289 val = readl(usb_phy->pmu + REG_HCI_PHY_CTL);
290 val &= ~data->cfg->hci_phy_ctl_clear;
291 writel(val, usb_phy->pmu + REG_HCI_PHY_CTL);
292 }
293
Andre Przywarab06b90f2023-06-12 00:32:38 +0100294 if (data->cfg->siddq_in_base) {
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530295 if (phy->id == 0) {
296 val = readl(data->base + data->cfg->phyctl_offset);
297 val |= PHY_CTL_VBUSVLDEXT;
298 val &= ~PHY_CTL_SIDDQ;
299 writel(val, data->base + data->cfg->phyctl_offset);
300 }
301 } else {
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530302 if (usb_phy->id == 0)
303 sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN,
304 PHY_RES45_CAL_DATA,
305 PHY_RES45_CAL_LEN);
Jagan Tekid3c38282018-05-07 13:03:26 +0530306
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530307 /* Adjust PHY's magnitude and rate */
308 sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE,
309 PHY_TX_MAGNITUDE | PHY_TX_RATE,
310 PHY_TX_AMPLITUDE_LEN);
Jagan Tekid3c38282018-05-07 13:03:26 +0530311
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530312 /* Disconnect threshold adjustment */
313 sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
314 data->cfg->disc_thresh, PHY_DISCON_TH_LEN);
315 }
Jagan Tekid3c38282018-05-07 13:03:26 +0530316
Paul Kocialkowski2ee06372019-03-14 10:38:00 +0000317#ifdef CONFIG_USB_MUSB_SUNXI
318 /* Needed for HCI and conflicts with MUSB, keep PHY0 on MUSB */
319 if (usb_phy->id != 0)
320 sun4i_usb_phy_passby(phy, true);
321
322 /* Route PHY0 to MUSB to allow USB gadget */
323 if (data->cfg->phy0_dual_route)
324 sun4i_usb_phy0_reroute(data, true);
325#else
Jagan Tekib8cbf9d2018-07-20 12:34:20 +0530326 sun4i_usb_phy_passby(phy, true);
Jagan Tekid3c38282018-05-07 13:03:26 +0530327
Paul Kocialkowski2ee06372019-03-14 10:38:00 +0000328 /* Route PHY0 to HCI to allow USB host */
329 if (data->cfg->phy0_dual_route)
330 sun4i_usb_phy0_reroute(data, false);
331#endif
Jagan Tekid3c38282018-05-07 13:03:26 +0530332
333 return 0;
334}
335
336static int sun4i_usb_phy_exit(struct phy *phy)
337{
338 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
339 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
Jagan Teki0dc33332018-08-06 12:16:39 +0530340 int ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530341
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530342 if (phy->id == 0) {
Andre Przywarab06b90f2023-06-12 00:32:38 +0100343 if (data->cfg->siddq_in_base) {
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530344 void __iomem *phyctl = data->base +
345 data->cfg->phyctl_offset;
346
347 writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl);
348 }
349 }
350
351 sun4i_usb_phy_passby(phy, false);
Jagan Tekid3c38282018-05-07 13:03:26 +0530352
Jagan Teki0dc33332018-08-06 12:16:39 +0530353 ret = clk_disable(&usb_phy->clocks);
354 if (ret) {
Sean Andersone4d3c972020-09-15 10:45:04 -0400355 dev_err(phy->dev, "failed to disable usb_%ldphy clock\n",
356 phy->id);
Jagan Teki0dc33332018-08-06 12:16:39 +0530357 return ret;
358 }
359
360 ret = reset_assert(&usb_phy->resets);
361 if (ret) {
Sean Andersone4d3c972020-09-15 10:45:04 -0400362 dev_err(phy->dev, "failed to assert usb_%ldreset reset\n",
363 phy->id);
Jagan Teki0dc33332018-08-06 12:16:39 +0530364 return ret;
365 }
Jagan Tekid3c38282018-05-07 13:03:26 +0530366
367 return 0;
368}
369
370static int sun4i_usb_phy_xlate(struct phy *phy,
371 struct ofnode_phandle_args *args)
372{
373 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
374
Andre Przywara6c53de52023-06-12 00:32:35 +0100375 if (args->args_count != 1)
376 return -EINVAL;
377
378 if (args->args[0] >= data->cfg->num_phys)
Jagan Tekid3c38282018-05-07 13:03:26 +0530379 return -EINVAL;
380
Andre Przywarab2f0f312019-06-23 15:09:49 +0100381 if (data->cfg->missing_phys & BIT(args->args[0]))
382 return -ENODEV;
383
Jagan Tekid3c38282018-05-07 13:03:26 +0530384 if (args->args_count)
385 phy->id = args->args[0];
386 else
387 phy->id = 0;
388
389 debug("%s: phy_id = %ld\n", __func__, phy->id);
390 return 0;
391}
392
Jagan Teki21fc42d2018-05-07 13:03:27 +0530393int sun4i_usb_phy_vbus_detect(struct phy *phy)
394{
395 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
396 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
Samuel Hollandf42dbdf2021-09-12 09:22:41 -0500397 int err = 1, retries = 3;
Jagan Teki21fc42d2018-05-07 13:03:27 +0530398
Andre Przywara3331d222022-06-07 23:36:18 +0100399 if (dm_gpio_is_valid(&usb_phy->gpio_vbus_det)) {
400 err = dm_gpio_get_value(&usb_phy->gpio_vbus_det);
Samuel Hollandf42dbdf2021-09-12 09:22:41 -0500401 /*
402 * Vbus may have been provided by the board and just turned off
403 * some milliseconds ago on reset. What we're measuring then is
404 * a residual charge on Vbus. Sleep a bit and try again.
405 */
406 while (err > 0 && retries--) {
407 mdelay(100);
Andre Przywara3331d222022-06-07 23:36:18 +0100408 err = dm_gpio_get_value(&usb_phy->gpio_vbus_det);
Samuel Hollandf42dbdf2021-09-12 09:22:41 -0500409 }
Samuel Hollandc70137c2021-09-12 09:22:42 -0500410 } else if (data->vbus_power_supply) {
411 err = regulator_get_enable(data->vbus_power_supply);
Jagan Teki21fc42d2018-05-07 13:03:27 +0530412 }
413
414 return err;
415}
416
417int sun4i_usb_phy_id_detect(struct phy *phy)
418{
419 struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
420 struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
421
Andre Przywara3331d222022-06-07 23:36:18 +0100422 if (!dm_gpio_is_valid(&usb_phy->gpio_id_det))
423 return -1;
Jagan Teki21fc42d2018-05-07 13:03:27 +0530424
Andre Przywara3331d222022-06-07 23:36:18 +0100425 return dm_gpio_get_value(&usb_phy->gpio_id_det);
Jagan Teki21fc42d2018-05-07 13:03:27 +0530426}
427
Jagan Teki37671e12018-05-07 13:03:37 +0530428void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled)
429{
430 sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2);
431}
432
Jagan Tekid3c38282018-05-07 13:03:26 +0530433static struct phy_ops sun4i_usb_phy_ops = {
434 .of_xlate = sun4i_usb_phy_xlate,
435 .init = sun4i_usb_phy_init,
436 .power_on = sun4i_usb_phy_power_on,
437 .power_off = sun4i_usb_phy_power_off,
438 .exit = sun4i_usb_phy_exit,
439};
440
441static int sun4i_usb_phy_probe(struct udevice *dev)
442{
Simon Glassfa20e932020-12-03 16:55:20 -0700443 struct sun4i_usb_phy_plat *plat = dev_get_plat(dev);
Jagan Tekid3c38282018-05-07 13:03:26 +0530444 struct sun4i_usb_phy_data *data = dev_get_priv(dev);
445 int i, ret;
446
447 data->cfg = (const struct sun4i_usb_phy_cfg *)dev_get_driver_data(dev);
448 if (!data->cfg)
449 return -EINVAL;
450
Matthias Schiffer47331932023-09-27 15:33:34 +0200451 data->base = (void __iomem *)dev_read_addr_name_ptr(dev, "phy_ctrl");
452 if (!data->base)
453 return -EINVAL;
Jagan Tekid3c38282018-05-07 13:03:26 +0530454
Samuel Hollandc70137c2021-09-12 09:22:42 -0500455 device_get_supply_regulator(dev, "usb0_vbus_power-supply",
456 &data->vbus_power_supply);
457
Jagan Tekid3c38282018-05-07 13:03:26 +0530458 data->usb_phy = plat;
459 for (i = 0; i < data->cfg->num_phys; i++) {
460 struct sun4i_usb_phy_plat *phy = &plat[i];
Samuel Holland9a361ef2023-10-31 01:39:53 -0500461 char name[32];
Jagan Tekid3c38282018-05-07 13:03:26 +0530462
Andre Przywarab2f0f312019-06-23 15:09:49 +0100463 if (data->cfg->missing_phys & BIT(i))
464 continue;
465
Samuel Holland9a361ef2023-10-31 01:39:53 -0500466 snprintf(name, sizeof(name), "usb%d_vbus-supply", i);
467 ret = device_get_supply_regulator(dev, name, &phy->vbus);
468 if (phy->vbus) {
469 ret = regulator_set_enable_if_allowed(phy->vbus, false);
Andre Przywara3331d222022-06-07 23:36:18 +0100470 if (ret)
471 return ret;
Jagan Tekid3c38282018-05-07 13:03:26 +0530472 }
473
Andre Przywara2624ded2025-03-30 17:13:23 +0100474 if (i == 0) {
475 ret = gpio_request_by_name(dev, "usb0_vbus_det-gpios",
476 0, &phy->gpio_vbus_det,
477 GPIOD_IS_IN);
478 if (ret && ret != -ENOENT) {
479 dev_err(dev,
480 "failed to get VBUS detect GPIO: %d\n",
481 ret);
Jagan Tekid3c38282018-05-07 13:03:26 +0530482 return ret;
Andre Przywara2624ded2025-03-30 17:13:23 +0100483 }
Jagan Tekid3c38282018-05-07 13:03:26 +0530484
Andre Przywarac35ab2d2025-03-30 17:13:50 +0100485 ret = gpio_request_by_name(dev, "usb0_id_det-gpios", 0,
486 &phy->gpio_id_det,
487 GPIOD_IS_IN | GPIOD_PULL_UP);
488 if (ret && ret != -ENOENT) {
489 dev_err(dev,
490 "failed to get ID detect GPIO: %d\n",
491 ret);
Jagan Tekid3c38282018-05-07 13:03:26 +0530492 return ret;
Andre Przywarac35ab2d2025-03-30 17:13:50 +0100493 }
Jagan Tekid3c38282018-05-07 13:03:26 +0530494 }
495
Jagan Teki0dc33332018-08-06 12:16:39 +0530496 if (data->cfg->dedicated_clocks)
497 snprintf(name, sizeof(name), "usb%d_phy", i);
498 else
499 strlcpy(name, "usb_phy", sizeof(name));
500
501 ret = clk_get_by_name(dev, name, &phy->clocks);
502 if (ret) {
503 dev_err(dev, "failed to get usb%d_phy clock phandle\n", i);
504 return ret;
505 }
506
Andre Przywara720f4e42023-06-12 00:32:39 +0100507 /* Helper clock from PHY2 for the H616 PHY quirk */
508 snprintf(name, sizeof(name), "pmu%d_clk", i);
509 ret = clk_get_by_name_optional(dev, name, &phy->clk2);
510 if (ret) {
511 dev_err(dev, "failed to get pmu%d_clk clock phandle\n",
512 i);
513 return ret;
514 }
515
Jagan Teki0dc33332018-08-06 12:16:39 +0530516 snprintf(name, sizeof(name), "usb%d_reset", i);
517 ret = reset_get_by_name(dev, name, &phy->resets);
518 if (ret) {
519 dev_err(dev, "failed to get usb%d_reset reset phandle\n", i);
520 return ret;
521 }
522
Jagan Tekid3c38282018-05-07 13:03:26 +0530523 if (i || data->cfg->phy0_dual_route) {
524 snprintf(name, sizeof(name), "pmu%d", i);
Matthias Schiffer47331932023-09-27 15:33:34 +0200525 phy->pmu = (void __iomem *)dev_read_addr_name_ptr(dev, name);
526 if (!phy->pmu)
527 return -EINVAL;
Jagan Tekid3c38282018-05-07 13:03:26 +0530528 }
529
530 phy->id = i;
Jagan Tekid3c38282018-05-07 13:03:26 +0530531 };
532
Jagan Tekid3c38282018-05-07 13:03:26 +0530533 debug("Allwinner Sun4I USB PHY driver loaded\n");
534 return 0;
535}
536
Jagan Teki5a3000f2018-05-07 13:03:31 +0530537static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
538 .num_phys = 3,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530539 .disc_thresh = 3,
540 .phyctl_offset = REG_PHYCTL_A10,
Jagan Teki0dc33332018-08-06 12:16:39 +0530541 .dedicated_clocks = false,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530542};
543
544static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
545 .num_phys = 2,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530546 .disc_thresh = 2,
547 .phyctl_offset = REG_PHYCTL_A10,
Jagan Teki0dc33332018-08-06 12:16:39 +0530548 .dedicated_clocks = false,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530549};
550
Jagan Teki1cbc80c2018-05-07 13:03:32 +0530551static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
552 .num_phys = 3,
Jagan Teki1cbc80c2018-05-07 13:03:32 +0530553 .disc_thresh = 3,
554 .phyctl_offset = REG_PHYCTL_A10,
Jagan Teki0dc33332018-08-06 12:16:39 +0530555 .dedicated_clocks = true,
Jagan Teki1cbc80c2018-05-07 13:03:32 +0530556};
557
Jagan Teki5a3000f2018-05-07 13:03:31 +0530558static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
559 .num_phys = 3,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530560 .disc_thresh = 2,
561 .phyctl_offset = REG_PHYCTL_A10,
Jagan Teki0dc33332018-08-06 12:16:39 +0530562 .dedicated_clocks = false,
Jagan Teki5a3000f2018-05-07 13:03:31 +0530563};
564
Jagan Teki00f9f6b2018-05-07 13:03:34 +0530565static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
566 .num_phys = 2,
Jagan Teki00f9f6b2018-05-07 13:03:34 +0530567 .disc_thresh = 3,
568 .phyctl_offset = REG_PHYCTL_A10,
Jagan Teki0dc33332018-08-06 12:16:39 +0530569 .dedicated_clocks = true,
Jagan Teki00f9f6b2018-05-07 13:03:34 +0530570};
571
Jagan Teki0e574bb2018-05-07 13:03:33 +0530572static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
573 .num_phys = 2,
Jagan Teki0e574bb2018-05-07 13:03:33 +0530574 .disc_thresh = 3,
575 .phyctl_offset = REG_PHYCTL_A33,
Jagan Teki0dc33332018-08-06 12:16:39 +0530576 .dedicated_clocks = true,
Jagan Teki0e574bb2018-05-07 13:03:33 +0530577};
578
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530579static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
580 .num_phys = 3,
Andre Przywarab06b90f2023-06-12 00:32:38 +0100581 .hsic_index = 2,
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530582 .phyctl_offset = REG_PHYCTL_A33,
Jagan Teki0dc33332018-08-06 12:16:39 +0530583 .dedicated_clocks = true,
Andre Przywarab06b90f2023-06-12 00:32:38 +0100584 .siddq_in_base = true,
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530585};
586
Jagan Tekic1b0e5a2018-05-07 13:03:28 +0530587static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
588 .num_phys = 4,
Jagan Tekic1b0e5a2018-05-07 13:03:28 +0530589 .disc_thresh = 3,
590 .phyctl_offset = REG_PHYCTL_A33,
Jagan Teki0dc33332018-08-06 12:16:39 +0530591 .dedicated_clocks = true,
Andre Przywara8662e7e2022-07-14 23:09:21 -0500592 .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
Jagan Tekic1b0e5a2018-05-07 13:03:28 +0530593 .phy0_dual_route = true,
594};
595
Andre Przywara47d49972020-01-01 23:44:48 +0000596static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
597 .num_phys = 3,
Andre Przywara47d49972020-01-01 23:44:48 +0000598 .disc_thresh = 3,
599 .phyctl_offset = REG_PHYCTL_A33,
600 .dedicated_clocks = true,
Andre Przywara8662e7e2022-07-14 23:09:21 -0500601 .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
Andre Przywara47d49972020-01-01 23:44:48 +0000602 .phy0_dual_route = true,
603};
604
Jagan Tekiac4bab42018-05-07 13:03:29 +0530605static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
606 .num_phys = 1,
Jagan Tekiac4bab42018-05-07 13:03:29 +0530607 .disc_thresh = 3,
608 .phyctl_offset = REG_PHYCTL_A33,
Jagan Teki0dc33332018-08-06 12:16:39 +0530609 .dedicated_clocks = true,
Andre Przywara8662e7e2022-07-14 23:09:21 -0500610 .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
Jagan Tekiac4bab42018-05-07 13:03:29 +0530611 .phy0_dual_route = true,
612};
613
Samuel Holland9f30cce2022-07-14 23:09:22 -0500614static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = {
615 .num_phys = 2,
Samuel Holland9f30cce2022-07-14 23:09:22 -0500616 .phyctl_offset = REG_PHYCTL_A33,
617 .dedicated_clocks = true,
618 .hci_phy_ctl_clear = PHY_CTL_SIDDQ,
619 .phy0_dual_route = true,
Andre Przywarab06b90f2023-06-12 00:32:38 +0100620 .siddq_in_base = true,
Samuel Holland9f30cce2022-07-14 23:09:22 -0500621};
622
Jagan Tekid3c38282018-05-07 13:03:26 +0530623static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
624 .num_phys = 2,
Jagan Tekid3c38282018-05-07 13:03:26 +0530625 .disc_thresh = 3,
626 .phyctl_offset = REG_PHYCTL_A33,
Jagan Teki0dc33332018-08-06 12:16:39 +0530627 .dedicated_clocks = true,
Andre Przywara8662e7e2022-07-14 23:09:21 -0500628 .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
Jagan Tekid3c38282018-05-07 13:03:26 +0530629 .phy0_dual_route = true,
Andre Przywarab2f0f312019-06-23 15:09:49 +0100630};
631
632static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
633 .num_phys = 4,
Andre Przywarab2f0f312019-06-23 15:09:49 +0100634 .disc_thresh = 3,
635 .phyctl_offset = REG_PHYCTL_A33,
636 .dedicated_clocks = true,
Andre Przywarab2f0f312019-06-23 15:09:49 +0100637 .phy0_dual_route = true,
Andre Przywarab06b90f2023-06-12 00:32:38 +0100638 .siddq_in_base = true,
Andre Przywarab2f0f312019-06-23 15:09:49 +0100639 .missing_phys = BIT(1) | BIT(2),
Jagan Tekid3c38282018-05-07 13:03:26 +0530640};
641
Andre Przywara8a3292a2023-06-12 00:32:40 +0100642static const struct sun4i_usb_phy_cfg sun50i_h616_cfg = {
643 .num_phys = 4,
644 .disc_thresh = 3,
645 .phyctl_offset = REG_PHYCTL_A33,
646 .dedicated_clocks = true,
647 .phy0_dual_route = true,
648 .hci_phy_ctl_clear = PHY_CTL_SIDDQ,
649 .needs_phy2_siddq = true,
650 .siddq_in_base = true,
651};
652
Andre Przywaraab81b0f2023-06-12 00:32:36 +0100653static const struct sun4i_usb_phy_cfg suniv_f1c100s_cfg = {
654 .num_phys = 1,
Andre Przywaraab81b0f2023-06-12 00:32:36 +0100655 .disc_thresh = 3,
656 .phyctl_offset = REG_PHYCTL_A10,
657 .dedicated_clocks = true,
658};
659
Jagan Tekid3c38282018-05-07 13:03:26 +0530660static const struct udevice_id sun4i_usb_phy_ids[] = {
Jagan Teki5a3000f2018-05-07 13:03:31 +0530661 { .compatible = "allwinner,sun4i-a10-usb-phy", .data = (ulong)&sun4i_a10_cfg },
662 { .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg },
Jagan Teki1cbc80c2018-05-07 13:03:32 +0530663 { .compatible = "allwinner,sun6i-a31-usb-phy", .data = (ulong)&sun6i_a31_cfg },
Jagan Teki5a3000f2018-05-07 13:03:31 +0530664 { .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg },
Jagan Teki00f9f6b2018-05-07 13:03:34 +0530665 { .compatible = "allwinner,sun8i-a23-usb-phy", .data = (ulong)&sun8i_a23_cfg },
Jagan Teki0e574bb2018-05-07 13:03:33 +0530666 { .compatible = "allwinner,sun8i-a33-usb-phy", .data = (ulong)&sun8i_a33_cfg },
Jagan Teki05a7b9f2018-05-07 13:03:30 +0530667 { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg },
Jagan Tekic1b0e5a2018-05-07 13:03:28 +0530668 { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg },
Andre Przywara47d49972020-01-01 23:44:48 +0000669 { .compatible = "allwinner,sun8i-r40-usb-phy", .data = (ulong)&sun8i_r40_cfg },
Jagan Tekiac4bab42018-05-07 13:03:29 +0530670 { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg },
Samuel Holland9f30cce2022-07-14 23:09:22 -0500671 { .compatible = "allwinner,sun20i-d1-usb-phy", .data = (ulong)&sun20i_d1_cfg },
Jagan Tekid3c38282018-05-07 13:03:26 +0530672 { .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg},
Andre Przywarab2f0f312019-06-23 15:09:49 +0100673 { .compatible = "allwinner,sun50i-h6-usb-phy", .data = (ulong)&sun50i_h6_cfg},
Andre Przywara8a3292a2023-06-12 00:32:40 +0100674 { .compatible = "allwinner,sun50i-h616-usb-phy", .data = (ulong)&sun50i_h616_cfg },
Andre Przywaraab81b0f2023-06-12 00:32:36 +0100675 { .compatible = "allwinner,suniv-f1c100s-usb-phy", .data = (ulong)&suniv_f1c100s_cfg },
Jagan Tekid3c38282018-05-07 13:03:26 +0530676 { }
677};
678
679U_BOOT_DRIVER(sun4i_usb_phy) = {
680 .name = "sun4i_usb_phy",
681 .id = UCLASS_PHY,
682 .of_match = sun4i_usb_phy_ids,
683 .ops = &sun4i_usb_phy_ops,
684 .probe = sun4i_usb_phy_probe,
Simon Glass71fa5b42020-12-03 16:55:18 -0700685 .plat_auto = sizeof(struct sun4i_usb_phy_plat[MAX_PHYS]),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700686 .priv_auto = sizeof(struct sun4i_usb_phy_data),
Jagan Tekid3c38282018-05-07 13:03:26 +0530687};