1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
26539c44dSDavid S. Miller /*
36539c44dSDavid S. Miller * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
46539c44dSDavid S. Miller *
56539c44dSDavid S. Miller * Author: Vitaly Bordug <vbordug@ru.mvista.com>
66539c44dSDavid S. Miller * Anton Vorontsov <avorontsov@ru.mvista.com>
76539c44dSDavid S. Miller *
86539c44dSDavid S. Miller * Copyright (c) 2006-2007 MontaVista Software, Inc.
96539c44dSDavid S. Miller */
106539c44dSDavid S. Miller
116539c44dSDavid S. Miller #include <linux/kernel.h>
126539c44dSDavid S. Miller #include <linux/module.h>
136539c44dSDavid S. Miller #include <linux/platform_device.h>
146539c44dSDavid S. Miller #include <linux/list.h>
156539c44dSDavid S. Miller #include <linux/mii.h>
166539c44dSDavid S. Miller #include <linux/phy.h>
176539c44dSDavid S. Miller #include <linux/phy_fixed.h>
186539c44dSDavid S. Miller #include <linux/err.h>
196539c44dSDavid S. Miller #include <linux/slab.h>
206539c44dSDavid S. Miller #include <linux/of.h>
215468e82fSLinus Walleij #include <linux/gpio/consumer.h>
2269fc58a5SFlorian Fainelli #include <linux/idr.h>
23b3e5464eSJoakim Tjernlund #include <linux/netdevice.h>
240f3b1cf2SHeiner Kallweit #include <linux/linkmode.h>
256539c44dSDavid S. Miller
265ae68b0cSRussell King #include "swphy.h"
275ae68b0cSRussell King
286539c44dSDavid S. Miller struct fixed_mdio_bus {
296539c44dSDavid S. Miller struct mii_bus *mii_bus;
306539c44dSDavid S. Miller struct list_head phys;
316539c44dSDavid S. Miller };
326539c44dSDavid S. Miller
336539c44dSDavid S. Miller struct fixed_phy {
346539c44dSDavid S. Miller int addr;
356539c44dSDavid S. Miller struct phy_device *phydev;
366539c44dSDavid S. Miller struct fixed_phy_status status;
37b3e5464eSJoakim Tjernlund bool no_carrier;
386539c44dSDavid S. Miller int (*link_update)(struct net_device *, struct fixed_phy_status *);
396539c44dSDavid S. Miller struct list_head node;
405468e82fSLinus Walleij struct gpio_desc *link_gpiod;
416539c44dSDavid S. Miller };
426539c44dSDavid S. Miller
436539c44dSDavid S. Miller static struct platform_device *pdev;
446539c44dSDavid S. Miller static struct fixed_mdio_bus platform_fmb = {
456539c44dSDavid S. Miller .phys = LIST_HEAD_INIT(platform_fmb.phys),
466539c44dSDavid S. Miller };
476539c44dSDavid S. Miller
fixed_phy_change_carrier(struct net_device * dev,bool new_carrier)48b3e5464eSJoakim Tjernlund int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
49b3e5464eSJoakim Tjernlund {
50b3e5464eSJoakim Tjernlund struct fixed_mdio_bus *fmb = &platform_fmb;
51b3e5464eSJoakim Tjernlund struct phy_device *phydev = dev->phydev;
52b3e5464eSJoakim Tjernlund struct fixed_phy *fp;
53b3e5464eSJoakim Tjernlund
54b3e5464eSJoakim Tjernlund if (!phydev || !phydev->mdio.bus)
55b3e5464eSJoakim Tjernlund return -EINVAL;
56b3e5464eSJoakim Tjernlund
57b3e5464eSJoakim Tjernlund list_for_each_entry(fp, &fmb->phys, node) {
58b3e5464eSJoakim Tjernlund if (fp->addr == phydev->mdio.addr) {
59b3e5464eSJoakim Tjernlund fp->no_carrier = !new_carrier;
60b3e5464eSJoakim Tjernlund return 0;
61b3e5464eSJoakim Tjernlund }
62b3e5464eSJoakim Tjernlund }
63b3e5464eSJoakim Tjernlund return -EINVAL;
64b3e5464eSJoakim Tjernlund }
65b3e5464eSJoakim Tjernlund EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
66b3e5464eSJoakim Tjernlund
fixed_phy_update(struct fixed_phy * fp)6737688e3fSRussell King static void fixed_phy_update(struct fixed_phy *fp)
686539c44dSDavid S. Miller {
695468e82fSLinus Walleij if (!fp->no_carrier && fp->link_gpiod)
705468e82fSLinus Walleij fp->status.link = !!gpiod_get_value_cansleep(fp->link_gpiod);
716539c44dSDavid S. Miller }
726539c44dSDavid S. Miller
fixed_mdio_read(struct mii_bus * bus,int phy_addr,int reg_num)736539c44dSDavid S. Miller static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
746539c44dSDavid S. Miller {
756539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = bus->priv;
766539c44dSDavid S. Miller struct fixed_phy *fp;
776539c44dSDavid S. Miller
786539c44dSDavid S. Miller list_for_each_entry(fp, &fmb->phys, node) {
796539c44dSDavid S. Miller if (fp->addr == phy_addr) {
80bf7afb29SRussell King struct fixed_phy_status state;
81bf7afb29SRussell King
82b3e5464eSJoakim Tjernlund fp->status.link = !fp->no_carrier;
8379cbb6bcSAhmed S. Darwish
846539c44dSDavid S. Miller /* Issue callback if user registered it. */
858f289805SMoritz Fischer if (fp->link_update)
866539c44dSDavid S. Miller fp->link_update(fp->phydev->attached_dev,
876539c44dSDavid S. Miller &fp->status);
8879cbb6bcSAhmed S. Darwish
898f289805SMoritz Fischer /* Check the GPIO for change in status */
9037688e3fSRussell King fixed_phy_update(fp);
91bf7afb29SRussell King state = fp->status;
92bf7afb29SRussell King
93bf7afb29SRussell King return swphy_read_reg(reg_num, &state);
946539c44dSDavid S. Miller }
956539c44dSDavid S. Miller }
966539c44dSDavid S. Miller
976539c44dSDavid S. Miller return 0xFFFF;
986539c44dSDavid S. Miller }
996539c44dSDavid S. Miller
fixed_mdio_write(struct mii_bus * bus,int phy_addr,int reg_num,u16 val)1006539c44dSDavid S. Miller static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
1016539c44dSDavid S. Miller u16 val)
1026539c44dSDavid S. Miller {
1036539c44dSDavid S. Miller return 0;
1046539c44dSDavid S. Miller }
1056539c44dSDavid S. Miller
1066539c44dSDavid S. Miller /*
1076539c44dSDavid S. Miller * If something weird is required to be done with link/speed,
1086539c44dSDavid S. Miller * network driver is able to assign a function to implement this.
1096539c44dSDavid S. Miller * May be useful for PHY's that need to be software-driven.
1106539c44dSDavid S. Miller */
fixed_phy_set_link_update(struct phy_device * phydev,int (* link_update)(struct net_device *,struct fixed_phy_status *))1116539c44dSDavid S. Miller int fixed_phy_set_link_update(struct phy_device *phydev,
1126539c44dSDavid S. Miller int (*link_update)(struct net_device *,
1136539c44dSDavid S. Miller struct fixed_phy_status *))
1146539c44dSDavid S. Miller {
1156539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
1166539c44dSDavid S. Miller struct fixed_phy *fp;
1176539c44dSDavid S. Miller
118e5a03bfdSAndrew Lunn if (!phydev || !phydev->mdio.bus)
1196539c44dSDavid S. Miller return -EINVAL;
1206539c44dSDavid S. Miller
1216539c44dSDavid S. Miller list_for_each_entry(fp, &fmb->phys, node) {
122e5a03bfdSAndrew Lunn if (fp->addr == phydev->mdio.addr) {
1236539c44dSDavid S. Miller fp->link_update = link_update;
1246539c44dSDavid S. Miller fp->phydev = phydev;
1256539c44dSDavid S. Miller return 0;
1266539c44dSDavid S. Miller }
1276539c44dSDavid S. Miller }
1286539c44dSDavid S. Miller
1296539c44dSDavid S. Miller return -ENOENT;
1306539c44dSDavid S. Miller }
1316539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
1326539c44dSDavid S. Miller
fixed_phy_add_gpiod(unsigned int irq,int phy_addr,struct fixed_phy_status * status,struct gpio_desc * gpiod)1335468e82fSLinus Walleij static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,
134a5597008SAndrew Lunn struct fixed_phy_status *status,
1355468e82fSLinus Walleij struct gpio_desc *gpiod)
1366539c44dSDavid S. Miller {
1376539c44dSDavid S. Miller int ret;
1386539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
1396539c44dSDavid S. Miller struct fixed_phy *fp;
1406539c44dSDavid S. Miller
14168888ce0SRussell King ret = swphy_validate_state(status);
14268888ce0SRussell King if (ret < 0)
14368888ce0SRussell King return ret;
14468888ce0SRussell King
1456539c44dSDavid S. Miller fp = kzalloc(sizeof(*fp), GFP_KERNEL);
1466539c44dSDavid S. Miller if (!fp)
1476539c44dSDavid S. Miller return -ENOMEM;
1486539c44dSDavid S. Miller
149185be5aeSRabin Vincent if (irq != PHY_POLL)
150e7f4dc35SAndrew Lunn fmb->mii_bus->irq[phy_addr] = irq;
1516539c44dSDavid S. Miller
1526539c44dSDavid S. Miller fp->addr = phy_addr;
1536539c44dSDavid S. Miller fp->status = *status;
1545468e82fSLinus Walleij fp->link_gpiod = gpiod;
1556539c44dSDavid S. Miller
15637688e3fSRussell King fixed_phy_update(fp);
1576539c44dSDavid S. Miller
1586539c44dSDavid S. Miller list_add_tail(&fp->node, &fmb->phys);
1596539c44dSDavid S. Miller
1606539c44dSDavid S. Miller return 0;
1615468e82fSLinus Walleij }
1626539c44dSDavid S. Miller
fixed_phy_add(unsigned int irq,int phy_addr,struct fixed_phy_status * status)1635468e82fSLinus Walleij int fixed_phy_add(unsigned int irq, int phy_addr,
164169d7a40SWenpeng Liang struct fixed_phy_status *status)
165169d7a40SWenpeng Liang {
1665468e82fSLinus Walleij return fixed_phy_add_gpiod(irq, phy_addr, status, NULL);
1676539c44dSDavid S. Miller }
1686539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_add);
1696539c44dSDavid S. Miller
17069fc58a5SFlorian Fainelli static DEFINE_IDA(phy_fixed_ida);
17169fc58a5SFlorian Fainelli
fixed_phy_del(int phy_addr)1725bcbe0f3SAndrew Lunn static void fixed_phy_del(int phy_addr)
1736539c44dSDavid S. Miller {
1746539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
1756539c44dSDavid S. Miller struct fixed_phy *fp, *tmp;
1766539c44dSDavid S. Miller
1776539c44dSDavid S. Miller list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
1786539c44dSDavid S. Miller if (fp->addr == phy_addr) {
1796539c44dSDavid S. Miller list_del(&fp->node);
1805468e82fSLinus Walleij if (fp->link_gpiod)
1815468e82fSLinus Walleij gpiod_put(fp->link_gpiod);
1826539c44dSDavid S. Miller kfree(fp);
1832f1de254SKe Liu ida_free(&phy_fixed_ida, phy_addr);
1846539c44dSDavid S. Miller return;
1856539c44dSDavid S. Miller }
1866539c44dSDavid S. Miller }
1876539c44dSDavid S. Miller }
1886539c44dSDavid S. Miller
1895468e82fSLinus Walleij #ifdef CONFIG_OF_GPIO
fixed_phy_get_gpiod(struct device_node * np)1905468e82fSLinus Walleij static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
1915468e82fSLinus Walleij {
1925468e82fSLinus Walleij struct device_node *fixed_link_node;
1935468e82fSLinus Walleij struct gpio_desc *gpiod;
1945468e82fSLinus Walleij
1955468e82fSLinus Walleij if (!np)
1965468e82fSLinus Walleij return NULL;
1975468e82fSLinus Walleij
1985468e82fSLinus Walleij fixed_link_node = of_get_child_by_name(np, "fixed-link");
1995468e82fSLinus Walleij if (!fixed_link_node)
2005468e82fSLinus Walleij return NULL;
2015468e82fSLinus Walleij
2025468e82fSLinus Walleij /*
2035468e82fSLinus Walleij * As the fixed link is just a device tree node without any
2045468e82fSLinus Walleij * Linux device associated with it, we simply have obtain
2055468e82fSLinus Walleij * the GPIO descriptor from the device tree like this.
2065468e82fSLinus Walleij */
2075ffcc858SDmitry Torokhov gpiod = fwnode_gpiod_get_index(of_fwnode_handle(fixed_link_node),
2085ffcc858SDmitry Torokhov "link", 0, GPIOD_IN, "mdio");
209d266f19fSDmitry Torokhov if (IS_ERR(gpiod) && PTR_ERR(gpiod) != -EPROBE_DEFER) {
210ab98c008SHubert Feurstein if (PTR_ERR(gpiod) != -ENOENT)
2115468e82fSLinus Walleij pr_err("error getting GPIO for fixed link %pOF, proceed without\n",
2125468e82fSLinus Walleij fixed_link_node);
2135468e82fSLinus Walleij gpiod = NULL;
2145468e82fSLinus Walleij }
215d266f19fSDmitry Torokhov of_node_put(fixed_link_node);
2165468e82fSLinus Walleij
2175468e82fSLinus Walleij return gpiod;
2185468e82fSLinus Walleij }
2195468e82fSLinus Walleij #else
fixed_phy_get_gpiod(struct device_node * np)2205468e82fSLinus Walleij static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
2215468e82fSLinus Walleij {
2225468e82fSLinus Walleij return NULL;
2235468e82fSLinus Walleij }
2245468e82fSLinus Walleij #endif
2255468e82fSLinus Walleij
__fixed_phy_register(unsigned int irq,struct fixed_phy_status * status,struct device_node * np,struct gpio_desc * gpiod)22671bd106dSMoritz Fischer static struct phy_device *__fixed_phy_register(unsigned int irq,
2276539c44dSDavid S. Miller struct fixed_phy_status *status,
22871bd106dSMoritz Fischer struct device_node *np,
22971bd106dSMoritz Fischer struct gpio_desc *gpiod)
2306539c44dSDavid S. Miller {
2316539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
2326539c44dSDavid S. Miller struct phy_device *phy;
2336539c44dSDavid S. Miller int phy_addr;
2346539c44dSDavid S. Miller int ret;
2356539c44dSDavid S. Miller
236185be5aeSRabin Vincent if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)
237185be5aeSRabin Vincent return ERR_PTR(-EPROBE_DEFER);
238185be5aeSRabin Vincent
2395468e82fSLinus Walleij /* Check if we have a GPIO associated with this fixed phy */
24071bd106dSMoritz Fischer if (!gpiod) {
2415468e82fSLinus Walleij gpiod = fixed_phy_get_gpiod(np);
242065e1ae0SFlorian Fainelli if (IS_ERR(gpiod))
243065e1ae0SFlorian Fainelli return ERR_CAST(gpiod);
24471bd106dSMoritz Fischer }
2455468e82fSLinus Walleij
2466539c44dSDavid S. Miller /* Get the next available PHY address, up to PHY_MAX_ADDR */
2472f1de254SKe Liu phy_addr = ida_alloc_max(&phy_fixed_ida, PHY_MAX_ADDR - 1, GFP_KERNEL);
24869fc58a5SFlorian Fainelli if (phy_addr < 0)
24969fc58a5SFlorian Fainelli return ERR_PTR(phy_addr);
2506539c44dSDavid S. Miller
2515468e82fSLinus Walleij ret = fixed_phy_add_gpiod(irq, phy_addr, status, gpiod);
25269fc58a5SFlorian Fainelli if (ret < 0) {
2532f1de254SKe Liu ida_free(&phy_fixed_ida, phy_addr);
2546539c44dSDavid S. Miller return ERR_PTR(ret);
25569fc58a5SFlorian Fainelli }
2566539c44dSDavid S. Miller
2576539c44dSDavid S. Miller phy = get_phy_device(fmb->mii_bus, phy_addr, false);
2584914a584SSergei Shtylyov if (IS_ERR(phy)) {
2596539c44dSDavid S. Miller fixed_phy_del(phy_addr);
2606539c44dSDavid S. Miller return ERR_PTR(-EINVAL);
2616539c44dSDavid S. Miller }
2626539c44dSDavid S. Miller
2634b195360SMadalin Bucur /* propagate the fixed link values to struct phy_device */
2644b195360SMadalin Bucur phy->link = status->link;
2654b195360SMadalin Bucur if (status->link) {
2664b195360SMadalin Bucur phy->speed = status->speed;
2674b195360SMadalin Bucur phy->duplex = status->duplex;
2684b195360SMadalin Bucur phy->pause = status->pause;
2694b195360SMadalin Bucur phy->asym_pause = status->asym_pause;
2704b195360SMadalin Bucur }
2714b195360SMadalin Bucur
2726539c44dSDavid S. Miller of_node_get(np);
273e5a03bfdSAndrew Lunn phy->mdio.dev.of_node = np;
2745a11dd7dSFlorian Fainelli phy->is_pseudo_fixed_link = true;
2756539c44dSDavid S. Miller
27634b31da4SAndrew Lunn switch (status->speed) {
27734b31da4SAndrew Lunn case SPEED_1000:
2783c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
2793c1bcc86SAndrew Lunn phy->supported);
2803c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
2813c1bcc86SAndrew Lunn phy->supported);
282df561f66SGustavo A. R. Silva fallthrough;
28334b31da4SAndrew Lunn case SPEED_100:
2843c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
2853c1bcc86SAndrew Lunn phy->supported);
2863c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
2873c1bcc86SAndrew Lunn phy->supported);
288df561f66SGustavo A. R. Silva fallthrough;
28934b31da4SAndrew Lunn case SPEED_10:
29034b31da4SAndrew Lunn default:
2913c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
2923c1bcc86SAndrew Lunn phy->supported);
2933c1bcc86SAndrew Lunn linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
2943c1bcc86SAndrew Lunn phy->supported);
29534b31da4SAndrew Lunn }
29634b31da4SAndrew Lunn
29722c0ef6bSHeiner Kallweit phy_advertise_supported(phy);
2980f3b1cf2SHeiner Kallweit
2996539c44dSDavid S. Miller ret = phy_device_register(phy);
3006539c44dSDavid S. Miller if (ret) {
3016539c44dSDavid S. Miller phy_device_free(phy);
3026539c44dSDavid S. Miller of_node_put(np);
3036539c44dSDavid S. Miller fixed_phy_del(phy_addr);
3046539c44dSDavid S. Miller return ERR_PTR(ret);
3056539c44dSDavid S. Miller }
3066539c44dSDavid S. Miller
3076539c44dSDavid S. Miller return phy;
3086539c44dSDavid S. Miller }
30971bd106dSMoritz Fischer
fixed_phy_register(unsigned int irq,struct fixed_phy_status * status,struct device_node * np)31071bd106dSMoritz Fischer struct phy_device *fixed_phy_register(unsigned int irq,
31171bd106dSMoritz Fischer struct fixed_phy_status *status,
31271bd106dSMoritz Fischer struct device_node *np)
31371bd106dSMoritz Fischer {
31471bd106dSMoritz Fischer return __fixed_phy_register(irq, status, np, NULL);
31571bd106dSMoritz Fischer }
3166539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_register);
3176539c44dSDavid S. Miller
31871bd106dSMoritz Fischer struct phy_device *
fixed_phy_register_with_gpiod(unsigned int irq,struct fixed_phy_status * status,struct gpio_desc * gpiod)31971bd106dSMoritz Fischer fixed_phy_register_with_gpiod(unsigned int irq,
32071bd106dSMoritz Fischer struct fixed_phy_status *status,
32171bd106dSMoritz Fischer struct gpio_desc *gpiod)
32271bd106dSMoritz Fischer {
32371bd106dSMoritz Fischer return __fixed_phy_register(irq, status, NULL, gpiod);
32471bd106dSMoritz Fischer }
32571bd106dSMoritz Fischer EXPORT_SYMBOL_GPL(fixed_phy_register_with_gpiod);
32671bd106dSMoritz Fischer
fixed_phy_unregister(struct phy_device * phy)3275bcbe0f3SAndrew Lunn void fixed_phy_unregister(struct phy_device *phy)
3285bcbe0f3SAndrew Lunn {
3295bcbe0f3SAndrew Lunn phy_device_remove(phy);
33013c9d934SJohan Hovold of_node_put(phy->mdio.dev.of_node);
3315bcbe0f3SAndrew Lunn fixed_phy_del(phy->mdio.addr);
3325bcbe0f3SAndrew Lunn }
3335bcbe0f3SAndrew Lunn EXPORT_SYMBOL_GPL(fixed_phy_unregister);
3345bcbe0f3SAndrew Lunn
fixed_mdio_bus_init(void)3356539c44dSDavid S. Miller static int __init fixed_mdio_bus_init(void)
3366539c44dSDavid S. Miller {
3376539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
3386539c44dSDavid S. Miller int ret;
3396539c44dSDavid S. Miller
3406539c44dSDavid S. Miller pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
341b0c1638fSFabio Estevam if (IS_ERR(pdev))
342b0c1638fSFabio Estevam return PTR_ERR(pdev);
3436539c44dSDavid S. Miller
3446539c44dSDavid S. Miller fmb->mii_bus = mdiobus_alloc();
3456539c44dSDavid S. Miller if (fmb->mii_bus == NULL) {
3466539c44dSDavid S. Miller ret = -ENOMEM;
3476539c44dSDavid S. Miller goto err_mdiobus_reg;
3486539c44dSDavid S. Miller }
3496539c44dSDavid S. Miller
3506539c44dSDavid S. Miller snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
3516539c44dSDavid S. Miller fmb->mii_bus->name = "Fixed MDIO Bus";
3526539c44dSDavid S. Miller fmb->mii_bus->priv = fmb;
3536539c44dSDavid S. Miller fmb->mii_bus->parent = &pdev->dev;
3546539c44dSDavid S. Miller fmb->mii_bus->read = &fixed_mdio_read;
3556539c44dSDavid S. Miller fmb->mii_bus->write = &fixed_mdio_write;
356*bfa54812SRasmus Villemoes fmb->mii_bus->phy_mask = ~0;
3576539c44dSDavid S. Miller
3586539c44dSDavid S. Miller ret = mdiobus_register(fmb->mii_bus);
3596539c44dSDavid S. Miller if (ret)
3606539c44dSDavid S. Miller goto err_mdiobus_alloc;
3616539c44dSDavid S. Miller
3626539c44dSDavid S. Miller return 0;
3636539c44dSDavid S. Miller
3646539c44dSDavid S. Miller err_mdiobus_alloc:
3656539c44dSDavid S. Miller mdiobus_free(fmb->mii_bus);
3666539c44dSDavid S. Miller err_mdiobus_reg:
3676539c44dSDavid S. Miller platform_device_unregister(pdev);
3686539c44dSDavid S. Miller return ret;
3696539c44dSDavid S. Miller }
3706539c44dSDavid S. Miller module_init(fixed_mdio_bus_init);
3716539c44dSDavid S. Miller
fixed_mdio_bus_exit(void)3726539c44dSDavid S. Miller static void __exit fixed_mdio_bus_exit(void)
3736539c44dSDavid S. Miller {
3746539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb;
3756539c44dSDavid S. Miller struct fixed_phy *fp, *tmp;
3766539c44dSDavid S. Miller
3776539c44dSDavid S. Miller mdiobus_unregister(fmb->mii_bus);
3786539c44dSDavid S. Miller mdiobus_free(fmb->mii_bus);
3796539c44dSDavid S. Miller platform_device_unregister(pdev);
3806539c44dSDavid S. Miller
3816539c44dSDavid S. Miller list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
3826539c44dSDavid S. Miller list_del(&fp->node);
3836539c44dSDavid S. Miller kfree(fp);
3846539c44dSDavid S. Miller }
38569fc58a5SFlorian Fainelli ida_destroy(&phy_fixed_ida);
3866539c44dSDavid S. Miller }
3876539c44dSDavid S. Miller module_exit(fixed_mdio_bus_exit);
3886539c44dSDavid S. Miller
3896539c44dSDavid S. Miller MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
3906539c44dSDavid S. Miller MODULE_AUTHOR("Vitaly Bordug");
3916539c44dSDavid S. Miller MODULE_LICENSE("GPL");
392