cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fixed_phy.c (9258B)


      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_GPIO
    190static 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 any
    204	 * Linux device associated with it, we simply have obtain
    205	 * 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#else
    220static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
    221{
    222	return NULL;
    223}
    224#endif
    225
    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
    357	ret = mdiobus_register(fmb->mii_bus);
    358	if (ret)
    359		goto err_mdiobus_alloc;
    360
    361	return 0;
    362
    363err_mdiobus_alloc:
    364	mdiobus_free(fmb->mii_bus);
    365err_mdiobus_reg:
    366	platform_device_unregister(pdev);
    367	return ret;
    368}
    369module_init(fixed_mdio_bus_init);
    370
    371static void __exit fixed_mdio_bus_exit(void)
    372{
    373	struct fixed_mdio_bus *fmb = &platform_fmb;
    374	struct fixed_phy *fp, *tmp;
    375
    376	mdiobus_unregister(fmb->mii_bus);
    377	mdiobus_free(fmb->mii_bus);
    378	platform_device_unregister(pdev);
    379
    380	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
    381		list_del(&fp->node);
    382		kfree(fp);
    383	}
    384	ida_destroy(&phy_fixed_ida);
    385}
    386module_exit(fixed_mdio_bus_exit);
    387
    388MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
    389MODULE_AUTHOR("Vitaly Bordug");
    390MODULE_LICENSE("GPL");