Linux Audio

Check our new training course

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