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