cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

realtek-mdio.c (7356B)


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