From eb00bb9f3865a891f7a37a8f2cf719005008b6a6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 4 Dec 2017 07:27:15 -0800 Subject: net: phy: Port MDIO bus miltiplexer framework from Linux kernel Port mdio-mux.c from Linux kernel to Barebox, to support adding dirvers that rely on that infrastructure/API. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 8 +++ drivers/net/phy/Makefile | 2 + drivers/net/phy/mdio-mux.c | 142 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 drivers/net/phy/mdio-mux.c (limited to 'drivers/net/phy') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index ea2e062656..08ffd2d456 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -66,6 +66,14 @@ config MDIO_GPIO ---help--- Supports GPIO lib-based MDIO busses. +config MDIO_BUS_MUX + bool + help + This module provides a driver framework for MDIO bus + multiplexers which connect one of several child MDIO busses + to a parent bus. Switching between child busses is done by + device specific drivers. + endif endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 13b8f6545d..89b4b80be1 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o +obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o + diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c new file mode 100644 index 0000000000..4e924e5863 --- /dev/null +++ b/drivers/net/phy/mdio-mux.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Zodiac Inflight Innovation + * Author: Andrey Smirnov + * + * Based on analogous code from Linux kernel + * + * Copyright (C) 2011, 2012 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include + +struct mdio_mux_parent_bus { + struct mii_bus *mii_bus; + int current_child; + int parent_id; + void *switch_data; + int (*switch_fn)(int current_child, int desired_child, void *data); +}; + +struct mdio_mux_child_bus { + struct mii_bus mii_bus; + struct mdio_mux_parent_bus *parent; + struct mdio_mux_child_bus *next; + int bus_number; +}; + +static int mdio_mux_read_or_write(struct mii_bus *bus, int phy_id, + int regnum, u16 *val) +{ + struct mdio_mux_child_bus *cb = bus->priv; + struct mdio_mux_parent_bus *pb = cb->parent; + int r; + + r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data); + if (!r) { + pb->current_child = cb->bus_number; + if (val) + r = pb->mii_bus->write(pb->mii_bus, phy_id, + regnum, *val); + else + r = pb->mii_bus->read(pb->mii_bus, phy_id, + regnum); + } + return r; +} + +static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum) +{ + return mdio_mux_read_or_write(bus, phy_id, regnum, NULL); +} + +static int mdio_mux_write(struct mii_bus *bus, int phy_id, + int regnum, u16 val) +{ + return mdio_mux_read_or_write(bus, phy_id, regnum, &val); +} + +int mdio_mux_init(struct device_d *dev, + struct device_node *mux_node, + int (*switch_fn)(int cur, int desired, void *data), + void *data, + struct mii_bus *mux_bus) +{ + static int parent_count = 0; + + struct device_node *parent_bus_node; + struct device_node *child_bus_node; + struct mdio_mux_parent_bus *pb; + struct mdio_mux_child_bus *cb; + struct mii_bus *parent_bus; + int r; + + if (!mux_node) + return -ENODEV; + + if (!mux_bus) { + parent_bus_node = of_parse_phandle(mux_node, + "mdio-parent-bus", 0); + + if (!parent_bus_node) + return -ENODEV; + + parent_bus = of_mdio_find_bus(parent_bus_node); + + if (!parent_bus) + return -EPROBE_DEFER; + } else { + parent_bus_node = NULL; + parent_bus = mux_bus; + } + + pb = xzalloc(sizeof(*pb)); + + pb->switch_data = data; + pb->switch_fn = switch_fn; + pb->current_child = -1; + pb->parent_id = parent_count++; + pb->mii_bus = parent_bus; + + for_each_available_child_of_node(mux_node, child_bus_node) { + int v; + + r = of_property_read_u32(child_bus_node, "reg", &v); + if (r) { + dev_err(dev, + "Error: Failed to find reg for child %pOF\n", + child_bus_node); + continue; + } + + cb = xzalloc(sizeof(*cb)); + cb->bus_number = v; + cb->parent = pb; + + cb->mii_bus.priv = cb; + cb->mii_bus.parent = dev; + cb->mii_bus.read = mdio_mux_read; + cb->mii_bus.write = mdio_mux_write; + cb->mii_bus.dev.device_node = child_bus_node; + + r = mdiobus_register(&cb->mii_bus); + if (r) { + dev_err(dev, + "Error: Failed to register MDIO bus for child %pOF\n", + child_bus_node); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(mdio_mux_init); -- cgit v1.2.3