1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Ingenic SoCs USB PHY driver
4 * Copyright (c) Paul Cercueil <paul@crapouillou.net>
5 * Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>
6 * Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
7 */
8
9#include <linux/bitfield.h>
10#include <linux/clk.h>
11#include <linux/delay.h>
12#include <linux/io.h>
13#include <linux/module.h>
14#include <linux/of.h>
15#include <linux/phy/phy.h>
16#include <linux/platform_device.h>
17#include <linux/regulator/consumer.h>
18
19/* OTGPHY register offsets */
20#define REG_USBPCR_OFFSET			0x00
21#define REG_USBRDT_OFFSET			0x04
22#define REG_USBVBFIL_OFFSET			0x08
23#define REG_USBPCR1_OFFSET			0x0c
24
25/* bits within the USBPCR register */
26#define USBPCR_USB_MODE				BIT(31)
27#define USBPCR_AVLD_REG				BIT(30)
28#define USBPCR_COMMONONN			BIT(25)
29#define USBPCR_VBUSVLDEXT			BIT(24)
30#define USBPCR_VBUSVLDEXTSEL		BIT(23)
31#define USBPCR_POR					BIT(22)
32#define USBPCR_SIDDQ				BIT(21)
33#define USBPCR_OTG_DISABLE			BIT(20)
34#define USBPCR_TXPREEMPHTUNE		BIT(6)
35
36#define USBPCR_IDPULLUP_MASK		GENMASK(29, 28)
37#define USBPCR_IDPULLUP_ALWAYS		0x2
38#define USBPCR_IDPULLUP_SUSPEND		0x1
39#define USBPCR_IDPULLUP_OTG			0x0
40
41#define USBPCR_COMPDISTUNE_MASK		GENMASK(19, 17)
42#define USBPCR_COMPDISTUNE_DFT		0x4
43
44#define USBPCR_OTGTUNE_MASK			GENMASK(16, 14)
45#define USBPCR_OTGTUNE_DFT			0x4
46
47#define USBPCR_SQRXTUNE_MASK		GENMASK(13, 11)
48#define USBPCR_SQRXTUNE_DCR_20PCT	0x7
49#define USBPCR_SQRXTUNE_DFT			0x3
50
51#define USBPCR_TXFSLSTUNE_MASK		GENMASK(10, 7)
52#define USBPCR_TXFSLSTUNE_DCR_50PPT	0xf
53#define USBPCR_TXFSLSTUNE_DCR_25PPT	0x7
54#define USBPCR_TXFSLSTUNE_DFT		0x3
55#define USBPCR_TXFSLSTUNE_INC_25PPT	0x1
56#define USBPCR_TXFSLSTUNE_INC_50PPT	0x0
57
58#define USBPCR_TXHSXVTUNE_MASK		GENMASK(5, 4)
59#define USBPCR_TXHSXVTUNE_DFT		0x3
60#define USBPCR_TXHSXVTUNE_DCR_15MV	0x1
61
62#define USBPCR_TXRISETUNE_MASK		GENMASK(5, 4)
63#define USBPCR_TXRISETUNE_DFT		0x3
64
65#define USBPCR_TXVREFTUNE_MASK		GENMASK(3, 0)
66#define USBPCR_TXVREFTUNE_INC_75PPT	0xb
67#define USBPCR_TXVREFTUNE_INC_25PPT	0x7
68#define USBPCR_TXVREFTUNE_DFT		0x5
69
70/* bits within the USBRDTR register */
71#define USBRDT_UTMI_RST				BIT(27)
72#define USBRDT_HB_MASK				BIT(26)
73#define USBRDT_VBFIL_LD_EN			BIT(25)
74#define USBRDT_IDDIG_EN				BIT(24)
75#define USBRDT_IDDIG_REG			BIT(23)
76#define USBRDT_VBFIL_EN				BIT(2)
77
78/* bits within the USBPCR1 register */
79#define USBPCR1_BVLD_REG			BIT(31)
80#define USBPCR1_DPPD				BIT(29)
81#define USBPCR1_DMPD				BIT(28)
82#define USBPCR1_USB_SEL				BIT(28)
83#define USBPCR1_PORT_RST			BIT(21)
84#define USBPCR1_WORD_IF_16BIT		BIT(19)
85
86struct ingenic_soc_info {
87	void (*usb_phy_init)(struct phy *phy);
88};
89
90struct ingenic_usb_phy {
91	const struct ingenic_soc_info *soc_info;
92
93	struct phy *phy;
94	void __iomem *base;
95	struct clk *clk;
96	struct regulator *vcc_supply;
97};
98
99static int ingenic_usb_phy_init(struct phy *phy)
100{
101	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
102	int err;
103	u32 reg;
104
105	err = clk_prepare_enable(priv->clk);
106	if (err) {
107		dev_err(&phy->dev, "Unable to start clock: %d\n", err);
108		return err;
109	}
110
111	priv->soc_info->usb_phy_init(phy);
112
113	/* Wait for PHY to reset */
114	usleep_range(30, 300);
115	reg = readl(priv->base + REG_USBPCR_OFFSET);
116	writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET);
117	usleep_range(300, 1000);
118
119	return 0;
120}
121
122static int ingenic_usb_phy_exit(struct phy *phy)
123{
124	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
125
126	clk_disable_unprepare(priv->clk);
127	regulator_disable(priv->vcc_supply);
128
129	return 0;
130}
131
132static int ingenic_usb_phy_power_on(struct phy *phy)
133{
134	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
135	int err;
136
137	err = regulator_enable(priv->vcc_supply);
138	if (err) {
139		dev_err(&phy->dev, "Unable to enable VCC: %d\n", err);
140		return err;
141	}
142
143	return 0;
144}
145
146static int ingenic_usb_phy_power_off(struct phy *phy)
147{
148	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
149
150	regulator_disable(priv->vcc_supply);
151
152	return 0;
153}
154
155static int ingenic_usb_phy_set_mode(struct phy *phy,
156				  enum phy_mode mode, int submode)
157{
158	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
159	u32 reg;
160
161	switch (mode) {
162	case PHY_MODE_USB_HOST:
163		reg = readl(priv->base + REG_USBPCR_OFFSET);
164		u32p_replace_bits(&reg, 1, USBPCR_USB_MODE);
165		u32p_replace_bits(&reg, 0, USBPCR_VBUSVLDEXT);
166		u32p_replace_bits(&reg, 0, USBPCR_VBUSVLDEXTSEL);
167		u32p_replace_bits(&reg, 0, USBPCR_OTG_DISABLE);
168		writel(reg, priv->base + REG_USBPCR_OFFSET);
169
170		break;
171	case PHY_MODE_USB_DEVICE:
172		reg = readl(priv->base + REG_USBPCR_OFFSET);
173		u32p_replace_bits(&reg, 0, USBPCR_USB_MODE);
174		u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXT);
175		u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXTSEL);
176		u32p_replace_bits(&reg, 1, USBPCR_OTG_DISABLE);
177		writel(reg, priv->base + REG_USBPCR_OFFSET);
178
179		break;
180	case PHY_MODE_USB_OTG:
181		reg = readl(priv->base + REG_USBPCR_OFFSET);
182		u32p_replace_bits(&reg, 1, USBPCR_USB_MODE);
183		u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXT);
184		u32p_replace_bits(&reg, 1, USBPCR_VBUSVLDEXTSEL);
185		u32p_replace_bits(&reg, 0, USBPCR_OTG_DISABLE);
186		writel(reg, priv->base + REG_USBPCR_OFFSET);
187
188		break;
189	default:
190		return -EINVAL;
191	}
192
193	return 0;
194}
195
196static const struct phy_ops ingenic_usb_phy_ops = {
197	.init		= ingenic_usb_phy_init,
198	.exit		= ingenic_usb_phy_exit,
199	.power_on	= ingenic_usb_phy_power_on,
200	.power_off	= ingenic_usb_phy_power_off,
201	.set_mode	= ingenic_usb_phy_set_mode,
202	.owner		= THIS_MODULE,
203};
204
205static void jz4770_usb_phy_init(struct phy *phy)
206{
207	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
208	u32 reg;
209
210	reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_POR |
211		FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_ALWAYS) |
212		FIELD_PREP(USBPCR_COMPDISTUNE_MASK, USBPCR_COMPDISTUNE_DFT) |
213		FIELD_PREP(USBPCR_OTGTUNE_MASK, USBPCR_OTGTUNE_DFT) |
214		FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DFT) |
215		FIELD_PREP(USBPCR_TXFSLSTUNE_MASK, USBPCR_TXFSLSTUNE_DFT) |
216		FIELD_PREP(USBPCR_TXRISETUNE_MASK, USBPCR_TXRISETUNE_DFT) |
217		FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_DFT);
218	writel(reg, priv->base + REG_USBPCR_OFFSET);
219}
220
221static void jz4775_usb_phy_init(struct phy *phy)
222{
223	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
224	u32 reg;
225
226	reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
227		USBPCR1_WORD_IF_16BIT;
228	writel(reg, priv->base + REG_USBPCR1_OFFSET);
229
230	reg = USBPCR_COMMONONN | USBPCR_POR |
231		FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_75PPT);
232	writel(reg, priv->base + REG_USBPCR_OFFSET);
233}
234
235static void jz4780_usb_phy_init(struct phy *phy)
236{
237	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
238	u32 reg;
239
240	reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
241		USBPCR1_WORD_IF_16BIT;
242	writel(reg, priv->base + REG_USBPCR1_OFFSET);
243
244	reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR;
245	writel(reg, priv->base + REG_USBPCR_OFFSET);
246}
247
248static void x1000_usb_phy_init(struct phy *phy)
249{
250	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
251	u32 reg;
252
253	reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT;
254	writel(reg, priv->base + REG_USBPCR1_OFFSET);
255
256	reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR |
257		FIELD_PREP(USBPCR_SQRXTUNE_MASK, USBPCR_SQRXTUNE_DCR_20PCT) |
258		FIELD_PREP(USBPCR_TXHSXVTUNE_MASK, USBPCR_TXHSXVTUNE_DCR_15MV) |
259		FIELD_PREP(USBPCR_TXVREFTUNE_MASK, USBPCR_TXVREFTUNE_INC_25PPT);
260	writel(reg, priv->base + REG_USBPCR_OFFSET);
261}
262
263static void x1830_usb_phy_init(struct phy *phy)
264{
265	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
266	u32 reg;
267
268	/* rdt */
269	writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET);
270
271	reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT |
272		USBPCR1_DMPD | USBPCR1_DPPD;
273	writel(reg, priv->base + REG_USBPCR1_OFFSET);
274
275	reg = USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR |
276		FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG);
277	writel(reg, priv->base + REG_USBPCR_OFFSET);
278}
279
280static void x2000_usb_phy_init(struct phy *phy)
281{
282	struct ingenic_usb_phy *priv = phy_get_drvdata(phy);
283	u32 reg;
284
285	reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_DPPD | USBPCR1_DMPD;
286	writel(reg & ~USBPCR1_PORT_RST, priv->base + REG_USBPCR1_OFFSET);
287
288	reg = USBPCR_POR | FIELD_PREP(USBPCR_IDPULLUP_MASK, USBPCR_IDPULLUP_OTG);
289	writel(reg, priv->base + REG_USBPCR_OFFSET);
290}
291
292static const struct ingenic_soc_info jz4770_soc_info = {
293	.usb_phy_init = jz4770_usb_phy_init,
294};
295
296static const struct ingenic_soc_info jz4775_soc_info = {
297	.usb_phy_init = jz4775_usb_phy_init,
298};
299
300static const struct ingenic_soc_info jz4780_soc_info = {
301	.usb_phy_init = jz4780_usb_phy_init,
302};
303
304static const struct ingenic_soc_info x1000_soc_info = {
305	.usb_phy_init = x1000_usb_phy_init,
306};
307
308static const struct ingenic_soc_info x1830_soc_info = {
309	.usb_phy_init = x1830_usb_phy_init,
310};
311
312static const struct ingenic_soc_info x2000_soc_info = {
313	.usb_phy_init = x2000_usb_phy_init,
314};
315
316static int ingenic_usb_phy_probe(struct platform_device *pdev)
317{
318	struct ingenic_usb_phy *priv;
319	struct phy_provider *provider;
320	struct device *dev = &pdev->dev;
321	int err;
322
323	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
324	if (!priv)
325		return -ENOMEM;
326
327	priv->soc_info = device_get_match_data(dev);
328	if (!priv->soc_info) {
329		dev_err(dev, "Error: No device match found\n");
330		return -ENODEV;
331	}
332
333	priv->base = devm_platform_ioremap_resource(pdev, 0);
334	if (IS_ERR(priv->base)) {
335		dev_err(dev, "Failed to map registers\n");
336		return PTR_ERR(priv->base);
337	}
338
339	priv->clk = devm_clk_get(dev, NULL);
340	if (IS_ERR(priv->clk)) {
341		err = PTR_ERR(priv->clk);
342		if (err != -EPROBE_DEFER)
343			dev_err(dev, "Failed to get clock\n");
344		return err;
345	}
346
347	priv->vcc_supply = devm_regulator_get(dev, "vcc");
348	if (IS_ERR(priv->vcc_supply)) {
349		err = PTR_ERR(priv->vcc_supply);
350		if (err != -EPROBE_DEFER)
351			dev_err(dev, "Failed to get regulator\n");
352		return err;
353	}
354
355	priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops);
356	if (IS_ERR(priv->phy))
357		return PTR_ERR(priv->phy);
358
359	phy_set_drvdata(priv->phy, priv);
360
361	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
362
363	return PTR_ERR_OR_ZERO(provider);
364}
365
366static const struct of_device_id ingenic_usb_phy_of_matches[] = {
367	{ .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info },
368	{ .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info },
369	{ .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info },
370	{ .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info },
371	{ .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info },
372	{ .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info },
373	{ /* sentinel */ }
374};
375MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches);
376
377static struct platform_driver ingenic_usb_phy_driver = {
378	.probe		= ingenic_usb_phy_probe,
379	.driver		= {
380		.name	= "ingenic-usb-phy",
381		.of_match_table = ingenic_usb_phy_of_matches,
382	},
383};
384module_platform_driver(ingenic_usb_phy_driver);
385
386MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
387MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
388MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
389MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver");
390MODULE_LICENSE("GPL");
391