xref: /linux/drivers/phy/broadcom/phy-bcm-sr-usb.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2016-2018 Broadcom
4  */
5 
6 #include <linux/delay.h>
7 #include <linux/io.h>
8 #include <linux/iopoll.h>
9 #include <linux/module.h>
10 #include <linux/of.h>
11 #include <linux/phy/phy.h>
12 #include <linux/platform_device.h>
13 
14 enum bcm_usb_phy_version {
15 	BCM_SR_USB_COMBO_PHY,
16 	BCM_SR_USB_HS_PHY,
17 };
18 
19 enum bcm_usb_phy_reg {
20 	PLL_CTRL,
21 	PHY_CTRL,
22 	PHY_PLL_CTRL,
23 };
24 
25 /* USB PHY registers */
26 
27 static const u8 bcm_usb_combo_phy_ss[] = {
28 	[PLL_CTRL]		= 0x18,
29 	[PHY_CTRL]		= 0x14,
30 };
31 
32 static const u8 bcm_usb_combo_phy_hs[] = {
33 	[PLL_CTRL]	= 0x0c,
34 	[PHY_CTRL]	= 0x10,
35 };
36 
37 static const u8 bcm_usb_hs_phy[] = {
38 	[PLL_CTRL]	= 0x8,
39 	[PHY_CTRL]	= 0xc,
40 };
41 
42 enum pll_ctrl_bits {
43 	PLL_RESETB,
44 	SSPLL_SUSPEND_EN,
45 	PLL_SEQ_START,
46 	PLL_LOCK,
47 };
48 
49 static const u8 u3pll_ctrl[] = {
50 	[PLL_RESETB]		= 0,
51 	[SSPLL_SUSPEND_EN]	= 1,
52 	[PLL_SEQ_START]		= 2,
53 	[PLL_LOCK]		= 3,
54 };
55 
56 #define HSPLL_PDIV_MASK		0xF
57 #define HSPLL_PDIV_VAL		0x1
58 
59 static const u8 u2pll_ctrl[] = {
60 	[PLL_RESETB]	= 5,
61 	[PLL_LOCK]	= 6,
62 };
63 
64 enum bcm_usb_phy_ctrl_bits {
65 	CORERDY,
66 	PHY_RESETB,
67 	PHY_PCTL,
68 };
69 
70 #define PHY_PCTL_MASK	0xffff
71 #define SSPHY_PCTL_VAL	0x0006
72 
73 static const u8 u3phy_ctrl[] = {
74 	[PHY_RESETB]	= 1,
75 	[PHY_PCTL]	= 2,
76 };
77 
78 static const u8 u2phy_ctrl[] = {
79 	[CORERDY]		= 0,
80 	[PHY_RESETB]		= 5,
81 	[PHY_PCTL]		= 6,
82 };
83 
84 struct bcm_usb_phy_cfg {
85 	uint32_t type;
86 	uint32_t version;
87 	void __iomem *regs;
88 	struct phy *phy;
89 	const u8 *offset;
90 };
91 
92 #define PLL_LOCK_RETRY_COUNT	1000
93 
94 enum bcm_usb_phy_type {
95 	USB_HS_PHY,
96 	USB_SS_PHY,
97 };
98 
99 #define NUM_BCM_SR_USB_COMBO_PHYS	2
100 
101 static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)
102 {
103 	writel(readl(addr) & ~clear, addr);
104 }
105 
106 static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
107 {
108 	writel(readl(addr) | set, addr);
109 }
110 
111 static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
112 {
113 	u32 data;
114 	int ret;
115 
116 	ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,
117 					PLL_LOCK_RETRY_COUNT);
118 	if (ret)
119 		pr_err("%s: FAIL\n", __func__);
120 
121 	return ret;
122 }
123 
124 static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
125 {
126 	int ret = 0;
127 	void __iomem *regs = phy_cfg->regs;
128 	const u8 *offset;
129 	u32 rd_data;
130 
131 	offset = phy_cfg->offset;
132 
133 	/* Set pctl with mode and soft reset */
134 	rd_data = readl(regs + offset[PHY_CTRL]);
135 	rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]);
136 	rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]);
137 	writel(rd_data, regs + offset[PHY_CTRL]);
138 
139 	bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
140 			      BIT(u3pll_ctrl[SSPLL_SUSPEND_EN]));
141 	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
142 			      BIT(u3pll_ctrl[PLL_SEQ_START]));
143 	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
144 			      BIT(u3pll_ctrl[PLL_RESETB]));
145 
146 	/* Maximum timeout for PLL reset done */
147 	msleep(30);
148 
149 	ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
150 				     BIT(u3pll_ctrl[PLL_LOCK]));
151 
152 	return ret;
153 }
154 
155 static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
156 {
157 	int ret = 0;
158 	void __iomem *regs = phy_cfg->regs;
159 	const u8 *offset;
160 
161 	offset = phy_cfg->offset;
162 
163 	bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
164 			      BIT(u2pll_ctrl[PLL_RESETB]));
165 	bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
166 			      BIT(u2pll_ctrl[PLL_RESETB]));
167 
168 	ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
169 				     BIT(u2pll_ctrl[PLL_LOCK]));
170 
171 	return ret;
172 }
173 
174 static int bcm_usb_phy_reset(struct phy *phy)
175 {
176 	struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
177 	void __iomem *regs = phy_cfg->regs;
178 	const u8 *offset;
179 
180 	offset = phy_cfg->offset;
181 
182 	if (phy_cfg->type == USB_HS_PHY) {
183 		bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL],
184 				      BIT(u2phy_ctrl[CORERDY]));
185 		bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
186 				      BIT(u2phy_ctrl[CORERDY]));
187 	}
188 
189 	return 0;
190 }
191 
192 static int bcm_usb_phy_init(struct phy *phy)
193 {
194 	struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
195 	int ret = -EINVAL;
196 
197 	if (phy_cfg->type == USB_SS_PHY)
198 		ret = bcm_usb_ss_phy_init(phy_cfg);
199 	else if (phy_cfg->type == USB_HS_PHY)
200 		ret = bcm_usb_hs_phy_init(phy_cfg);
201 
202 	return ret;
203 }
204 
205 static const struct phy_ops sr_phy_ops = {
206 	.init		= bcm_usb_phy_init,
207 	.reset		= bcm_usb_phy_reset,
208 	.owner		= THIS_MODULE,
209 };
210 
211 static struct phy *bcm_usb_phy_xlate(struct device *dev,
212 				     struct of_phandle_args *args)
213 {
214 	struct bcm_usb_phy_cfg *phy_cfg;
215 	int phy_idx;
216 
217 	phy_cfg = dev_get_drvdata(dev);
218 	if (!phy_cfg)
219 		return ERR_PTR(-EINVAL);
220 
221 	if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) {
222 		phy_idx = args->args[0];
223 
224 		if (WARN_ON(phy_idx > 1))
225 			return ERR_PTR(-ENODEV);
226 
227 		return phy_cfg[phy_idx].phy;
228 	} else
229 		return phy_cfg->phy;
230 }
231 
232 static int bcm_usb_phy_create(struct device *dev, struct device_node *node,
233 			      void __iomem *regs, uint32_t version)
234 {
235 	struct bcm_usb_phy_cfg *phy_cfg;
236 	int idx;
237 
238 	if (version == BCM_SR_USB_COMBO_PHY) {
239 		phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS *
240 				       sizeof(struct bcm_usb_phy_cfg),
241 				       GFP_KERNEL);
242 		if (!phy_cfg)
243 			return -ENOMEM;
244 
245 		for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) {
246 			phy_cfg[idx].regs = regs;
247 			phy_cfg[idx].version = version;
248 			if (idx == 0) {
249 				phy_cfg[idx].offset = bcm_usb_combo_phy_hs;
250 				phy_cfg[idx].type = USB_HS_PHY;
251 			} else {
252 				phy_cfg[idx].offset = bcm_usb_combo_phy_ss;
253 				phy_cfg[idx].type = USB_SS_PHY;
254 			}
255 			phy_cfg[idx].phy = devm_phy_create(dev, node,
256 							   &sr_phy_ops);
257 			if (IS_ERR(phy_cfg[idx].phy))
258 				return PTR_ERR(phy_cfg[idx].phy);
259 
260 			phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]);
261 		}
262 	} else if (version == BCM_SR_USB_HS_PHY) {
263 		phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg),
264 				       GFP_KERNEL);
265 		if (!phy_cfg)
266 			return -ENOMEM;
267 
268 		phy_cfg->regs = regs;
269 		phy_cfg->version = version;
270 		phy_cfg->offset = bcm_usb_hs_phy;
271 		phy_cfg->type = USB_HS_PHY;
272 		phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops);
273 		if (IS_ERR(phy_cfg->phy))
274 			return PTR_ERR(phy_cfg->phy);
275 
276 		phy_set_drvdata(phy_cfg->phy, phy_cfg);
277 	} else
278 		return -ENODEV;
279 
280 	dev_set_drvdata(dev, phy_cfg);
281 
282 	return 0;
283 }
284 
285 static const struct of_device_id bcm_usb_phy_of_match[] = {
286 	{
287 		.compatible = "brcm,sr-usb-combo-phy",
288 		.data = (void *)BCM_SR_USB_COMBO_PHY,
289 	},
290 	{
291 		.compatible = "brcm,sr-usb-hs-phy",
292 		.data = (void *)BCM_SR_USB_HS_PHY,
293 	},
294 	{ /* sentinel */ },
295 };
296 MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);
297 
298 static int bcm_usb_phy_probe(struct platform_device *pdev)
299 {
300 	struct device *dev = &pdev->dev;
301 	struct device_node *dn = dev->of_node;
302 	const struct of_device_id *of_id;
303 	void __iomem *regs;
304 	int ret;
305 	enum bcm_usb_phy_version version;
306 	struct phy_provider *phy_provider;
307 
308 	regs = devm_platform_ioremap_resource(pdev, 0);
309 	if (IS_ERR(regs))
310 		return PTR_ERR(regs);
311 
312 	of_id = of_match_node(bcm_usb_phy_of_match, dn);
313 	if (of_id)
314 		version = (enum bcm_usb_phy_version)of_id->data;
315 	else
316 		return -ENODEV;
317 
318 	ret = bcm_usb_phy_create(dev, dn, regs, version);
319 	if (ret)
320 		return ret;
321 
322 	phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate);
323 
324 	return PTR_ERR_OR_ZERO(phy_provider);
325 }
326 
327 static struct platform_driver bcm_usb_phy_driver = {
328 	.driver = {
329 		.name = "phy-bcm-sr-usb",
330 		.of_match_table = bcm_usb_phy_of_match,
331 	},
332 	.probe = bcm_usb_phy_probe,
333 };
334 module_platform_driver(bcm_usb_phy_driver);
335 
336 MODULE_AUTHOR("Broadcom");
337 MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");
338 MODULE_LICENSE("GPL v2");
339