1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2021 Marvell |
4 | * |
5 | * Authors: |
6 | * Konstantin Porotchkin <kostap@marvell.com> |
7 | * |
8 | * Marvell CP110 UTMI PHY driver |
9 | */ |
10 | |
11 | #include <linux/io.h> |
12 | #include <linux/iopoll.h> |
13 | #include <linux/mfd/syscon.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/phy/phy.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/usb/of.h> |
20 | #include <linux/usb/otg.h> |
21 | |
22 | #define UTMI_PHY_PORTS 2 |
23 | |
24 | /* CP110 UTMI register macro definetions */ |
25 | #define SYSCON_USB_CFG_REG 0x420 |
26 | #define USB_CFG_DEVICE_EN_MASK BIT(0) |
27 | #define USB_CFG_DEVICE_MUX_OFFSET 1 |
28 | #define USB_CFG_DEVICE_MUX_MASK BIT(1) |
29 | #define USB_CFG_PLL_MASK BIT(25) |
30 | |
31 | #define SYSCON_UTMI_CFG_REG(id) (0x440 + (id) * 4) |
32 | #define UTMI_PHY_CFG_PU_MASK BIT(5) |
33 | |
34 | #define UTMI_PLL_CTRL_REG 0x0 |
35 | #define PLL_REFDIV_OFFSET 0 |
36 | #define PLL_REFDIV_MASK GENMASK(6, 0) |
37 | #define PLL_REFDIV_VAL 0x5 |
38 | #define PLL_FBDIV_OFFSET 16 |
39 | #define PLL_FBDIV_MASK GENMASK(24, 16) |
40 | #define PLL_FBDIV_VAL 0x60 |
41 | #define PLL_SEL_LPFR_MASK GENMASK(29, 28) |
42 | #define PLL_RDY BIT(31) |
43 | #define UTMI_CAL_CTRL_REG 0x8 |
44 | #define IMPCAL_VTH_OFFSET 8 |
45 | #define IMPCAL_VTH_MASK GENMASK(10, 8) |
46 | #define IMPCAL_VTH_VAL 0x7 |
47 | #define IMPCAL_DONE BIT(23) |
48 | #define PLLCAL_DONE BIT(31) |
49 | #define UTMI_TX_CH_CTRL_REG 0xC |
50 | #define DRV_EN_LS_OFFSET 12 |
51 | #define DRV_EN_LS_MASK GENMASK(15, 12) |
52 | #define IMP_SEL_LS_OFFSET 16 |
53 | #define IMP_SEL_LS_MASK GENMASK(19, 16) |
54 | #define TX_AMP_OFFSET 20 |
55 | #define TX_AMP_MASK GENMASK(22, 20) |
56 | #define TX_AMP_VAL 0x4 |
57 | #define UTMI_RX_CH_CTRL0_REG 0x14 |
58 | #define SQ_DET_EN BIT(15) |
59 | #define SQ_ANA_DTC_SEL BIT(28) |
60 | #define UTMI_RX_CH_CTRL1_REG 0x18 |
61 | #define SQ_AMP_CAL_OFFSET 0 |
62 | #define SQ_AMP_CAL_MASK GENMASK(2, 0) |
63 | #define SQ_AMP_CAL_VAL 1 |
64 | #define SQ_AMP_CAL_EN BIT(3) |
65 | #define UTMI_CTRL_STATUS0_REG 0x24 |
66 | #define SUSPENDM BIT(22) |
67 | #define TEST_SEL BIT(25) |
68 | #define UTMI_CHGDTC_CTRL_REG 0x38 |
69 | #define VDAT_OFFSET 8 |
70 | #define VDAT_MASK GENMASK(9, 8) |
71 | #define VDAT_VAL 1 |
72 | #define VSRC_OFFSET 10 |
73 | #define VSRC_MASK GENMASK(11, 10) |
74 | #define VSRC_VAL 1 |
75 | |
76 | #define PLL_LOCK_DELAY_US 10000 |
77 | #define PLL_LOCK_TIMEOUT_US 1000000 |
78 | |
79 | #define PORT_REGS(p) ((p)->priv->regs + (p)->id * 0x1000) |
80 | |
81 | /** |
82 | * struct mvebu_cp110_utmi - PHY driver data |
83 | * |
84 | * @regs: PHY registers |
85 | * @syscon: Regmap with system controller registers |
86 | * @dev: device driver handle |
87 | * @ops: phy ops |
88 | */ |
89 | struct mvebu_cp110_utmi { |
90 | void __iomem *regs; |
91 | struct regmap *syscon; |
92 | struct device *dev; |
93 | const struct phy_ops *ops; |
94 | }; |
95 | |
96 | /** |
97 | * struct mvebu_cp110_utmi_port - PHY port data |
98 | * |
99 | * @priv: PHY driver data |
100 | * @id: PHY port ID |
101 | * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL |
102 | */ |
103 | struct mvebu_cp110_utmi_port { |
104 | struct mvebu_cp110_utmi *priv; |
105 | u32 id; |
106 | enum usb_dr_mode dr_mode; |
107 | }; |
108 | |
109 | static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port) |
110 | { |
111 | u32 reg; |
112 | |
113 | /* |
114 | * Setup PLL. |
115 | * The reference clock is the frequency of quartz resonator |
116 | * connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC. |
117 | * Register init values are matching the 40MHz default clock. |
118 | * The crystal used for all platform boards is now 25MHz. |
119 | * See the functional specification for details. |
120 | */ |
121 | reg = readl(PORT_REGS(port) + UTMI_PLL_CTRL_REG); |
122 | reg &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK | PLL_SEL_LPFR_MASK); |
123 | reg |= (PLL_REFDIV_VAL << PLL_REFDIV_OFFSET) | |
124 | (PLL_FBDIV_VAL << PLL_FBDIV_OFFSET); |
125 | writel(val: reg, PORT_REGS(port) + UTMI_PLL_CTRL_REG); |
126 | |
127 | /* Impedance Calibration Threshold Setting */ |
128 | reg = readl(PORT_REGS(port) + UTMI_CAL_CTRL_REG); |
129 | reg &= ~IMPCAL_VTH_MASK; |
130 | reg |= IMPCAL_VTH_VAL << IMPCAL_VTH_OFFSET; |
131 | writel(val: reg, PORT_REGS(port) + UTMI_CAL_CTRL_REG); |
132 | |
133 | /* Set LS TX driver strength coarse control */ |
134 | reg = readl(PORT_REGS(port) + UTMI_TX_CH_CTRL_REG); |
135 | reg &= ~TX_AMP_MASK; |
136 | reg |= TX_AMP_VAL << TX_AMP_OFFSET; |
137 | writel(val: reg, PORT_REGS(port) + UTMI_TX_CH_CTRL_REG); |
138 | |
139 | /* Disable SQ and enable analog squelch detect */ |
140 | reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG); |
141 | reg &= ~SQ_DET_EN; |
142 | reg |= SQ_ANA_DTC_SEL; |
143 | writel(val: reg, PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG); |
144 | |
145 | /* |
146 | * Set External squelch calibration number and |
147 | * enable the External squelch calibration |
148 | */ |
149 | reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG); |
150 | reg &= ~SQ_AMP_CAL_MASK; |
151 | reg |= (SQ_AMP_CAL_VAL << SQ_AMP_CAL_OFFSET) | SQ_AMP_CAL_EN; |
152 | writel(val: reg, PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG); |
153 | |
154 | /* |
155 | * Set Control VDAT Reference Voltage - 0.325V and |
156 | * Control VSRC Reference Voltage - 0.6V |
157 | */ |
158 | reg = readl(PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG); |
159 | reg &= ~(VDAT_MASK | VSRC_MASK); |
160 | reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET); |
161 | writel(val: reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG); |
162 | } |
163 | |
164 | static int mvebu_cp110_utmi_phy_power_off(struct phy *phy) |
165 | { |
166 | struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy); |
167 | struct mvebu_cp110_utmi *utmi = port->priv; |
168 | int i; |
169 | |
170 | /* Power down UTMI PHY port */ |
171 | regmap_clear_bits(map: utmi->syscon, SYSCON_UTMI_CFG_REG(port->id), |
172 | UTMI_PHY_CFG_PU_MASK); |
173 | |
174 | for (i = 0; i < UTMI_PHY_PORTS; i++) { |
175 | int test = regmap_test_bits(map: utmi->syscon, |
176 | SYSCON_UTMI_CFG_REG(i), |
177 | UTMI_PHY_CFG_PU_MASK); |
178 | /* skip PLL shutdown if there are active UTMI PHY ports */ |
179 | if (test != 0) |
180 | return 0; |
181 | } |
182 | |
183 | /* PLL Power down if all UTMI PHYs are down */ |
184 | regmap_clear_bits(map: utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK); |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | static int mvebu_cp110_utmi_phy_power_on(struct phy *phy) |
190 | { |
191 | struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy); |
192 | struct mvebu_cp110_utmi *utmi = port->priv; |
193 | struct device *dev = &phy->dev; |
194 | int ret; |
195 | u32 reg; |
196 | |
197 | /* It is necessary to power off UTMI before configuration */ |
198 | ret = mvebu_cp110_utmi_phy_power_off(phy); |
199 | if (ret) { |
200 | dev_err(dev, "UTMI power OFF before power ON failed\n" ); |
201 | return ret; |
202 | } |
203 | |
204 | /* |
205 | * If UTMI port is connected to USB Device controller, |
206 | * configure the USB MUX prior to UTMI PHY initialization. |
207 | * The single USB device controller can be connected |
208 | * to UTMI0 or to UTMI1 PHY port, but not to both. |
209 | */ |
210 | if (port->dr_mode == USB_DR_MODE_PERIPHERAL) { |
211 | regmap_update_bits(map: utmi->syscon, SYSCON_USB_CFG_REG, |
212 | USB_CFG_DEVICE_EN_MASK | USB_CFG_DEVICE_MUX_MASK, |
213 | USB_CFG_DEVICE_EN_MASK | |
214 | (port->id << USB_CFG_DEVICE_MUX_OFFSET)); |
215 | } |
216 | |
217 | /* Set Test suspendm mode and enable Test UTMI select */ |
218 | reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); |
219 | reg |= SUSPENDM | TEST_SEL; |
220 | writel(val: reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); |
221 | |
222 | /* Wait for UTMI power down */ |
223 | mdelay(1); |
224 | |
225 | /* PHY port setup first */ |
226 | mvebu_cp110_utmi_port_setup(port); |
227 | |
228 | /* Power UP UTMI PHY */ |
229 | regmap_set_bits(map: utmi->syscon, SYSCON_UTMI_CFG_REG(port->id), |
230 | UTMI_PHY_CFG_PU_MASK); |
231 | |
232 | /* Disable Test UTMI select */ |
233 | reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); |
234 | reg &= ~TEST_SEL; |
235 | writel(val: reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG); |
236 | |
237 | /* Wait for impedance calibration */ |
238 | ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg, |
239 | reg & IMPCAL_DONE, |
240 | PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); |
241 | if (ret) { |
242 | dev_err(dev, "Failed to end UTMI impedance calibration\n" ); |
243 | return ret; |
244 | } |
245 | |
246 | /* Wait for PLL calibration */ |
247 | ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg, |
248 | reg & PLLCAL_DONE, |
249 | PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); |
250 | if (ret) { |
251 | dev_err(dev, "Failed to end UTMI PLL calibration\n" ); |
252 | return ret; |
253 | } |
254 | |
255 | /* Wait for PLL ready */ |
256 | ret = readl_poll_timeout(PORT_REGS(port) + UTMI_PLL_CTRL_REG, reg, |
257 | reg & PLL_RDY, |
258 | PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); |
259 | if (ret) { |
260 | dev_err(dev, "PLL is not ready\n" ); |
261 | return ret; |
262 | } |
263 | |
264 | /* PLL Power up */ |
265 | regmap_set_bits(map: utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static const struct phy_ops mvebu_cp110_utmi_phy_ops = { |
271 | .power_on = mvebu_cp110_utmi_phy_power_on, |
272 | .power_off = mvebu_cp110_utmi_phy_power_off, |
273 | .owner = THIS_MODULE, |
274 | }; |
275 | |
276 | static const struct of_device_id mvebu_cp110_utmi_of_match[] = { |
277 | { .compatible = "marvell,cp110-utmi-phy" }, |
278 | {}, |
279 | }; |
280 | MODULE_DEVICE_TABLE(of, mvebu_cp110_utmi_of_match); |
281 | |
282 | static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev) |
283 | { |
284 | struct device *dev = &pdev->dev; |
285 | struct mvebu_cp110_utmi *utmi; |
286 | struct phy_provider *provider; |
287 | struct device_node *child; |
288 | u32 usb_devices = 0; |
289 | |
290 | utmi = devm_kzalloc(dev, size: sizeof(*utmi), GFP_KERNEL); |
291 | if (!utmi) |
292 | return -ENOMEM; |
293 | |
294 | utmi->dev = dev; |
295 | |
296 | /* Get system controller region */ |
297 | utmi->syscon = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
298 | property: "marvell,system-controller" ); |
299 | if (IS_ERR(ptr: utmi->syscon)) { |
300 | dev_err(dev, "Missing UTMI system controller\n" ); |
301 | return PTR_ERR(ptr: utmi->syscon); |
302 | } |
303 | |
304 | /* Get UTMI memory region */ |
305 | utmi->regs = devm_platform_ioremap_resource(pdev, index: 0); |
306 | if (IS_ERR(ptr: utmi->regs)) |
307 | return PTR_ERR(ptr: utmi->regs); |
308 | |
309 | for_each_available_child_of_node(dev->of_node, child) { |
310 | struct mvebu_cp110_utmi_port *port; |
311 | struct phy *phy; |
312 | int ret; |
313 | u32 port_id; |
314 | |
315 | ret = of_property_read_u32(np: child, propname: "reg" , out_value: &port_id); |
316 | if ((ret < 0) || (port_id >= UTMI_PHY_PORTS)) { |
317 | dev_err(dev, |
318 | "invalid 'reg' property on child %pOF\n" , |
319 | child); |
320 | continue; |
321 | } |
322 | |
323 | port = devm_kzalloc(dev, size: sizeof(*port), GFP_KERNEL); |
324 | if (!port) { |
325 | of_node_put(node: child); |
326 | return -ENOMEM; |
327 | } |
328 | |
329 | port->dr_mode = of_usb_get_dr_mode_by_phy(np: child, arg0: -1); |
330 | if ((port->dr_mode != USB_DR_MODE_HOST) && |
331 | (port->dr_mode != USB_DR_MODE_PERIPHERAL)) { |
332 | dev_err(&pdev->dev, |
333 | "Missing dual role setting of the port%d, will use HOST mode\n" , |
334 | port_id); |
335 | port->dr_mode = USB_DR_MODE_HOST; |
336 | } |
337 | |
338 | if (port->dr_mode == USB_DR_MODE_PERIPHERAL) { |
339 | usb_devices++; |
340 | if (usb_devices > 1) { |
341 | dev_err(dev, |
342 | "Single USB device allowed! Port%d will use HOST mode\n" , |
343 | port_id); |
344 | port->dr_mode = USB_DR_MODE_HOST; |
345 | } |
346 | } |
347 | |
348 | /* Retrieve PHY capabilities */ |
349 | utmi->ops = &mvebu_cp110_utmi_phy_ops; |
350 | |
351 | /* Instantiate the PHY */ |
352 | phy = devm_phy_create(dev, node: child, ops: utmi->ops); |
353 | if (IS_ERR(ptr: phy)) { |
354 | dev_err(dev, "Failed to create the UTMI PHY\n" ); |
355 | of_node_put(node: child); |
356 | return PTR_ERR(ptr: phy); |
357 | } |
358 | |
359 | port->priv = utmi; |
360 | port->id = port_id; |
361 | phy_set_drvdata(phy, data: port); |
362 | |
363 | /* Ensure the PHY is powered off */ |
364 | mvebu_cp110_utmi_phy_power_off(phy); |
365 | } |
366 | |
367 | dev_set_drvdata(dev, data: utmi); |
368 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
369 | |
370 | return PTR_ERR_OR_ZERO(ptr: provider); |
371 | } |
372 | |
373 | static struct platform_driver mvebu_cp110_utmi_driver = { |
374 | .probe = mvebu_cp110_utmi_phy_probe, |
375 | .driver = { |
376 | .name = "mvebu-cp110-utmi-phy" , |
377 | .of_match_table = mvebu_cp110_utmi_of_match, |
378 | }, |
379 | }; |
380 | module_platform_driver(mvebu_cp110_utmi_driver); |
381 | |
382 | MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>" ); |
383 | MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver" ); |
384 | MODULE_LICENSE("GPL v2" ); |
385 | |