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