Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  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
250static void realtek_mdio_shutdown(struct mdio_device *mdiodev)
251{
252	struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
253
254	if (!priv)
255		return;
256
257	dsa_switch_shutdown(priv->ds);
258
259	dev_set_drvdata(&mdiodev->dev, NULL);
260}
261
262static const struct of_device_id realtek_mdio_of_match[] = {
263#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB)
264	{ .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, },
265#endif
266#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB)
267	{ .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, },
268#endif
269	{ /* sentinel */ },
270};
271MODULE_DEVICE_TABLE(of, realtek_mdio_of_match);
272
273static struct mdio_driver realtek_mdio_driver = {
274	.mdiodrv.driver = {
275		.name = "realtek-mdio",
276		.of_match_table = of_match_ptr(realtek_mdio_of_match),
277	},
278	.probe  = realtek_mdio_probe,
279	.remove = realtek_mdio_remove,
280	.shutdown = realtek_mdio_shutdown,
281};
282
283mdio_module_driver(realtek_mdio_driver);
284
285MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
286MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
287MODULE_LICENSE("GPL");