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 #include <linux/seqlock.h> 27 #include <linux/idr.h> 28 29 #include "swphy.h" 30 31 struct fixed_mdio_bus { 32 struct mii_bus *mii_bus; 33 struct list_head phys; 34 }; 35 36 struct fixed_phy { 37 int addr; 38 struct phy_device *phydev; 39 seqcount_t seqcount; 40 struct fixed_phy_status status; 41 int (*link_update)(struct net_device *, struct fixed_phy_status *); 42 struct list_head node; 43 int link_gpio; 44 }; 45 46 static struct platform_device *pdev; 47 static struct fixed_mdio_bus platform_fmb = { 48 .phys = LIST_HEAD_INIT(platform_fmb.phys), 49 }; 50 51 static void fixed_phy_update(struct fixed_phy *fp) 52 { 53 if (gpio_is_valid(fp->link_gpio)) 54 fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio); 55 } 56 57 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) 58 { 59 struct fixed_mdio_bus *fmb = bus->priv; 60 struct fixed_phy *fp; 61 62 list_for_each_entry(fp, &fmb->phys, node) { 63 if (fp->addr == phy_addr) { 64 struct fixed_phy_status state; 65 int s; 66 67 do { 68 s = read_seqcount_begin(&fp->seqcount); 69 /* Issue callback if user registered it. */ 70 if (fp->link_update) { 71 fp->link_update(fp->phydev->attached_dev, 72 &fp->status); 73 fixed_phy_update(fp); 74 } 75 state = fp->status; 76 } while (read_seqcount_retry(&fp->seqcount, s)); 77 78 return swphy_read_reg(reg_num, &state); 79 } 80 } 81 82 return 0xFFFF; 83 } 84 85 static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, 86 u16 val) 87 { 88 return 0; 89 } 90 91 /* 92 * If something weird is required to be done with link/speed, 93 * network driver is able to assign a function to implement this. 94 * May be useful for PHY's that need to be software-driven. 95 */ 96 int fixed_phy_set_link_update(struct phy_device *phydev, 97 int (*link_update)(struct net_device *, 98 struct fixed_phy_status *)) 99 { 100 struct fixed_mdio_bus *fmb = &platform_fmb; 101 struct fixed_phy *fp; 102 103 if (!phydev || !phydev->mdio.bus) 104 return -EINVAL; 105 106 list_for_each_entry(fp, &fmb->phys, node) { 107 if (fp->addr == phydev->mdio.addr) { 108 fp->link_update = link_update; 109 fp->phydev = phydev; 110 return 0; 111 } 112 } 113 114 return -ENOENT; 115 } 116 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 117 118 int fixed_phy_add(unsigned int irq, int phy_addr, 119 struct fixed_phy_status *status, 120 int link_gpio) 121 { 122 int ret; 123 struct fixed_mdio_bus *fmb = &platform_fmb; 124 struct fixed_phy *fp; 125 126 ret = swphy_validate_state(status); 127 if (ret < 0) 128 return ret; 129 130 fp = kzalloc(sizeof(*fp), GFP_KERNEL); 131 if (!fp) 132 return -ENOMEM; 133 134 seqcount_init(&fp->seqcount); 135 136 if (irq != PHY_POLL) 137 fmb->mii_bus->irq[phy_addr] = irq; 138 139 fp->addr = phy_addr; 140 fp->status = *status; 141 fp->link_gpio = link_gpio; 142 143 if (gpio_is_valid(fp->link_gpio)) { 144 ret = gpio_request_one(fp->link_gpio, GPIOF_DIR_IN, 145 "fixed-link-gpio-link"); 146 if (ret) 147 goto err_regs; 148 } 149 150 fixed_phy_update(fp); 151 152 list_add_tail(&fp->node, &fmb->phys); 153 154 return 0; 155 156 err_regs: 157 kfree(fp); 158 return ret; 159 } 160 EXPORT_SYMBOL_GPL(fixed_phy_add); 161 162 static DEFINE_IDA(phy_fixed_ida); 163 164 static void fixed_phy_del(int phy_addr) 165 { 166 struct fixed_mdio_bus *fmb = &platform_fmb; 167 struct fixed_phy *fp, *tmp; 168 169 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 170 if (fp->addr == phy_addr) { 171 list_del(&fp->node); 172 if (gpio_is_valid(fp->link_gpio)) 173 gpio_free(fp->link_gpio); 174 kfree(fp); 175 ida_simple_remove(&phy_fixed_ida, phy_addr); 176 return; 177 } 178 } 179 } 180 181 struct phy_device *fixed_phy_register(unsigned int irq, 182 struct fixed_phy_status *status, 183 int link_gpio, 184 struct device_node *np) 185 { 186 struct fixed_mdio_bus *fmb = &platform_fmb; 187 struct phy_device *phy; 188 int phy_addr; 189 int ret; 190 191 if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED) 192 return ERR_PTR(-EPROBE_DEFER); 193 194 /* Get the next available PHY address, up to PHY_MAX_ADDR */ 195 phy_addr = ida_simple_get(&phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL); 196 if (phy_addr < 0) 197 return ERR_PTR(phy_addr); 198 199 ret = fixed_phy_add(irq, phy_addr, status, link_gpio); 200 if (ret < 0) { 201 ida_simple_remove(&phy_fixed_ida, phy_addr); 202 return ERR_PTR(ret); 203 } 204 205 phy = get_phy_device(fmb->mii_bus, phy_addr, false); 206 if (IS_ERR(phy)) { 207 fixed_phy_del(phy_addr); 208 return ERR_PTR(-EINVAL); 209 } 210 211 /* propagate the fixed link values to struct phy_device */ 212 phy->link = status->link; 213 if (status->link) { 214 phy->speed = status->speed; 215 phy->duplex = status->duplex; 216 phy->pause = status->pause; 217 phy->asym_pause = status->asym_pause; 218 } 219 220 of_node_get(np); 221 phy->mdio.dev.of_node = np; 222 phy->is_pseudo_fixed_link = true; 223 224 switch (status->speed) { 225 case SPEED_1000: 226 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 227 phy->supported); 228 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 229 phy->supported); 230 /* fall through */ 231 case SPEED_100: 232 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, 233 phy->supported); 234 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 235 phy->supported); 236 /* fall through */ 237 case SPEED_10: 238 default: 239 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, 240 phy->supported); 241 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, 242 phy->supported); 243 } 244 245 ret = phy_device_register(phy); 246 if (ret) { 247 phy_device_free(phy); 248 of_node_put(np); 249 fixed_phy_del(phy_addr); 250 return ERR_PTR(ret); 251 } 252 253 return phy; 254 } 255 EXPORT_SYMBOL_GPL(fixed_phy_register); 256 257 void fixed_phy_unregister(struct phy_device *phy) 258 { 259 phy_device_remove(phy); 260 of_node_put(phy->mdio.dev.of_node); 261 fixed_phy_del(phy->mdio.addr); 262 } 263 EXPORT_SYMBOL_GPL(fixed_phy_unregister); 264 265 static int __init fixed_mdio_bus_init(void) 266 { 267 struct fixed_mdio_bus *fmb = &platform_fmb; 268 int ret; 269 270 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); 271 if (IS_ERR(pdev)) 272 return PTR_ERR(pdev); 273 274 fmb->mii_bus = mdiobus_alloc(); 275 if (fmb->mii_bus == NULL) { 276 ret = -ENOMEM; 277 goto err_mdiobus_reg; 278 } 279 280 snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); 281 fmb->mii_bus->name = "Fixed MDIO Bus"; 282 fmb->mii_bus->priv = fmb; 283 fmb->mii_bus->parent = &pdev->dev; 284 fmb->mii_bus->read = &fixed_mdio_read; 285 fmb->mii_bus->write = &fixed_mdio_write; 286 287 ret = mdiobus_register(fmb->mii_bus); 288 if (ret) 289 goto err_mdiobus_alloc; 290 291 return 0; 292 293 err_mdiobus_alloc: 294 mdiobus_free(fmb->mii_bus); 295 err_mdiobus_reg: 296 platform_device_unregister(pdev); 297 return ret; 298 } 299 module_init(fixed_mdio_bus_init); 300 301 static void __exit fixed_mdio_bus_exit(void) 302 { 303 struct fixed_mdio_bus *fmb = &platform_fmb; 304 struct fixed_phy *fp, *tmp; 305 306 mdiobus_unregister(fmb->mii_bus); 307 mdiobus_free(fmb->mii_bus); 308 platform_device_unregister(pdev); 309 310 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 311 list_del(&fp->node); 312 kfree(fp); 313 } 314 ida_destroy(&phy_fixed_ida); 315 } 316 module_exit(fixed_mdio_bus_exit); 317 318 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 319 MODULE_AUTHOR("Vitaly Bordug"); 320 MODULE_LICENSE("GPL"); 321