xref: /openbmc/linux/drivers/net/phy/fixed_phy.c (revision bfa54812)
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