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/mii.h> 14 #include <linux/phy.h> 15 #include <linux/phy_fixed.h> 16 #include <linux/err.h> 17 #include <linux/slab.h> 18 #include <linux/of.h> 19 #include <linux/netdevice.h> 20 21 #include "swphy.h" 22 23 /* The DSA loop driver may allocate 4 fixed PHY's, and 4 additional 24 * fixed PHY's for a system should be sufficient. 25 */ 26 #define NUM_FP 8 27 28 struct fixed_phy { 29 struct phy_device *phydev; 30 struct fixed_phy_status status; 31 int (*link_update)(struct net_device *, struct fixed_phy_status *); 32 }; 33 34 static DECLARE_BITMAP(fixed_phy_ids, NUM_FP); 35 static struct fixed_phy fmb_fixed_phys[NUM_FP]; 36 static struct mii_bus *fmb_mii_bus; 37 38 static struct fixed_phy *fixed_phy_find(int addr) 39 { 40 return test_bit(addr, fixed_phy_ids) ? fmb_fixed_phys + addr : NULL; 41 } 42 43 int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier) 44 { 45 struct phy_device *phydev = dev->phydev; 46 struct fixed_phy *fp; 47 48 if (!phydev || !phydev->mdio.bus) 49 return -EINVAL; 50 51 fp = fixed_phy_find(phydev->mdio.addr); 52 if (!fp) 53 return -EINVAL; 54 55 fp->status.link = new_carrier; 56 57 return 0; 58 } 59 EXPORT_SYMBOL_GPL(fixed_phy_change_carrier); 60 61 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) 62 { 63 struct fixed_phy *fp; 64 65 fp = fixed_phy_find(phy_addr); 66 if (!fp) 67 return 0xffff; 68 69 if (fp->link_update) 70 fp->link_update(fp->phydev->attached_dev, &fp->status); 71 72 return swphy_read_reg(reg_num, &fp->status); 73 } 74 75 static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, 76 u16 val) 77 { 78 return 0; 79 } 80 81 /* 82 * If something weird is required to be done with link/speed, 83 * network driver is able to assign a function to implement this. 84 * May be useful for PHY's that need to be software-driven. 85 */ 86 int fixed_phy_set_link_update(struct phy_device *phydev, 87 int (*link_update)(struct net_device *, 88 struct fixed_phy_status *)) 89 { 90 struct fixed_phy *fp; 91 92 if (!phydev || !phydev->mdio.bus) 93 return -EINVAL; 94 95 fp = fixed_phy_find(phydev->mdio.addr); 96 if (!fp) 97 return -ENOENT; 98 99 fp->link_update = link_update; 100 fp->phydev = phydev; 101 102 return 0; 103 } 104 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 105 106 static void fixed_phy_del(int phy_addr) 107 { 108 struct fixed_phy *fp; 109 110 fp = fixed_phy_find(phy_addr); 111 if (!fp) 112 return; 113 114 memset(fp, 0, sizeof(*fp)); 115 clear_bit(phy_addr, fixed_phy_ids); 116 } 117 118 static int fixed_phy_get_free_addr(void) 119 { 120 int addr; 121 122 do { 123 addr = find_first_zero_bit(fixed_phy_ids, NUM_FP); 124 if (addr == NUM_FP) 125 return -ENOSPC; 126 } while (test_and_set_bit(addr, fixed_phy_ids)); 127 128 return addr; 129 } 130 131 struct phy_device *fixed_phy_register(const struct fixed_phy_status *status, 132 struct device_node *np) 133 { 134 struct phy_device *phy; 135 int phy_addr; 136 int ret; 137 138 ret = swphy_validate_state(status); 139 if (ret < 0) 140 return ERR_PTR(ret); 141 142 if (!fmb_mii_bus || fmb_mii_bus->state != MDIOBUS_REGISTERED) 143 return ERR_PTR(-EPROBE_DEFER); 144 145 /* Get the next available PHY address, up to NUM_FP */ 146 phy_addr = fixed_phy_get_free_addr(); 147 if (phy_addr < 0) 148 return ERR_PTR(phy_addr); 149 150 fmb_fixed_phys[phy_addr].status = *status; 151 fmb_fixed_phys[phy_addr].status.link = true; 152 153 phy = get_phy_device(fmb_mii_bus, phy_addr, false); 154 if (IS_ERR(phy)) { 155 fixed_phy_del(phy_addr); 156 return ERR_PTR(-EINVAL); 157 } 158 159 of_node_get(np); 160 phy->mdio.dev.of_node = np; 161 phy->is_pseudo_fixed_link = true; 162 163 ret = phy_device_register(phy); 164 if (ret) { 165 phy_device_free(phy); 166 of_node_put(np); 167 fixed_phy_del(phy_addr); 168 return ERR_PTR(ret); 169 } 170 171 return phy; 172 } 173 EXPORT_SYMBOL_GPL(fixed_phy_register); 174 175 struct phy_device *fixed_phy_register_100fd(void) 176 { 177 static const struct fixed_phy_status status = { 178 .speed = SPEED_100, 179 .duplex = DUPLEX_FULL, 180 }; 181 182 return fixed_phy_register(&status, NULL); 183 } 184 EXPORT_SYMBOL_GPL(fixed_phy_register_100fd); 185 186 void fixed_phy_unregister(struct phy_device *phy) 187 { 188 phy_device_remove(phy); 189 of_node_put(phy->mdio.dev.of_node); 190 fixed_phy_del(phy->mdio.addr); 191 phy_device_free(phy); 192 } 193 EXPORT_SYMBOL_GPL(fixed_phy_unregister); 194 195 static int __init fixed_mdio_bus_init(void) 196 { 197 int ret; 198 199 fmb_mii_bus = mdiobus_alloc(); 200 if (!fmb_mii_bus) 201 return -ENOMEM; 202 203 snprintf(fmb_mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); 204 fmb_mii_bus->name = "Fixed MDIO Bus"; 205 fmb_mii_bus->read = &fixed_mdio_read; 206 fmb_mii_bus->write = &fixed_mdio_write; 207 fmb_mii_bus->phy_mask = ~0; 208 209 ret = mdiobus_register(fmb_mii_bus); 210 if (ret) 211 goto err_mdiobus_alloc; 212 213 return 0; 214 215 err_mdiobus_alloc: 216 mdiobus_free(fmb_mii_bus); 217 return ret; 218 } 219 module_init(fixed_mdio_bus_init); 220 221 static void __exit fixed_mdio_bus_exit(void) 222 { 223 mdiobus_unregister(fmb_mii_bus); 224 mdiobus_free(fmb_mii_bus); 225 } 226 module_exit(fixed_mdio_bus_exit); 227 228 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 229 MODULE_AUTHOR("Vitaly Bordug"); 230 MODULE_LICENSE("GPL"); 231