xref: /linux/drivers/net/dsa/realtek/realtek-mdio.c (revision 1553a1c48281243359a9529a10ddb551f3b967ab)
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 
44 static 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 
66 out_unlock:
67 	mutex_unlock(&bus->mdio_lock);
68 
69 	return ret;
70 }
71 
72 static 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 
98 out_unlock:
99 	mutex_unlock(&bus->mdio_lock);
100 
101 	return ret;
102 }
103 
104 static 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  */
121 int 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 }
143 EXPORT_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_new 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  */
155 void 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 }
166 EXPORT_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  */
178 void 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 }
187 EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
188