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" 28aac94001SLuiz Angelo Daros de Luca 29aac94001SLuiz Angelo Daros de Luca /* Read/write via mdiobus */ 30aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL0_REG 31 31aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_REG 29 32aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL1_REG 21 33aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDRESS_REG 23 34aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_WRITE_REG 24 35aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_READ_REG 25 36aac94001SLuiz Angelo Daros de Luca 37aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_OP 0xFFFF 38aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDR_OP 0x000E 39aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_READ_OP 0x0001 40aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_WRITE_OP 0x0003 41aac94001SLuiz Angelo Daros de Luca 42aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_write(void *ctx, u32 reg, u32 val) 43aac94001SLuiz Angelo Daros de Luca { 44aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = ctx; 45aac94001SLuiz Angelo Daros de Luca struct mii_bus *bus = priv->bus; 46aac94001SLuiz Angelo Daros de Luca int ret; 47aac94001SLuiz Angelo Daros de Luca 48aac94001SLuiz Angelo Daros de Luca mutex_lock(&bus->mdio_lock); 49aac94001SLuiz Angelo Daros de Luca 50aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 51aac94001SLuiz Angelo Daros de Luca if (ret) 52aac94001SLuiz Angelo Daros de Luca goto out_unlock; 53aac94001SLuiz Angelo Daros de Luca 54aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 55aac94001SLuiz Angelo Daros de Luca if (ret) 56aac94001SLuiz Angelo Daros de Luca goto out_unlock; 57aac94001SLuiz Angelo Daros de Luca 58aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val); 59aac94001SLuiz Angelo Daros de Luca if (ret) 60aac94001SLuiz Angelo Daros de Luca goto out_unlock; 61aac94001SLuiz Angelo Daros de Luca 62aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP); 63aac94001SLuiz Angelo Daros de Luca 64aac94001SLuiz Angelo Daros de Luca out_unlock: 65aac94001SLuiz Angelo Daros de Luca mutex_unlock(&bus->mdio_lock); 66aac94001SLuiz Angelo Daros de Luca 67aac94001SLuiz Angelo Daros de Luca return ret; 68aac94001SLuiz Angelo Daros de Luca } 69aac94001SLuiz Angelo Daros de Luca 70aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) 71aac94001SLuiz Angelo Daros de Luca { 72aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = ctx; 73aac94001SLuiz Angelo Daros de Luca struct mii_bus *bus = priv->bus; 74aac94001SLuiz Angelo Daros de Luca int ret; 75aac94001SLuiz Angelo Daros de Luca 76aac94001SLuiz Angelo Daros de Luca mutex_lock(&bus->mdio_lock); 77aac94001SLuiz Angelo Daros de Luca 78aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 79aac94001SLuiz Angelo Daros de Luca if (ret) 80aac94001SLuiz Angelo Daros de Luca goto out_unlock; 81aac94001SLuiz Angelo Daros de Luca 82aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 83aac94001SLuiz Angelo Daros de Luca if (ret) 84aac94001SLuiz Angelo Daros de Luca goto out_unlock; 85aac94001SLuiz Angelo Daros de Luca 86aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP); 87aac94001SLuiz Angelo Daros de Luca if (ret) 88aac94001SLuiz Angelo Daros de Luca goto out_unlock; 89aac94001SLuiz Angelo Daros de Luca 90aac94001SLuiz Angelo Daros de Luca ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); 91aac94001SLuiz Angelo Daros de Luca if (ret >= 0) { 92aac94001SLuiz Angelo Daros de Luca *val = ret; 93aac94001SLuiz Angelo Daros de Luca ret = 0; 94aac94001SLuiz Angelo Daros de Luca } 95aac94001SLuiz Angelo Daros de Luca 96aac94001SLuiz Angelo Daros de Luca out_unlock: 97aac94001SLuiz Angelo Daros de Luca mutex_unlock(&bus->mdio_lock); 98aac94001SLuiz Angelo Daros de Luca 99aac94001SLuiz Angelo Daros de Luca return ret; 100aac94001SLuiz Angelo Daros de Luca } 101aac94001SLuiz Angelo Daros de Luca 102907e772fSAlvin Šipraga static void realtek_mdio_lock(void *ctx) 103907e772fSAlvin Šipraga { 104907e772fSAlvin Šipraga struct realtek_priv *priv = ctx; 105907e772fSAlvin Šipraga 106907e772fSAlvin Šipraga mutex_lock(&priv->map_lock); 107907e772fSAlvin Šipraga } 108907e772fSAlvin Šipraga 109907e772fSAlvin Šipraga static void realtek_mdio_unlock(void *ctx) 110907e772fSAlvin Šipraga { 111907e772fSAlvin Šipraga struct realtek_priv *priv = ctx; 112907e772fSAlvin Šipraga 113907e772fSAlvin Šipraga mutex_unlock(&priv->map_lock); 114907e772fSAlvin Šipraga } 115907e772fSAlvin Šipraga 116aac94001SLuiz Angelo Daros de Luca static const struct regmap_config realtek_mdio_regmap_config = { 117aac94001SLuiz Angelo Daros de Luca .reg_bits = 10, /* A4..A0 R4..R0 */ 118aac94001SLuiz Angelo Daros de Luca .val_bits = 16, 119aac94001SLuiz Angelo Daros de Luca .reg_stride = 1, 120aac94001SLuiz Angelo Daros de Luca /* PHY regs are at 0x8000 */ 121aac94001SLuiz Angelo Daros de Luca .max_register = 0xffff, 122aac94001SLuiz Angelo Daros de Luca .reg_format_endian = REGMAP_ENDIAN_BIG, 123aac94001SLuiz Angelo Daros de Luca .reg_read = realtek_mdio_read, 124aac94001SLuiz Angelo Daros de Luca .reg_write = realtek_mdio_write, 125aac94001SLuiz Angelo Daros de Luca .cache_type = REGCACHE_NONE, 126907e772fSAlvin Šipraga .lock = realtek_mdio_lock, 127907e772fSAlvin Šipraga .unlock = realtek_mdio_unlock, 128907e772fSAlvin Šipraga }; 129907e772fSAlvin Šipraga 130907e772fSAlvin Šipraga static const struct regmap_config realtek_mdio_nolock_regmap_config = { 131907e772fSAlvin Šipraga .reg_bits = 10, /* A4..A0 R4..R0 */ 132907e772fSAlvin Šipraga .val_bits = 16, 133907e772fSAlvin Šipraga .reg_stride = 1, 134907e772fSAlvin Šipraga /* PHY regs are at 0x8000 */ 135907e772fSAlvin Šipraga .max_register = 0xffff, 136907e772fSAlvin Šipraga .reg_format_endian = REGMAP_ENDIAN_BIG, 137907e772fSAlvin Šipraga .reg_read = realtek_mdio_read, 138907e772fSAlvin Šipraga .reg_write = realtek_mdio_write, 139907e772fSAlvin Šipraga .cache_type = REGCACHE_NONE, 140907e772fSAlvin Šipraga .disable_locking = true, 141aac94001SLuiz Angelo Daros de Luca }; 142aac94001SLuiz Angelo Daros de Luca 143aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_probe(struct mdio_device *mdiodev) 144aac94001SLuiz Angelo Daros de Luca { 145aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv; 146aac94001SLuiz Angelo Daros de Luca struct device *dev = &mdiodev->dev; 147aac94001SLuiz Angelo Daros de Luca const struct realtek_variant *var; 148907e772fSAlvin Šipraga struct regmap_config rc; 149aac94001SLuiz Angelo Daros de Luca struct device_node *np; 150907e772fSAlvin Šipraga int ret; 151aac94001SLuiz Angelo Daros de Luca 152aac94001SLuiz Angelo Daros de Luca var = of_device_get_match_data(dev); 153aac94001SLuiz Angelo Daros de Luca if (!var) 154aac94001SLuiz Angelo Daros de Luca return -EINVAL; 155aac94001SLuiz Angelo Daros de Luca 156b93eb564SAhmad Fatoum priv = devm_kzalloc(&mdiodev->dev, 157b93eb564SAhmad Fatoum size_add(sizeof(*priv), var->chip_data_sz), 158b93eb564SAhmad Fatoum GFP_KERNEL); 159aac94001SLuiz Angelo Daros de Luca if (!priv) 160aac94001SLuiz Angelo Daros de Luca return -ENOMEM; 161aac94001SLuiz Angelo Daros de Luca 162907e772fSAlvin Šipraga mutex_init(&priv->map_lock); 163907e772fSAlvin Šipraga 164907e772fSAlvin Šipraga rc = realtek_mdio_regmap_config; 165907e772fSAlvin Šipraga rc.lock_arg = priv; 166907e772fSAlvin Šipraga priv->map = devm_regmap_init(dev, NULL, priv, &rc); 167aac94001SLuiz Angelo Daros de Luca if (IS_ERR(priv->map)) { 168aac94001SLuiz Angelo Daros de Luca ret = PTR_ERR(priv->map); 169aac94001SLuiz Angelo Daros de Luca dev_err(dev, "regmap init failed: %d\n", ret); 170aac94001SLuiz Angelo Daros de Luca return ret; 171aac94001SLuiz Angelo Daros de Luca } 172aac94001SLuiz Angelo Daros de Luca 173907e772fSAlvin Šipraga rc = realtek_mdio_nolock_regmap_config; 174907e772fSAlvin Šipraga priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); 175907e772fSAlvin Šipraga if (IS_ERR(priv->map_nolock)) { 176907e772fSAlvin Šipraga ret = PTR_ERR(priv->map_nolock); 177907e772fSAlvin Šipraga dev_err(dev, "regmap init failed: %d\n", ret); 178907e772fSAlvin Šipraga return ret; 179907e772fSAlvin Šipraga } 180907e772fSAlvin Šipraga 181aac94001SLuiz Angelo Daros de Luca priv->mdio_addr = mdiodev->addr; 182aac94001SLuiz Angelo Daros de Luca priv->bus = mdiodev->bus; 183aac94001SLuiz Angelo Daros de Luca priv->dev = &mdiodev->dev; 184aac94001SLuiz Angelo Daros de Luca priv->chip_data = (void *)priv + sizeof(*priv); 185aac94001SLuiz Angelo Daros de Luca 186aac94001SLuiz Angelo Daros de Luca priv->clk_delay = var->clk_delay; 187aac94001SLuiz Angelo Daros de Luca priv->cmd_read = var->cmd_read; 188aac94001SLuiz Angelo Daros de Luca priv->cmd_write = var->cmd_write; 189aac94001SLuiz Angelo Daros de Luca priv->ops = var->ops; 190aac94001SLuiz Angelo Daros de Luca 191aac94001SLuiz Angelo Daros de Luca priv->write_reg_noack = realtek_mdio_write; 192aac94001SLuiz Angelo Daros de Luca 193aac94001SLuiz Angelo Daros de Luca np = dev->of_node; 194aac94001SLuiz Angelo Daros de Luca 195aac94001SLuiz Angelo Daros de Luca dev_set_drvdata(dev, priv); 196aac94001SLuiz Angelo Daros de Luca 197aac94001SLuiz Angelo Daros de Luca /* TODO: if power is software controlled, set up any regulators here */ 198aac94001SLuiz Angelo Daros de Luca priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); 199aac94001SLuiz Angelo Daros de Luca 20005f7b042SLuiz Angelo Daros de Luca priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 20105f7b042SLuiz Angelo Daros de Luca if (IS_ERR(priv->reset)) { 20205f7b042SLuiz Angelo Daros de Luca dev_err(dev, "failed to get RESET GPIO\n"); 20305f7b042SLuiz Angelo Daros de Luca return PTR_ERR(priv->reset); 20405f7b042SLuiz Angelo Daros de Luca } 20505f7b042SLuiz Angelo Daros de Luca 20605f7b042SLuiz Angelo Daros de Luca if (priv->reset) { 20705f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 1); 20805f7b042SLuiz Angelo Daros de Luca dev_dbg(dev, "asserted RESET\n"); 20905f7b042SLuiz Angelo Daros de Luca msleep(REALTEK_HW_STOP_DELAY); 21005f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 0); 21105f7b042SLuiz Angelo Daros de Luca msleep(REALTEK_HW_START_DELAY); 21205f7b042SLuiz Angelo Daros de Luca dev_dbg(dev, "deasserted RESET\n"); 21305f7b042SLuiz Angelo Daros de Luca } 21405f7b042SLuiz Angelo Daros de Luca 215aac94001SLuiz Angelo Daros de Luca ret = priv->ops->detect(priv); 216aac94001SLuiz Angelo Daros de Luca if (ret) { 217aac94001SLuiz Angelo Daros de Luca dev_err(dev, "unable to detect switch\n"); 218aac94001SLuiz Angelo Daros de Luca return ret; 219aac94001SLuiz Angelo Daros de Luca } 220aac94001SLuiz Angelo Daros de Luca 221aac94001SLuiz Angelo Daros de Luca priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); 222aac94001SLuiz Angelo Daros de Luca if (!priv->ds) 223aac94001SLuiz Angelo Daros de Luca return -ENOMEM; 224aac94001SLuiz Angelo Daros de Luca 225aac94001SLuiz Angelo Daros de Luca priv->ds->dev = dev; 226aac94001SLuiz Angelo Daros de Luca priv->ds->num_ports = priv->num_ports; 227aac94001SLuiz Angelo Daros de Luca priv->ds->priv = priv; 228aac94001SLuiz Angelo Daros de Luca priv->ds->ops = var->ds_ops_mdio; 229aac94001SLuiz Angelo Daros de Luca 230aac94001SLuiz Angelo Daros de Luca ret = dsa_register_switch(priv->ds); 231aac94001SLuiz Angelo Daros de Luca if (ret) { 232aac94001SLuiz Angelo Daros de Luca dev_err(priv->dev, "unable to register switch ret = %d\n", ret); 233aac94001SLuiz Angelo Daros de Luca return ret; 234aac94001SLuiz Angelo Daros de Luca } 235aac94001SLuiz Angelo Daros de Luca 236aac94001SLuiz Angelo Daros de Luca return 0; 237aac94001SLuiz Angelo Daros de Luca } 238aac94001SLuiz Angelo Daros de Luca 239aac94001SLuiz Angelo Daros de Luca static void realtek_mdio_remove(struct mdio_device *mdiodev) 240aac94001SLuiz Angelo Daros de Luca { 241aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 242aac94001SLuiz Angelo Daros de Luca 243aac94001SLuiz Angelo Daros de Luca if (!priv) 244aac94001SLuiz Angelo Daros de Luca return; 245aac94001SLuiz Angelo Daros de Luca 246aac94001SLuiz Angelo Daros de Luca dsa_unregister_switch(priv->ds); 247aac94001SLuiz Angelo Daros de Luca 24805f7b042SLuiz Angelo Daros de Luca /* leave the device reset asserted */ 24905f7b042SLuiz Angelo Daros de Luca if (priv->reset) 25005f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 1); 251aac94001SLuiz Angelo Daros de Luca } 252aac94001SLuiz Angelo Daros de Luca 253aac94001SLuiz Angelo Daros de Luca static void realtek_mdio_shutdown(struct mdio_device *mdiodev) 254aac94001SLuiz Angelo Daros de Luca { 255aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 256aac94001SLuiz Angelo Daros de Luca 257aac94001SLuiz Angelo Daros de Luca if (!priv) 258aac94001SLuiz Angelo Daros de Luca return; 259aac94001SLuiz Angelo Daros de Luca 260aac94001SLuiz Angelo Daros de Luca dsa_switch_shutdown(priv->ds); 261aac94001SLuiz Angelo Daros de Luca 262aac94001SLuiz Angelo Daros de Luca dev_set_drvdata(&mdiodev->dev, NULL); 263aac94001SLuiz Angelo Daros de Luca } 264aac94001SLuiz Angelo Daros de Luca 265aac94001SLuiz Angelo Daros de Luca static const struct of_device_id realtek_mdio_of_match[] = { 266aac94001SLuiz Angelo Daros de Luca #if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) 267aac94001SLuiz Angelo Daros de Luca { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, 268aac94001SLuiz Angelo Daros de Luca #endif 269aac94001SLuiz Angelo Daros de Luca #if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) 270aac94001SLuiz Angelo Daros de Luca { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, 271aac94001SLuiz Angelo Daros de Luca #endif 272aac94001SLuiz Angelo Daros de Luca { /* sentinel */ }, 273aac94001SLuiz Angelo Daros de Luca }; 274aac94001SLuiz Angelo Daros de Luca MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); 275aac94001SLuiz Angelo Daros de Luca 276aac94001SLuiz Angelo Daros de Luca static struct mdio_driver realtek_mdio_driver = { 277aac94001SLuiz Angelo Daros de Luca .mdiodrv.driver = { 278aac94001SLuiz Angelo Daros de Luca .name = "realtek-mdio", 279aae249dfSRuan Jinjie .of_match_table = realtek_mdio_of_match, 280aac94001SLuiz Angelo Daros de Luca }, 281aac94001SLuiz Angelo Daros de Luca .probe = realtek_mdio_probe, 282aac94001SLuiz Angelo Daros de Luca .remove = realtek_mdio_remove, 283aac94001SLuiz Angelo Daros de Luca .shutdown = realtek_mdio_shutdown, 284aac94001SLuiz Angelo Daros de Luca }; 285aac94001SLuiz Angelo Daros de Luca 286aac94001SLuiz Angelo Daros de Luca mdio_module_driver(realtek_mdio_driver); 287aac94001SLuiz Angelo Daros de Luca 288aac94001SLuiz Angelo Daros de Luca MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); 289aac94001SLuiz Angelo Daros de Luca MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); 290aac94001SLuiz Angelo Daros de Luca MODULE_LICENSE("GPL"); 291*ded3813bSLuiz Angelo Daros de Luca MODULE_IMPORT_NS(REALTEK_DSA); 292