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 */
66struct 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 */
79struct 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
86static 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(addr: 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(val: reg, addr: utmi->regs + USB2_PHY_PLL_CTRL_REG0);
103
104 /* Enable PHY pull up and disable USB2 suspend */
105 regmap_update_bits(map: 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(addr: utmi->regs + USB2_PHY_OTG_CTRL);
112 reg |= PHY_PU_OTG;
113 writel(val: reg, addr: utmi->regs + USB2_PHY_OTG_CTRL);
114
115 /* Disable PHY charger detection */
116 reg = readl(addr: 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(val: reg, addr: utmi->regs + USB2_PHY_CHRGR_DETECT);
120
121 /* Disable PHY DP/DM pull-down (used for device mode) */
122 regmap_update_bits(map: utmi->usb_misc, USB2_PHY_CTRL(usb32),
123 USB2_DP_PULLDN_DEV_MODE |
124 USB2_DM_PULLDN_DEV_MODE, val: 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
164static 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 reg = readl(addr: utmi->regs + USB2_PHY_CTRL(usb32));
172 reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32));
173 writel(val: reg, addr: utmi->regs + USB2_PHY_CTRL(usb32));
174
175 /* Power down OTG module */
176 if (usb32) {
177 reg = readl(addr: utmi->regs + USB2_PHY_OTG_CTRL);
178 reg &= ~PHY_PU_OTG;
179 writel(val: reg, addr: utmi->regs + USB2_PHY_OTG_CTRL);
180 }
181
182 return 0;
183}
184
185static const struct phy_ops mvebu_a3700_utmi_phy_ops = {
186 .power_on = mvebu_a3700_utmi_phy_power_on,
187 .power_off = mvebu_a3700_utmi_phy_power_off,
188 .owner = THIS_MODULE,
189};
190
191static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = {
192 .usb32 = true,
193 .ops = &mvebu_a3700_utmi_phy_ops,
194};
195
196static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = {
197 .usb32 = false,
198 .ops = &mvebu_a3700_utmi_phy_ops,
199};
200
201static const struct of_device_id mvebu_a3700_utmi_of_match[] = {
202 {
203 .compatible = "marvell,a3700-utmi-otg-phy",
204 .data = &mvebu_a3700_utmi_otg_phy_caps,
205 },
206 {
207 .compatible = "marvell,a3700-utmi-host-phy",
208 .data = &mvebu_a3700_utmi_host_phy_caps,
209 },
210 {},
211};
212MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match);
213
214static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev)
215{
216 struct device *dev = &pdev->dev;
217 struct mvebu_a3700_utmi *utmi;
218 struct phy_provider *provider;
219
220 utmi = devm_kzalloc(dev, size: sizeof(*utmi), GFP_KERNEL);
221 if (!utmi)
222 return -ENOMEM;
223
224 /* Get UTMI memory region */
225 utmi->regs = devm_platform_ioremap_resource(pdev, index: 0);
226 if (IS_ERR(ptr: utmi->regs))
227 return PTR_ERR(ptr: utmi->regs);
228
229 /* Get miscellaneous Host/PHY region */
230 utmi->usb_misc = syscon_regmap_lookup_by_phandle(np: dev->of_node,
231 property: "marvell,usb-misc-reg");
232 if (IS_ERR(ptr: utmi->usb_misc)) {
233 dev_err(dev,
234 "Missing USB misc purpose system controller\n");
235 return PTR_ERR(ptr: utmi->usb_misc);
236 }
237
238 /* Retrieve PHY capabilities */
239 utmi->caps = of_device_get_match_data(dev);
240
241 /* Instantiate the PHY */
242 utmi->phy = devm_phy_create(dev, NULL, ops: utmi->caps->ops);
243 if (IS_ERR(ptr: utmi->phy)) {
244 dev_err(dev, "Failed to create the UTMI PHY\n");
245 return PTR_ERR(ptr: utmi->phy);
246 }
247
248 phy_set_drvdata(phy: utmi->phy, data: utmi);
249
250 /* Ensure the PHY is powered off */
251 utmi->caps->ops->power_off(utmi->phy);
252
253 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
254
255 return PTR_ERR_OR_ZERO(ptr: provider);
256}
257
258static struct platform_driver mvebu_a3700_utmi_driver = {
259 .probe = mvebu_a3700_utmi_phy_probe,
260 .driver = {
261 .name = "mvebu-a3700-utmi-phy",
262 .of_match_table = mvebu_a3700_utmi_of_match,
263 },
264};
265module_platform_driver(mvebu_a3700_utmi_driver);
266
267MODULE_AUTHOR("Igal Liberman <igall@marvell.com>");
268MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
269MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver");
270MODULE_LICENSE("GPL v2");
271

source code of linux/drivers/phy/marvell/phy-mvebu-a3700-utmi.c