gpio_mdio.c (6296B)
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/of_platform.h> 24 25#define DELAY 1 26 27static void __iomem *gpio_regs; 28 29struct 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 37static inline void mdio_lo(struct mii_bus *bus) 38{ 39 out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus)); 40} 41 42static inline void mdio_hi(struct mii_bus *bus) 43{ 44 out_le32(gpio_regs, 1 << MDIO_PIN(bus)); 45} 46 47static inline void mdc_lo(struct mii_bus *bus) 48{ 49 out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus)); 50} 51 52static inline void mdc_hi(struct mii_bus *bus) 53{ 54 out_le32(gpio_regs, 1 << MDC_PIN(bus)); 55} 56 57static 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 62static inline void mdio_tristate(struct mii_bus *bus) 63{ 64 out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus))); 65} 66 67static inline int mdio_read(struct mii_bus *bus) 68{ 69 return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus))); 70} 71 72static 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). */ 85static 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 115static 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 154static 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 200static int gpio_mdio_reset(struct mii_bus *bus) 201{ 202 /*nothing here - dunno how to reset it*/ 203 return 0; 204} 205 206 207static 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 254out_free_irq: 255 kfree(new_bus); 256out_free_priv: 257 kfree(priv); 258out: 259 return err; 260} 261 262 263static int 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 return 0; 276} 277 278static const struct of_device_id gpio_mdio_match[] = 279{ 280 { 281 .compatible = "gpio-mdio", 282 }, 283 {}, 284}; 285MODULE_DEVICE_TABLE(of, gpio_mdio_match); 286 287static struct platform_driver gpio_mdio_driver = 288{ 289 .probe = gpio_mdio_probe, 290 .remove = gpio_mdio_remove, 291 .driver = { 292 .name = "gpio-mdio-bitbang", 293 .of_match_table = gpio_mdio_match, 294 }, 295}; 296 297static int gpio_mdio_init(void) 298{ 299 struct device_node *np; 300 301 np = of_find_compatible_node(NULL, NULL, "1682m-gpio"); 302 if (!np) 303 np = of_find_compatible_node(NULL, NULL, 304 "pasemi,pwrficient-gpio"); 305 if (!np) 306 return -ENODEV; 307 gpio_regs = of_iomap(np, 0); 308 of_node_put(np); 309 310 if (!gpio_regs) 311 return -ENODEV; 312 313 return platform_driver_register(&gpio_mdio_driver); 314} 315module_init(gpio_mdio_init); 316 317static void gpio_mdio_exit(void) 318{ 319 platform_driver_unregister(&gpio_mdio_driver); 320 if (gpio_regs) 321 iounmap(gpio_regs); 322} 323module_exit(gpio_mdio_exit); 324 325MODULE_LICENSE("GPL"); 326MODULE_AUTHOR("Olof Johansson <olof@lixom.net>"); 327MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");