392 lines · c
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/platform_device.h>14#include <linux/list.h>15#include <linux/mii.h>16#include <linux/phy.h>17#include <linux/phy_fixed.h>18#include <linux/err.h>19#include <linux/slab.h>20#include <linux/of.h>21#include <linux/gpio/consumer.h>22#include <linux/idr.h>23#include <linux/netdevice.h>24#include <linux/linkmode.h>25 26#include "swphy.h"27 28struct fixed_mdio_bus {29 struct mii_bus *mii_bus;30 struct list_head phys;31};32 33struct fixed_phy {34 int addr;35 struct phy_device *phydev;36 struct fixed_phy_status status;37 bool no_carrier;38 int (*link_update)(struct net_device *, struct fixed_phy_status *);39 struct list_head node;40 struct gpio_desc *link_gpiod;41};42 43static struct platform_device *pdev;44static struct fixed_mdio_bus platform_fmb = {45 .phys = LIST_HEAD_INIT(platform_fmb.phys),46};47 48int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)49{50 struct fixed_mdio_bus *fmb = &platform_fmb;51 struct phy_device *phydev = dev->phydev;52 struct fixed_phy *fp;53 54 if (!phydev || !phydev->mdio.bus)55 return -EINVAL;56 57 list_for_each_entry(fp, &fmb->phys, node) {58 if (fp->addr == phydev->mdio.addr) {59 fp->no_carrier = !new_carrier;60 return 0;61 }62 }63 return -EINVAL;64}65EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);66 67static void fixed_phy_update(struct fixed_phy *fp)68{69 if (!fp->no_carrier && fp->link_gpiod)70 fp->status.link = !!gpiod_get_value_cansleep(fp->link_gpiod);71}72 73static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)74{75 struct fixed_mdio_bus *fmb = bus->priv;76 struct fixed_phy *fp;77 78 list_for_each_entry(fp, &fmb->phys, node) {79 if (fp->addr == phy_addr) {80 struct fixed_phy_status state;81 82 fp->status.link = !fp->no_carrier;83 84 /* Issue callback if user registered it. */85 if (fp->link_update)86 fp->link_update(fp->phydev->attached_dev,87 &fp->status);88 89 /* Check the GPIO for change in status */90 fixed_phy_update(fp);91 state = fp->status;92 93 return swphy_read_reg(reg_num, &state);94 }95 }96 97 return 0xFFFF;98}99 100static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,101 u16 val)102{103 return 0;104}105 106/*107 * If something weird is required to be done with link/speed,108 * network driver is able to assign a function to implement this.109 * May be useful for PHY's that need to be software-driven.110 */111int fixed_phy_set_link_update(struct phy_device *phydev,112 int (*link_update)(struct net_device *,113 struct fixed_phy_status *))114{115 struct fixed_mdio_bus *fmb = &platform_fmb;116 struct fixed_phy *fp;117 118 if (!phydev || !phydev->mdio.bus)119 return -EINVAL;120 121 list_for_each_entry(fp, &fmb->phys, node) {122 if (fp->addr == phydev->mdio.addr) {123 fp->link_update = link_update;124 fp->phydev = phydev;125 return 0;126 }127 }128 129 return -ENOENT;130}131EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);132 133static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,134 struct fixed_phy_status *status,135 struct gpio_desc *gpiod)136{137 int ret;138 struct fixed_mdio_bus *fmb = &platform_fmb;139 struct fixed_phy *fp;140 141 ret = swphy_validate_state(status);142 if (ret < 0)143 return ret;144 145 fp = kzalloc(sizeof(*fp), GFP_KERNEL);146 if (!fp)147 return -ENOMEM;148 149 if (irq != PHY_POLL)150 fmb->mii_bus->irq[phy_addr] = irq;151 152 fp->addr = phy_addr;153 fp->status = *status;154 fp->link_gpiod = gpiod;155 156 fixed_phy_update(fp);157 158 list_add_tail(&fp->node, &fmb->phys);159 160 return 0;161}162 163int fixed_phy_add(unsigned int irq, int phy_addr,164 struct fixed_phy_status *status)165{166 return fixed_phy_add_gpiod(irq, phy_addr, status, NULL);167}168EXPORT_SYMBOL_GPL(fixed_phy_add);169 170static DEFINE_IDA(phy_fixed_ida);171 172static void fixed_phy_del(int phy_addr)173{174 struct fixed_mdio_bus *fmb = &platform_fmb;175 struct fixed_phy *fp, *tmp;176 177 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {178 if (fp->addr == phy_addr) {179 list_del(&fp->node);180 if (fp->link_gpiod)181 gpiod_put(fp->link_gpiod);182 kfree(fp);183 ida_free(&phy_fixed_ida, phy_addr);184 return;185 }186 }187}188 189#ifdef CONFIG_OF_GPIO190static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)191{192 struct device_node *fixed_link_node;193 struct gpio_desc *gpiod;194 195 if (!np)196 return NULL;197 198 fixed_link_node = of_get_child_by_name(np, "fixed-link");199 if (!fixed_link_node)200 return NULL;201 202 /*203 * As the fixed link is just a device tree node without any204 * Linux device associated with it, we simply have obtain205 * the GPIO descriptor from the device tree like this.206 */207 gpiod = fwnode_gpiod_get_index(of_fwnode_handle(fixed_link_node),208 "link", 0, GPIOD_IN, "mdio");209 if (IS_ERR(gpiod) && PTR_ERR(gpiod) != -EPROBE_DEFER) {210 if (PTR_ERR(gpiod) != -ENOENT)211 pr_err("error getting GPIO for fixed link %pOF, proceed without\n",212 fixed_link_node);213 gpiod = NULL;214 }215 of_node_put(fixed_link_node);216 217 return gpiod;218}219#else220static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)221{222 return NULL;223}224#endif225 226static struct phy_device *__fixed_phy_register(unsigned int irq,227 struct fixed_phy_status *status,228 struct device_node *np,229 struct gpio_desc *gpiod)230{231 struct fixed_mdio_bus *fmb = &platform_fmb;232 struct phy_device *phy;233 int phy_addr;234 int ret;235 236 if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)237 return ERR_PTR(-EPROBE_DEFER);238 239 /* Check if we have a GPIO associated with this fixed phy */240 if (!gpiod) {241 gpiod = fixed_phy_get_gpiod(np);242 if (IS_ERR(gpiod))243 return ERR_CAST(gpiod);244 }245 246 /* Get the next available PHY address, up to PHY_MAX_ADDR */247 phy_addr = ida_alloc_max(&phy_fixed_ida, PHY_MAX_ADDR - 1, GFP_KERNEL);248 if (phy_addr < 0)249 return ERR_PTR(phy_addr);250 251 ret = fixed_phy_add_gpiod(irq, phy_addr, status, gpiod);252 if (ret < 0) {253 ida_free(&phy_fixed_ida, phy_addr);254 return ERR_PTR(ret);255 }256 257 phy = get_phy_device(fmb->mii_bus, phy_addr, false);258 if (IS_ERR(phy)) {259 fixed_phy_del(phy_addr);260 return ERR_PTR(-EINVAL);261 }262 263 /* propagate the fixed link values to struct phy_device */264 phy->link = status->link;265 if (status->link) {266 phy->speed = status->speed;267 phy->duplex = status->duplex;268 phy->pause = status->pause;269 phy->asym_pause = status->asym_pause;270 }271 272 of_node_get(np);273 phy->mdio.dev.of_node = np;274 phy->is_pseudo_fixed_link = true;275 276 switch (status->speed) {277 case SPEED_1000:278 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,279 phy->supported);280 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,281 phy->supported);282 fallthrough;283 case SPEED_100:284 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,285 phy->supported);286 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,287 phy->supported);288 fallthrough;289 case SPEED_10:290 default:291 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,292 phy->supported);293 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,294 phy->supported);295 }296 297 phy_advertise_supported(phy);298 299 ret = phy_device_register(phy);300 if (ret) {301 phy_device_free(phy);302 of_node_put(np);303 fixed_phy_del(phy_addr);304 return ERR_PTR(ret);305 }306 307 return phy;308}309 310struct phy_device *fixed_phy_register(unsigned int irq,311 struct fixed_phy_status *status,312 struct device_node *np)313{314 return __fixed_phy_register(irq, status, np, NULL);315}316EXPORT_SYMBOL_GPL(fixed_phy_register);317 318struct phy_device *319fixed_phy_register_with_gpiod(unsigned int irq,320 struct fixed_phy_status *status,321 struct gpio_desc *gpiod)322{323 return __fixed_phy_register(irq, status, NULL, gpiod);324}325EXPORT_SYMBOL_GPL(fixed_phy_register_with_gpiod);326 327void fixed_phy_unregister(struct phy_device *phy)328{329 phy_device_remove(phy);330 of_node_put(phy->mdio.dev.of_node);331 fixed_phy_del(phy->mdio.addr);332}333EXPORT_SYMBOL_GPL(fixed_phy_unregister);334 335static int __init fixed_mdio_bus_init(void)336{337 struct fixed_mdio_bus *fmb = &platform_fmb;338 int ret;339 340 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);341 if (IS_ERR(pdev))342 return PTR_ERR(pdev);343 344 fmb->mii_bus = mdiobus_alloc();345 if (fmb->mii_bus == NULL) {346 ret = -ENOMEM;347 goto err_mdiobus_reg;348 }349 350 snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");351 fmb->mii_bus->name = "Fixed MDIO Bus";352 fmb->mii_bus->priv = fmb;353 fmb->mii_bus->parent = &pdev->dev;354 fmb->mii_bus->read = &fixed_mdio_read;355 fmb->mii_bus->write = &fixed_mdio_write;356 fmb->mii_bus->phy_mask = ~0;357 358 ret = mdiobus_register(fmb->mii_bus);359 if (ret)360 goto err_mdiobus_alloc;361 362 return 0;363 364err_mdiobus_alloc:365 mdiobus_free(fmb->mii_bus);366err_mdiobus_reg:367 platform_device_unregister(pdev);368 return ret;369}370module_init(fixed_mdio_bus_init);371 372static void __exit fixed_mdio_bus_exit(void)373{374 struct fixed_mdio_bus *fmb = &platform_fmb;375 struct fixed_phy *fp, *tmp;376 377 mdiobus_unregister(fmb->mii_bus);378 mdiobus_free(fmb->mii_bus);379 platform_device_unregister(pdev);380 381 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {382 list_del(&fp->node);383 kfree(fp);384 }385 ida_destroy(&phy_fixed_ida);386}387module_exit(fixed_mdio_bus_exit);388 389MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");390MODULE_AUTHOR("Vitaly Bordug");391MODULE_LICENSE("GPL");392