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