1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Realtek MDIO interface driver 3 * 4 * ASICs we intend to support with this driver: 5 * 6 * RTL8366 - The original version, apparently 7 * RTL8369 - Similar enough to have the same datsheet as RTL8366 8 * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite 9 * different register layout from the other two 10 * RTL8366S - Is this "RTL8366 super"? 11 * RTL8367 - Has an OpenWRT driver as well 12 * RTL8368S - Seems to be an alternative name for RTL8366RB 13 * RTL8370 - Also uses SMI 14 * 15 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 16 * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> 17 * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> 18 * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> 19 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> 20 */ 21 22 #include <linux/module.h> 23 #include <linux/of.h> 24 #include <linux/overflow.h> 25 #include <linux/regmap.h> 26 27 #include "realtek.h" 28 #include "realtek-mdio.h" 29 #include "rtl83xx.h" 30 31 /* Read/write via mdiobus */ 32 #define REALTEK_MDIO_CTRL0_REG 31 33 #define REALTEK_MDIO_START_REG 29 34 #define REALTEK_MDIO_CTRL1_REG 21 35 #define REALTEK_MDIO_ADDRESS_REG 23 36 #define REALTEK_MDIO_DATA_WRITE_REG 24 37 #define REALTEK_MDIO_DATA_READ_REG 25 38 39 #define REALTEK_MDIO_START_OP 0xFFFF 40 #define REALTEK_MDIO_ADDR_OP 0x000E 41 #define REALTEK_MDIO_READ_OP 0x0001 42 #define REALTEK_MDIO_WRITE_OP 0x0003 43 44 static int realtek_mdio_write(void *ctx, u32 reg, u32 val) 45 { 46 struct realtek_priv *priv = ctx; 47 struct mii_bus *bus = priv->bus; 48 int ret; 49 50 mutex_lock(&bus->mdio_lock); 51 52 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 53 if (ret) 54 goto out_unlock; 55 56 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 57 if (ret) 58 goto out_unlock; 59 60 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val); 61 if (ret) 62 goto out_unlock; 63 64 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP); 65 66 out_unlock: 67 mutex_unlock(&bus->mdio_lock); 68 69 return ret; 70 } 71 72 static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) 73 { 74 struct realtek_priv *priv = ctx; 75 struct mii_bus *bus = priv->bus; 76 int ret; 77 78 mutex_lock(&bus->mdio_lock); 79 80 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 81 if (ret) 82 goto out_unlock; 83 84 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 85 if (ret) 86 goto out_unlock; 87 88 ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP); 89 if (ret) 90 goto out_unlock; 91 92 ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); 93 if (ret >= 0) { 94 *val = ret; 95 ret = 0; 96 } 97 98 out_unlock: 99 mutex_unlock(&bus->mdio_lock); 100 101 return ret; 102 } 103 104 static const struct realtek_interface_info realtek_mdio_info = { 105 .reg_read = realtek_mdio_read, 106 .reg_write = realtek_mdio_write, 107 }; 108 109 /** 110 * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch 111 * @mdiodev: mdio_device to probe on. 112 * 113 * This function should be used as the .probe in an mdio_driver. After 114 * calling the common probe function for both interfaces, it initializes the 115 * values specific for MDIO-connected devices. Finally, it calls a common 116 * function to register the DSA switch. 117 * 118 * Context: Can sleep. Takes and releases priv->map_lock. 119 * Return: Returns 0 on success, a negative error on failure. 120 */ 121 int realtek_mdio_probe(struct mdio_device *mdiodev) 122 { 123 struct device *dev = &mdiodev->dev; 124 struct realtek_priv *priv; 125 int ret; 126 127 priv = rtl83xx_probe(dev, &realtek_mdio_info); 128 if (IS_ERR(priv)) 129 return PTR_ERR(priv); 130 131 priv->bus = mdiodev->bus; 132 priv->mdio_addr = mdiodev->addr; 133 priv->write_reg_noack = realtek_mdio_write; 134 135 ret = rtl83xx_register_switch(priv); 136 if (ret) { 137 rtl83xx_remove(priv); 138 return ret; 139 } 140 141 return 0; 142 } 143 EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA); 144 145 /** 146 * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch 147 * @mdiodev: mdio_device to be removed. 148 * 149 * This function should be used as the .remove_new in an mdio_driver. First 150 * it unregisters the DSA switch and then it calls the common remove function. 151 * 152 * Context: Can sleep. 153 * Return: Nothing. 154 */ 155 void realtek_mdio_remove(struct mdio_device *mdiodev) 156 { 157 struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 158 159 if (!priv) 160 return; 161 162 rtl83xx_unregister_switch(priv); 163 164 rtl83xx_remove(priv); 165 } 166 EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); 167 168 /** 169 * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch 170 * @mdiodev: mdio_device shutting down. 171 * 172 * This function should be used as the .shutdown in a platform_driver. It calls 173 * the common shutdown function. 174 * 175 * Context: Can sleep. 176 * Return: Nothing. 177 */ 178 void realtek_mdio_shutdown(struct mdio_device *mdiodev) 179 { 180 struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 181 182 if (!priv) 183 return; 184 185 rtl83xx_shutdown(priv); 186 } 187 EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); 188