Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netdev/phy: add MDIO bus multiplexer driven by a memory-mapped device

Add support for an MDIO bus multiplexer controlled by a simple memory-mapped
device, like an FPGA. The device must be memory-mapped and contain only
8-bit registers (which keeps things simple).

Tested on a Freescale P5020DS board which uses the "PIXIS" FPGA attached
to the localbus.

Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Timur Tabi and committed by
David S. Miller
6cc2ff82 e92bdf4b

+259
+75
Documentation/devicetree/bindings/net/mdio-mux-mmioreg.txt
··· 1 + Properties for an MDIO bus multiplexer controlled by a memory-mapped device 2 + 3 + This is a special case of a MDIO bus multiplexer. A memory-mapped device, 4 + like an FPGA, is used to control which child bus is connected. The mdio-mux 5 + node must be a child of the memory-mapped device. The driver currently only 6 + supports devices with eight-bit registers. 7 + 8 + Required properties in addition to the generic multiplexer properties: 9 + 10 + - compatible : string, must contain "mdio-mux-mmioreg" 11 + 12 + - reg : integer, contains the offset of the register that controls the bus 13 + multiplexer. The size field in the 'reg' property is the size of 14 + register, and must therefore be 1. 15 + 16 + - mux-mask : integer, contains an eight-bit mask that specifies which 17 + bits in the register control the actual bus multiplexer. The 18 + 'reg' property of each child mdio-mux node must be constrained by 19 + this mask. 20 + 21 + Example: 22 + 23 + The FPGA node defines a memory-mapped FPGA with a register space of 0x30 bytes. 24 + For the "EMI2" MDIO bus, register 9 (BRDCFG1) controls the mux on that bus. 25 + A bitmask of 0x6 means that bits 1 and 2 (bit 0 is lsb) are the bits on 26 + BRDCFG1 that control the actual mux. 27 + 28 + /* The FPGA node */ 29 + fpga: board-control@3,0 { 30 + #address-cells = <1>; 31 + #size-cells = <1>; 32 + compatible = "fsl,p5020ds-fpga", "fsl,fpga-ngpixis"; 33 + reg = <3 0 0x30>; 34 + ranges = <0 3 0 0x30>; 35 + 36 + mdio-mux-emi2 { 37 + compatible = "mdio-mux-mmioreg", "mdio-mux"; 38 + mdio-parent-bus = <&xmdio0>; 39 + #address-cells = <1>; 40 + #size-cells = <0>; 41 + reg = <9 1>; // BRDCFG1 42 + mux-mask = <0x6>; // EMI2 43 + 44 + emi2_slot1: mdio@0 { // Slot 1 XAUI (FM2) 45 + reg = <0>; 46 + #address-cells = <1>; 47 + #size-cells = <0>; 48 + 49 + phy_xgmii_slot1: ethernet-phy@0 { 50 + compatible = "ethernet-phy-ieee802.3-c45"; 51 + reg = <4>; 52 + }; 53 + }; 54 + 55 + emi2_slot2: mdio@2 { // Slot 2 XAUI (FM1) 56 + reg = <2>; 57 + #address-cells = <1>; 58 + #size-cells = <0>; 59 + 60 + phy_xgmii_slot2: ethernet-phy@4 { 61 + compatible = "ethernet-phy-ieee802.3-c45"; 62 + reg = <0>; 63 + }; 64 + }; 65 + }; 66 + }; 67 + 68 + /* The parent MDIO bus. */ 69 + xmdio0: mdio@f1000 { 70 + #address-cells = <1>; 71 + #size-cells = <0>; 72 + compatible = "fsl,fman-xmdio"; 73 + reg = <0xf1000 0x1000>; 74 + interrupts = <100 1 0 0>; 75 + };
+13
drivers/net/phy/Kconfig
··· 159 159 several child MDIO busses to a parent bus. Child bus 160 160 selection is under the control of GPIO lines. 161 161 162 + config MDIO_BUS_MUX_MMIOREG 163 + tristate "Support for MMIO device-controlled MDIO bus multiplexers" 164 + depends on OF_MDIO 165 + select MDIO_BUS_MUX 166 + help 167 + This module provides a driver for MDIO bus multiplexers that 168 + are controlled via a simple memory-mapped device, like an FPGA. 169 + The multiplexer connects one of several child MDIO busses to a 170 + parent bus. Child bus selection is under the control of one of 171 + the FPGA's registers. 172 + 173 + Currently, only 8-bit registers are supported. 174 + 162 175 endif # PHYLIB 163 176 164 177 config MICREL_KS8995MA
+1
drivers/net/phy/Makefile
··· 28 28 obj-$(CONFIG_AMD_PHY) += amd.o 29 29 obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o 30 30 obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o 31 + obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
+170
drivers/net/phy/mdio-mux-mmioreg.c
··· 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_mdio.h> 16 + #include <linux/module.h> 17 + #include <linux/init.h> 18 + #include <linux/phy.h> 19 + #include <linux/mdio-mux.h> 20 + 21 + struct mdio_mux_mmioreg_state { 22 + void *mux_handle; 23 + phys_addr_t phys; 24 + uint8_t mask; 25 + }; 26 + 27 + /* 28 + * MDIO multiplexing switch function 29 + * 30 + * This function is called by the mdio-mux layer when it thinks the mdio bus 31 + * multiplexer needs to switch. 32 + * 33 + * 'current_child' is the current value of the mux register (masked via 34 + * s->mask). 35 + * 36 + * 'desired_child' is the value of the 'reg' property of the target child MDIO 37 + * node. 38 + * 39 + * The first time this function is called, current_child == -1. 40 + * 41 + * If current_child == desired_child, then the mux is already set to the 42 + * correct bus. 43 + */ 44 + static int mdio_mux_mmioreg_switch_fn(int current_child, int desired_child, 45 + void *data) 46 + { 47 + struct mdio_mux_mmioreg_state *s = data; 48 + 49 + if (current_child ^ desired_child) { 50 + void *p = ioremap(s->phys, 1); 51 + uint8_t x, y; 52 + 53 + if (!p) 54 + return -ENOMEM; 55 + 56 + x = ioread8(p); 57 + y = (x & ~s->mask) | desired_child; 58 + if (x != y) { 59 + iowrite8((x & ~s->mask) | desired_child, p); 60 + pr_debug("%s: %02x -> %02x\n", __func__, x, y); 61 + } 62 + 63 + iounmap(p); 64 + } 65 + 66 + return 0; 67 + } 68 + 69 + static int __devinit mdio_mux_mmioreg_probe(struct platform_device *pdev) 70 + { 71 + struct device_node *np2, *np = pdev->dev.of_node; 72 + struct mdio_mux_mmioreg_state *s; 73 + struct resource res; 74 + const __be32 *iprop; 75 + int len, ret; 76 + 77 + dev_dbg(&pdev->dev, "probing node %s\n", np->full_name); 78 + 79 + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); 80 + if (!s) 81 + return -ENOMEM; 82 + 83 + ret = of_address_to_resource(np, 0, &res); 84 + if (ret) { 85 + dev_err(&pdev->dev, "could not obtain memory map for node %s\n", 86 + np->full_name); 87 + return ret; 88 + } 89 + s->phys = res.start; 90 + 91 + if (resource_size(&res) != sizeof(uint8_t)) { 92 + dev_err(&pdev->dev, "only 8-bit registers are supported\n"); 93 + return -EINVAL; 94 + } 95 + 96 + iprop = of_get_property(np, "mux-mask", &len); 97 + if (!iprop || len != sizeof(uint32_t)) { 98 + dev_err(&pdev->dev, "missing or invalid mux-mask property\n"); 99 + return -ENODEV; 100 + } 101 + if (be32_to_cpup(iprop) > 255) { 102 + dev_err(&pdev->dev, "only 8-bit registers are supported\n"); 103 + return -EINVAL; 104 + } 105 + s->mask = be32_to_cpup(iprop); 106 + 107 + /* 108 + * Verify that the 'reg' property of each child MDIO bus does not 109 + * set any bits outside of the 'mask'. 110 + */ 111 + for_each_available_child_of_node(np, np2) { 112 + iprop = of_get_property(np2, "reg", &len); 113 + if (!iprop || len != sizeof(uint32_t)) { 114 + dev_err(&pdev->dev, "mdio-mux child node %s is " 115 + "missing a 'reg' property\n", np2->full_name); 116 + return -ENODEV; 117 + } 118 + if (be32_to_cpup(iprop) & ~s->mask) { 119 + dev_err(&pdev->dev, "mdio-mux child node %s has " 120 + "a 'reg' value with unmasked bits\n", 121 + np2->full_name); 122 + return -ENODEV; 123 + } 124 + } 125 + 126 + ret = mdio_mux_init(&pdev->dev, mdio_mux_mmioreg_switch_fn, 127 + &s->mux_handle, s); 128 + if (ret) { 129 + dev_err(&pdev->dev, "failed to register mdio-mux bus %s\n", 130 + np->full_name); 131 + return ret; 132 + } 133 + 134 + pdev->dev.platform_data = s; 135 + 136 + return 0; 137 + } 138 + 139 + static int __devexit mdio_mux_mmioreg_remove(struct platform_device *pdev) 140 + { 141 + struct mdio_mux_mmioreg_state *s = dev_get_platdata(&pdev->dev); 142 + 143 + mdio_mux_uninit(s->mux_handle); 144 + 145 + return 0; 146 + } 147 + 148 + static struct of_device_id mdio_mux_mmioreg_match[] = { 149 + { 150 + .compatible = "mdio-mux-mmioreg", 151 + }, 152 + {}, 153 + }; 154 + MODULE_DEVICE_TABLE(of, mdio_mux_mmioreg_match); 155 + 156 + static struct platform_driver mdio_mux_mmioreg_driver = { 157 + .driver = { 158 + .name = "mdio-mux-mmioreg", 159 + .owner = THIS_MODULE, 160 + .of_match_table = mdio_mux_mmioreg_match, 161 + }, 162 + .probe = mdio_mux_mmioreg_probe, 163 + .remove = __devexit_p(mdio_mux_mmioreg_remove), 164 + }; 165 + 166 + module_platform_driver(mdio_mux_mmioreg_driver); 167 + 168 + MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 169 + MODULE_DESCRIPTION("Memory-mapped device MDIO MUX driver"); 170 + MODULE_LICENSE("GPL v2");