net: phy: Provide Module 4 KSZ9477 errata (DS80000754C)
[linux-2.6-block.git] / drivers / net / dsa / mt7530-mdio.c
CommitLineData
cb675afc
DG
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/gpio/consumer.h>
4#include <linux/mdio.h>
5#include <linux/module.h>
6#include <linux/pcs/pcs-mtk-lynxi.h>
7#include <linux/of_irq.h>
8#include <linux/of_mdio.h>
9#include <linux/of_net.h>
10#include <linux/of_platform.h>
11#include <linux/regmap.h>
12#include <linux/reset.h>
13#include <linux/regulator/consumer.h>
14#include <net/dsa.h>
15
16#include "mt7530.h"
17
18static int
19mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
20{
21 struct mii_bus *bus = context;
22 u16 page, r, lo, hi;
23 int ret;
24
25 page = (reg >> 6) & 0x3ff;
26 r = (reg >> 2) & 0xf;
27 lo = val & 0xffff;
28 hi = val >> 16;
29
30 /* MT7530 uses 31 as the pseudo port */
31 ret = bus->write(bus, 0x1f, 0x1f, page);
32 if (ret < 0)
33 return ret;
34
35 ret = bus->write(bus, 0x1f, r, lo);
36 if (ret < 0)
37 return ret;
38
39 ret = bus->write(bus, 0x1f, 0x10, hi);
40 return ret;
41}
42
43static int
44mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
45{
46 struct mii_bus *bus = context;
47 u16 page, r, lo, hi;
48 int ret;
49
50 page = (reg >> 6) & 0x3ff;
51 r = (reg >> 2) & 0xf;
52
53 /* MT7530 uses 31 as the pseudo port */
54 ret = bus->write(bus, 0x1f, 0x1f, page);
55 if (ret < 0)
56 return ret;
57
58 lo = bus->read(bus, 0x1f, r);
59 hi = bus->read(bus, 0x1f, 0x10);
60
61 *val = (hi << 16) | (lo & 0xffff);
62
63 return 0;
64}
65
66static void
67mt7530_mdio_regmap_lock(void *mdio_lock)
68{
69 mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
70}
71
72static void
73mt7530_mdio_regmap_unlock(void *mdio_lock)
74{
75 mutex_unlock(mdio_lock);
76}
77
78static const struct regmap_bus mt7530_regmap_bus = {
79 .reg_write = mt7530_regmap_write,
80 .reg_read = mt7530_regmap_read,
81};
82
83static int
91daa4f6 84mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii)
cb675afc 85{
91daa4f6 86 struct regmap_config *mt7531_pcs_config[2] = {};
cb675afc
DG
87 struct phylink_pcs *pcs;
88 struct regmap *regmap;
89 int i, ret = 0;
90
91daa4f6
DG
91 /* MT7531AE has two SGMII units for port 5 and port 6
92 * MT7531BE has only one SGMII unit for port 6
93 */
94 for (i = dual_sgmii ? 0 : 1; i < 2; i++) {
cb675afc
DG
95 mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
96 sizeof(struct regmap_config),
97 GFP_KERNEL);
98 if (!mt7531_pcs_config[i]) {
99 ret = -ENOMEM;
100 break;
101 }
102
103 mt7531_pcs_config[i]->name = i ? "port6" : "port5";
104 mt7531_pcs_config[i]->reg_bits = 16;
105 mt7531_pcs_config[i]->val_bits = 32;
106 mt7531_pcs_config[i]->reg_stride = 4;
107 mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
108 mt7531_pcs_config[i]->max_register = 0x17c;
109 mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
110 mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
111 mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
112
113 regmap = devm_regmap_init(priv->dev,
114 &mt7530_regmap_bus, priv->bus,
115 mt7531_pcs_config[i]);
116 if (IS_ERR(regmap)) {
117 ret = PTR_ERR(regmap);
118 break;
119 }
120 pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
121 MT7531_PHYA_CTRL_SIGNAL3, 0);
122 if (!pcs) {
123 ret = -ENXIO;
124 break;
125 }
126 priv->ports[5 + i].sgmii_pcs = pcs;
127 }
128
129 if (ret && i)
130 mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);
131
132 return ret;
133}
134
135static const struct of_device_id mt7530_of_match[] = {
136 { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
137 { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
138 { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
139 { /* sentinel */ },
140};
141MODULE_DEVICE_TABLE(of, mt7530_of_match);
142
143static int
144mt7530_probe(struct mdio_device *mdiodev)
145{
146 static struct regmap_config *regmap_config;
147 struct mt7530_priv *priv;
148 struct device_node *dn;
149 int ret;
150
151 dn = mdiodev->dev.of_node;
152
153 priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
154 if (!priv)
155 return -ENOMEM;
156
157 priv->bus = mdiodev->bus;
158 priv->dev = &mdiodev->dev;
159
160 ret = mt7530_probe_common(priv);
161 if (ret)
162 return ret;
163
164 /* Use medatek,mcm property to distinguish hardware type that would
165 * cause a little bit differences on power-on sequence.
166 * Not MCM that indicates switch works as the remote standalone
167 * integrated circuit so the GPIO pin would be used to complete
168 * the reset, otherwise memory-mapped register accessing used
169 * through syscon provides in the case of MCM.
170 */
171 priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
172 if (priv->mcm) {
173 dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
174
175 priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
176 if (IS_ERR(priv->rstc)) {
177 dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
178 return PTR_ERR(priv->rstc);
179 }
180 } else {
181 priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
182 GPIOD_OUT_LOW);
183 if (IS_ERR(priv->reset)) {
184 dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
185 return PTR_ERR(priv->reset);
186 }
187 }
188
189 if (priv->id == ID_MT7530) {
190 priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
191 if (IS_ERR(priv->core_pwr))
192 return PTR_ERR(priv->core_pwr);
193
194 priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
195 if (IS_ERR(priv->io_pwr))
196 return PTR_ERR(priv->io_pwr);
197 }
198
199 regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
200 GFP_KERNEL);
201 if (!regmap_config)
202 return -ENOMEM;
203
204 regmap_config->reg_bits = 16;
205 regmap_config->val_bits = 32;
206 regmap_config->reg_stride = 4;
207 regmap_config->max_register = MT7530_CREV;
208 regmap_config->disable_locking = true;
209 priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
210 priv->bus, regmap_config);
211 if (IS_ERR(priv->regmap))
212 return PTR_ERR(priv->regmap);
213
91daa4f6
DG
214 if (priv->id == ID_MT7531)
215 priv->create_sgmii = mt7531_create_sgmii;
cb675afc
DG
216
217 return dsa_register_switch(priv->ds);
218}
219
220static void
221mt7530_remove(struct mdio_device *mdiodev)
222{
223 struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
224 int ret = 0, i;
225
226 if (!priv)
227 return;
228
229 ret = regulator_disable(priv->core_pwr);
230 if (ret < 0)
231 dev_err(priv->dev,
232 "Failed to disable core power: %d\n", ret);
233
234 ret = regulator_disable(priv->io_pwr);
235 if (ret < 0)
236 dev_err(priv->dev, "Failed to disable io pwr: %d\n",
237 ret);
238
239 mt7530_remove_common(priv);
240
241 for (i = 0; i < 2; ++i)
242 mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
243}
244
245static void mt7530_shutdown(struct mdio_device *mdiodev)
246{
247 struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
248
249 if (!priv)
250 return;
251
252 dsa_switch_shutdown(priv->ds);
253
254 dev_set_drvdata(&mdiodev->dev, NULL);
255}
256
257static struct mdio_driver mt7530_mdio_driver = {
258 .probe = mt7530_probe,
259 .remove = mt7530_remove,
260 .shutdown = mt7530_shutdown,
261 .mdiodrv.driver = {
262 .name = "mt7530-mdio",
263 .of_match_table = mt7530_of_match,
264 },
265};
266
267mdio_module_driver(mt7530_mdio_driver);
268
269MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
270MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");
271MODULE_LICENSE("GPL");