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