brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · aef739c Raw
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