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(lock: &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(lock: &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, interface_info: &realtek_mdio_info);
128 if (IS_ERR(ptr: priv))
129 return PTR_ERR(ptr: 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_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 */
155void realtek_mdio_remove(struct mdio_device *mdiodev)
156{
157 struct realtek_priv *priv = dev_get_drvdata(dev: &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(dev: &mdiodev->dev);
181
182 if (!priv)
183 return;
184
185 rtl83xx_shutdown(priv);
186}
187EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
188

source code of linux/drivers/net/dsa/realtek/realtek-mdio.c