xref: /linux/drivers/phy/canaan/phy-k230-usb.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1*8787fa1dSJiayu Du // SPDX-License-Identifier: GPL-2.0-only
2*8787fa1dSJiayu Du /*
3*8787fa1dSJiayu Du  * Canaan usb PHY driver
4*8787fa1dSJiayu Du  *
5*8787fa1dSJiayu Du  * Copyright (C) 2026 Jiayu Du <jiayu.riscv@isrc.iscas.ac.cn>
6*8787fa1dSJiayu Du  */
7*8787fa1dSJiayu Du 
8*8787fa1dSJiayu Du #include <linux/bitfield.h>
9*8787fa1dSJiayu Du #include <linux/io.h>
10*8787fa1dSJiayu Du #include <linux/of_address.h>
11*8787fa1dSJiayu Du #include <linux/phy/phy.h>
12*8787fa1dSJiayu Du #include <linux/platform_device.h>
13*8787fa1dSJiayu Du 
14*8787fa1dSJiayu Du #define MAX_PHYS		2
15*8787fa1dSJiayu Du 
16*8787fa1dSJiayu Du /* Register offsets within the HiSysConfig system controller */
17*8787fa1dSJiayu Du #define K230_USB0_TEST_REG_BASE     0x70
18*8787fa1dSJiayu Du #define K230_USB0_CTL_REG_BASE      0xb0
19*8787fa1dSJiayu Du #define K230_USB1_TEST_REG_BASE     0x90
20*8787fa1dSJiayu Du #define K230_USB1_CTL_REG_BASE      0xb8
21*8787fa1dSJiayu Du 
22*8787fa1dSJiayu Du /* Relative offsets within each PHY's control/test block */
23*8787fa1dSJiayu Du #define CTL0_OFFSET		0x00
24*8787fa1dSJiayu Du #define CTL1_OFFSET		0x04
25*8787fa1dSJiayu Du #define TEST_CTL3_OFFSET	0x0c
26*8787fa1dSJiayu Du 
27*8787fa1dSJiayu Du /* Bit definitions for TEST_CTL3 */
28*8787fa1dSJiayu Du #define USB_IDPULLUP0		BIT(4)
29*8787fa1dSJiayu Du #define USB_DMPULLDOWN0		BIT(8)
30*8787fa1dSJiayu Du #define USB_DPPULLDOWN0		BIT(9)
31*8787fa1dSJiayu Du 
32*8787fa1dSJiayu Du /* USB control register 0 in HiSysConfig system controller */
33*8787fa1dSJiayu Du /* PLL Integral Path Tune */
34*8787fa1dSJiayu Du #define USB_CTL0_PLLITUNE_MASK		GENMASK(23, 22)
35*8787fa1dSJiayu Du 
36*8787fa1dSJiayu Du /* PLL Proportional Path Tune */
37*8787fa1dSJiayu Du #define USB_CTL0_PLLPTUNE_MASK		GENMASK(21, 18)
38*8787fa1dSJiayu Du 
39*8787fa1dSJiayu Du /* PLL Bandwidth Adjustment */
40*8787fa1dSJiayu Du #define USB_CTL0_PLLBTUNE_MASK		GENMASK(17, 17)
41*8787fa1dSJiayu Du 
42*8787fa1dSJiayu Du /* VReg18 Bypass Control */
43*8787fa1dSJiayu Du #define USB_CTL0_VREGBYPASS_MASK	GENMASK(16, 16)
44*8787fa1dSJiayu Du 
45*8787fa1dSJiayu Du /* Retention Mode Enable */
46*8787fa1dSJiayu Du #define USB_CTL0_RETENABLEN_MASK	GENMASK(15, 15)
47*8787fa1dSJiayu Du 
48*8787fa1dSJiayu Du /* Reserved Request Input */
49*8787fa1dSJiayu Du #define USB_CTL0_RESREQIN_MASK		GENMASK(14, 14)
50*8787fa1dSJiayu Du 
51*8787fa1dSJiayu Du /* External VBUS Valid Select */
52*8787fa1dSJiayu Du #define USB_CTL0_VBUSVLDEXTSEL0_MASK	GENMASK(13, 13)
53*8787fa1dSJiayu Du 
54*8787fa1dSJiayu Du /* OTG Block Disable Control */
55*8787fa1dSJiayu Du #define USB_CTL0_OTGDISABLE0_MASK	GENMASK(12, 12)
56*8787fa1dSJiayu Du 
57*8787fa1dSJiayu Du /* Drive VBUS Enable */
58*8787fa1dSJiayu Du #define USB_CTL0_DRVVBUS0_MASK		GENMASK(11, 11)
59*8787fa1dSJiayu Du 
60*8787fa1dSJiayu Du /* Autoresume Mode Enable */
61*8787fa1dSJiayu Du #define USB_CTL0_AUTORSMENB0_MASK	GENMASK(10, 10)
62*8787fa1dSJiayu Du 
63*8787fa1dSJiayu Du /* HS Transceiver Asynchronous Control */
64*8787fa1dSJiayu Du #define USB_CTL0_HSXCVREXTCTL0_MASK	GENMASK(9, 9)
65*8787fa1dSJiayu Du 
66*8787fa1dSJiayu Du /* USB 1.1 Transmit Data */
67*8787fa1dSJiayu Du #define USB_CTL0_FSDATAEXT0_MASK	GENMASK(8, 8)
68*8787fa1dSJiayu Du 
69*8787fa1dSJiayu Du /* USB 1.1 SE0 Generation */
70*8787fa1dSJiayu Du #define USB_CTL0_FSSE0EXT0_MASK		GENMASK(7, 7)
71*8787fa1dSJiayu Du 
72*8787fa1dSJiayu Du /* USB 1.1 Data Enable */
73*8787fa1dSJiayu Du #define USB_CTL0_TXENABLEN0_MASK	GENMASK(6, 6)
74*8787fa1dSJiayu Du 
75*8787fa1dSJiayu Du /* Disconnect Threshold */
76*8787fa1dSJiayu Du #define USB_CTL0_COMPDISTUNE0_MASK	GENMASK(5, 3)
77*8787fa1dSJiayu Du 
78*8787fa1dSJiayu Du /* Squelch Threshold */
79*8787fa1dSJiayu Du #define USB_CTL0_SQRXTUNE0_MASK		GENMASK(2, 0)
80*8787fa1dSJiayu Du 
81*8787fa1dSJiayu Du /* USB control register 1 in HiSysConfig system controller */
82*8787fa1dSJiayu Du /* Data Detect Voltage */
83*8787fa1dSJiayu Du #define USB_CTL1_VDATREFTUNE0_MASK	GENMASK(23, 22)
84*8787fa1dSJiayu Du 
85*8787fa1dSJiayu Du /* VBUS Valid Threshold */
86*8787fa1dSJiayu Du #define USB_CTL1_OTGTUNE0_MASK		GENMASK(21, 19)
87*8787fa1dSJiayu Du 
88*8787fa1dSJiayu Du /* Transmitter High-Speed Crossover */
89*8787fa1dSJiayu Du #define USB_CTL1_TXHSXVTUNE0_MASK	GENMASK(18, 17)
90*8787fa1dSJiayu Du 
91*8787fa1dSJiayu Du /* FS/LS Source Impedance */
92*8787fa1dSJiayu Du #define USB_CTL1_TXFSLSTUNE0_MASK	GENMASK(16, 13)
93*8787fa1dSJiayu Du 
94*8787fa1dSJiayu Du /* HS DC Voltage Level */
95*8787fa1dSJiayu Du #define USB_CTL1_TXVREFTUNE0_MASK	GENMASK(12, 9)
96*8787fa1dSJiayu Du 
97*8787fa1dSJiayu Du /* HS Transmitter Rise/Fall Time */
98*8787fa1dSJiayu Du #define USB_CTL1_TXRISETUNE0_MASK	GENMASK(8, 7)
99*8787fa1dSJiayu Du 
100*8787fa1dSJiayu Du /* USB Source Impedance */
101*8787fa1dSJiayu Du #define USB_CTL1_TXRESTUNE0_MASK	GENMASK(6, 5)
102*8787fa1dSJiayu Du 
103*8787fa1dSJiayu Du /* HS Transmitter Pre-Emphasis Current Control */
104*8787fa1dSJiayu Du #define USB_CTL1_TXPREEMPAMPTUNE0_MASK	GENMASK(4, 3)
105*8787fa1dSJiayu Du 
106*8787fa1dSJiayu Du /* HS Transmitter Pre-Emphasis Duration Control */
107*8787fa1dSJiayu Du #define USB_CTL1_TXPREEMPPULSETUNE0_MASK	GENMASK(2, 2)
108*8787fa1dSJiayu Du 
109*8787fa1dSJiayu Du /* charging detection */
110*8787fa1dSJiayu Du #define USB_CTL1_CHRGSRCPUENB0_MASK	GENMASK(1, 0)
111*8787fa1dSJiayu Du 
112*8787fa1dSJiayu Du #define K230_PHY_CTL0_VAL \
113*8787fa1dSJiayu Du ( \
114*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_PLLITUNE_MASK, 0x0) | \
115*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_PLLPTUNE_MASK, 0xc) | \
116*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_PLLBTUNE_MASK, 0x1) | \
117*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_VREGBYPASS_MASK, 0x1) | \
118*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_RETENABLEN_MASK, 0x1) | \
119*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_RESREQIN_MASK, 0x0) | \
120*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_VBUSVLDEXTSEL0_MASK, 0x0) | \
121*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_OTGDISABLE0_MASK, 0x0) | \
122*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_DRVVBUS0_MASK, 0x1) | \
123*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_AUTORSMENB0_MASK, 0x0) | \
124*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_HSXCVREXTCTL0_MASK, 0x0) | \
125*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_FSDATAEXT0_MASK, 0x0) | \
126*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_FSSE0EXT0_MASK, 0x0) | \
127*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_TXENABLEN0_MASK, 0x0) | \
128*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_COMPDISTUNE0_MASK, 0x3) | \
129*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL0_SQRXTUNE0_MASK, 0x3) \
130*8787fa1dSJiayu Du )
131*8787fa1dSJiayu Du 
132*8787fa1dSJiayu Du #define K230_PHY_CTL1_VAL \
133*8787fa1dSJiayu Du ( \
134*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_VDATREFTUNE0_MASK, 0x1) | \
135*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_OTGTUNE0_MASK, 0x3) | \
136*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXHSXVTUNE0_MASK, 0x3) | \
137*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXFSLSTUNE0_MASK, 0x3) | \
138*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXVREFTUNE0_MASK, 0x3) | \
139*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXRISETUNE0_MASK, 0x1) | \
140*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXRESTUNE0_MASK, 0x1) | \
141*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXPREEMPAMPTUNE0_MASK, 0x0) | \
142*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_TXPREEMPPULSETUNE0_MASK, 0x0) | \
143*8787fa1dSJiayu Du 	FIELD_PREP(USB_CTL1_CHRGSRCPUENB0_MASK, 0x0) \
144*8787fa1dSJiayu Du )
145*8787fa1dSJiayu Du 
146*8787fa1dSJiayu Du struct k230_usb_phy_instance {
147*8787fa1dSJiayu Du 	struct k230_usb_phy_global *global;
148*8787fa1dSJiayu Du 	struct phy *phy;
149*8787fa1dSJiayu Du 	u32 test_offset;
150*8787fa1dSJiayu Du 	u32 ctl_offset;
151*8787fa1dSJiayu Du 	int index;
152*8787fa1dSJiayu Du };
153*8787fa1dSJiayu Du 
154*8787fa1dSJiayu Du struct k230_usb_phy_global {
155*8787fa1dSJiayu Du 	struct k230_usb_phy_instance phys[MAX_PHYS];
156*8787fa1dSJiayu Du 	void __iomem *base;
157*8787fa1dSJiayu Du };
158*8787fa1dSJiayu Du 
159*8787fa1dSJiayu Du static int k230_usb_phy_power_on(struct phy *phy)
160*8787fa1dSJiayu Du {
161*8787fa1dSJiayu Du 	struct k230_usb_phy_instance *inst = phy_get_drvdata(phy);
162*8787fa1dSJiayu Du 	struct k230_usb_phy_global *global = inst->global;
163*8787fa1dSJiayu Du 	void __iomem *base = global->base;
164*8787fa1dSJiayu Du 	u32 val;
165*8787fa1dSJiayu Du 
166*8787fa1dSJiayu Du 	/* Apply recommended settings */
167*8787fa1dSJiayu Du 	writel(K230_PHY_CTL0_VAL, base + inst->ctl_offset + CTL0_OFFSET);
168*8787fa1dSJiayu Du 	writel(K230_PHY_CTL1_VAL, base + inst->ctl_offset + CTL1_OFFSET);
169*8787fa1dSJiayu Du 
170*8787fa1dSJiayu Du 	/* Configure test register (pull-ups/pull-downs) */
171*8787fa1dSJiayu Du 	val = readl(base + inst->test_offset + TEST_CTL3_OFFSET);
172*8787fa1dSJiayu Du 	val |= USB_IDPULLUP0;
173*8787fa1dSJiayu Du 
174*8787fa1dSJiayu Du 	if (inst->index == 1)
175*8787fa1dSJiayu Du 		val |= (USB_DMPULLDOWN0 | USB_DPPULLDOWN0);
176*8787fa1dSJiayu Du 	else
177*8787fa1dSJiayu Du 		val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0);
178*8787fa1dSJiayu Du 
179*8787fa1dSJiayu Du 	writel(val, base + inst->test_offset + TEST_CTL3_OFFSET);
180*8787fa1dSJiayu Du 
181*8787fa1dSJiayu Du 	return 0;
182*8787fa1dSJiayu Du }
183*8787fa1dSJiayu Du 
184*8787fa1dSJiayu Du static int k230_usb_phy_power_off(struct phy *phy)
185*8787fa1dSJiayu Du {
186*8787fa1dSJiayu Du 	struct k230_usb_phy_instance *inst = phy_get_drvdata(phy);
187*8787fa1dSJiayu Du 	struct k230_usb_phy_global *global = inst->global;
188*8787fa1dSJiayu Du 	void __iomem *base = global->base;
189*8787fa1dSJiayu Du 	u32 val;
190*8787fa1dSJiayu Du 
191*8787fa1dSJiayu Du 	val = readl(base + inst->test_offset + TEST_CTL3_OFFSET);
192*8787fa1dSJiayu Du 	val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0);
193*8787fa1dSJiayu Du 	writel(val, base + inst->test_offset + TEST_CTL3_OFFSET);
194*8787fa1dSJiayu Du 
195*8787fa1dSJiayu Du 	return 0;
196*8787fa1dSJiayu Du }
197*8787fa1dSJiayu Du 
198*8787fa1dSJiayu Du static const struct phy_ops k230_usb_phy_ops = {
199*8787fa1dSJiayu Du 	.power_on = k230_usb_phy_power_on,
200*8787fa1dSJiayu Du 	.power_off = k230_usb_phy_power_off,
201*8787fa1dSJiayu Du 	.owner = THIS_MODULE,
202*8787fa1dSJiayu Du };
203*8787fa1dSJiayu Du 
204*8787fa1dSJiayu Du static struct phy *k230_usb_phy_xlate(struct device *dev,
205*8787fa1dSJiayu Du 				      const struct of_phandle_args *args)
206*8787fa1dSJiayu Du {
207*8787fa1dSJiayu Du 	struct k230_usb_phy_global *global = dev_get_drvdata(dev);
208*8787fa1dSJiayu Du 	unsigned int idx = args->args[0];
209*8787fa1dSJiayu Du 
210*8787fa1dSJiayu Du 	if (idx >= MAX_PHYS)
211*8787fa1dSJiayu Du 		return ERR_PTR(-EINVAL);
212*8787fa1dSJiayu Du 
213*8787fa1dSJiayu Du 	return global->phys[idx].phy;
214*8787fa1dSJiayu Du }
215*8787fa1dSJiayu Du 
216*8787fa1dSJiayu Du static int k230_usb_phy_probe(struct platform_device *pdev)
217*8787fa1dSJiayu Du {
218*8787fa1dSJiayu Du 	struct k230_usb_phy_global *global;
219*8787fa1dSJiayu Du 	struct device *dev = &pdev->dev;
220*8787fa1dSJiayu Du 	struct phy_provider *provider;
221*8787fa1dSJiayu Du 	int i;
222*8787fa1dSJiayu Du 
223*8787fa1dSJiayu Du 	global = devm_kzalloc(dev, sizeof(*global), GFP_KERNEL);
224*8787fa1dSJiayu Du 	if (!global)
225*8787fa1dSJiayu Du 		return -ENOMEM;
226*8787fa1dSJiayu Du 	dev_set_drvdata(dev, global);
227*8787fa1dSJiayu Du 
228*8787fa1dSJiayu Du 	global->base = devm_platform_ioremap_resource(pdev, 0);
229*8787fa1dSJiayu Du 	if (IS_ERR(global->base))
230*8787fa1dSJiayu Du 		return dev_err_probe(dev, PTR_ERR(global->base),
231*8787fa1dSJiayu Du 				     "failed to map registers\n");
232*8787fa1dSJiayu Du 
233*8787fa1dSJiayu Du 	static const struct {
234*8787fa1dSJiayu Du 		u32 test_offset;
235*8787fa1dSJiayu Du 		u32 ctl_offset;
236*8787fa1dSJiayu Du 	} phy_reg_info[MAX_PHYS] = {
237*8787fa1dSJiayu Du 		[0] = { K230_USB0_TEST_REG_BASE, K230_USB0_CTL_REG_BASE },
238*8787fa1dSJiayu Du 		[1] = { K230_USB1_TEST_REG_BASE, K230_USB1_CTL_REG_BASE },
239*8787fa1dSJiayu Du 	};
240*8787fa1dSJiayu Du 
241*8787fa1dSJiayu Du 	for (i = 0; i < MAX_PHYS; i++) {
242*8787fa1dSJiayu Du 		struct k230_usb_phy_instance *inst = &global->phys[i];
243*8787fa1dSJiayu Du 		struct phy *phy;
244*8787fa1dSJiayu Du 
245*8787fa1dSJiayu Du 		inst->global = global;
246*8787fa1dSJiayu Du 		inst->index = i;
247*8787fa1dSJiayu Du 		inst->test_offset = phy_reg_info[i].test_offset;
248*8787fa1dSJiayu Du 		inst->ctl_offset  = phy_reg_info[i].ctl_offset;
249*8787fa1dSJiayu Du 
250*8787fa1dSJiayu Du 		phy = devm_phy_create(dev, NULL, &k230_usb_phy_ops);
251*8787fa1dSJiayu Du 		if (IS_ERR(phy)) {
252*8787fa1dSJiayu Du 			dev_err(dev, "failed to create phy%d\n", i);
253*8787fa1dSJiayu Du 			return PTR_ERR(phy);
254*8787fa1dSJiayu Du 		}
255*8787fa1dSJiayu Du 
256*8787fa1dSJiayu Du 		phy_set_drvdata(phy, inst);
257*8787fa1dSJiayu Du 		inst->phy = phy;
258*8787fa1dSJiayu Du 	}
259*8787fa1dSJiayu Du 
260*8787fa1dSJiayu Du 	provider = devm_of_phy_provider_register(dev, k230_usb_phy_xlate);
261*8787fa1dSJiayu Du 	if (IS_ERR(provider))
262*8787fa1dSJiayu Du 		return PTR_ERR(provider);
263*8787fa1dSJiayu Du 
264*8787fa1dSJiayu Du 	return 0;
265*8787fa1dSJiayu Du }
266*8787fa1dSJiayu Du 
267*8787fa1dSJiayu Du static const struct of_device_id k230_usb_phy_of_match[] = {
268*8787fa1dSJiayu Du 	{ .compatible = "canaan,k230-usb-phy" },
269*8787fa1dSJiayu Du 	{}
270*8787fa1dSJiayu Du };
271*8787fa1dSJiayu Du MODULE_DEVICE_TABLE(of, k230_usb_phy_of_match);
272*8787fa1dSJiayu Du 
273*8787fa1dSJiayu Du static struct platform_driver k230_usb_phy_driver = {
274*8787fa1dSJiayu Du 	.probe = k230_usb_phy_probe,
275*8787fa1dSJiayu Du 	.driver = {
276*8787fa1dSJiayu Du 		.name = "k230-usb-phy",
277*8787fa1dSJiayu Du 		.of_match_table = k230_usb_phy_of_match,
278*8787fa1dSJiayu Du 	},
279*8787fa1dSJiayu Du };
280*8787fa1dSJiayu Du module_platform_driver(k230_usb_phy_driver);
281*8787fa1dSJiayu Du 
282*8787fa1dSJiayu Du MODULE_DESCRIPTION("Canaan Kendryte K230 USB 2.0 PHY driver");
283*8787fa1dSJiayu Du MODULE_AUTHOR("Jiayu Du <jiayu.riscv@isrc.iscas.ac.cn>");
284*8787fa1dSJiayu Du MODULE_LICENSE("GPL");
285