Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Simple memory-mapped device MDIO MUX driver
0004  *
0005  * Author: Timur Tabi <timur@freescale.com>
0006  *
0007  * Copyright 2012 Freescale Semiconductor, Inc.
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/mdio-mux.h>
0012 #include <linux/module.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_mdio.h>
0015 #include <linux/phy.h>
0016 #include <linux/platform_device.h>
0017 
0018 struct mdio_mux_mmioreg_state {
0019     void *mux_handle;
0020     phys_addr_t phys;
0021     unsigned int iosize;
0022     unsigned int mask;
0023 };
0024 
0025 /*
0026  * MDIO multiplexing switch function
0027  *
0028  * This function is called by the mdio-mux layer when it thinks the mdio bus
0029  * multiplexer needs to switch.
0030  *
0031  * 'current_child' is the current value of the mux register (masked via
0032  * s->mask).
0033  *
0034  * 'desired_child' is the value of the 'reg' property of the target child MDIO
0035  * node.
0036  *
0037  * The first time this function is called, current_child == -1.
0038  *
0039  * If current_child == desired_child, then the mux is already set to the
0040  * correct bus.
0041  */
0042 static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child,
0043                       void *data)
0044 {
0045     struct mdio_mux_mmioreg_state *s = data;
0046 
0047     if (current_child ^ desired_child) {
0048         void __iomem *p = ioremap(s->phys, s->iosize);
0049         if (!p)
0050             return -ENOMEM;
0051 
0052         switch (s->iosize) {
0053         case sizeof(uint8_t): {
0054             uint8_t x, y;
0055 
0056             x = ioread8(p);
0057             y = (x & ~s->mask) | desired_child;
0058             if (x != y) {
0059                 iowrite8((x & ~s->mask) | desired_child, p);
0060                 pr_debug("%s: %02x -> %02x\n", __func__, x, y);
0061             }
0062 
0063             break;
0064         }
0065         case sizeof(uint16_t): {
0066             uint16_t x, y;
0067 
0068             x = ioread16(p);
0069             y = (x & ~s->mask) | desired_child;
0070             if (x != y) {
0071                 iowrite16((x & ~s->mask) | desired_child, p);
0072                 pr_debug("%s: %04x -> %04x\n", __func__, x, y);
0073             }
0074 
0075             break;
0076         }
0077         case sizeof(uint32_t): {
0078             uint32_t x, y;
0079 
0080             x = ioread32(p);
0081             y = (x & ~s->mask) | desired_child;
0082             if (x != y) {
0083                 iowrite32((x & ~s->mask) | desired_child, p);
0084                 pr_debug("%s: %08x -> %08x\n", __func__, x, y);
0085             }
0086 
0087             break;
0088         }
0089         }
0090 
0091         iounmap(p);
0092     }
0093 
0094     return 0;
0095 }
0096 
0097 static int mdio_mux_mmioreg_probe(struct platform_device *pdev)
0098 {
0099     struct device_node *np2, *np = pdev->dev.of_node;
0100     struct mdio_mux_mmioreg_state *s;
0101     struct resource res;
0102     const __be32 *iprop;
0103     int len, ret;
0104 
0105     dev_dbg(&pdev->dev, "probing node %pOF\n", np);
0106 
0107     s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
0108     if (!s)
0109         return -ENOMEM;
0110 
0111     ret = of_address_to_resource(np, 0, &res);
0112     if (ret) {
0113         dev_err(&pdev->dev, "could not obtain memory map for node %pOF\n",
0114             np);
0115         return ret;
0116     }
0117     s->phys = res.start;
0118 
0119     s->iosize = resource_size(&res);
0120     if (s->iosize != sizeof(uint8_t) &&
0121         s->iosize != sizeof(uint16_t) &&
0122         s->iosize != sizeof(uint32_t)) {
0123         dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n");
0124         return -EINVAL;
0125     }
0126 
0127     iprop = of_get_property(np, "mux-mask", &len);
0128     if (!iprop || len != sizeof(uint32_t)) {
0129         dev_err(&pdev->dev, "missing or invalid mux-mask property\n");
0130         return -ENODEV;
0131     }
0132     if (be32_to_cpup(iprop) >= BIT(s->iosize * 8)) {
0133         dev_err(&pdev->dev, "only 8/16/32-bit registers are supported\n");
0134         return -EINVAL;
0135     }
0136     s->mask = be32_to_cpup(iprop);
0137 
0138     /*
0139      * Verify that the 'reg' property of each child MDIO bus does not
0140      * set any bits outside of the 'mask'.
0141      */
0142     for_each_available_child_of_node(np, np2) {
0143         iprop = of_get_property(np2, "reg", &len);
0144         if (!iprop || len != sizeof(uint32_t)) {
0145             dev_err(&pdev->dev, "mdio-mux child node %pOF is "
0146                 "missing a 'reg' property\n", np2);
0147             of_node_put(np2);
0148             return -ENODEV;
0149         }
0150         if (be32_to_cpup(iprop) & ~s->mask) {
0151             dev_err(&pdev->dev, "mdio-mux child node %pOF has "
0152                 "a 'reg' value with unmasked bits\n",
0153                 np2);
0154             of_node_put(np2);
0155             return -ENODEV;
0156         }
0157     }
0158 
0159     ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node,
0160                 mdio_mux_mmioreg_switch_fn,
0161                 &s->mux_handle, s, NULL);
0162     if (ret) {
0163         if (ret != -EPROBE_DEFER)
0164             dev_err(&pdev->dev,
0165                 "failed to register mdio-mux bus %pOF\n", np);
0166         return ret;
0167     }
0168 
0169     pdev->dev.platform_data = s;
0170 
0171     return 0;
0172 }
0173 
0174 static int mdio_mux_mmioreg_remove(struct platform_device *pdev)
0175 {
0176     struct mdio_mux_mmioreg_state *s = dev_get_platdata(&pdev->dev);
0177 
0178     mdio_mux_uninit(s->mux_handle);
0179 
0180     return 0;
0181 }
0182 
0183 static const struct of_device_id mdio_mux_mmioreg_match[] = {
0184     {
0185         .compatible = "mdio-mux-mmioreg",
0186     },
0187     {},
0188 };
0189 MODULE_DEVICE_TABLE(of, mdio_mux_mmioreg_match);
0190 
0191 static struct platform_driver mdio_mux_mmioreg_driver = {
0192     .driver = {
0193         .name       = "mdio-mux-mmioreg",
0194         .of_match_table = mdio_mux_mmioreg_match,
0195     },
0196     .probe      = mdio_mux_mmioreg_probe,
0197     .remove     = mdio_mux_mmioreg_remove,
0198 };
0199 
0200 module_platform_driver(mdio_mux_mmioreg_driver);
0201 
0202 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
0203 MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver");
0204 MODULE_LICENSE("GPL v2");