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