1 /*
2 * Allwinner GMAC MDIO interface driver
3 *
4 * Copyright 2022 Allwinnertech
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11 #include <linux/delay.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15 #include <linux/of_address.h>
16 #include <linux/of_mdio.h>
17 #include <linux/phy.h>
18 #include <linux/platform_device.h>
19 #include <linux/iopoll.h>
20
21 #define SUNXI_MDIO_CONFIG 0x0
22 #define SUNXI_MDIO_DATA 0x4
23
24 #define SUNXI_MDIO_BUSY 0x00000001
25 #define SUNXI_MDIO_WRITE 0x00000002
26 #define SUNXI_MDIO_PHY_MASK 0x0000FFC0
27 #define SUNXI_MDIO_CR_MASK 0x0000001
28 #define SUNXI_MDIO_CLK 0x00000008
29 #define SUNXI_MDIO_MDC_DIV 0x3
30
31 /* bits 4 3 2 | AHB1 Clock | MDC Clock
32 * -------------------------------------------------------
33 * 0 0 0 | 60 ~ 100 MHz | div-42
34 * 0 0 1 | 100 ~ 150 MHz | div-62
35 * 0 1 0 | 20 ~ 35 MHz | div-16
36 * 0 1 1 | 35 ~ 60 MHz | div-26
37 * 1 0 0 | 150 ~ 250 MHz | div-102
38 * 1 0 1 | 250 ~ 300 MHz | div-124
39 * 1 1 x | Reserved |
40 */
41 #define SUNXI_MDIO_MDC_DIV_RATIO_M 0x07
42 #define SUNXI_MDIO_MDC_DIV_RATIO_M_BIT 20
43 #define SUNXI_MDIO_PHY_ADDR 0x0001F000
44 #define SUNXI_MDIO_PHY_ADDR_OFFSET 12
45 #define SUNXI_MDIO_PHY_REG 0x000007F0
46 #define SUNXI_MDIO_PHY_REG_OFFSET 4
47 #define SUNXI_MDIO_RESET 0x4
48 #define SUNXI_MDIO_RESET_OFFSET 2
49
50 #define SUNXI_MDIO_WR_TIMEOUT 10 /* ms */
51
52 struct mii_reg_dump {
53 u32 addr;
54 u16 reg;
55 u16 value;
56 };
57
58 struct sunxi_mdio {
59 struct device *dev;
60 void __iomem *base;
61 };
62
63 struct mii_reg_dump mii_reg;
64 /**
65 * sunxi_parse_read_str - parse the input string for write attri.
66 * @str: string to be parsed, eg: "0x00 0x01".
67 * @addr: store the reg address. eg: 0x00.
68 * @reg: store the expect value. eg: 0x01.
69 *
70 * return 0 if success, otherwise failed.
71 */
sunxi_parse_read_str(char * str,u16 * addr,u16 * reg)72 static int sunxi_parse_read_str(char *str, u16 *addr, u16 *reg)
73 {
74 char *ptr = str;
75 char *tstr = NULL;
76 int ret;
77
78 /*
79 * Skip the leading whitespace, find the true split symbol.
80 * And it must be 'address value'.
81 */
82 tstr = strim(str);
83 ptr = strchr(tstr, ' ');
84 if (!ptr)
85 return -EINVAL;
86
87 /*
88 * Replaced split symbol with a %NUL-terminator temporary.
89 * Will be fixed at end.
90 */
91 *ptr = '\0';
92 ret = kstrtos16(tstr, 16, addr);
93 if (ret)
94 goto out;
95
96 ret = kstrtos16(skip_spaces(ptr + 1), 16, reg);
97
98 out:
99 return ret;
100 }
101
102 /**
103 * sunxi_parse_write_str - parse the input string for compare attri.
104 * @str: string to be parsed, eg: "0x00 0x11 0x11".
105 * @addr: store the address. eg: 0x00.
106 * @reg: store the reg. eg: 0x11.
107 * @val: store the value. eg: 0x11.
108 *
109 * return 0 if success, otherwise failed.
110 */
sunxi_parse_write_str(char * str,u16 * addr,u16 * reg,u16 * val)111 static int sunxi_parse_write_str(char *str, u16 *addr,
112 u16 *reg, u16 *val)
113 {
114 u16 result_addr[3] = { 0 };
115 char *ptr = str;
116 char *ptr2 = NULL;
117 int i, ret = 0;
118
119 for (i = 0; i < ARRAY_SIZE(result_addr); i++) {
120 ptr = skip_spaces(ptr);
121 ptr2 = strchr(ptr, ' ');
122 if (ptr2)
123 *ptr2 = '\0';
124
125 ret = kstrtou16(ptr, 16, &result_addr[i]);
126
127 if (!ptr2 || ret)
128 break;
129
130 ptr = ptr2 + 1;
131 }
132
133 *addr = result_addr[0];
134 *reg = result_addr[1];
135 *val = result_addr[2];
136
137 return ret;
138 }
139
140 /*
141 * Wait until any existing MII operation is complete
142 * Read 0 indicate finish in read or write operation
143 * Read 1 indicate busy
144 * */
sunxi_mdio_busy_wait(struct sunxi_mdio * chip)145 static void sunxi_mdio_busy_wait(struct sunxi_mdio *chip)
146 {
147 unsigned long timeout = jiffies + msecs_to_jiffies(SUNXI_MDIO_WR_TIMEOUT);
148 u32 reg;
149
150 do {
151 reg = readl(chip->base + SUNXI_MDIO_CONFIG);
152
153 if ((reg & SUNXI_MDIO_BUSY) != 1) {
154 break;
155 }
156 } while (time_before(jiffies, timeout));
157 }
158
159 /**
160 * sunxi_mdio_read - GMAC MII bus read func
161 * @bus: mii bus struct
162 * @phyaddr: phy address
163 * @phyreg: phy register
164 *
165 * Called when phy_write is used.
166 *
167 * Returns reg value for specific phy register.
168 */
sunxi_mdio_read(struct mii_bus * bus,int phyaddr,int phyreg)169 static int sunxi_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
170 {
171 unsigned int value = 0;
172 struct sunxi_mdio *chip = bus->priv;
173
174 /* Mask the MDC_DIV_RATIO */
175 value |= ((SUNXI_MDIO_MDC_DIV & SUNXI_MDIO_MDC_DIV_RATIO_M) << SUNXI_MDIO_MDC_DIV_RATIO_M_BIT);
176 value |= (((phyaddr << SUNXI_MDIO_PHY_ADDR_OFFSET) & (SUNXI_MDIO_PHY_ADDR)) |
177 ((phyreg << SUNXI_MDIO_PHY_REG_OFFSET) & (SUNXI_MDIO_PHY_REG)) |
178 SUNXI_MDIO_BUSY);
179
180 writel(value, chip->base + SUNXI_MDIO_CONFIG);
181
182 sunxi_mdio_busy_wait(chip);
183
184 return (int)readl(chip->base + SUNXI_MDIO_DATA);
185 }
186
187 /**
188 * sunxi_mdio_write - GMAC MII bus write func
189 * @bus: mii bus struct
190 * @phyaddr: phy address
191 * @phyreg: phy register
192 * @data: the value to be written to the register
193 *
194 * Called when phy_wirte is used.
195 *
196 * Returns 0 for a successful open, or appropriate error code
197 */
sunxi_mdio_write(struct mii_bus * bus,int phyaddr,int phyreg,unsigned short data)198 static int sunxi_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, unsigned short data)
199 {
200 unsigned int value;
201 struct sunxi_mdio *chip = bus->priv;
202
203 value = ((SUNXI_MDIO_MDC_DIV_RATIO_M << SUNXI_MDIO_MDC_DIV_RATIO_M_BIT) & readl(chip->base+ SUNXI_MDIO_CONFIG)) |
204 (SUNXI_MDIO_MDC_DIV << SUNXI_MDIO_MDC_DIV_RATIO_M_BIT);
205 value |= (((phyaddr << SUNXI_MDIO_PHY_ADDR_OFFSET) & (SUNXI_MDIO_PHY_ADDR)) |
206 ((phyreg << SUNXI_MDIO_PHY_REG_OFFSET) & (SUNXI_MDIO_PHY_REG))) |
207 SUNXI_MDIO_WRITE | SUNXI_MDIO_BUSY;
208
209 sunxi_mdio_busy_wait(chip);
210
211 /* Set the MII address register to write */
212 writel(data, chip->base + SUNXI_MDIO_DATA);
213 writel(value, chip->base + SUNXI_MDIO_CONFIG);
214
215 sunxi_mdio_busy_wait(chip);
216
217 return 0;
218 }
219
sunxi_mdio_reset(struct mii_bus * bus)220 static int sunxi_mdio_reset(struct mii_bus *bus)
221 {
222 struct sunxi_mdio *chip = bus->priv;
223
224 writel((SUNXI_MDIO_RESET << SUNXI_MDIO_RESET_OFFSET), chip->base + SUNXI_MDIO_CONFIG);
225
226 sunxi_mdio_busy_wait(chip);
227 return 0;
228 }
229
sunxi_mdio_read_show(struct device * dev,struct device_attribute * attr,char * buf)230 static ssize_t sunxi_mdio_read_show(struct device *dev,
231 struct device_attribute *attr, char *buf)
232 {
233 struct platform_device *pdev = to_platform_device(dev);
234 struct mii_bus *bus = platform_get_drvdata(pdev);
235
236 mii_reg.value = sunxi_mdio_read(bus, mii_reg.addr, mii_reg.reg);
237 return sprintf(buf, "ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n",
238 mii_reg.addr, mii_reg.reg, mii_reg.value);
239 }
240
sunxi_mdio_read_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)241 static ssize_t sunxi_mdio_read_store(struct device *dev,
242 struct device_attribute *attr, const char *buf, size_t count)
243 {
244 int ret = 0;
245 u16 reg, addr;
246 char *ptr;
247
248 ptr = (char *)buf;
249
250 if (!dev) {
251 pr_err("Argment is invalid\n");
252 return count;
253 }
254
255 ret = sunxi_parse_read_str(ptr, &addr, ®);
256 if (ret)
257 return ret;
258
259 mii_reg.addr = addr;
260 mii_reg.reg = reg;
261
262 return count;
263 }
264
sunxi_mdio_write_show(struct device * dev,struct device_attribute * attr,char * buf)265 static ssize_t sunxi_mdio_write_show(struct device *dev,
266 struct device_attribute *attr, char *buf)
267 {
268 struct platform_device *pdev = to_platform_device(dev);
269 struct mii_bus *bus = platform_get_drvdata(pdev);
270 u16 bef_val, aft_val;
271
272 bef_val = sunxi_mdio_read(bus, mii_reg.addr, mii_reg.reg);
273 sunxi_mdio_write(bus, mii_reg.addr, mii_reg.reg, mii_reg.value);
274 aft_val = sunxi_mdio_read(bus, mii_reg.addr, mii_reg.reg);
275 return sprintf(buf, "before ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n"
276 "after ADDR[0x%02x]:REG[0x%02x] = 0x%04x\n",
277 mii_reg.addr, mii_reg.reg, bef_val,
278 mii_reg.addr, mii_reg.reg, aft_val);
279 }
280
sunxi_mdio_write_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)281 static ssize_t sunxi_mdio_write_store(struct device *dev,
282 struct device_attribute *attr, const char *buf, size_t count)
283 {
284 int ret = 0;
285 u16 reg, addr, val;
286 char *ptr;
287
288 ptr = (char *)buf;
289
290 ret = sunxi_parse_write_str(ptr, &addr, ®, &val);
291 if (ret)
292 return ret;
293
294 mii_reg.reg = reg;
295 mii_reg.addr = addr;
296 mii_reg.value = val;
297
298 return count;
299 }
300
301 static DEVICE_ATTR(mii_read, 0664, sunxi_mdio_read_show, sunxi_mdio_read_store);
302 static DEVICE_ATTR(mii_write, 0664, sunxi_mdio_write_show, sunxi_mdio_write_store);
303
sunxi_mdio_sysfs_create(struct device * dev)304 static void sunxi_mdio_sysfs_create(struct device *dev)
305 {
306 device_create_file(dev, &dev_attr_mii_read);
307 device_create_file(dev, &dev_attr_mii_write);
308 }
309
sunxi_mdio_sysfs_destroy(struct device * dev)310 static void sunxi_mdio_sysfs_destroy(struct device *dev)
311 {
312 device_remove_file(dev, &dev_attr_mii_read);
313 device_remove_file(dev, &dev_attr_mii_write);
314 }
315 /**
316 * sunxi_mdio_probe - GMAC MII bus probe func
317 *
318 * sunxi mdio probe must run after sunxi emac probe,
319 * because mdio clk was enabled in emac driver.
320 */
sunxi_mdio_probe(struct platform_device * pdev)321 static int sunxi_mdio_probe(struct platform_device *pdev)
322 {
323 struct device_node *np = pdev->dev.of_node;
324 struct device *dev = &pdev->dev;
325 struct mii_bus *bus;
326 struct sunxi_mdio *chip;
327 int ret;
328 #ifdef DEBUG
329 struct phy_device *phy;
330 int addr;
331 #endif
332
333 dev_dbg(dev, "%s() BEGIN\n", __func__);
334
335 chip = devm_kzalloc(dev, sizeof(struct sunxi_mdio), GFP_KERNEL);
336 if (!chip)
337 return -ENOMEM;
338
339 bus = mdiobus_alloc_size(sizeof(*bus));
340 if (!bus) {
341 dev_err(dev, "Error: alloc mii bus failed\n");
342 return -ENOMEM;
343 }
344
345 bus->name = dev_name(dev);
346 bus->read = sunxi_mdio_read;
347 bus->write = sunxi_mdio_write;
348 bus->reset = sunxi_mdio_reset;
349 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
350 bus->parent = &pdev->dev;
351 bus->priv = chip;
352
353 chip->dev = dev;
354 chip->base = of_iomap(np, 0);
355 if (IS_ERR(chip->base)) {
356 ret = PTR_ERR(chip->base);
357 goto iomap_err;
358 }
359
360 ret = of_mdiobus_register(bus, np);
361 if (ret < 0)
362 goto mdio_register_err;
363
364 platform_set_drvdata(pdev, bus);
365
366 sunxi_mdio_sysfs_create(dev);
367
368 #ifdef DEBUG
369 /* scan and dump the bus */
370 for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
371 phy = mdiobus_get_phy(bus, addr);
372 if (phy)
373 dev_info(dev, "PHY ID: 0x%08x, ADDR: 0x%x, DEVICE: %s, DRIVER: %s\n",
374 phy->phy_id, addr, phydev_name(phy),
375 phy->drv ? phy->drv->name : "Generic PHY");
376 }
377 #endif
378
379 dev_dbg(dev, "%s() SUCCESS\n", __func__);
380 return 0;
381
382 mdio_register_err:
383 iounmap(chip->base);
384 iomap_err:
385 mdiobus_free(bus);
386 return ret;
387 }
388
sunxi_mdio_remove(struct platform_device * pdev)389 static int sunxi_mdio_remove(struct platform_device *pdev)
390 {
391 struct mii_bus *bus = platform_get_drvdata(pdev);
392 struct device *dev = &pdev->dev;
393 struct sunxi_mdio *chip = bus->priv;
394
395 sunxi_mdio_sysfs_destroy(dev);
396 mdiobus_unregister(bus);
397 iounmap(chip->base);
398 mdiobus_free(bus);
399
400 return 0;
401 }
402
403 static const struct of_device_id sunxi_mdio_dt_ids[] = {
404 { .compatible = "allwinner,sunxi-mdio" },
405 { }
406 };
407 MODULE_DEVICE_TABLE(of, sunxi_mdio_dt_ids);
408
409 static struct platform_driver sunxi_mdio_driver = {
410 .probe = sunxi_mdio_probe,
411 .remove = sunxi_mdio_remove,
412 .driver = {
413 .name = "sunxi-mdio",
414 .of_match_table = sunxi_mdio_dt_ids,
415 },
416 };
417
sunxi_mdio_init(void)418 static int __init sunxi_mdio_init(void)
419 {
420 return platform_driver_register(&sunxi_mdio_driver);
421 }
422 late_initcall(sunxi_mdio_init);
423
sunxi_mdio_exit(void)424 static void __exit sunxi_mdio_exit(void)
425 {
426 platform_driver_unregister(&sunxi_mdio_driver);
427 }
428 module_exit(sunxi_mdio_exit);
429
430 MODULE_DESCRIPTION("Allwinner GMAC MDIO interface driver");
431 MODULE_AUTHOR("xuminghui <xuminghui@allwinnertech.com>");
432 MODULE_LICENSE("GPL");
433 MODULE_VERSION("1.0.0");
434