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

mdio-mux.c (4905B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2011, 2012 Cavium, Inc.
      4 */
      5
      6#include <linux/device.h>
      7#include <linux/mdio-mux.h>
      8#include <linux/module.h>
      9#include <linux/of_mdio.h>
     10#include <linux/phy.h>
     11#include <linux/platform_device.h>
     12
     13#define DRV_DESCRIPTION "MDIO bus multiplexer driver"
     14
     15struct mdio_mux_child_bus;
     16
     17struct mdio_mux_parent_bus {
     18	struct mii_bus *mii_bus;
     19	int current_child;
     20	int parent_id;
     21	void *switch_data;
     22	int (*switch_fn)(int current_child, int desired_child, void *data);
     23
     24	/* List of our children linked through their next fields. */
     25	struct mdio_mux_child_bus *children;
     26};
     27
     28struct mdio_mux_child_bus {
     29	struct mii_bus *mii_bus;
     30	struct mdio_mux_parent_bus *parent;
     31	struct mdio_mux_child_bus *next;
     32	int bus_number;
     33};
     34
     35/*
     36 * The parent bus' lock is used to order access to the switch_fn.
     37 */
     38static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum)
     39{
     40	struct mdio_mux_child_bus *cb = bus->priv;
     41	struct mdio_mux_parent_bus *pb = cb->parent;
     42	int r;
     43
     44	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);
     45	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
     46	if (r)
     47		goto out;
     48
     49	pb->current_child = cb->bus_number;
     50
     51	r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
     52out:
     53	mutex_unlock(&pb->mii_bus->mdio_lock);
     54
     55	return r;
     56}
     57
     58/*
     59 * The parent bus' lock is used to order access to the switch_fn.
     60 */
     61static int mdio_mux_write(struct mii_bus *bus, int phy_id,
     62			  int regnum, u16 val)
     63{
     64	struct mdio_mux_child_bus *cb = bus->priv;
     65	struct mdio_mux_parent_bus *pb = cb->parent;
     66
     67	int r;
     68
     69	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);
     70	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
     71	if (r)
     72		goto out;
     73
     74	pb->current_child = cb->bus_number;
     75
     76	r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);
     77out:
     78	mutex_unlock(&pb->mii_bus->mdio_lock);
     79
     80	return r;
     81}
     82
     83static int parent_count;
     84
     85static void mdio_mux_uninit_children(struct mdio_mux_parent_bus *pb)
     86{
     87	struct mdio_mux_child_bus *cb = pb->children;
     88
     89	while (cb) {
     90		mdiobus_unregister(cb->mii_bus);
     91		mdiobus_free(cb->mii_bus);
     92		cb = cb->next;
     93	}
     94}
     95
     96int mdio_mux_init(struct device *dev,
     97		  struct device_node *mux_node,
     98		  int (*switch_fn)(int cur, int desired, void *data),
     99		  void **mux_handle,
    100		  void *data,
    101		  struct mii_bus *mux_bus)
    102{
    103	struct device_node *parent_bus_node;
    104	struct device_node *child_bus_node;
    105	int r, ret_val;
    106	struct mii_bus *parent_bus;
    107	struct mdio_mux_parent_bus *pb;
    108	struct mdio_mux_child_bus *cb;
    109
    110	if (!mux_node)
    111		return -ENODEV;
    112
    113	if (!mux_bus) {
    114		parent_bus_node = of_parse_phandle(mux_node,
    115						   "mdio-parent-bus", 0);
    116
    117		if (!parent_bus_node)
    118			return -ENODEV;
    119
    120		parent_bus = of_mdio_find_bus(parent_bus_node);
    121		if (!parent_bus) {
    122			ret_val = -EPROBE_DEFER;
    123			goto err_parent_bus;
    124		}
    125	} else {
    126		parent_bus_node = NULL;
    127		parent_bus = mux_bus;
    128		get_device(&parent_bus->dev);
    129	}
    130
    131	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
    132	if (!pb) {
    133		ret_val = -ENOMEM;
    134		goto err_pb_kz;
    135	}
    136
    137	pb->switch_data = data;
    138	pb->switch_fn = switch_fn;
    139	pb->current_child = -1;
    140	pb->parent_id = parent_count++;
    141	pb->mii_bus = parent_bus;
    142
    143	ret_val = -ENODEV;
    144	for_each_available_child_of_node(mux_node, child_bus_node) {
    145		int v;
    146
    147		r = of_property_read_u32(child_bus_node, "reg", &v);
    148		if (r) {
    149			dev_err(dev,
    150				"Error: Failed to find reg for child %pOF\n",
    151				child_bus_node);
    152			continue;
    153		}
    154
    155		cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
    156		if (!cb) {
    157			ret_val = -ENOMEM;
    158			goto err_loop;
    159		}
    160		cb->bus_number = v;
    161		cb->parent = pb;
    162
    163		cb->mii_bus = mdiobus_alloc();
    164		if (!cb->mii_bus) {
    165			ret_val = -ENOMEM;
    166			goto err_loop;
    167		}
    168		cb->mii_bus->priv = cb;
    169
    170		cb->mii_bus->name = "mdio_mux";
    171		snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x.%x",
    172			 cb->mii_bus->name, pb->parent_id, v);
    173		cb->mii_bus->parent = dev;
    174		cb->mii_bus->read = mdio_mux_read;
    175		cb->mii_bus->write = mdio_mux_write;
    176		r = of_mdiobus_register(cb->mii_bus, child_bus_node);
    177		if (r) {
    178			mdiobus_free(cb->mii_bus);
    179			if (r == -EPROBE_DEFER) {
    180				ret_val = r;
    181				goto err_loop;
    182			}
    183			devm_kfree(dev, cb);
    184			dev_err(dev,
    185				"Error: Failed to register MDIO bus for child %pOF\n",
    186				child_bus_node);
    187		} else {
    188			cb->next = pb->children;
    189			pb->children = cb;
    190		}
    191	}
    192	if (pb->children) {
    193		*mux_handle = pb;
    194		return 0;
    195	}
    196
    197	dev_err(dev, "Error: No acceptable child buses found\n");
    198
    199err_loop:
    200	mdio_mux_uninit_children(pb);
    201	of_node_put(child_bus_node);
    202err_pb_kz:
    203	put_device(&parent_bus->dev);
    204err_parent_bus:
    205	of_node_put(parent_bus_node);
    206	return ret_val;
    207}
    208EXPORT_SYMBOL_GPL(mdio_mux_init);
    209
    210void mdio_mux_uninit(void *mux_handle)
    211{
    212	struct mdio_mux_parent_bus *pb = mux_handle;
    213
    214	mdio_mux_uninit_children(pb);
    215	put_device(&pb->mii_bus->dev);
    216}
    217EXPORT_SYMBOL_GPL(mdio_mux_uninit);
    218
    219MODULE_DESCRIPTION(DRV_DESCRIPTION);
    220MODULE_AUTHOR("David Daney");
    221MODULE_LICENSE("GPL v2");