1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Intel Keem Bay USB PHY driver |
4 | * Copyright (C) 2020 Intel Corporation |
5 | */ |
6 | |
7 | #include <linux/bitfield.h> |
8 | #include <linux/bits.h> |
9 | #include <linux/clk.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/regmap.h> |
16 | |
17 | /* USS (USB Subsystem) clock control registers */ |
18 | #define USS_CPR_CLK_EN 0x00 |
19 | #define USS_CPR_CLK_SET 0x04 |
20 | #define USS_CPR_CLK_CLR 0x08 |
21 | #define USS_CPR_RST_EN 0x10 |
22 | #define USS_CPR_RST_SET 0x14 |
23 | #define USS_CPR_RST_CLR 0x18 |
24 | |
25 | /* USS clock/reset bit fields */ |
26 | #define USS_CPR_PHY_TST BIT(6) |
27 | #define USS_CPR_LOW_JIT BIT(5) |
28 | #define USS_CPR_CORE BIT(4) |
29 | #define USS_CPR_SUSPEND BIT(3) |
30 | #define USS_CPR_ALT_REF BIT(2) |
31 | #define USS_CPR_REF BIT(1) |
32 | #define USS_CPR_SYS BIT(0) |
33 | #define USS_CPR_MASK GENMASK(6, 0) |
34 | |
35 | /* USS APB slave registers */ |
36 | #define USS_USB_CTRL_CFG0 0x10 |
37 | #define VCC_RESET_N_MASK BIT(31) |
38 | |
39 | #define USS_USB_PHY_CFG0 0x30 |
40 | #define POR_MASK BIT(15) |
41 | #define PHY_RESET_MASK BIT(14) |
42 | #define PHY_REF_USE_PAD_MASK BIT(5) |
43 | |
44 | #define USS_USB_PHY_CFG6 0x64 |
45 | #define PHY0_SRAM_EXT_LD_DONE_MASK BIT(23) |
46 | |
47 | #define USS_USB_PARALLEL_IF_CTRL 0xa0 |
48 | #define USB_PHY_CR_PARA_SEL_MASK BIT(2) |
49 | |
50 | #define USS_USB_TSET_SIGNALS_AND_GLOB 0xac |
51 | #define USB_PHY_CR_PARA_CLK_EN_MASK BIT(7) |
52 | |
53 | #define USS_USB_STATUS_REG 0xb8 |
54 | #define PHY0_SRAM_INIT_DONE_MASK BIT(3) |
55 | |
56 | #define USS_USB_TIEOFFS_CONSTANTS_REG1 0xc0 |
57 | #define IDDQ_ENABLE_MASK BIT(10) |
58 | |
59 | struct keembay_usb_phy { |
60 | struct device *dev; |
61 | struct regmap *regmap_cpr; |
62 | struct regmap *regmap_slv; |
63 | }; |
64 | |
65 | static const struct regmap_config keembay_regmap_config = { |
66 | .reg_bits = 32, |
67 | .val_bits = 32, |
68 | .reg_stride = 4, |
69 | .max_register = USS_USB_TIEOFFS_CONSTANTS_REG1, |
70 | }; |
71 | |
72 | static int keembay_usb_clocks_on(struct keembay_usb_phy *priv) |
73 | { |
74 | int ret; |
75 | |
76 | ret = regmap_update_bits(map: priv->regmap_cpr, USS_CPR_CLK_SET, |
77 | USS_CPR_MASK, USS_CPR_MASK); |
78 | if (ret) { |
79 | dev_err(priv->dev, "error clock set: %d\n" , ret); |
80 | return ret; |
81 | } |
82 | |
83 | ret = regmap_update_bits(map: priv->regmap_cpr, USS_CPR_RST_SET, |
84 | USS_CPR_MASK, USS_CPR_MASK); |
85 | if (ret) { |
86 | dev_err(priv->dev, "error reset set: %d\n" , ret); |
87 | return ret; |
88 | } |
89 | |
90 | ret = regmap_update_bits(map: priv->regmap_slv, |
91 | USS_USB_TIEOFFS_CONSTANTS_REG1, |
92 | IDDQ_ENABLE_MASK, |
93 | FIELD_PREP(IDDQ_ENABLE_MASK, 0)); |
94 | if (ret) { |
95 | dev_err(priv->dev, "error iddq disable: %d\n" , ret); |
96 | return ret; |
97 | } |
98 | |
99 | /* Wait 30us to ensure all analog blocks are powered up. */ |
100 | usleep_range(min: 30, max: 60); |
101 | |
102 | ret = regmap_update_bits(map: priv->regmap_slv, USS_USB_PHY_CFG0, |
103 | PHY_REF_USE_PAD_MASK, |
104 | FIELD_PREP(PHY_REF_USE_PAD_MASK, 1)); |
105 | if (ret) |
106 | dev_err(priv->dev, "error ref clock select: %d\n" , ret); |
107 | |
108 | return ret; |
109 | } |
110 | |
111 | static int keembay_usb_core_off(struct keembay_usb_phy *priv) |
112 | { |
113 | int ret; |
114 | |
115 | ret = regmap_update_bits(map: priv->regmap_slv, USS_USB_CTRL_CFG0, |
116 | VCC_RESET_N_MASK, |
117 | FIELD_PREP(VCC_RESET_N_MASK, 0)); |
118 | if (ret) |
119 | dev_err(priv->dev, "error core reset: %d\n" , ret); |
120 | |
121 | return ret; |
122 | } |
123 | |
124 | static int keembay_usb_core_on(struct keembay_usb_phy *priv) |
125 | { |
126 | int ret; |
127 | |
128 | ret = regmap_update_bits(map: priv->regmap_slv, USS_USB_CTRL_CFG0, |
129 | VCC_RESET_N_MASK, |
130 | FIELD_PREP(VCC_RESET_N_MASK, 1)); |
131 | if (ret) |
132 | dev_err(priv->dev, "error core on: %d\n" , ret); |
133 | |
134 | return ret; |
135 | } |
136 | |
137 | static int keembay_usb_phys_on(struct keembay_usb_phy *priv) |
138 | { |
139 | int ret; |
140 | |
141 | ret = regmap_update_bits(map: priv->regmap_slv, USS_USB_PHY_CFG0, |
142 | POR_MASK | PHY_RESET_MASK, |
143 | FIELD_PREP(POR_MASK | PHY_RESET_MASK, 0)); |
144 | if (ret) |
145 | dev_err(priv->dev, "error phys on: %d\n" , ret); |
146 | |
147 | return ret; |
148 | } |
149 | |
150 | static int keembay_usb_phy_init(struct phy *phy) |
151 | { |
152 | struct keembay_usb_phy *priv = phy_get_drvdata(phy); |
153 | u32 val; |
154 | int ret; |
155 | |
156 | ret = keembay_usb_core_off(priv); |
157 | if (ret) |
158 | return ret; |
159 | |
160 | /* |
161 | * According to Keem Bay datasheet, wait minimum 20us after clock |
162 | * enable before bringing PHYs out of reset. |
163 | */ |
164 | usleep_range(min: 20, max: 40); |
165 | |
166 | ret = keembay_usb_phys_on(priv); |
167 | if (ret) |
168 | return ret; |
169 | |
170 | ret = regmap_update_bits(map: priv->regmap_slv, |
171 | USS_USB_TSET_SIGNALS_AND_GLOB, |
172 | USB_PHY_CR_PARA_CLK_EN_MASK, |
173 | FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 0)); |
174 | if (ret) { |
175 | dev_err(priv->dev, "error cr clock disable: %d\n" , ret); |
176 | return ret; |
177 | } |
178 | |
179 | /* |
180 | * According to Keem Bay datasheet, wait 2us after disabling the |
181 | * clock into the USB 3.x parallel interface. |
182 | */ |
183 | udelay(2); |
184 | |
185 | ret = regmap_update_bits(map: priv->regmap_slv, |
186 | USS_USB_PARALLEL_IF_CTRL, |
187 | USB_PHY_CR_PARA_SEL_MASK, |
188 | FIELD_PREP(USB_PHY_CR_PARA_SEL_MASK, 1)); |
189 | if (ret) { |
190 | dev_err(priv->dev, "error cr select: %d\n" , ret); |
191 | return ret; |
192 | } |
193 | |
194 | ret = regmap_update_bits(map: priv->regmap_slv, |
195 | USS_USB_TSET_SIGNALS_AND_GLOB, |
196 | USB_PHY_CR_PARA_CLK_EN_MASK, |
197 | FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 1)); |
198 | if (ret) { |
199 | dev_err(priv->dev, "error cr clock enable: %d\n" , ret); |
200 | return ret; |
201 | } |
202 | |
203 | ret = regmap_read_poll_timeout(priv->regmap_slv, USS_USB_STATUS_REG, |
204 | val, val & PHY0_SRAM_INIT_DONE_MASK, |
205 | USEC_PER_MSEC, 10 * USEC_PER_MSEC); |
206 | if (ret) { |
207 | dev_err(priv->dev, "SRAM init not done: %d\n" , ret); |
208 | return ret; |
209 | } |
210 | |
211 | ret = regmap_update_bits(map: priv->regmap_slv, USS_USB_PHY_CFG6, |
212 | PHY0_SRAM_EXT_LD_DONE_MASK, |
213 | FIELD_PREP(PHY0_SRAM_EXT_LD_DONE_MASK, 1)); |
214 | if (ret) { |
215 | dev_err(priv->dev, "error SRAM init done set: %d\n" , ret); |
216 | return ret; |
217 | } |
218 | |
219 | /* |
220 | * According to Keem Bay datasheet, wait 20us after setting the |
221 | * SRAM load done bit, before releasing the controller reset. |
222 | */ |
223 | usleep_range(min: 20, max: 40); |
224 | |
225 | return keembay_usb_core_on(priv); |
226 | } |
227 | |
228 | static const struct phy_ops ops = { |
229 | .init = keembay_usb_phy_init, |
230 | .owner = THIS_MODULE, |
231 | }; |
232 | |
233 | static int keembay_usb_phy_probe(struct platform_device *pdev) |
234 | { |
235 | struct device *dev = &pdev->dev; |
236 | struct keembay_usb_phy *priv; |
237 | struct phy *generic_phy; |
238 | struct phy_provider *phy_provider; |
239 | void __iomem *base; |
240 | int ret; |
241 | |
242 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
243 | if (!priv) |
244 | return -ENOMEM; |
245 | |
246 | base = devm_platform_ioremap_resource_byname(pdev, name: "cpr-apb-base" ); |
247 | if (IS_ERR(ptr: base)) |
248 | return PTR_ERR(ptr: base); |
249 | |
250 | priv->regmap_cpr = devm_regmap_init_mmio(dev, base, |
251 | &keembay_regmap_config); |
252 | if (IS_ERR(ptr: priv->regmap_cpr)) |
253 | return PTR_ERR(ptr: priv->regmap_cpr); |
254 | |
255 | base = devm_platform_ioremap_resource_byname(pdev, name: "slv-apb-base" ); |
256 | if (IS_ERR(ptr: base)) |
257 | return PTR_ERR(ptr: base); |
258 | |
259 | priv->regmap_slv = devm_regmap_init_mmio(dev, base, |
260 | &keembay_regmap_config); |
261 | if (IS_ERR(ptr: priv->regmap_slv)) |
262 | return PTR_ERR(ptr: priv->regmap_slv); |
263 | |
264 | generic_phy = devm_phy_create(dev, node: dev->of_node, ops: &ops); |
265 | if (IS_ERR(ptr: generic_phy)) |
266 | return dev_err_probe(dev, err: PTR_ERR(ptr: generic_phy), |
267 | fmt: "failed to create PHY\n" ); |
268 | |
269 | phy_set_drvdata(phy: generic_phy, data: priv); |
270 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
271 | if (IS_ERR(ptr: phy_provider)) |
272 | return dev_err_probe(dev, err: PTR_ERR(ptr: phy_provider), |
273 | fmt: "failed to register phy provider\n" ); |
274 | |
275 | /* Setup USB subsystem clocks */ |
276 | ret = keembay_usb_clocks_on(priv); |
277 | if (ret) |
278 | return ret; |
279 | |
280 | /* and turn on the DWC3 core, prior to DWC3 driver init. */ |
281 | return keembay_usb_core_on(priv); |
282 | } |
283 | |
284 | static const struct of_device_id keembay_usb_phy_dt_ids[] = { |
285 | { .compatible = "intel,keembay-usb-phy" }, |
286 | {} |
287 | }; |
288 | MODULE_DEVICE_TABLE(of, keembay_usb_phy_dt_ids); |
289 | |
290 | static struct platform_driver keembay_usb_phy_driver = { |
291 | .probe = keembay_usb_phy_probe, |
292 | .driver = { |
293 | .name = "keembay-usb-phy" , |
294 | .of_match_table = keembay_usb_phy_dt_ids, |
295 | }, |
296 | }; |
297 | module_platform_driver(keembay_usb_phy_driver); |
298 | |
299 | MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>" ); |
300 | MODULE_DESCRIPTION("Intel Keem Bay USB PHY driver" ); |
301 | MODULE_LICENSE("GPL v2" ); |
302 | |