xref: /linux/drivers/net/dsa/realtek/realtek-mdio.c (revision 8be040ecd94c1a9a137927d18534edfae0a9b68a)
1aac94001SLuiz Angelo Daros de Luca // SPDX-License-Identifier: GPL-2.0+
2aac94001SLuiz Angelo Daros de Luca /* Realtek MDIO interface driver
3aac94001SLuiz Angelo Daros de Luca  *
4aac94001SLuiz Angelo Daros de Luca  * ASICs we intend to support with this driver:
5aac94001SLuiz Angelo Daros de Luca  *
6aac94001SLuiz Angelo Daros de Luca  * RTL8366   - The original version, apparently
7aac94001SLuiz Angelo Daros de Luca  * RTL8369   - Similar enough to have the same datsheet as RTL8366
8aac94001SLuiz Angelo Daros de Luca  * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite
9aac94001SLuiz Angelo Daros de Luca  *             different register layout from the other two
10aac94001SLuiz Angelo Daros de Luca  * RTL8366S  - Is this "RTL8366 super"?
11aac94001SLuiz Angelo Daros de Luca  * RTL8367   - Has an OpenWRT driver as well
12aac94001SLuiz Angelo Daros de Luca  * RTL8368S  - Seems to be an alternative name for RTL8366RB
13aac94001SLuiz Angelo Daros de Luca  * RTL8370   - Also uses SMI
14aac94001SLuiz Angelo Daros de Luca  *
15aac94001SLuiz Angelo Daros de Luca  * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
16aac94001SLuiz Angelo Daros de Luca  * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
17aac94001SLuiz Angelo Daros de Luca  * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
18aac94001SLuiz Angelo Daros de Luca  * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
19aac94001SLuiz Angelo Daros de Luca  * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
20aac94001SLuiz Angelo Daros de Luca  */
21aac94001SLuiz Angelo Daros de Luca 
22aac94001SLuiz Angelo Daros de Luca #include <linux/module.h>
23f44a9010SRob Herring #include <linux/of.h>
24b93eb564SAhmad Fatoum #include <linux/overflow.h>
25aac94001SLuiz Angelo Daros de Luca #include <linux/regmap.h>
26aac94001SLuiz Angelo Daros de Luca 
27aac94001SLuiz Angelo Daros de Luca #include "realtek.h"
28bce254b8SLuiz Angelo Daros de Luca #include "realtek-mdio.h"
29*8be040ecSLuiz Angelo Daros de Luca #include "rtl83xx.h"
30aac94001SLuiz Angelo Daros de Luca 
31aac94001SLuiz Angelo Daros de Luca /* Read/write via mdiobus */
32aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL0_REG		31
33aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_REG		29
34aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_CTRL1_REG		21
35aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDRESS_REG	23
36aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_WRITE_REG	24
37aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_DATA_READ_REG	25
38aac94001SLuiz Angelo Daros de Luca 
39aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_START_OP		0xFFFF
40aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_ADDR_OP		0x000E
41aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_READ_OP		0x0001
42aac94001SLuiz Angelo Daros de Luca #define REALTEK_MDIO_WRITE_OP		0x0003
43aac94001SLuiz Angelo Daros de Luca 
44aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_write(void *ctx, u32 reg, u32 val)
45aac94001SLuiz Angelo Daros de Luca {
46aac94001SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ctx;
47aac94001SLuiz Angelo Daros de Luca 	struct mii_bus *bus = priv->bus;
48aac94001SLuiz Angelo Daros de Luca 	int ret;
49aac94001SLuiz Angelo Daros de Luca 
50aac94001SLuiz Angelo Daros de Luca 	mutex_lock(&bus->mdio_lock);
51aac94001SLuiz Angelo Daros de Luca 
52aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP);
53aac94001SLuiz Angelo Daros de Luca 	if (ret)
54aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
55aac94001SLuiz Angelo Daros de Luca 
56aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg);
57aac94001SLuiz Angelo Daros de Luca 	if (ret)
58aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
59aac94001SLuiz Angelo Daros de Luca 
60aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, val);
61aac94001SLuiz Angelo Daros de Luca 	if (ret)
62aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
63aac94001SLuiz Angelo Daros de Luca 
64aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_WRITE_OP);
65aac94001SLuiz Angelo Daros de Luca 
66aac94001SLuiz Angelo Daros de Luca out_unlock:
67aac94001SLuiz Angelo Daros de Luca 	mutex_unlock(&bus->mdio_lock);
68aac94001SLuiz Angelo Daros de Luca 
69aac94001SLuiz Angelo Daros de Luca 	return ret;
70aac94001SLuiz Angelo Daros de Luca }
71aac94001SLuiz Angelo Daros de Luca 
72aac94001SLuiz Angelo Daros de Luca static int realtek_mdio_read(void *ctx, u32 reg, u32 *val)
73aac94001SLuiz Angelo Daros de Luca {
74aac94001SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ctx;
75aac94001SLuiz Angelo Daros de Luca 	struct mii_bus *bus = priv->bus;
76aac94001SLuiz Angelo Daros de Luca 	int ret;
77aac94001SLuiz Angelo Daros de Luca 
78aac94001SLuiz Angelo Daros de Luca 	mutex_lock(&bus->mdio_lock);
79aac94001SLuiz Angelo Daros de Luca 
80aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, REALTEK_MDIO_ADDR_OP);
81aac94001SLuiz Angelo Daros de Luca 	if (ret)
82aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
83aac94001SLuiz Angelo Daros de Luca 
84aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, reg);
85aac94001SLuiz Angelo Daros de Luca 	if (ret)
86aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
87aac94001SLuiz Angelo Daros de Luca 
88aac94001SLuiz Angelo Daros de Luca 	ret = bus->write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, REALTEK_MDIO_READ_OP);
89aac94001SLuiz Angelo Daros de Luca 	if (ret)
90aac94001SLuiz Angelo Daros de Luca 		goto out_unlock;
91aac94001SLuiz Angelo Daros de Luca 
92aac94001SLuiz Angelo Daros de Luca 	ret = bus->read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG);
93aac94001SLuiz Angelo Daros de Luca 	if (ret >= 0) {
94aac94001SLuiz Angelo Daros de Luca 		*val = ret;
95aac94001SLuiz Angelo Daros de Luca 		ret = 0;
96aac94001SLuiz Angelo Daros de Luca 	}
97aac94001SLuiz Angelo Daros de Luca 
98aac94001SLuiz Angelo Daros de Luca out_unlock:
99aac94001SLuiz Angelo Daros de Luca 	mutex_unlock(&bus->mdio_lock);
100aac94001SLuiz Angelo Daros de Luca 
101aac94001SLuiz Angelo Daros de Luca 	return ret;
102aac94001SLuiz Angelo Daros de Luca }
103aac94001SLuiz Angelo Daros de Luca 
104*8be040ecSLuiz Angelo Daros de Luca static const struct realtek_interface_info realtek_mdio_info = {
105aac94001SLuiz Angelo Daros de Luca 	.reg_read = realtek_mdio_read,
106aac94001SLuiz Angelo Daros de Luca 	.reg_write = realtek_mdio_write,
107aac94001SLuiz Angelo Daros de Luca };
108aac94001SLuiz Angelo Daros de Luca 
109bce254b8SLuiz Angelo Daros de Luca /**
110bce254b8SLuiz Angelo Daros de Luca  * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch
111bce254b8SLuiz Angelo Daros de Luca  * @mdiodev: mdio_device to probe on.
112bce254b8SLuiz Angelo Daros de Luca  *
113*8be040ecSLuiz Angelo Daros de Luca  * This function should be used as the .probe in an mdio_driver. After
114*8be040ecSLuiz Angelo Daros de Luca  * calling the common probe function for both interfaces, it initializes the
115*8be040ecSLuiz Angelo Daros de Luca  * values specific for MDIO-connected devices. Finally, it calls a common
116*8be040ecSLuiz Angelo Daros de Luca  * function to register the DSA switch.
117bce254b8SLuiz Angelo Daros de Luca  *
118bce254b8SLuiz Angelo Daros de Luca  * Context: Can sleep. Takes and releases priv->map_lock.
119bce254b8SLuiz Angelo Daros de Luca  * Return: Returns 0 on success, a negative error on failure.
120bce254b8SLuiz Angelo Daros de Luca  */
121bce254b8SLuiz Angelo Daros de Luca int realtek_mdio_probe(struct mdio_device *mdiodev)
122aac94001SLuiz Angelo Daros de Luca {
123aac94001SLuiz Angelo Daros de Luca 	struct device *dev = &mdiodev->dev;
124*8be040ecSLuiz Angelo Daros de Luca 	struct realtek_priv *priv;
125907e772fSAlvin Šipraga 	int ret;
126aac94001SLuiz Angelo Daros de Luca 
127*8be040ecSLuiz Angelo Daros de Luca 	priv = rtl83xx_probe(dev, &realtek_mdio_info);
128*8be040ecSLuiz Angelo Daros de Luca 	if (IS_ERR(priv))
129*8be040ecSLuiz Angelo Daros de Luca 		return PTR_ERR(priv);
130aac94001SLuiz Angelo Daros de Luca 
131aac94001SLuiz Angelo Daros de Luca 	priv->bus = mdiodev->bus;
132*8be040ecSLuiz Angelo Daros de Luca 	priv->mdio_addr = mdiodev->addr;
133aac94001SLuiz Angelo Daros de Luca 	priv->write_reg_noack = realtek_mdio_write;
134*8be040ecSLuiz Angelo Daros de Luca 	priv->ds_ops = priv->variant->ds_ops_mdio;
135aac94001SLuiz Angelo Daros de Luca 
136*8be040ecSLuiz Angelo Daros de Luca 	ret = rtl83xx_register_switch(priv);
137aac94001SLuiz Angelo Daros de Luca 	if (ret) {
138*8be040ecSLuiz Angelo Daros de Luca 		rtl83xx_remove(priv);
139aac94001SLuiz Angelo Daros de Luca 		return ret;
140aac94001SLuiz Angelo Daros de Luca 	}
141aac94001SLuiz Angelo Daros de Luca 
142aac94001SLuiz Angelo Daros de Luca 	return 0;
143aac94001SLuiz Angelo Daros de Luca }
144bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);
145aac94001SLuiz Angelo Daros de Luca 
146bce254b8SLuiz Angelo Daros de Luca /**
147bce254b8SLuiz Angelo Daros de Luca  * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch
148bce254b8SLuiz Angelo Daros de Luca  * @mdiodev: mdio_device to be removed.
149bce254b8SLuiz Angelo Daros de Luca  *
150bce254b8SLuiz Angelo Daros de Luca  * This function should be used as the .remove_new in an mdio_driver. First
151*8be040ecSLuiz Angelo Daros de Luca  * it unregisters the DSA switch and then it calls the common remove function.
152bce254b8SLuiz Angelo Daros de Luca  *
153bce254b8SLuiz Angelo Daros de Luca  * Context: Can sleep.
154bce254b8SLuiz Angelo Daros de Luca  * Return: Nothing.
155bce254b8SLuiz Angelo Daros de Luca  */
156bce254b8SLuiz Angelo Daros de Luca void realtek_mdio_remove(struct mdio_device *mdiodev)
157aac94001SLuiz Angelo Daros de Luca {
158aac94001SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
159aac94001SLuiz Angelo Daros de Luca 
160aac94001SLuiz Angelo Daros de Luca 	if (!priv)
161aac94001SLuiz Angelo Daros de Luca 		return;
162aac94001SLuiz Angelo Daros de Luca 
163*8be040ecSLuiz Angelo Daros de Luca 	rtl83xx_unregister_switch(priv);
164aac94001SLuiz Angelo Daros de Luca 
165*8be040ecSLuiz Angelo Daros de Luca 	rtl83xx_remove(priv);
166aac94001SLuiz Angelo Daros de Luca }
167bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
168aac94001SLuiz Angelo Daros de Luca 
169bce254b8SLuiz Angelo Daros de Luca /**
170bce254b8SLuiz Angelo Daros de Luca  * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
171bce254b8SLuiz Angelo Daros de Luca  * @mdiodev: mdio_device shutting down.
172bce254b8SLuiz Angelo Daros de Luca  *
173*8be040ecSLuiz Angelo Daros de Luca  * This function should be used as the .shutdown in a platform_driver. It calls
174*8be040ecSLuiz Angelo Daros de Luca  * the common shutdown function.
175bce254b8SLuiz Angelo Daros de Luca  *
176bce254b8SLuiz Angelo Daros de Luca  * Context: Can sleep.
177bce254b8SLuiz Angelo Daros de Luca  * Return: Nothing.
178bce254b8SLuiz Angelo Daros de Luca  */
179bce254b8SLuiz Angelo Daros de Luca void realtek_mdio_shutdown(struct mdio_device *mdiodev)
180aac94001SLuiz Angelo Daros de Luca {
181aac94001SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
182aac94001SLuiz Angelo Daros de Luca 
183aac94001SLuiz Angelo Daros de Luca 	if (!priv)
184aac94001SLuiz Angelo Daros de Luca 		return;
185aac94001SLuiz Angelo Daros de Luca 
186*8be040ecSLuiz Angelo Daros de Luca 	rtl83xx_shutdown(priv);
187aac94001SLuiz Angelo Daros de Luca }
188bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
189aac94001SLuiz Angelo Daros de Luca 
190aac94001SLuiz Angelo Daros de Luca MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
191aac94001SLuiz Angelo Daros de Luca MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface");
192aac94001SLuiz Angelo Daros de Luca MODULE_LICENSE("GPL");
193ded3813bSLuiz Angelo Daros de Luca MODULE_IMPORT_NS(REALTEK_DSA);
194