linux/drivers/net/phy/mdio-mux.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2011, 2012 Cavium, Inc.
   7 */
   8
   9#include <linux/platform_device.h>
  10#include <linux/mdio-mux.h>
  11#include <linux/of_mdio.h>
  12#include <linux/device.h>
  13#include <linux/module.h>
  14#include <linux/phy.h>
  15
  16#define DRV_VERSION "1.0"
  17#define DRV_DESCRIPTION "MDIO bus multiplexer driver"
  18
  19struct mdio_mux_child_bus;
  20
  21struct mdio_mux_parent_bus {
  22        struct mii_bus *mii_bus;
  23        int current_child;
  24        int parent_id;
  25        void *switch_data;
  26        int (*switch_fn)(int current_child, int desired_child, void *data);
  27
  28        /* List of our children linked through their next fields. */
  29        struct mdio_mux_child_bus *children;
  30};
  31
  32struct mdio_mux_child_bus {
  33        struct mii_bus *mii_bus;
  34        struct mdio_mux_parent_bus *parent;
  35        struct mdio_mux_child_bus *next;
  36        int bus_number;
  37};
  38
  39/*
  40 * The parent bus' lock is used to order access to the switch_fn.
  41 */
  42static int mdio_mux_read(struct mii_bus *bus, int phy_id, int regnum)
  43{
  44        struct mdio_mux_child_bus *cb = bus->priv;
  45        struct mdio_mux_parent_bus *pb = cb->parent;
  46        int r;
  47
  48        /* In theory multiple mdio_mux could be stacked, thus creating
  49         * more than a single level of nesting.  But in practice,
  50         * SINGLE_DEPTH_NESTING will cover the vast majority of use
  51         * cases.  We use it, instead of trying to handle the general
  52         * case.
  53         */
  54        mutex_lock_nested(&pb->mii_bus->mdio_lock, SINGLE_DEPTH_NESTING);
  55        r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
  56        if (r)
  57                goto out;
  58
  59        pb->current_child = cb->bus_number;
  60
  61        r = pb->mii_bus->read(pb->mii_bus, phy_id, regnum);
  62out:
  63        mutex_unlock(&pb->mii_bus->mdio_lock);
  64
  65        return r;
  66}
  67
  68/*
  69 * The parent bus' lock is used to order access to the switch_fn.
  70 */
  71static int mdio_mux_write(struct mii_bus *bus, int phy_id,
  72                          int regnum, u16 val)
  73{
  74        struct mdio_mux_child_bus *cb = bus->priv;
  75        struct mdio_mux_parent_bus *pb = cb->parent;
  76
  77        int r;
  78
  79        mutex_lock_nested(&pb->mii_bus->mdio_lock, SINGLE_DEPTH_NESTING);
  80        r = pb->switch_fn(pb->current_child, cb->bus_number, pb->switch_data);
  81        if (r)
  82                goto out;
  83
  84        pb->current_child = cb->bus_number;
  85
  86        r = pb->mii_bus->write(pb->mii_bus, phy_id, regnum, val);
  87out:
  88        mutex_unlock(&pb->mii_bus->mdio_lock);
  89
  90        return r;
  91}
  92
  93static int parent_count;
  94
  95int mdio_mux_init(struct device *dev,
  96                  int (*switch_fn)(int cur, int desired, void *data),
  97                  void **mux_handle,
  98                  void *data)
  99{
 100        struct device_node *parent_bus_node;
 101        struct device_node *child_bus_node;
 102        int r, ret_val;
 103        struct mii_bus *parent_bus;
 104        struct mdio_mux_parent_bus *pb;
 105        struct mdio_mux_child_bus *cb;
 106
 107        if (!dev->of_node)
 108                return -ENODEV;
 109
 110        parent_bus_node = of_parse_phandle(dev->of_node, "mdio-parent-bus", 0);
 111
 112        if (!parent_bus_node)
 113                return -ENODEV;
 114
 115        pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
 116        if (pb == NULL) {
 117                ret_val = -ENOMEM;
 118                goto err_parent_bus;
 119        }
 120
 121        parent_bus = of_mdio_find_bus(parent_bus_node);
 122        if (parent_bus == NULL) {
 123                ret_val = -EPROBE_DEFER;
 124                goto err_parent_bus;
 125        }
 126
 127        pb->switch_data = data;
 128        pb->switch_fn = switch_fn;
 129        pb->current_child = -1;
 130        pb->parent_id = parent_count++;
 131        pb->mii_bus = parent_bus;
 132
 133        ret_val = -ENODEV;
 134        for_each_available_child_of_node(dev->of_node, child_bus_node) {
 135                u32 v;
 136
 137                r = of_property_read_u32(child_bus_node, "reg", &v);
 138                if (r)
 139                        continue;
 140
 141                cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
 142                if (cb == NULL) {
 143                        dev_err(dev,
 144                                "Error: Failed to allocate memory for child\n");
 145                        ret_val = -ENOMEM;
 146                        of_node_put(child_bus_node);
 147                        break;
 148                }
 149                cb->bus_number = v;
 150                cb->parent = pb;
 151
 152                cb->mii_bus = mdiobus_alloc();
 153                if (!cb->mii_bus) {
 154                        ret_val = -ENOMEM;
 155                        of_node_put(child_bus_node);
 156                        break;
 157                }
 158                cb->mii_bus->priv = cb;
 159
 160                cb->mii_bus->name = "mdio_mux";
 161                snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
 162                         pb->parent_id, v);
 163                cb->mii_bus->parent = dev;
 164                cb->mii_bus->read = mdio_mux_read;
 165                cb->mii_bus->write = mdio_mux_write;
 166                r = of_mdiobus_register(cb->mii_bus, child_bus_node);
 167                if (r) {
 168                        mdiobus_free(cb->mii_bus);
 169                        devm_kfree(dev, cb);
 170                } else {
 171                        of_node_get(child_bus_node);
 172                        cb->next = pb->children;
 173                        pb->children = cb;
 174                }
 175        }
 176        if (pb->children) {
 177                *mux_handle = pb;
 178                dev_info(dev, "Version " DRV_VERSION "\n");
 179                return 0;
 180        }
 181
 182        /* balance the reference of_mdio_find_bus() took */
 183        put_device(&pb->mii_bus->dev);
 184
 185err_parent_bus:
 186        of_node_put(parent_bus_node);
 187        return ret_val;
 188}
 189EXPORT_SYMBOL_GPL(mdio_mux_init);
 190
 191void mdio_mux_uninit(void *mux_handle)
 192{
 193        struct mdio_mux_parent_bus *pb = mux_handle;
 194        struct mdio_mux_child_bus *cb = pb->children;
 195
 196        while (cb) {
 197                mdiobus_unregister(cb->mii_bus);
 198                mdiobus_free(cb->mii_bus);
 199                cb = cb->next;
 200        }
 201
 202        /* balance the reference of_mdio_find_bus() in mdio_mux_init() took */
 203        put_device(&pb->mii_bus->dev);
 204}
 205EXPORT_SYMBOL_GPL(mdio_mux_uninit);
 206
 207MODULE_DESCRIPTION(DRV_DESCRIPTION);
 208MODULE_VERSION(DRV_VERSION);
 209MODULE_AUTHOR("David Daney");
 210MODULE_LICENSE("GPL");
 211