Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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
 84mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii)
 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	/* 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++) {
 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
214	if (priv->id == ID_MT7531)
215		priv->create_sgmii = mt7531_create_sgmii;
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");