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