1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2018 Marvell
4 *
5 * Authors:
6 * Igal Liberman <igall@marvell.com>
7 * Miquèl Raynal <miquel.raynal@bootlin.com>
8 *
9 * Marvell A3700 UTMI PHY driver
10 */
11
12 #include <linux/io.h>
13 #include <linux/iopoll.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/phy/phy.h>
18 #include <linux/platform_device.h>
19 #include <linux/regmap.h>
20
21 /* Armada 3700 UTMI PHY registers */
22 #define USB2_PHY_PLL_CTRL_REG0 0x0
23 #define PLL_REF_DIV_OFF 0
24 #define PLL_REF_DIV_MASK GENMASK(6, 0)
25 #define PLL_REF_DIV_5 5
26 #define PLL_FB_DIV_OFF 16
27 #define PLL_FB_DIV_MASK GENMASK(24, 16)
28 #define PLL_FB_DIV_96 96
29 #define PLL_SEL_LPFR_OFF 28
30 #define PLL_SEL_LPFR_MASK GENMASK(29, 28)
31 #define PLL_READY BIT(31)
32 #define USB2_PHY_CAL_CTRL 0x8
33 #define PHY_PLLCAL_DONE BIT(31)
34 #define PHY_IMPCAL_DONE BIT(23)
35 #define USB2_RX_CHAN_CTRL1 0x18
36 #define USB2PHY_SQCAL_DONE BIT(31)
37 #define USB2_PHY_OTG_CTRL 0x34
38 #define PHY_PU_OTG BIT(4)
39 #define USB2_PHY_CHRGR_DETECT 0x38
40 #define PHY_CDP_EN BIT(2)
41 #define PHY_DCP_EN BIT(3)
42 #define PHY_PD_EN BIT(4)
43 #define PHY_PU_CHRG_DTC BIT(5)
44 #define PHY_CDP_DM_AUTO BIT(7)
45 #define PHY_ENSWITCH_DP BIT(12)
46 #define PHY_ENSWITCH_DM BIT(13)
47
48 /* Armada 3700 USB miscellaneous registers */
49 #define USB2_PHY_CTRL(usb32) (usb32 ? 0x20 : 0x4)
50 #define RB_USB2PHY_PU BIT(0)
51 #define USB2_DP_PULLDN_DEV_MODE BIT(5)
52 #define USB2_DM_PULLDN_DEV_MODE BIT(6)
53 #define RB_USB2PHY_SUSPM(usb32) (usb32 ? BIT(14) : BIT(7))
54
55 #define PLL_LOCK_DELAY_US 10000
56 #define PLL_LOCK_TIMEOUT_US 1000000
57
58 /**
59 * struct mvebu_a3700_utmi_caps - PHY capabilities
60 *
61 * @usb32: Flag indicating which PHY is in use (impacts the register map):
62 * - The UTMI PHY wired to the USB3/USB2 controller (otg)
63 * - The UTMI PHY wired to the USB2 controller (host only)
64 * @ops: PHY operations
65 */
66 struct mvebu_a3700_utmi_caps {
67 int usb32;
68 const struct phy_ops *ops;
69 };
70
71 /**
72 * struct mvebu_a3700_utmi - PHY driver data
73 *
74 * @regs: PHY registers
75 * @usb_misc: Regmap with USB miscellaneous registers including PHY ones
76 * @caps: PHY capabilities
77 * @phy: PHY handle
78 */
79 struct mvebu_a3700_utmi {
80 void __iomem *regs;
81 struct regmap *usb_misc;
82 const struct mvebu_a3700_utmi_caps *caps;
83 struct phy *phy;
84 };
85
mvebu_a3700_utmi_phy_power_on(struct phy * phy)86 static int mvebu_a3700_utmi_phy_power_on(struct phy *phy)
87 {
88 struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
89 struct device *dev = &phy->dev;
90 int usb32 = utmi->caps->usb32;
91 int ret = 0;
92 u32 reg;
93
94 /*
95 * Setup PLL. 40MHz clock used to be the default, being 25MHz now.
96 * See "PLL Settings for Typical REFCLK" table.
97 */
98 reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0);
99 reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK);
100 reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) |
101 (PLL_FB_DIV_96 << PLL_FB_DIV_OFF);
102 writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0);
103
104 /* Enable PHY pull up and disable USB2 suspend */
105 regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
106 RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU,
107 RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU);
108
109 if (usb32) {
110 /* Power up OTG module */
111 reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
112 reg |= PHY_PU_OTG;
113 writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
114
115 /* Disable PHY charger detection */
116 reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT);
117 reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC |
118 PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM);
119 writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT);
120
121 /* Disable PHY DP/DM pull-down (used for device mode) */
122 regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
123 USB2_DP_PULLDN_DEV_MODE |
124 USB2_DM_PULLDN_DEV_MODE, 0);
125 }
126
127 /* Wait for PLL calibration */
128 ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
129 reg & PHY_PLLCAL_DONE,
130 PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
131 if (ret) {
132 dev_err(dev, "Failed to end USB2 PLL calibration\n");
133 return ret;
134 }
135
136 /* Wait for impedance calibration */
137 ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
138 reg & PHY_IMPCAL_DONE,
139 PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
140 if (ret) {
141 dev_err(dev, "Failed to end USB2 impedance calibration\n");
142 return ret;
143 }
144
145 /* Wait for squelch calibration */
146 ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg,
147 reg & USB2PHY_SQCAL_DONE,
148 PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
149 if (ret) {
150 dev_err(dev, "Failed to end USB2 unknown calibration\n");
151 return ret;
152 }
153
154 /* Wait for PLL to be locked */
155 ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg,
156 reg & PLL_READY,
157 PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
158 if (ret)
159 dev_err(dev, "Failed to lock USB2 PLL\n");
160
161 return ret;
162 }
163
mvebu_a3700_utmi_phy_power_off(struct phy * phy)164 static int mvebu_a3700_utmi_phy_power_off(struct phy *phy)
165 {
166 struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
167 int usb32 = utmi->caps->usb32;
168 u32 reg;
169
170 /* Disable PHY pull-up and enable USB2 suspend */
171 regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
172 RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0);
173
174 /* Power down OTG module */
175 if (usb32) {
176 reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
177 reg &= ~PHY_PU_OTG;
178 writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
179 }
180
181 return 0;
182 }
183
184 static const struct phy_ops mvebu_a3700_utmi_phy_ops = {
185 .power_on = mvebu_a3700_utmi_phy_power_on,
186 .power_off = mvebu_a3700_utmi_phy_power_off,
187 .owner = THIS_MODULE,
188 };
189
190 static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = {
191 .usb32 = true,
192 .ops = &mvebu_a3700_utmi_phy_ops,
193 };
194
195 static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = {
196 .usb32 = false,
197 .ops = &mvebu_a3700_utmi_phy_ops,
198 };
199
200 static const struct of_device_id mvebu_a3700_utmi_of_match[] = {
201 {
202 .compatible = "marvell,a3700-utmi-otg-phy",
203 .data = &mvebu_a3700_utmi_otg_phy_caps,
204 },
205 {
206 .compatible = "marvell,a3700-utmi-host-phy",
207 .data = &mvebu_a3700_utmi_host_phy_caps,
208 },
209 {},
210 };
211 MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match);
212
mvebu_a3700_utmi_phy_probe(struct platform_device * pdev)213 static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev)
214 {
215 struct device *dev = &pdev->dev;
216 struct mvebu_a3700_utmi *utmi;
217 struct phy_provider *provider;
218
219 utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
220 if (!utmi)
221 return -ENOMEM;
222
223 /* Get UTMI memory region */
224 utmi->regs = devm_platform_ioremap_resource(pdev, 0);
225 if (IS_ERR(utmi->regs))
226 return PTR_ERR(utmi->regs);
227
228 /* Get miscellaneous Host/PHY region */
229 utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node,
230 "marvell,usb-misc-reg");
231 if (IS_ERR(utmi->usb_misc)) {
232 dev_err(dev,
233 "Missing USB misc purpose system controller\n");
234 return PTR_ERR(utmi->usb_misc);
235 }
236
237 /* Retrieve PHY capabilities */
238 utmi->caps = of_device_get_match_data(dev);
239
240 /* Instantiate the PHY */
241 utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops);
242 if (IS_ERR(utmi->phy)) {
243 dev_err(dev, "Failed to create the UTMI PHY\n");
244 return PTR_ERR(utmi->phy);
245 }
246
247 phy_set_drvdata(utmi->phy, utmi);
248
249 /* Ensure the PHY is powered off */
250 utmi->caps->ops->power_off(utmi->phy);
251
252 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
253
254 return PTR_ERR_OR_ZERO(provider);
255 }
256
257 static struct platform_driver mvebu_a3700_utmi_driver = {
258 .probe = mvebu_a3700_utmi_phy_probe,
259 .driver = {
260 .name = "mvebu-a3700-utmi-phy",
261 .of_match_table = mvebu_a3700_utmi_of_match,
262 },
263 };
264 module_platform_driver(mvebu_a3700_utmi_driver);
265
266 MODULE_AUTHOR("Igal Liberman <igall@marvell.com>");
267 MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
268 MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver");
269 MODULE_LICENSE("GPL v2");
270