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
realtek_mdio_write(void * ctx,u32 reg,u32 val)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
realtek_mdio_read(void * ctx,u32 reg,u32 * val)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 */
realtek_mdio_probe(struct mdio_device * mdiodev)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;
134aac94001SLuiz Angelo Daros de Luca
135*8be040ecSLuiz Angelo Daros de Luca ret = rtl83xx_register_switch(priv);
136aac94001SLuiz Angelo Daros de Luca if (ret) {
137*8be040ecSLuiz Angelo Daros de Luca rtl83xx_remove(priv);
138aac94001SLuiz Angelo Daros de Luca return ret;
139aac94001SLuiz Angelo Daros de Luca }
140aac94001SLuiz Angelo Daros de Luca
141aac94001SLuiz Angelo Daros de Luca return 0;
142aac94001SLuiz Angelo Daros de Luca }
143bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA);
144aac94001SLuiz Angelo Daros de Luca
145bce254b8SLuiz Angelo Daros de Luca /**
146bce254b8SLuiz Angelo Daros de Luca * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch
147bce254b8SLuiz Angelo Daros de Luca * @mdiodev: mdio_device to be removed.
148bce254b8SLuiz Angelo Daros de Luca *
149bce254b8SLuiz Angelo Daros de Luca * This function should be used as the .remove_new in an mdio_driver. First
150*8be040ecSLuiz Angelo Daros de Luca * it unregisters the DSA switch and then it calls the common remove function.
151bce254b8SLuiz Angelo Daros de Luca *
152bce254b8SLuiz Angelo Daros de Luca * Context: Can sleep.
153bce254b8SLuiz Angelo Daros de Luca * Return: Nothing.
154bce254b8SLuiz Angelo Daros de Luca */
realtek_mdio_remove(struct mdio_device * mdiodev)155bce254b8SLuiz Angelo Daros de Luca void realtek_mdio_remove(struct mdio_device *mdiodev)
156aac94001SLuiz Angelo Daros de Luca {
157aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
158aac94001SLuiz Angelo Daros de Luca
159aac94001SLuiz Angelo Daros de Luca if (!priv)
160aac94001SLuiz Angelo Daros de Luca return;
161aac94001SLuiz Angelo Daros de Luca
162*8be040ecSLuiz Angelo Daros de Luca rtl83xx_unregister_switch(priv);
163aac94001SLuiz Angelo Daros de Luca
164*8be040ecSLuiz Angelo Daros de Luca rtl83xx_remove(priv);
165aac94001SLuiz Angelo Daros de Luca }
166bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA);
167aac94001SLuiz Angelo Daros de Luca
168bce254b8SLuiz Angelo Daros de Luca /**
169bce254b8SLuiz Angelo Daros de Luca * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch
170bce254b8SLuiz Angelo Daros de Luca * @mdiodev: mdio_device shutting down.
171bce254b8SLuiz Angelo Daros de Luca *
172*8be040ecSLuiz Angelo Daros de Luca * This function should be used as the .shutdown in a platform_driver. It calls
173*8be040ecSLuiz Angelo Daros de Luca * the common shutdown function.
174bce254b8SLuiz Angelo Daros de Luca *
175bce254b8SLuiz Angelo Daros de Luca * Context: Can sleep.
176bce254b8SLuiz Angelo Daros de Luca * Return: Nothing.
177bce254b8SLuiz Angelo Daros de Luca */
realtek_mdio_shutdown(struct mdio_device * mdiodev)178bce254b8SLuiz Angelo Daros de Luca void realtek_mdio_shutdown(struct mdio_device *mdiodev)
179aac94001SLuiz Angelo Daros de Luca {
180aac94001SLuiz Angelo Daros de Luca struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev);
181aac94001SLuiz Angelo Daros de Luca
182aac94001SLuiz Angelo Daros de Luca if (!priv)
183aac94001SLuiz Angelo Daros de Luca return;
184aac94001SLuiz Angelo Daros de Luca
185*8be040ecSLuiz Angelo Daros de Luca rtl83xx_shutdown(priv);
186aac94001SLuiz Angelo Daros de Luca }
187bce254b8SLuiz Angelo Daros de Luca EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA);
188