brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 51df42c Raw
269 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 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 int19mt7530_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 43static int44mt7530_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	lo = bus->read(bus, priv->mdiodev->addr, r);59	hi = bus->read(bus, priv->mdiodev->addr, 0x10);60 61	*val = (hi << 16) | (lo & 0xffff);62 63	return 0;64}65 66static void67mt7530_mdio_regmap_lock(void *mdio_lock)68{69	mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);70}71 72static void73mt7530_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 int84mt7531_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, &mt7530_regmap_bus, priv,111					  mt7531_pcs_config[i]);112		if (IS_ERR(regmap)) {113			ret = PTR_ERR(regmap);114			break;115		}116		pcs = mtk_pcs_lynxi_create(priv->dev, regmap,117					   MT7531_PHYA_CTRL_SIGNAL3, 0);118		if (!pcs) {119			ret = -ENXIO;120			break;121		}122		priv->ports[5 + i].sgmii_pcs = pcs;123	}124 125	if (ret && i)126		mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);127 128	return ret;129}130 131static const struct of_device_id mt7530_of_match[] = {132	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },133	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },134	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },135	{ /* sentinel */ },136};137MODULE_DEVICE_TABLE(of, mt7530_of_match);138 139static int140mt7530_probe(struct mdio_device *mdiodev)141{142	static struct regmap_config *regmap_config;143	struct mt7530_priv *priv;144	struct device_node *dn;145	int ret;146 147	dn = mdiodev->dev.of_node;148 149	priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);150	if (!priv)151		return -ENOMEM;152 153	priv->bus = mdiodev->bus;154	priv->dev = &mdiodev->dev;155	priv->mdiodev = mdiodev;156 157	ret = mt7530_probe_common(priv);158	if (ret)159		return ret;160 161	/* Use medatek,mcm property to distinguish hardware type that would162	 * cause a little bit differences on power-on sequence.163	 * Not MCM that indicates switch works as the remote standalone164	 * integrated circuit so the GPIO pin would be used to complete165	 * the reset, otherwise memory-mapped register accessing used166	 * 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, priv,207					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 217static void218mt7530_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 242static 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 254static 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 264mdio_module_driver(mt7530_mdio_driver);265 266MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");267MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");268MODULE_LICENSE("GPL");269