1 /* 2 * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) 3 * 4 * Author: Vitaly Bordug <vbordug@ru.mvista.com> 5 * Anton Vorontsov <avorontsov@ru.mvista.com> 6 * 7 * Copyright (c) 2006-2007 MontaVista Software, Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/platform_device.h> 18 #include <linux/list.h> 19 #include <linux/mii.h> 20 #include <linux/phy.h> 21 #include <linux/phy_fixed.h> 22 #include <linux/err.h> 23 #include <linux/slab.h> 24 #include <linux/of.h> 25 #include <linux/gpio.h> 26 27 #include "swphy.h" 28 29 struct fixed_mdio_bus { 30 struct mii_bus *mii_bus; 31 struct list_head phys; 32 }; 33 34 struct fixed_phy { 35 int addr; 36 struct phy_device *phydev; 37 struct fixed_phy_status status; 38 int (*link_update)(struct net_device *, struct fixed_phy_status *); 39 struct list_head node; 40 int link_gpio; 41 }; 42 43 static struct platform_device *pdev; 44 static struct fixed_mdio_bus platform_fmb = { 45 .phys = LIST_HEAD_INIT(platform_fmb.phys), 46 }; 47 48 static void fixed_phy_update(struct fixed_phy *fp) 49 { 50 if (gpio_is_valid(fp->link_gpio)) 51 fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio); 52 } 53 54 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) 55 { 56 struct fixed_mdio_bus *fmb = bus->priv; 57 struct fixed_phy *fp; 58 59 list_for_each_entry(fp, &fmb->phys, node) { 60 if (fp->addr == phy_addr) { 61 /* Issue callback if user registered it. */ 62 if (fp->link_update) { 63 fp->link_update(fp->phydev->attached_dev, 64 &fp->status); 65 fixed_phy_update(fp); 66 } 67 return swphy_read_reg(reg_num, &fp->status); 68 } 69 } 70 71 return 0xFFFF; 72 } 73 74 static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, 75 u16 val) 76 { 77 return 0; 78 } 79 80 /* 81 * If something weird is required to be done with link/speed, 82 * network driver is able to assign a function to implement this. 83 * May be useful for PHY's that need to be software-driven. 84 */ 85 int fixed_phy_set_link_update(struct phy_device *phydev, 86 int (*link_update)(struct net_device *, 87 struct fixed_phy_status *)) 88 { 89 struct fixed_mdio_bus *fmb = &platform_fmb; 90 struct fixed_phy *fp; 91 92 if (!phydev || !phydev->mdio.bus) 93 return -EINVAL; 94 95 list_for_each_entry(fp, &fmb->phys, node) { 96 if (fp->addr == phydev->mdio.addr) { 97 fp->link_update = link_update; 98 fp->phydev = phydev; 99 return 0; 100 } 101 } 102 103 return -ENOENT; 104 } 105 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 106 107 int fixed_phy_update_state(struct phy_device *phydev, 108 const struct fixed_phy_status *status, 109 const struct fixed_phy_status *changed) 110 { 111 struct fixed_mdio_bus *fmb = &platform_fmb; 112 struct fixed_phy *fp; 113 114 if (!phydev || phydev->mdio.bus != fmb->mii_bus) 115 return -EINVAL; 116 117 list_for_each_entry(fp, &fmb->phys, node) { 118 if (fp->addr == phydev->mdio.addr) { 119 #define _UPD(x) if (changed->x) \ 120 fp->status.x = status->x 121 _UPD(link); 122 _UPD(speed); 123 _UPD(duplex); 124 _UPD(pause); 125 _UPD(asym_pause); 126 #undef _UPD 127 fixed_phy_update(fp); 128 return 0; 129 } 130 } 131 132 return -ENOENT; 133 } 134 EXPORT_SYMBOL(fixed_phy_update_state); 135 136 int fixed_phy_add(unsigned int irq, int phy_addr, 137 struct fixed_phy_status *status, 138 int link_gpio) 139 { 140 int ret; 141 struct fixed_mdio_bus *fmb = &platform_fmb; 142 struct fixed_phy *fp; 143 144 ret = swphy_validate_state(status); 145 if (ret < 0) 146 return ret; 147 148 fp = kzalloc(sizeof(*fp), GFP_KERNEL); 149 if (!fp) 150 return -ENOMEM; 151 152 if (irq != PHY_POLL) 153 fmb->mii_bus->irq[phy_addr] = irq; 154 155 fp->addr = phy_addr; 156 fp->status = *status; 157 fp->link_gpio = link_gpio; 158 159 if (gpio_is_valid(fp->link_gpio)) { 160 ret = gpio_request_one(fp->link_gpio, GPIOF_DIR_IN, 161 "fixed-link-gpio-link"); 162 if (ret) 163 goto err_regs; 164 } 165 166 fixed_phy_update(fp); 167 168 list_add_tail(&fp->node, &fmb->phys); 169 170 return 0; 171 172 err_regs: 173 kfree(fp); 174 return ret; 175 } 176 EXPORT_SYMBOL_GPL(fixed_phy_add); 177 178 static void fixed_phy_del(int phy_addr) 179 { 180 struct fixed_mdio_bus *fmb = &platform_fmb; 181 struct fixed_phy *fp, *tmp; 182 183 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 184 if (fp->addr == phy_addr) { 185 list_del(&fp->node); 186 if (gpio_is_valid(fp->link_gpio)) 187 gpio_free(fp->link_gpio); 188 kfree(fp); 189 return; 190 } 191 } 192 } 193 194 static int phy_fixed_addr; 195 static DEFINE_SPINLOCK(phy_fixed_addr_lock); 196 197 struct phy_device *fixed_phy_register(unsigned int irq, 198 struct fixed_phy_status *status, 199 int link_gpio, 200 struct device_node *np) 201 { 202 struct fixed_mdio_bus *fmb = &platform_fmb; 203 struct phy_device *phy; 204 int phy_addr; 205 int ret; 206 207 if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED) 208 return ERR_PTR(-EPROBE_DEFER); 209 210 /* Get the next available PHY address, up to PHY_MAX_ADDR */ 211 spin_lock(&phy_fixed_addr_lock); 212 if (phy_fixed_addr == PHY_MAX_ADDR) { 213 spin_unlock(&phy_fixed_addr_lock); 214 return ERR_PTR(-ENOSPC); 215 } 216 phy_addr = phy_fixed_addr++; 217 spin_unlock(&phy_fixed_addr_lock); 218 219 ret = fixed_phy_add(irq, phy_addr, status, link_gpio); 220 if (ret < 0) 221 return ERR_PTR(ret); 222 223 phy = get_phy_device(fmb->mii_bus, phy_addr, false); 224 if (IS_ERR(phy)) { 225 fixed_phy_del(phy_addr); 226 return ERR_PTR(-EINVAL); 227 } 228 229 /* propagate the fixed link values to struct phy_device */ 230 phy->link = status->link; 231 if (status->link) { 232 phy->speed = status->speed; 233 phy->duplex = status->duplex; 234 phy->pause = status->pause; 235 phy->asym_pause = status->asym_pause; 236 } 237 238 of_node_get(np); 239 phy->mdio.dev.of_node = np; 240 phy->is_pseudo_fixed_link = true; 241 242 switch (status->speed) { 243 case SPEED_1000: 244 phy->supported = PHY_1000BT_FEATURES; 245 break; 246 case SPEED_100: 247 phy->supported = PHY_100BT_FEATURES; 248 break; 249 case SPEED_10: 250 default: 251 phy->supported = PHY_10BT_FEATURES; 252 } 253 254 ret = phy_device_register(phy); 255 if (ret) { 256 phy_device_free(phy); 257 of_node_put(np); 258 fixed_phy_del(phy_addr); 259 return ERR_PTR(ret); 260 } 261 262 return phy; 263 } 264 EXPORT_SYMBOL_GPL(fixed_phy_register); 265 266 void fixed_phy_unregister(struct phy_device *phy) 267 { 268 phy_device_remove(phy); 269 270 fixed_phy_del(phy->mdio.addr); 271 } 272 EXPORT_SYMBOL_GPL(fixed_phy_unregister); 273 274 static int __init fixed_mdio_bus_init(void) 275 { 276 struct fixed_mdio_bus *fmb = &platform_fmb; 277 int ret; 278 279 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); 280 if (IS_ERR(pdev)) { 281 ret = PTR_ERR(pdev); 282 goto err_pdev; 283 } 284 285 fmb->mii_bus = mdiobus_alloc(); 286 if (fmb->mii_bus == NULL) { 287 ret = -ENOMEM; 288 goto err_mdiobus_reg; 289 } 290 291 snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); 292 fmb->mii_bus->name = "Fixed MDIO Bus"; 293 fmb->mii_bus->priv = fmb; 294 fmb->mii_bus->parent = &pdev->dev; 295 fmb->mii_bus->read = &fixed_mdio_read; 296 fmb->mii_bus->write = &fixed_mdio_write; 297 298 ret = mdiobus_register(fmb->mii_bus); 299 if (ret) 300 goto err_mdiobus_alloc; 301 302 return 0; 303 304 err_mdiobus_alloc: 305 mdiobus_free(fmb->mii_bus); 306 err_mdiobus_reg: 307 platform_device_unregister(pdev); 308 err_pdev: 309 return ret; 310 } 311 module_init(fixed_mdio_bus_init); 312 313 static void __exit fixed_mdio_bus_exit(void) 314 { 315 struct fixed_mdio_bus *fmb = &platform_fmb; 316 struct fixed_phy *fp, *tmp; 317 318 mdiobus_unregister(fmb->mii_bus); 319 mdiobus_free(fmb->mii_bus); 320 platform_device_unregister(pdev); 321 322 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 323 list_del(&fp->node); 324 kfree(fp); 325 } 326 } 327 module_exit(fixed_mdio_bus_exit); 328 329 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 330 MODULE_AUTHOR("Vitaly Bordug"); 331 MODULE_LICENSE("GPL"); 332