1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021 Marvell
6 * Konstantin Porotchkin <kostap@marvell.com>
8 * Marvell CP110 UTMI PHY driver
12 #include <linux/iopoll.h>
13 #include <linux/mfd/syscon.h>
14 #include <linux/module.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>
22 #define UTMI_PHY_PORTS 2
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)
31 #define SYSCON_UTMI_CFG_REG(id) (0x440 + (id) * 4)
32 #define UTMI_PHY_CFG_PU_MASK BIT(5)
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_DIG_CTRL1_REG 0x20
66 #define SWAP_DPDM BIT(15)
67 #define UTMI_CTRL_STATUS0_REG 0x24
68 #define SUSPENDM BIT(22)
69 #define TEST_SEL BIT(25)
70 #define UTMI_CHGDTC_CTRL_REG 0x38
72 #define VDAT_MASK GENMASK(9, 8)
74 #define VSRC_OFFSET 10
75 #define VSRC_MASK GENMASK(11, 10)
78 #define PLL_LOCK_DELAY_US 10000
79 #define PLL_LOCK_TIMEOUT_US 1000000
81 #define PORT_REGS(p) ((p)->priv->regs + (p)->id * 0x1000)
84 * struct mvebu_cp110_utmi - PHY driver data
86 * @regs: PHY registers
87 * @syscon: Regmap with system controller registers
88 * @dev: device driver handle
91 struct mvebu_cp110_utmi
{
93 struct regmap
*syscon
;
95 const struct phy_ops
*ops
;
99 * struct mvebu_cp110_utmi_port - PHY port data
101 * @priv: PHY driver data
103 * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL
104 * @swap_dx: whether to swap d+/d- signals
106 struct mvebu_cp110_utmi_port
{
107 struct mvebu_cp110_utmi
*priv
;
109 enum usb_dr_mode dr_mode
;
113 static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port
*port
)
119 * The reference clock is the frequency of quartz resonator
120 * connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC.
121 * Register init values are matching the 40MHz default clock.
122 * The crystal used for all platform boards is now 25MHz.
123 * See the functional specification for details.
125 reg
= readl(PORT_REGS(port
) + UTMI_PLL_CTRL_REG
);
126 reg
&= ~(PLL_REFDIV_MASK
| PLL_FBDIV_MASK
| PLL_SEL_LPFR_MASK
);
127 reg
|= (PLL_REFDIV_VAL
<< PLL_REFDIV_OFFSET
) |
128 (PLL_FBDIV_VAL
<< PLL_FBDIV_OFFSET
);
129 writel(reg
, PORT_REGS(port
) + UTMI_PLL_CTRL_REG
);
131 /* Impedance Calibration Threshold Setting */
132 reg
= readl(PORT_REGS(port
) + UTMI_CAL_CTRL_REG
);
133 reg
&= ~IMPCAL_VTH_MASK
;
134 reg
|= IMPCAL_VTH_VAL
<< IMPCAL_VTH_OFFSET
;
135 writel(reg
, PORT_REGS(port
) + UTMI_CAL_CTRL_REG
);
137 /* Set LS TX driver strength coarse control */
138 reg
= readl(PORT_REGS(port
) + UTMI_TX_CH_CTRL_REG
);
140 reg
|= TX_AMP_VAL
<< TX_AMP_OFFSET
;
141 writel(reg
, PORT_REGS(port
) + UTMI_TX_CH_CTRL_REG
);
143 /* Disable SQ and enable analog squelch detect */
144 reg
= readl(PORT_REGS(port
) + UTMI_RX_CH_CTRL0_REG
);
146 reg
|= SQ_ANA_DTC_SEL
;
147 writel(reg
, PORT_REGS(port
) + UTMI_RX_CH_CTRL0_REG
);
150 * Set External squelch calibration number and
151 * enable the External squelch calibration
153 reg
= readl(PORT_REGS(port
) + UTMI_RX_CH_CTRL1_REG
);
154 reg
&= ~SQ_AMP_CAL_MASK
;
155 reg
|= (SQ_AMP_CAL_VAL
<< SQ_AMP_CAL_OFFSET
) | SQ_AMP_CAL_EN
;
156 writel(reg
, PORT_REGS(port
) + UTMI_RX_CH_CTRL1_REG
);
159 * Set Control VDAT Reference Voltage - 0.325V and
160 * Control VSRC Reference Voltage - 0.6V
162 reg
= readl(PORT_REGS(port
) + UTMI_CHGDTC_CTRL_REG
);
163 reg
&= ~(VDAT_MASK
| VSRC_MASK
);
164 reg
|= (VDAT_VAL
<< VDAT_OFFSET
) | (VSRC_VAL
<< VSRC_OFFSET
);
165 writel(reg
, PORT_REGS(port
) + UTMI_CHGDTC_CTRL_REG
);
168 reg
= readl(PORT_REGS(port
) + UTMI_DIG_CTRL1_REG
);
172 writel(reg
, PORT_REGS(port
) + UTMI_DIG_CTRL1_REG
);
175 static int mvebu_cp110_utmi_phy_power_off(struct phy
*phy
)
177 struct mvebu_cp110_utmi_port
*port
= phy_get_drvdata(phy
);
178 struct mvebu_cp110_utmi
*utmi
= port
->priv
;
181 /* Power down UTMI PHY port */
182 regmap_clear_bits(utmi
->syscon
, SYSCON_UTMI_CFG_REG(port
->id
),
183 UTMI_PHY_CFG_PU_MASK
);
185 for (i
= 0; i
< UTMI_PHY_PORTS
; i
++) {
186 int test
= regmap_test_bits(utmi
->syscon
,
187 SYSCON_UTMI_CFG_REG(i
),
188 UTMI_PHY_CFG_PU_MASK
);
189 /* skip PLL shutdown if there are active UTMI PHY ports */
194 /* PLL Power down if all UTMI PHYs are down */
195 regmap_clear_bits(utmi
->syscon
, SYSCON_USB_CFG_REG
, USB_CFG_PLL_MASK
);
200 static int mvebu_cp110_utmi_phy_power_on(struct phy
*phy
)
202 struct mvebu_cp110_utmi_port
*port
= phy_get_drvdata(phy
);
203 struct mvebu_cp110_utmi
*utmi
= port
->priv
;
204 struct device
*dev
= &phy
->dev
;
208 /* It is necessary to power off UTMI before configuration */
209 ret
= mvebu_cp110_utmi_phy_power_off(phy
);
211 dev_err(dev
, "UTMI power OFF before power ON failed\n");
216 * If UTMI port is connected to USB Device controller,
217 * configure the USB MUX prior to UTMI PHY initialization.
218 * The single USB device controller can be connected
219 * to UTMI0 or to UTMI1 PHY port, but not to both.
221 if (port
->dr_mode
== USB_DR_MODE_PERIPHERAL
) {
222 regmap_update_bits(utmi
->syscon
, SYSCON_USB_CFG_REG
,
223 USB_CFG_DEVICE_EN_MASK
| USB_CFG_DEVICE_MUX_MASK
,
224 USB_CFG_DEVICE_EN_MASK
|
225 (port
->id
<< USB_CFG_DEVICE_MUX_OFFSET
));
228 /* Set Test suspendm mode and enable Test UTMI select */
229 reg
= readl(PORT_REGS(port
) + UTMI_CTRL_STATUS0_REG
);
230 reg
|= SUSPENDM
| TEST_SEL
;
231 writel(reg
, PORT_REGS(port
) + UTMI_CTRL_STATUS0_REG
);
233 /* Wait for UTMI power down */
236 /* PHY port setup first */
237 mvebu_cp110_utmi_port_setup(port
);
239 /* Power UP UTMI PHY */
240 regmap_set_bits(utmi
->syscon
, SYSCON_UTMI_CFG_REG(port
->id
),
241 UTMI_PHY_CFG_PU_MASK
);
243 /* Disable Test UTMI select */
244 reg
= readl(PORT_REGS(port
) + UTMI_CTRL_STATUS0_REG
);
246 writel(reg
, PORT_REGS(port
) + UTMI_CTRL_STATUS0_REG
);
248 /* Wait for impedance calibration */
249 ret
= readl_poll_timeout(PORT_REGS(port
) + UTMI_CAL_CTRL_REG
, reg
,
251 PLL_LOCK_DELAY_US
, PLL_LOCK_TIMEOUT_US
);
253 dev_err(dev
, "Failed to end UTMI impedance calibration\n");
257 /* Wait for PLL calibration */
258 ret
= readl_poll_timeout(PORT_REGS(port
) + UTMI_CAL_CTRL_REG
, reg
,
260 PLL_LOCK_DELAY_US
, PLL_LOCK_TIMEOUT_US
);
262 dev_err(dev
, "Failed to end UTMI PLL calibration\n");
266 /* Wait for PLL ready */
267 ret
= readl_poll_timeout(PORT_REGS(port
) + UTMI_PLL_CTRL_REG
, reg
,
269 PLL_LOCK_DELAY_US
, PLL_LOCK_TIMEOUT_US
);
271 dev_err(dev
, "PLL is not ready\n");
276 regmap_set_bits(utmi
->syscon
, SYSCON_USB_CFG_REG
, USB_CFG_PLL_MASK
);
281 static const struct phy_ops mvebu_cp110_utmi_phy_ops
= {
282 .power_on
= mvebu_cp110_utmi_phy_power_on
,
283 .power_off
= mvebu_cp110_utmi_phy_power_off
,
284 .owner
= THIS_MODULE
,
287 static const struct of_device_id mvebu_cp110_utmi_of_match
[] = {
288 { .compatible
= "marvell,cp110-utmi-phy" },
291 MODULE_DEVICE_TABLE(of
, mvebu_cp110_utmi_of_match
);
293 static int mvebu_cp110_utmi_phy_probe(struct platform_device
*pdev
)
295 struct device
*dev
= &pdev
->dev
;
296 struct mvebu_cp110_utmi
*utmi
;
297 struct phy_provider
*provider
;
298 struct device_node
*child
;
302 utmi
= devm_kzalloc(dev
, sizeof(*utmi
), GFP_KERNEL
);
308 /* Get system controller region */
309 utmi
->syscon
= syscon_regmap_lookup_by_phandle(dev
->of_node
,
310 "marvell,system-controller");
311 if (IS_ERR(utmi
->syscon
)) {
312 dev_err(dev
, "Missing UTMI system controller\n");
313 return PTR_ERR(utmi
->syscon
);
316 /* Get UTMI memory region */
317 utmi
->regs
= devm_platform_ioremap_resource(pdev
, 0);
318 if (IS_ERR(utmi
->regs
))
319 return PTR_ERR(utmi
->regs
);
321 for_each_available_child_of_node(dev
->of_node
, child
) {
322 struct mvebu_cp110_utmi_port
*port
;
327 ret
= of_property_read_u32(child
, "reg", &port_id
);
328 if ((ret
< 0) || (port_id
>= UTMI_PHY_PORTS
)) {
330 "invalid 'reg' property on child %pOF\n",
335 port
= devm_kzalloc(dev
, sizeof(*port
), GFP_KERNEL
);
341 port
->dr_mode
= of_usb_get_dr_mode_by_phy(child
, -1);
342 if ((port
->dr_mode
!= USB_DR_MODE_HOST
) &&
343 (port
->dr_mode
!= USB_DR_MODE_PERIPHERAL
)) {
345 "Missing dual role setting of the port%d, will use HOST mode\n",
347 port
->dr_mode
= USB_DR_MODE_HOST
;
350 if (port
->dr_mode
== USB_DR_MODE_PERIPHERAL
) {
352 if (usb_devices
> 1) {
354 "Single USB device allowed! Port%d will use HOST mode\n",
356 port
->dr_mode
= USB_DR_MODE_HOST
;
360 of_property_for_each_u32(dev
->of_node
, "swap-dx-lanes", swap_dx
)
361 if (swap_dx
== port_id
)
364 /* Retrieve PHY capabilities */
365 utmi
->ops
= &mvebu_cp110_utmi_phy_ops
;
367 /* Instantiate the PHY */
368 phy
= devm_phy_create(dev
, child
, utmi
->ops
);
370 dev_err(dev
, "Failed to create the UTMI PHY\n");
377 phy_set_drvdata(phy
, port
);
379 /* Ensure the PHY is powered off */
380 mvebu_cp110_utmi_phy_power_off(phy
);
383 dev_set_drvdata(dev
, utmi
);
384 provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
386 return PTR_ERR_OR_ZERO(provider
);
389 static struct platform_driver mvebu_cp110_utmi_driver
= {
390 .probe
= mvebu_cp110_utmi_phy_probe
,
392 .name
= "mvebu-cp110-utmi-phy",
393 .of_match_table
= mvebu_cp110_utmi_of_match
,
396 module_platform_driver(mvebu_cp110_utmi_driver
);
398 MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>");
399 MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver");
400 MODULE_LICENSE("GPL v2");