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