brintos

brintos / linux-shallow public Read only

0
0
Text · 9.4 KiB · 88590ae Raw
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