188 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Realtek MDIO interface driver3 *4 * ASICs we intend to support with this driver:5 *6 * RTL8366 - The original version, apparently7 * RTL8369 - Similar enough to have the same datsheet as RTL83668 * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite9 * different register layout from the other two10 * RTL8366S - Is this "RTL8366 super"?11 * RTL8367 - Has an OpenWRT driver as well12 * RTL8368S - Seems to be an alternative name for RTL8366RB13 * RTL8370 - Also uses SMI14 *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 3133#define REALTEK_MDIO_START_REG 2934#define REALTEK_MDIO_CTRL1_REG 2135#define REALTEK_MDIO_ADDRESS_REG 2336#define REALTEK_MDIO_DATA_WRITE_REG 2437#define REALTEK_MDIO_DATA_READ_REG 2538 39#define REALTEK_MDIO_START_OP 0xFFFF40#define REALTEK_MDIO_ADDR_OP 0x000E41#define REALTEK_MDIO_READ_OP 0x000142#define REALTEK_MDIO_WRITE_OP 0x000343 44static 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 66out_unlock:67 mutex_unlock(&bus->mdio_lock);68 69 return ret;70}71 72static 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 98out_unlock:99 mutex_unlock(&bus->mdio_lock);100 101 return ret;102}103 104static 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 switch111 * @mdiodev: mdio_device to probe on.112 *113 * This function should be used as the .probe in an mdio_driver. After114 * calling the common probe function for both interfaces, it initializes the115 * values specific for MDIO-connected devices. Finally, it calls a common116 * 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 */121int 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}143EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);144 145/**146 * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch147 * @mdiodev: mdio_device to be removed.148 *149 * This function should be used as the .remove_new in an mdio_driver. First150 * it unregisters the DSA switch and then it calls the common remove function.151 *152 * Context: Can sleep.153 * Return: Nothing.154 */155void 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}166EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);167 168/**169 * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch170 * @mdiodev: mdio_device shutting down.171 *172 * This function should be used as the .shutdown in a platform_driver. It calls173 * the common shutdown function.174 *175 * Context: Can sleep.176 * Return: Nothing.177 */178void 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}187EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);188