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> 23aac94001SLuiz Angelo Daros de Luca #include <linux/of_device.h> 24aac94001SLuiz Angelo Daros de Luca #include <linux/regmap.h> 25aac94001SLuiz Angelo Daros de Luca 26aac94001SLuiz Angelo Daros de Luca #include "realtek.h" 27aac94001SLuiz Angelo Daros de Luca 28aac94001SLuiz Angelo Daros de Luca /* Read/write via mdiobus */ 29aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL0_REG 31 30aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_REG 29 31aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL1_REG 21 32aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDRESS_REG 23 33aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_WRITE_REG 24 34aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_READ_REG 25 35aac94001SLuiz Angelo Daros de Luca 36aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_OP 0xFFFF 37aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDR_OP 0x000E 38aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_READ_OP 0x0001 39aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_WRITE_OP 0x0003 40aac94001SLuiz Angelo Daros de Luca 41aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_write(void *ctx, u32 reg, u32 val) 42aac94001SLuiz Angelo Daros de Luca { 43aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = ctx; 44aac94001SLuiz Angelo Daros de Luca struct mii_bus *bus = priv->bus; 45aac94001SLuiz Angelo Daros de Luca int ret; 46aac94001SLuiz Angelo Daros de Luca 47aac94001SLuiz Angelo Daros de Luca mutex_lock(&bus->mdio_lock); 48aac94001SLuiz Angelo Daros de Luca 49aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 50aac94001SLuiz Angelo Daros de Luca if (ret) 51aac94001SLuiz Angelo Daros de Luca goto out_unlock; 52aac94001SLuiz Angelo Daros de Luca 53aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 54aac94001SLuiz Angelo Daros de Luca if (ret) 55aac94001SLuiz Angelo Daros de Luca goto out_unlock; 56aac94001SLuiz Angelo Daros de Luca 57aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val); 58aac94001SLuiz Angelo Daros de Luca if (ret) 59aac94001SLuiz Angelo Daros de Luca goto out_unlock; 60aac94001SLuiz Angelo Daros de Luca 61aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP); 62aac94001SLuiz Angelo Daros de Luca 63aac94001SLuiz Angelo Daros de Luca out_unlock: 64aac94001SLuiz Angelo Daros de Luca mutex_unlock(&bus->mdio_lock); 65aac94001SLuiz Angelo Daros de Luca 66aac94001SLuiz Angelo Daros de Luca return ret; 67aac94001SLuiz Angelo Daros de Luca } 68aac94001SLuiz Angelo Daros de Luca 69aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) 70aac94001SLuiz Angelo Daros de Luca { 71aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = ctx; 72aac94001SLuiz Angelo Daros de Luca struct mii_bus *bus = priv->bus; 73aac94001SLuiz Angelo Daros de Luca int ret; 74aac94001SLuiz Angelo Daros de Luca 75aac94001SLuiz Angelo Daros de Luca mutex_lock(&bus->mdio_lock); 76aac94001SLuiz Angelo Daros de Luca 77aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP); 78aac94001SLuiz Angelo Daros de Luca if (ret) 79aac94001SLuiz Angelo Daros de Luca goto out_unlock; 80aac94001SLuiz Angelo Daros de Luca 81aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg); 82aac94001SLuiz Angelo Daros de Luca if (ret) 83aac94001SLuiz Angelo Daros de Luca goto out_unlock; 84aac94001SLuiz Angelo Daros de Luca 85aac94001SLuiz Angelo Daros de Luca ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP); 86aac94001SLuiz Angelo Daros de Luca if (ret) 87aac94001SLuiz Angelo Daros de Luca goto out_unlock; 88aac94001SLuiz Angelo Daros de Luca 89aac94001SLuiz Angelo Daros de Luca ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); 90aac94001SLuiz Angelo Daros de Luca if (ret >= 0) { 91aac94001SLuiz Angelo Daros de Luca *val = ret; 92aac94001SLuiz Angelo Daros de Luca ret = 0; 93aac94001SLuiz Angelo Daros de Luca } 94aac94001SLuiz Angelo Daros de Luca 95aac94001SLuiz Angelo Daros de Luca out_unlock: 96aac94001SLuiz Angelo Daros de Luca mutex_unlock(&bus->mdio_lock); 97aac94001SLuiz Angelo Daros de Luca 98aac94001SLuiz Angelo Daros de Luca return ret; 99aac94001SLuiz Angelo Daros de Luca } 100aac94001SLuiz Angelo Daros de Luca 101aac94001SLuiz Angelo Daros de Luca static const struct regmap_config realtek_mdio_regmap_config = { 102aac94001SLuiz Angelo Daros de Luca .reg_bits = 10, /* A4..A0 R4..R0 */ 103aac94001SLuiz Angelo Daros de Luca .val_bits = 16, 104aac94001SLuiz Angelo Daros de Luca .reg_stride = 1, 105aac94001SLuiz Angelo Daros de Luca /* PHY regs are at 0x8000 */ 106aac94001SLuiz Angelo Daros de Luca .max_register = 0xffff, 107aac94001SLuiz Angelo Daros de Luca .reg_format_endian = REGMAP_ENDIAN_BIG, 108aac94001SLuiz Angelo Daros de Luca .reg_read = realtek_mdio_read, 109aac94001SLuiz Angelo Daros de Luca .reg_write = realtek_mdio_write, 110aac94001SLuiz Angelo Daros de Luca .cache_type = REGCACHE_NONE, 111aac94001SLuiz Angelo Daros de Luca }; 112aac94001SLuiz Angelo Daros de Luca 113aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_probe(struct mdio_device *mdiodev) 114aac94001SLuiz Angelo Daros de Luca { 115aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv; 116aac94001SLuiz Angelo Daros de Luca struct device *dev = &mdiodev->dev; 117aac94001SLuiz Angelo Daros de Luca const struct realtek_variant *var; 118aac94001SLuiz Angelo Daros de Luca int ret; 119aac94001SLuiz Angelo Daros de Luca struct device_node *np; 120aac94001SLuiz Angelo Daros de Luca 121aac94001SLuiz Angelo Daros de Luca var = of_device_get_match_data(dev); 122aac94001SLuiz Angelo Daros de Luca if (!var) 123aac94001SLuiz Angelo Daros de Luca return -EINVAL; 124aac94001SLuiz Angelo Daros de Luca 125aac94001SLuiz Angelo Daros de Luca priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); 126aac94001SLuiz Angelo Daros de Luca if (!priv) 127aac94001SLuiz Angelo Daros de Luca return -ENOMEM; 128aac94001SLuiz Angelo Daros de Luca 129aac94001SLuiz Angelo Daros de Luca priv->map = devm_regmap_init(dev, NULL, priv, &realtek_mdio_regmap_config); 130aac94001SLuiz Angelo Daros de Luca if (IS_ERR(priv->map)) { 131aac94001SLuiz Angelo Daros de Luca ret = PTR_ERR(priv->map); 132aac94001SLuiz Angelo Daros de Luca dev_err(dev, "regmap init failed: %d\n", ret); 133aac94001SLuiz Angelo Daros de Luca return ret; 134aac94001SLuiz Angelo Daros de Luca } 135aac94001SLuiz Angelo Daros de Luca 136aac94001SLuiz Angelo Daros de Luca priv->mdio_addr = mdiodev->addr; 137aac94001SLuiz Angelo Daros de Luca priv->bus = mdiodev->bus; 138aac94001SLuiz Angelo Daros de Luca priv->dev = &mdiodev->dev; 139aac94001SLuiz Angelo Daros de Luca priv->chip_data = (void *)priv + sizeof(*priv); 140aac94001SLuiz Angelo Daros de Luca 141aac94001SLuiz Angelo Daros de Luca priv->clk_delay = var->clk_delay; 142aac94001SLuiz Angelo Daros de Luca priv->cmd_read = var->cmd_read; 143aac94001SLuiz Angelo Daros de Luca priv->cmd_write = var->cmd_write; 144aac94001SLuiz Angelo Daros de Luca priv->ops = var->ops; 145aac94001SLuiz Angelo Daros de Luca 146aac94001SLuiz Angelo Daros de Luca priv->write_reg_noack = realtek_mdio_write; 147aac94001SLuiz Angelo Daros de Luca 148aac94001SLuiz Angelo Daros de Luca np = dev->of_node; 149aac94001SLuiz Angelo Daros de Luca 150aac94001SLuiz Angelo Daros de Luca dev_set_drvdata(dev, priv); 151aac94001SLuiz Angelo Daros de Luca 152aac94001SLuiz Angelo Daros de Luca /* TODO: if power is software controlled, set up any regulators here */ 153aac94001SLuiz Angelo Daros de Luca priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); 154aac94001SLuiz Angelo Daros de Luca 155*05f7b042SLuiz Angelo Daros de Luca priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 156*05f7b042SLuiz Angelo Daros de Luca if (IS_ERR(priv->reset)) { 157*05f7b042SLuiz Angelo Daros de Luca dev_err(dev, "failed to get RESET GPIO\n"); 158*05f7b042SLuiz Angelo Daros de Luca return PTR_ERR(priv->reset); 159*05f7b042SLuiz Angelo Daros de Luca } 160*05f7b042SLuiz Angelo Daros de Luca 161*05f7b042SLuiz Angelo Daros de Luca if (priv->reset) { 162*05f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 1); 163*05f7b042SLuiz Angelo Daros de Luca dev_dbg(dev, "asserted RESET\n"); 164*05f7b042SLuiz Angelo Daros de Luca msleep(REALTEK_HW_STOP_DELAY); 165*05f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 0); 166*05f7b042SLuiz Angelo Daros de Luca msleep(REALTEK_HW_START_DELAY); 167*05f7b042SLuiz Angelo Daros de Luca dev_dbg(dev, "deasserted RESET\n"); 168*05f7b042SLuiz Angelo Daros de Luca } 169*05f7b042SLuiz Angelo Daros de Luca 170aac94001SLuiz Angelo Daros de Luca ret = priv->ops->detect(priv); 171aac94001SLuiz Angelo Daros de Luca if (ret) { 172aac94001SLuiz Angelo Daros de Luca dev_err(dev, "unable to detect switch\n"); 173aac94001SLuiz Angelo Daros de Luca return ret; 174aac94001SLuiz Angelo Daros de Luca } 175aac94001SLuiz Angelo Daros de Luca 176aac94001SLuiz Angelo Daros de Luca priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); 177aac94001SLuiz Angelo Daros de Luca if (!priv->ds) 178aac94001SLuiz Angelo Daros de Luca return -ENOMEM; 179aac94001SLuiz Angelo Daros de Luca 180aac94001SLuiz Angelo Daros de Luca priv->ds->dev = dev; 181aac94001SLuiz Angelo Daros de Luca priv->ds->num_ports = priv->num_ports; 182aac94001SLuiz Angelo Daros de Luca priv->ds->priv = priv; 183aac94001SLuiz Angelo Daros de Luca priv->ds->ops = var->ds_ops_mdio; 184aac94001SLuiz Angelo Daros de Luca 185aac94001SLuiz Angelo Daros de Luca ret = dsa_register_switch(priv->ds); 186aac94001SLuiz Angelo Daros de Luca if (ret) { 187aac94001SLuiz Angelo Daros de Luca dev_err(priv->dev, "unable to register switch ret = %d\n", ret); 188aac94001SLuiz Angelo Daros de Luca return ret; 189aac94001SLuiz Angelo Daros de Luca } 190aac94001SLuiz Angelo Daros de Luca 191aac94001SLuiz Angelo Daros de Luca return 0; 192aac94001SLuiz Angelo Daros de Luca } 193aac94001SLuiz Angelo Daros de Luca 194aac94001SLuiz Angelo Daros de Luca static void realtek_mdio_remove(struct mdio_device *mdiodev) 195aac94001SLuiz Angelo Daros de Luca { 196aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 197aac94001SLuiz Angelo Daros de Luca 198aac94001SLuiz Angelo Daros de Luca if (!priv) 199aac94001SLuiz Angelo Daros de Luca return; 200aac94001SLuiz Angelo Daros de Luca 201aac94001SLuiz Angelo Daros de Luca dsa_unregister_switch(priv->ds); 202aac94001SLuiz Angelo Daros de Luca 203*05f7b042SLuiz Angelo Daros de Luca /* leave the device reset asserted */ 204*05f7b042SLuiz Angelo Daros de Luca if (priv->reset) 205*05f7b042SLuiz Angelo Daros de Luca gpiod_set_value(priv->reset, 1); 206*05f7b042SLuiz Angelo Daros de Luca 207aac94001SLuiz Angelo Daros de Luca dev_set_drvdata(&mdiodev->dev, NULL); 208aac94001SLuiz Angelo Daros de Luca } 209aac94001SLuiz Angelo Daros de Luca 210aac94001SLuiz Angelo Daros de Luca static void realtek_mdio_shutdown(struct mdio_device *mdiodev) 211aac94001SLuiz Angelo Daros de Luca { 212aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); 213aac94001SLuiz Angelo Daros de Luca 214aac94001SLuiz Angelo Daros de Luca if (!priv) 215aac94001SLuiz Angelo Daros de Luca return; 216aac94001SLuiz Angelo Daros de Luca 217aac94001SLuiz Angelo Daros de Luca dsa_switch_shutdown(priv->ds); 218aac94001SLuiz Angelo Daros de Luca 219aac94001SLuiz Angelo Daros de Luca dev_set_drvdata(&mdiodev->dev, NULL); 220aac94001SLuiz Angelo Daros de Luca } 221aac94001SLuiz Angelo Daros de Luca 222aac94001SLuiz Angelo Daros de Luca static const struct of_device_id realtek_mdio_of_match[] = { 223aac94001SLuiz Angelo Daros de Luca #if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) 224aac94001SLuiz Angelo Daros de Luca { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, 225aac94001SLuiz Angelo Daros de Luca #endif 226aac94001SLuiz Angelo Daros de Luca #if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) 227aac94001SLuiz Angelo Daros de Luca { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, 228d40f607cSLuiz Angelo Daros de Luca { .compatible = "realtek,rtl8367s", .data = &rtl8365mb_variant, }, 229aac94001SLuiz Angelo Daros de Luca #endif 230aac94001SLuiz Angelo Daros de Luca { /* sentinel */ }, 231aac94001SLuiz Angelo Daros de Luca }; 232aac94001SLuiz Angelo Daros de Luca MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); 233aac94001SLuiz Angelo Daros de Luca 234aac94001SLuiz Angelo Daros de Luca static struct mdio_driver realtek_mdio_driver = { 235aac94001SLuiz Angelo Daros de Luca .mdiodrv.driver = { 236aac94001SLuiz Angelo Daros de Luca .name = "realtek-mdio", 237aac94001SLuiz Angelo Daros de Luca .of_match_table = of_match_ptr(realtek_mdio_of_match), 238aac94001SLuiz Angelo Daros de Luca }, 239aac94001SLuiz Angelo Daros de Luca .probe = realtek_mdio_probe, 240aac94001SLuiz Angelo Daros de Luca .remove = realtek_mdio_remove, 241aac94001SLuiz Angelo Daros de Luca .shutdown = realtek_mdio_shutdown, 242aac94001SLuiz Angelo Daros de Luca }; 243aac94001SLuiz Angelo Daros de Luca 244aac94001SLuiz Angelo Daros de Luca mdio_module_driver(realtek_mdio_driver); 245aac94001SLuiz Angelo Daros de Luca 246aac94001SLuiz Angelo Daros de Luca MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); 247aac94001SLuiz Angelo Daros de Luca MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); 248aac94001SLuiz Angelo Daros de Luca MODULE_LICENSE("GPL"); 249