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