blob: 04f0f0e7817d0f2bbd38c814bd5b5ca6507511ff [file] [log] [blame]
Bhupesh Sharma9f153c62024-04-03 14:07:38 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4 * Copyright (C) 2023 Bhupesh Sharma <bhupesh.sharma@linaro.org>
5 *
6 * Based on Linux driver
7 */
8
Bhupesh Sharma9f153c62024-04-03 14:07:38 +02009#include <dm.h>
10#include <dm/device_compat.h>
11#include <dm/devres.h>
12#include <generic-phy.h>
13#include <malloc.h>
14#include <reset.h>
15
16#include <asm/io.h>
17#include <linux/bitops.h>
Bhupesh Sharma9f153c62024-04-03 14:07:38 +020018#include <linux/delay.h>
19#include <linux/iopoll.h>
20
21#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c)
22#define SLEEPM BIT(0)
23#define OPMODE_MASK GENMASK(4, 3)
24#define OPMODE_NORMAL (0x00)
25#define OPMODE_NONDRIVING BIT(3)
26#define TERMSEL BIT(5)
27
28#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50)
29#define POR BIT(1)
30
31#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
32#define SIDDQ BIT(2)
33#define RETENABLEN BIT(3)
34#define FSEL_MASK GENMASK(6, 4)
35#define FSEL_DEFAULT (0x3 << 4)
36
37#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58)
38#define VBUSVLDEXTSEL0 BIT(4)
39#define PLLBTUNE BIT(5)
40
41#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c)
42#define VREGBYPASS BIT(0)
43
44#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60)
45#define VBUSVLDEXT0 BIT(0)
46
47#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64)
48#define USB2_AUTO_RESUME BIT(0)
49#define USB2_SUSPEND_N BIT(2)
50#define USB2_SUSPEND_N_SEL BIT(3)
51
52#define USB2_PHY_USB_PHY_CFG0 (0x94)
53#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)
54#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)
55
56#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0)
57#define REFCLK_SEL_MASK GENMASK(1, 0)
58#define REFCLK_SEL_DEFAULT (0x2 << 0)
59
60struct qcom_snps_hsphy {
61 void __iomem *base;
Bhupesh Sharma9f153c62024-04-03 14:07:38 +020062 struct reset_ctl_bulk resets;
63};
64
65/*
66 * We should just be able to use clrsetbits_le32() here, but this results
67 * in crashes on some boards. This is suspected to be because of some bus
68 * arbitration quirks with the PHY (i.e. it takes several bus clock cycles
69 * for the write to actually go through). The readl_relaxed() at the end will
70 * block until the write is completed (and all registers updated), and thus
71 * ensure that we don't access the PHY registers when they're in an
72 * undetermined state.
73 */
74static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
75 u32 mask, u32 val)
76{
77 u32 reg;
78
79 reg = readl_relaxed(base + offset);
80
81 reg &= ~mask;
82 reg |= val & mask;
83 writel_relaxed(reg, base + offset);
84
85 /* Ensure above write is completed */
86 readl_relaxed(base + offset);
87}
88
89static int qcom_snps_hsphy_usb_init(struct phy *phy)
90{
91 struct qcom_snps_hsphy *priv = dev_get_priv(phy->dev);
92
93 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_CFG0,
94 UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
95 UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
96 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_UTMI_CTRL5, POR,
97 POR);
98 qcom_snps_hsphy_write_mask(priv->base,
99 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, FSEL_MASK, 0);
100 qcom_snps_hsphy_write_mask(priv->base,
101 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
102 PLLBTUNE, PLLBTUNE);
103 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
104 REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
105 qcom_snps_hsphy_write_mask(priv->base,
106 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
107 VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
108 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
109 VBUSVLDEXT0, VBUSVLDEXT0);
110
111 qcom_snps_hsphy_write_mask(priv->base,
112 USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
113 VREGBYPASS, VREGBYPASS);
114
115 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
116 USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
117 USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
118
119 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
120 SLEEPM, SLEEPM);
121
122 qcom_snps_hsphy_write_mask(
123 priv->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, SIDDQ, 0);
124
125 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_UTMI_CTRL5, POR,
126 0);
127
128 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
129 USB2_SUSPEND_N_SEL, 0);
130
131 qcom_snps_hsphy_write_mask(priv->base, USB2_PHY_USB_PHY_CFG0,
132 UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
133
134 return 0;
135}
136
137static int qcom_snps_hsphy_power_on(struct phy *phy)
138{
139 struct qcom_snps_hsphy *priv = dev_get_priv(phy->dev);
140 int ret;
141
Bhupesh Sharma9f153c62024-04-03 14:07:38 +0200142 ret = reset_deassert_bulk(&priv->resets);
143 if (ret)
144 return ret;
145
146 ret = qcom_snps_hsphy_usb_init(phy);
147 if (ret)
148 return ret;
149
150 return 0;
151}
152
153static int qcom_snps_hsphy_power_off(struct phy *phy)
154{
155 struct qcom_snps_hsphy *priv = dev_get_priv(phy->dev);
156
157 reset_assert_bulk(&priv->resets);
Bhupesh Sharma9f153c62024-04-03 14:07:38 +0200158
159 return 0;
160}
161
162static int qcom_snps_hsphy_phy_probe(struct udevice *dev)
163{
164 struct qcom_snps_hsphy *priv = dev_get_priv(dev);
165 int ret;
166
167 priv->base = dev_read_addr_ptr(dev);
168 if (IS_ERR(priv->base))
169 return PTR_ERR(priv->base);
170
Bhupesh Sharma9f153c62024-04-03 14:07:38 +0200171 ret = reset_get_bulk(dev, &priv->resets);
172 if (ret < 0) {
173 printf("failed to get resets, ret = %d\n", ret);
174 return ret;
175 }
176
Bhupesh Sharma9f153c62024-04-03 14:07:38 +0200177 reset_deassert_bulk(&priv->resets);
178
179 return 0;
180}
181
182static struct phy_ops qcom_snps_hsphy_phy_ops = {
183 .power_on = qcom_snps_hsphy_power_on,
184 .power_off = qcom_snps_hsphy_power_off,
185};
186
187static const struct udevice_id qcom_snps_hsphy_phy_ids[] = {
188 { .compatible = "qcom,sm8150-usb-hs-phy" },
189 { .compatible = "qcom,usb-snps-hs-5nm-phy" },
190 { .compatible = "qcom,usb-snps-hs-7nm-phy" },
191 { .compatible = "qcom,usb-snps-femto-v2-phy" },
192 {}
193};
194
195U_BOOT_DRIVER(qcom_usb_qcom_snps_hsphy) = {
196 .name = "qcom-snps-hsphy",
197 .id = UCLASS_PHY,
198 .of_match = qcom_snps_hsphy_phy_ids,
199 .ops = &qcom_snps_hsphy_phy_ops,
200 .probe = qcom_snps_hsphy_phy_probe,
201 .priv_auto = sizeof(struct qcom_snps_hsphy),
202};