brintos

brintos / linux-shallow public Read only

0
0
Text · 6.0 KiB · fe0e46b Raw
271 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 58static int mdio_mux_read_c45(struct mii_bus *bus, int phy_id, int dev_addr,59			     int regnum)60{61	struct mdio_mux_child_bus *cb = bus->priv;62	struct mdio_mux_parent_bus *pb = cb->parent;63	int r;64 65	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);66	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);67	if (r)68		goto out;69 70	pb->current_child = cb->bus_number;71 72	r = pb->mii_bus->read_c45(pb->mii_bus, phy_id, dev_addr, regnum);73out:74	mutex_unlock(&pb->mii_bus->mdio_lock);75 76	return r;77}78 79/*80 * The parent bus' lock is used to order access to the switch_fn.81 */82static int mdio_mux_write(struct mii_bus *bus, int phy_id,83			  int regnum, u16 val)84{85	struct mdio_mux_child_bus *cb = bus->priv;86	struct mdio_mux_parent_bus *pb = cb->parent;87 88	int r;89 90	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);91	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);92	if (r)93		goto out;94 95	pb->current_child = cb->bus_number;96 97	r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);98out:99	mutex_unlock(&pb->mii_bus->mdio_lock);100 101	return r;102}103 104static int mdio_mux_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,105			      int regnum, u16 val)106{107	struct mdio_mux_child_bus *cb = bus->priv;108	struct mdio_mux_parent_bus *pb = cb->parent;109 110	int r;111 112	mutex_lock_nested(&pb->mii_bus->mdio_lock, MDIO_MUTEX_MUX);113	r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);114	if (r)115		goto out;116 117	pb->current_child = cb->bus_number;118 119	r = pb->mii_bus->write_c45(pb->mii_bus, phy_id, dev_addr, regnum, val);120out:121	mutex_unlock(&pb->mii_bus->mdio_lock);122 123	return r;124}125 126static int parent_count;127 128static void mdio_mux_uninit_children(struct mdio_mux_parent_bus *pb)129{130	struct mdio_mux_child_bus *cb = pb->children;131 132	while (cb) {133		mdiobus_unregister(cb->mii_bus);134		mdiobus_free(cb->mii_bus);135		cb = cb->next;136	}137}138 139int mdio_mux_init(struct device *dev,140		  struct device_node *mux_node,141		  int (*switch_fn)(int cur, int desired, void *data),142		  void **mux_handle,143		  void *data,144		  struct mii_bus *mux_bus)145{146	struct device_node *parent_bus_node;147	struct device_node *child_bus_node;148	int r, ret_val;149	struct mii_bus *parent_bus;150	struct mdio_mux_parent_bus *pb;151	struct mdio_mux_child_bus *cb;152 153	if (!mux_node)154		return -ENODEV;155 156	if (!mux_bus) {157		parent_bus_node = of_parse_phandle(mux_node,158						   "mdio-parent-bus", 0);159 160		if (!parent_bus_node)161			return -ENODEV;162 163		parent_bus = of_mdio_find_bus(parent_bus_node);164		if (!parent_bus) {165			ret_val = -EPROBE_DEFER;166			goto err_parent_bus;167		}168	} else {169		parent_bus_node = NULL;170		parent_bus = mux_bus;171		get_device(&parent_bus->dev);172	}173 174	pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);175	if (!pb) {176		ret_val = -ENOMEM;177		goto err_pb_kz;178	}179 180	pb->switch_data = data;181	pb->switch_fn = switch_fn;182	pb->current_child = -1;183	pb->parent_id = parent_count++;184	pb->mii_bus = parent_bus;185 186	ret_val = -ENODEV;187	for_each_available_child_of_node(mux_node, child_bus_node) {188		int v;189 190		r = of_property_read_u32(child_bus_node, "reg", &v);191		if (r) {192			dev_err(dev,193				"Error: Failed to find reg for child %pOF: %pe\n",194				child_bus_node, ERR_PTR(r));195			continue;196		}197 198		cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);199		if (!cb) {200			ret_val = -ENOMEM;201			goto err_loop;202		}203		cb->bus_number = v;204		cb->parent = pb;205 206		cb->mii_bus = mdiobus_alloc();207		if (!cb->mii_bus) {208			ret_val = -ENOMEM;209			goto err_loop;210		}211		cb->mii_bus->priv = cb;212 213		cb->mii_bus->name = "mdio_mux";214		snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x.%x",215			 cb->mii_bus->name, pb->parent_id, v);216		cb->mii_bus->parent = dev;217		if (parent_bus->read)218			cb->mii_bus->read = mdio_mux_read;219		if (parent_bus->write)220			cb->mii_bus->write = mdio_mux_write;221		if (parent_bus->read_c45)222			cb->mii_bus->read_c45 = mdio_mux_read_c45;223		if (parent_bus->write_c45)224			cb->mii_bus->write_c45 = mdio_mux_write_c45;225		r = of_mdiobus_register(cb->mii_bus, child_bus_node);226		if (r) {227			mdiobus_free(cb->mii_bus);228			if (r == -EPROBE_DEFER) {229				ret_val = r;230				goto err_loop;231			}232			devm_kfree(dev, cb);233			dev_err(dev,234				"Error: Failed to register MDIO bus for child %pOF: %pe\n",235				child_bus_node, ERR_PTR(r));236		} else {237			cb->next = pb->children;238			pb->children = cb;239		}240	}241	if (pb->children) {242		*mux_handle = pb;243		return 0;244	}245 246	dev_err(dev, "Error: No acceptable child buses found\n");247 248err_loop:249	mdio_mux_uninit_children(pb);250	of_node_put(child_bus_node);251err_pb_kz:252	put_device(&parent_bus->dev);253err_parent_bus:254	of_node_put(parent_bus_node);255	return ret_val;256}257EXPORT_SYMBOL_GPL(mdio_mux_init);258 259void mdio_mux_uninit(void *mux_handle)260{261	struct mdio_mux_parent_bus *pb = mux_handle;262 263	mdio_mux_uninit_children(pb);264	put_device(&pb->mii_bus->dev);265}266EXPORT_SYMBOL_GPL(mdio_mux_uninit);267 268MODULE_DESCRIPTION(DRV_DESCRIPTION);269MODULE_AUTHOR("David Daney");270MODULE_LICENSE("GPL v2");271