linux/drivers/net/phy/mdio-mux-mmioreg.c
<<
>>
Prefs
   1/*
   2 * Simple memory-mapped device MDIO MUX driver
   3 *
   4 * Author: Timur Tabi <timur@freescale.com>
   5 *
   6 * Copyright 2012 Freescale Semiconductor, Inc.
   7 *
   8 * This file is licensed under the terms of the GNU General Public License
   9 * version 2.  This program is licensed "as is" without any warranty of any
  10 * kind, whether express or implied.
  11 */
  12
  13#include <linux/platform_device.h>
  14#include <linux/device.h>
  15#include <linux/of_address.h>
  16#include <linux/of_mdio.h>
  17#include <linux/module.h>
  18#include <linux/phy.h>
  19#include <linux/mdio-mux.h>
  20
  21struct mdio_mux_mmioreg_state {
  22        void *mux_handle;
  23        phys_addr_t phys;
  24        unsigned int iosize;
  25        unsigned int mask;
  26};
  27
  28/*
  29 * MDIO multiplexing switch function
  30 *
  31 * This function is called by the mdio-mux layer when it thinks the mdio bus
  32 * multiplexer needs to switch.
  33 *
  34 * 'current_child' is the current value of the mux register (masked via
  35 * s->mask).
  36 *
  37 * 'desired_child' is the value of the 'reg' property of the target child MDIO
  38 * node.
  39 *
  40 * The first time this function is called, current_child == -1.
  41 *
  42 * If current_child == desired_child, then the mux is already set to the
  43 * correct bus.
  44 */
  45static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child,
  46                                      void *data)
  47{
  48        struct mdio_mux_mmioreg_state *s = data;
  49
  50        if (current_child ^ desired_child) {
  51                void __iomem *p = ioremap(s->phys, s->iosize);
  52                if (!p)
  53                        return -ENOMEM;
  54
  55                switch (s->iosize) {
  56                case sizeof(uint8_t): {
  57                        uint8_t x, y;
  58
  59                        x = ioread8(p);
  60                        y = (x & ~s->mask) | desired_child;
  61                        if (x != y) {
  62                                iowrite8((x & ~s->mask) | desired_child, p);
  63                                pr_debug("%s: %02x -> %02x\n", __func__, x, y);
  64                        }
  65
  66                        break;
  67                }
  68                case sizeof(uint16_t): {
  69                        uint16_t x, y;
  70
  71                        x = ioread16(p);
  72                        y = (x & ~s->mask) | desired_child;
  73                        if (x != y) {
  74                                iowrite16((x & ~s->mask) | desired_child, p);
  75                                pr_debug("%s: %04x -> %04x\n", __func__, x, y);
  76                        }
  77
  78                        break;
  79                }
  80                case sizeof(uint32_t): {
  81                        uint32_t x, y;
  82
  83                        x = ioread32(p);
  84                        y = (x & ~s->mask) | desired_child;
  85                        if (x != y) {
  86                                iowrite32((x & ~s->mask) | desired_child, p);
  87                                pr_debug("%s: %08x -> %08x\n", __func__, x, y);
  88                        }
  89
  90                        break;
  91                }
  92                }
  93
  94                iounmap(p);
  95        }
  96
  97        return 0;
  98}
  99
 100static int mdio_mux_mmioreg_probe(struct platform_device *pdev)
 101{
 102        struct device_node *np2, *np = pdev->dev.of_node;
 103        struct mdio_mux_mmioreg_state *s;
 104        struct resource res;
 105        const __be32 *iprop;
 106        int len, ret;
 107
 108        dev_dbg(&pdev->dev, "probing node %pOF\n", np);
 109
 110        s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
 111        if (!s)
 112                return -ENOMEM;
 113
 114        ret = of_address_to_resource(np, 0, &res);
 115        if (ret) {
 116                dev_err(&pdev->dev, "could not obtain memory map for node %pOF\n",
 117                        np);
 118                return ret;
 119        }
 120        s->phys = res.start;
 121
 122        s->iosize = resource_size(&res);
 123        if (s->iosize != sizeof(uint8_t) &&
 124            s->iosize != sizeof(uint16_t) &&
 125            s->iosize != sizeof(uint32_t)) {
 126                dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n");
 127                return -EINVAL;
 128        }
 129
 130        iprop = of_get_property(np, "mux-mask", &len);
 131        if (!iprop || len != sizeof(uint32_t)) {
 132                dev_err(&pdev->dev, "missing or invalid mux-mask property\n");
 133                return -ENODEV;
 134        }
 135        if (be32_to_cpup(iprop) >= BIT(s->iosize * 8)) {
 136                dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n");
 137                return -EINVAL;
 138        }
 139        s->mask = be32_to_cpup(iprop);
 140
 141        /*
 142         * Verify that the 'reg' property of each child MDIO bus does not
 143         * set any bits outside of the 'mask'.
 144         */
 145        for_each_available_child_of_node(np, np2) {
 146                iprop = of_get_property(np2, "reg", &len);
 147                if (!iprop || len != sizeof(uint32_t)) {
 148                        dev_err(&pdev->dev, "mdio-mux child node %pOF is "
 149                                "missing a 'reg' property\n", np2);
 150                        of_node_put(np2);
 151                        return -ENODEV;
 152                }
 153                if (be32_to_cpup(iprop) & ~s->mask) {
 154                        dev_err(&pdev->dev, "mdio-mux child node %pOF has "
 155                                "a 'reg' value with unmasked bits\n",
 156                                np2);
 157                        of_node_put(np2);
 158                        return -ENODEV;
 159                }
 160        }
 161
 162        ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node,
 163                            mdio_mux_mmioreg_switch_fn,
 164                            &s->mux_handle, s, NULL);
 165        if (ret) {
 166                if (ret != -EPROBE_DEFER)
 167                        dev_err(&pdev->dev,
 168                                "failed to register mdio-mux bus %pOF\n", np);
 169                return ret;
 170        }
 171
 172        pdev->dev.platform_data = s;
 173
 174        return 0;
 175}
 176
 177static int mdio_mux_mmioreg_remove(struct platform_device *pdev)
 178{
 179        struct mdio_mux_mmioreg_state *s = dev_get_platdata(&pdev->dev);
 180
 181        mdio_mux_uninit(s->mux_handle);
 182
 183        return 0;
 184}
 185
 186static const struct of_device_id mdio_mux_mmioreg_match[] = {
 187        {
 188                .compatible = "mdio-mux-mmioreg",
 189        },
 190        {},
 191};
 192MODULE_DEVICE_TABLE(of, mdio_mux_mmioreg_match);
 193
 194static struct platform_driver mdio_mux_mmioreg_driver = {
 195        .driver = {
 196                .name           = "mdio-mux-mmioreg",
 197                .of_match_table = mdio_mux_mmioreg_match,
 198        },
 199        .probe          = mdio_mux_mmioreg_probe,
 200        .remove         = mdio_mux_mmioreg_remove,
 201};
 202
 203module_platform_driver(mdio_mux_mmioreg_driver);
 204
 205MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 206MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
 207MODULE_LICENSE("GPL v2");
 208