• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &reg);
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, &reg, &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