409 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Realtek Simple Management Interface (SMI) driver3 * It can be discussed how "simple" this interface is.4 *5 * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels6 * but the protocol is not MDIO at all. Instead it is a Realtek7 * pecularity that need to bit-bang the lines in a special way to8 * communicate with the switch.9 *10 * ASICs we intend to support with this driver:11 *12 * RTL8366 - The original version, apparently13 * RTL8369 - Similar enough to have the same datsheet as RTL836614 * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite15 * different register layout from the other two16 * RTL8366S - Is this "RTL8366 super"?17 * RTL8367 - Has an OpenWRT driver as well18 * RTL8368S - Seems to be an alternative name for RTL8366RB19 * RTL8370 - Also uses SMI20 *21 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>22 * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>23 * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>24 * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>25 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>26 */27 28#include <linux/kernel.h>29#include <linux/module.h>30#include <linux/device.h>31#include <linux/spinlock.h>32#include <linux/skbuff.h>33#include <linux/of.h>34#include <linux/delay.h>35#include <linux/gpio/consumer.h>36#include <linux/platform_device.h>37#include <linux/regmap.h>38#include <linux/bitops.h>39#include <linux/if_bridge.h>40 41#include "realtek.h"42#include "realtek-smi.h"43#include "rtl83xx.h"44 45#define REALTEK_SMI_ACK_RETRY_COUNT 546 47static inline void realtek_smi_clk_delay(struct realtek_priv *priv)48{49 ndelay(priv->variant->clk_delay);50}51 52static void realtek_smi_start(struct realtek_priv *priv)53{54 /* Set GPIO pins to output mode, with initial state:55 * SCK = 0, SDA = 156 */57 gpiod_direction_output(priv->mdc, 0);58 gpiod_direction_output(priv->mdio, 1);59 realtek_smi_clk_delay(priv);60 61 /* CLK 1: 0 -> 1, 1 -> 0 */62 gpiod_set_value(priv->mdc, 1);63 realtek_smi_clk_delay(priv);64 gpiod_set_value(priv->mdc, 0);65 realtek_smi_clk_delay(priv);66 67 /* CLK 2: */68 gpiod_set_value(priv->mdc, 1);69 realtek_smi_clk_delay(priv);70 gpiod_set_value(priv->mdio, 0);71 realtek_smi_clk_delay(priv);72 gpiod_set_value(priv->mdc, 0);73 realtek_smi_clk_delay(priv);74 gpiod_set_value(priv->mdio, 1);75}76 77static void realtek_smi_stop(struct realtek_priv *priv)78{79 realtek_smi_clk_delay(priv);80 gpiod_set_value(priv->mdio, 0);81 gpiod_set_value(priv->mdc, 1);82 realtek_smi_clk_delay(priv);83 gpiod_set_value(priv->mdio, 1);84 realtek_smi_clk_delay(priv);85 gpiod_set_value(priv->mdc, 1);86 realtek_smi_clk_delay(priv);87 gpiod_set_value(priv->mdc, 0);88 realtek_smi_clk_delay(priv);89 gpiod_set_value(priv->mdc, 1);90 91 /* Add a click */92 realtek_smi_clk_delay(priv);93 gpiod_set_value(priv->mdc, 0);94 realtek_smi_clk_delay(priv);95 gpiod_set_value(priv->mdc, 1);96 97 /* Set GPIO pins to input mode */98 gpiod_direction_input(priv->mdio);99 gpiod_direction_input(priv->mdc);100}101 102static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len)103{104 for (; len > 0; len--) {105 realtek_smi_clk_delay(priv);106 107 /* Prepare data */108 gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1))));109 realtek_smi_clk_delay(priv);110 111 /* Clocking */112 gpiod_set_value(priv->mdc, 1);113 realtek_smi_clk_delay(priv);114 gpiod_set_value(priv->mdc, 0);115 }116}117 118static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data)119{120 gpiod_direction_input(priv->mdio);121 122 for (*data = 0; len > 0; len--) {123 u32 u;124 125 realtek_smi_clk_delay(priv);126 127 /* Clocking */128 gpiod_set_value(priv->mdc, 1);129 realtek_smi_clk_delay(priv);130 u = !!gpiod_get_value(priv->mdio);131 gpiod_set_value(priv->mdc, 0);132 133 *data |= (u << (len - 1));134 }135 136 gpiod_direction_output(priv->mdio, 0);137}138 139static int realtek_smi_wait_for_ack(struct realtek_priv *priv)140{141 int retry_cnt;142 143 retry_cnt = 0;144 do {145 u32 ack;146 147 realtek_smi_read_bits(priv, 1, &ack);148 if (ack == 0)149 break;150 151 if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) {152 dev_err(priv->dev, "ACK timeout\n");153 return -ETIMEDOUT;154 }155 } while (1);156 157 return 0;158}159 160static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data)161{162 realtek_smi_write_bits(priv, data, 8);163 return realtek_smi_wait_for_ack(priv);164}165 166static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data)167{168 realtek_smi_write_bits(priv, data, 8);169 return 0;170}171 172static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data)173{174 u32 t;175 176 /* Read data */177 realtek_smi_read_bits(priv, 8, &t);178 *data = (t & 0xff);179 180 /* Send an ACK */181 realtek_smi_write_bits(priv, 0x00, 1);182 183 return 0;184}185 186static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data)187{188 u32 t;189 190 /* Read data */191 realtek_smi_read_bits(priv, 8, &t);192 *data = (t & 0xff);193 194 /* Send an ACK */195 realtek_smi_write_bits(priv, 0x01, 1);196 197 return 0;198}199 200static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data)201{202 unsigned long flags;203 u8 lo = 0;204 u8 hi = 0;205 int ret;206 207 spin_lock_irqsave(&priv->lock, flags);208 209 realtek_smi_start(priv);210 211 /* Send READ command */212 ret = realtek_smi_write_byte(priv, priv->variant->cmd_read);213 if (ret)214 goto out;215 216 /* Set ADDR[7:0] */217 ret = realtek_smi_write_byte(priv, addr & 0xff);218 if (ret)219 goto out;220 221 /* Set ADDR[15:8] */222 ret = realtek_smi_write_byte(priv, addr >> 8);223 if (ret)224 goto out;225 226 /* Read DATA[7:0] */227 realtek_smi_read_byte0(priv, &lo);228 /* Read DATA[15:8] */229 realtek_smi_read_byte1(priv, &hi);230 231 *data = ((u32)lo) | (((u32)hi) << 8);232 233 ret = 0;234 235 out:236 realtek_smi_stop(priv);237 spin_unlock_irqrestore(&priv->lock, flags);238 239 return ret;240}241 242static int realtek_smi_write_reg(struct realtek_priv *priv,243 u32 addr, u32 data, bool ack)244{245 unsigned long flags;246 int ret;247 248 spin_lock_irqsave(&priv->lock, flags);249 250 realtek_smi_start(priv);251 252 /* Send WRITE command */253 ret = realtek_smi_write_byte(priv, priv->variant->cmd_write);254 if (ret)255 goto out;256 257 /* Set ADDR[7:0] */258 ret = realtek_smi_write_byte(priv, addr & 0xff);259 if (ret)260 goto out;261 262 /* Set ADDR[15:8] */263 ret = realtek_smi_write_byte(priv, addr >> 8);264 if (ret)265 goto out;266 267 /* Write DATA[7:0] */268 ret = realtek_smi_write_byte(priv, data & 0xff);269 if (ret)270 goto out;271 272 /* Write DATA[15:8] */273 if (ack)274 ret = realtek_smi_write_byte(priv, data >> 8);275 else276 ret = realtek_smi_write_byte_noack(priv, data >> 8);277 if (ret)278 goto out;279 280 ret = 0;281 282 out:283 realtek_smi_stop(priv);284 spin_unlock_irqrestore(&priv->lock, flags);285 286 return ret;287}288 289/* There is one single case when we need to use this accessor and that290 * is when issueing soft reset. Since the device reset as soon as we write291 * that bit, no ACK will come back for natural reasons.292 */293static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val)294{295 return realtek_smi_write_reg(ctx, reg, val, false);296}297 298/* Regmap accessors */299 300static int realtek_smi_write(void *ctx, u32 reg, u32 val)301{302 struct realtek_priv *priv = ctx;303 304 return realtek_smi_write_reg(priv, reg, val, true);305}306 307static int realtek_smi_read(void *ctx, u32 reg, u32 *val)308{309 struct realtek_priv *priv = ctx;310 311 return realtek_smi_read_reg(priv, reg, val);312}313 314static const struct realtek_interface_info realtek_smi_info = {315 .reg_read = realtek_smi_read,316 .reg_write = realtek_smi_write,317};318 319/**320 * realtek_smi_probe() - Probe a platform device for an SMI-connected switch321 * @pdev: platform_device to probe on.322 *323 * This function should be used as the .probe in a platform_driver. After324 * calling the common probe function for both interfaces, it initializes the325 * values specific for SMI-connected devices. Finally, it calls a common326 * function to register the DSA switch.327 *328 * Context: Can sleep. Takes and releases priv->map_lock.329 * Return: Returns 0 on success, a negative error on failure.330 */331int realtek_smi_probe(struct platform_device *pdev)332{333 struct device *dev = &pdev->dev;334 struct realtek_priv *priv;335 int ret;336 337 priv = rtl83xx_probe(dev, &realtek_smi_info);338 if (IS_ERR(priv))339 return PTR_ERR(priv);340 341 /* Fetch MDIO pins */342 priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW);343 if (IS_ERR(priv->mdc)) {344 rtl83xx_remove(priv);345 return PTR_ERR(priv->mdc);346 }347 348 priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW);349 if (IS_ERR(priv->mdio)) {350 rtl83xx_remove(priv);351 return PTR_ERR(priv->mdio);352 }353 354 priv->write_reg_noack = realtek_smi_write_reg_noack;355 356 ret = rtl83xx_register_switch(priv);357 if (ret) {358 rtl83xx_remove(priv);359 return ret;360 }361 362 return 0;363}364EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA);365 366/**367 * realtek_smi_remove() - Remove the driver of a SMI-connected switch368 * @pdev: platform_device to be removed.369 *370 * This function should be used as the .remove_new in a platform_driver. First371 * it unregisters the DSA switch and then it calls the common remove function.372 *373 * Context: Can sleep.374 * Return: Nothing.375 */376void realtek_smi_remove(struct platform_device *pdev)377{378 struct realtek_priv *priv = platform_get_drvdata(pdev);379 380 if (!priv)381 return;382 383 rtl83xx_unregister_switch(priv);384 385 rtl83xx_remove(priv);386}387EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA);388 389/**390 * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch391 * @pdev: platform_device shutting down.392 *393 * This function should be used as the .shutdown in a platform_driver. It calls394 * the common shutdown function.395 *396 * Context: Can sleep.397 * Return: Nothing.398 */399void realtek_smi_shutdown(struct platform_device *pdev)400{401 struct realtek_priv *priv = platform_get_drvdata(pdev);402 403 if (!priv)404 return;405 406 rtl83xx_shutdown(priv);407}408EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA);409