1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2006-2007 PA Semi, Inc 4 * 5 * Author: Olof Johansson, PA Semi 6 * 7 * Maintained by: Olof Johansson <olof@lixom.net> 8 * 9 * Based on drivers/net/fs_enet/mii-bitbang.c. 10 */ 11 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/types.h> 15 #include <linux/slab.h> 16 #include <linux/sched.h> 17 #include <linux/errno.h> 18 #include <linux/ioport.h> 19 #include <linux/interrupt.h> 20 #include <linux/phy.h> 21 #include <linux/of_address.h> 22 #include <linux/of_mdio.h> 23 #include <linux/platform_device.h> 24 25 #define DELAY 1 26 27 static void __iomem *gpio_regs; 28 29 struct gpio_priv { 30 int mdc_pin; 31 int mdio_pin; 32 }; 33 34 #define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin) 35 #define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin) 36 37 static inline void mdio_lo(struct mii_bus *bus) 38 { 39 out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus)); 40 } 41 42 static inline void mdio_hi(struct mii_bus *bus) 43 { 44 out_le32(gpio_regs, 1 << MDIO_PIN(bus)); 45 } 46 47 static inline void mdc_lo(struct mii_bus *bus) 48 { 49 out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus)); 50 } 51 52 static inline void mdc_hi(struct mii_bus *bus) 53 { 54 out_le32(gpio_regs, 1 << MDC_PIN(bus)); 55 } 56 57 static inline void mdio_active(struct mii_bus *bus) 58 { 59 out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus))); 60 } 61 62 static inline void mdio_tristate(struct mii_bus *bus) 63 { 64 out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus))); 65 } 66 67 static inline int mdio_read(struct mii_bus *bus) 68 { 69 return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus))); 70 } 71 72 static void clock_out(struct mii_bus *bus, int bit) 73 { 74 if (bit) 75 mdio_hi(bus); 76 else 77 mdio_lo(bus); 78 udelay(DELAY); 79 mdc_hi(bus); 80 udelay(DELAY); 81 mdc_lo(bus); 82 } 83 84 /* Utility to send the preamble, address, and register (common to read and write). */ 85 static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg) 86 { 87 int i; 88 89 /* CFE uses a really long preamble (40 bits). We'll do the same. */ 90 mdio_active(bus); 91 for (i = 0; i < 40; i++) { 92 clock_out(bus, 1); 93 } 94 95 /* send the start bit (01) and the read opcode (10) or write (10) */ 96 clock_out(bus, 0); 97 clock_out(bus, 1); 98 99 clock_out(bus, read); 100 clock_out(bus, !read); 101 102 /* send the PHY address */ 103 for (i = 0; i < 5; i++) { 104 clock_out(bus, (addr & 0x10) != 0); 105 addr <<= 1; 106 } 107 108 /* send the register address */ 109 for (i = 0; i < 5; i++) { 110 clock_out(bus, (reg & 0x10) != 0); 111 reg <<= 1; 112 } 113 } 114 115 static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location) 116 { 117 u16 rdreg; 118 int ret, i; 119 u8 addr = phy_id & 0xff; 120 u8 reg = location & 0xff; 121 122 bitbang_pre(bus, 1, addr, reg); 123 124 /* tri-state our MDIO I/O pin so we can read */ 125 mdio_tristate(bus); 126 udelay(DELAY); 127 mdc_hi(bus); 128 udelay(DELAY); 129 mdc_lo(bus); 130 131 /* read 16 bits of register data, MSB first */ 132 rdreg = 0; 133 for (i = 0; i < 16; i++) { 134 mdc_lo(bus); 135 udelay(DELAY); 136 mdc_hi(bus); 137 udelay(DELAY); 138 mdc_lo(bus); 139 udelay(DELAY); 140 rdreg <<= 1; 141 rdreg |= mdio_read(bus); 142 } 143 144 mdc_hi(bus); 145 udelay(DELAY); 146 mdc_lo(bus); 147 udelay(DELAY); 148 149 ret = rdreg; 150 151 return ret; 152 } 153 154 static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val) 155 { 156 int i; 157 158 u8 addr = phy_id & 0xff; 159 u8 reg = location & 0xff; 160 u16 value = val & 0xffff; 161 162 bitbang_pre(bus, 0, addr, reg); 163 164 /* send the turnaround (10) */ 165 mdc_lo(bus); 166 mdio_hi(bus); 167 udelay(DELAY); 168 mdc_hi(bus); 169 udelay(DELAY); 170 mdc_lo(bus); 171 mdio_lo(bus); 172 udelay(DELAY); 173 mdc_hi(bus); 174 udelay(DELAY); 175 176 /* write 16 bits of register data, MSB first */ 177 for (i = 0; i < 16; i++) { 178 mdc_lo(bus); 179 if (value & 0x8000) 180 mdio_hi(bus); 181 else 182 mdio_lo(bus); 183 udelay(DELAY); 184 mdc_hi(bus); 185 udelay(DELAY); 186 value <<= 1; 187 } 188 189 /* 190 * Tri-state the MDIO line. 191 */ 192 mdio_tristate(bus); 193 mdc_lo(bus); 194 udelay(DELAY); 195 mdc_hi(bus); 196 udelay(DELAY); 197 return 0; 198 } 199 200 static int gpio_mdio_reset(struct mii_bus *bus) 201 { 202 /*nothing here - dunno how to reset it*/ 203 return 0; 204 } 205 206 207 static int gpio_mdio_probe(struct platform_device *ofdev) 208 { 209 struct device *dev = &ofdev->dev; 210 struct device_node *np = ofdev->dev.of_node; 211 struct mii_bus *new_bus; 212 struct gpio_priv *priv; 213 const unsigned int *prop; 214 int err; 215 216 err = -ENOMEM; 217 priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL); 218 if (!priv) 219 goto out; 220 221 new_bus = mdiobus_alloc(); 222 223 if (!new_bus) 224 goto out_free_priv; 225 226 new_bus->name = "pasemi gpio mdio bus"; 227 new_bus->read = &gpio_mdio_read; 228 new_bus->write = &gpio_mdio_write; 229 new_bus->reset = &gpio_mdio_reset; 230 231 prop = of_get_property(np, "reg", NULL); 232 snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop); 233 new_bus->priv = priv; 234 235 prop = of_get_property(np, "mdc-pin", NULL); 236 priv->mdc_pin = *prop; 237 238 prop = of_get_property(np, "mdio-pin", NULL); 239 priv->mdio_pin = *prop; 240 241 new_bus->parent = dev; 242 dev_set_drvdata(dev, new_bus); 243 244 err = of_mdiobus_register(new_bus, np); 245 246 if (err != 0) { 247 pr_err("%s: Cannot register as MDIO bus, err %d\n", 248 new_bus->name, err); 249 goto out_free_irq; 250 } 251 252 return 0; 253 254 out_free_irq: 255 kfree(new_bus); 256 out_free_priv: 257 kfree(priv); 258 out: 259 return err; 260 } 261 262 263 static void gpio_mdio_remove(struct platform_device *dev) 264 { 265 struct mii_bus *bus = dev_get_drvdata(&dev->dev); 266 267 mdiobus_unregister(bus); 268 269 dev_set_drvdata(&dev->dev, NULL); 270 271 kfree(bus->priv); 272 bus->priv = NULL; 273 mdiobus_free(bus); 274 } 275 276 static const struct of_device_id gpio_mdio_match[] = 277 { 278 { 279 .compatible = "gpio-mdio", 280 }, 281 {}, 282 }; 283 MODULE_DEVICE_TABLE(of, gpio_mdio_match); 284 285 static struct platform_driver gpio_mdio_driver = 286 { 287 .probe = gpio_mdio_probe, 288 .remove_new = gpio_mdio_remove, 289 .driver = { 290 .name = "gpio-mdio-bitbang", 291 .of_match_table = gpio_mdio_match, 292 }, 293 }; 294 295 static int __init gpio_mdio_init(void) 296 { 297 struct device_node *np; 298 299 np = of_find_compatible_node(NULL, NULL, "1682m-gpio"); 300 if (!np) 301 np = of_find_compatible_node(NULL, NULL, 302 "pasemi,pwrficient-gpio"); 303 if (!np) 304 return -ENODEV; 305 gpio_regs = of_iomap(np, 0); 306 of_node_put(np); 307 308 if (!gpio_regs) 309 return -ENODEV; 310 311 return platform_driver_register(&gpio_mdio_driver); 312 } 313 module_init(gpio_mdio_init); 314 315 static void __exit gpio_mdio_exit(void) 316 { 317 platform_driver_unregister(&gpio_mdio_driver); 318 if (gpio_regs) 319 iounmap(gpio_regs); 320 } 321 module_exit(gpio_mdio_exit); 322 323 MODULE_LICENSE("GPL"); 324 MODULE_AUTHOR("Olof Johansson <olof@lixom.net>"); 325 MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards"); 326
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.