-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net: dsa: mt7530: introduce separate MDIO driver
Split MT7530 switch driver into a common part and a part specific for MDIO connected switches and multi-chip modules. Move MDIO-specific functions to newly introduced mt7530-mdio.c while keeping the common parts in mt7530.c. Introduce new Kconfig symbol CONFIG_NET_DSA_MT7530_MDIO which is implied by CONFIG_NET_DSA_MT7530. Signed-off-by: Daniel Golle <daniel@makrotopia.org> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
- Loading branch information
Daniel Golle
authored and
David S. Miller
committed
Apr 3, 2023
1 parent
7f54cc9
commit cb675af
Showing
6 changed files
with
300 additions
and
258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
|
||
#include <linux/gpio/consumer.h> | ||
#include <linux/mdio.h> | ||
#include <linux/module.h> | ||
#include <linux/pcs/pcs-mtk-lynxi.h> | ||
#include <linux/of_irq.h> | ||
#include <linux/of_mdio.h> | ||
#include <linux/of_net.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/regmap.h> | ||
#include <linux/reset.h> | ||
#include <linux/regulator/consumer.h> | ||
#include <net/dsa.h> | ||
|
||
#include "mt7530.h" | ||
|
||
static int | ||
mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) | ||
{ | ||
struct mii_bus *bus = context; | ||
u16 page, r, lo, hi; | ||
int ret; | ||
|
||
page = (reg >> 6) & 0x3ff; | ||
r = (reg >> 2) & 0xf; | ||
lo = val & 0xffff; | ||
hi = val >> 16; | ||
|
||
/* MT7530 uses 31 as the pseudo port */ | ||
ret = bus->write(bus, 0x1f, 0x1f, page); | ||
if (ret < 0) | ||
return ret; | ||
|
||
ret = bus->write(bus, 0x1f, r, lo); | ||
if (ret < 0) | ||
return ret; | ||
|
||
ret = bus->write(bus, 0x1f, 0x10, hi); | ||
return ret; | ||
} | ||
|
||
static int | ||
mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) | ||
{ | ||
struct mii_bus *bus = context; | ||
u16 page, r, lo, hi; | ||
int ret; | ||
|
||
page = (reg >> 6) & 0x3ff; | ||
r = (reg >> 2) & 0xf; | ||
|
||
/* MT7530 uses 31 as the pseudo port */ | ||
ret = bus->write(bus, 0x1f, 0x1f, page); | ||
if (ret < 0) | ||
return ret; | ||
|
||
lo = bus->read(bus, 0x1f, r); | ||
hi = bus->read(bus, 0x1f, 0x10); | ||
|
||
*val = (hi << 16) | (lo & 0xffff); | ||
|
||
return 0; | ||
} | ||
|
||
static void | ||
mt7530_mdio_regmap_lock(void *mdio_lock) | ||
{ | ||
mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED); | ||
} | ||
|
||
static void | ||
mt7530_mdio_regmap_unlock(void *mdio_lock) | ||
{ | ||
mutex_unlock(mdio_lock); | ||
} | ||
|
||
static const struct regmap_bus mt7530_regmap_bus = { | ||
.reg_write = mt7530_regmap_write, | ||
.reg_read = mt7530_regmap_read, | ||
}; | ||
|
||
static int | ||
mt7531_create_sgmii(struct mt7530_priv *priv) | ||
{ | ||
struct regmap_config *mt7531_pcs_config[2]; | ||
struct phylink_pcs *pcs; | ||
struct regmap *regmap; | ||
int i, ret = 0; | ||
|
||
for (i = 0; i < 2; i++) { | ||
mt7531_pcs_config[i] = devm_kzalloc(priv->dev, | ||
sizeof(struct regmap_config), | ||
GFP_KERNEL); | ||
if (!mt7531_pcs_config[i]) { | ||
ret = -ENOMEM; | ||
break; | ||
} | ||
|
||
mt7531_pcs_config[i]->name = i ? "port6" : "port5"; | ||
mt7531_pcs_config[i]->reg_bits = 16; | ||
mt7531_pcs_config[i]->val_bits = 32; | ||
mt7531_pcs_config[i]->reg_stride = 4; | ||
mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); | ||
mt7531_pcs_config[i]->max_register = 0x17c; | ||
mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock; | ||
mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; | ||
mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; | ||
|
||
regmap = devm_regmap_init(priv->dev, | ||
&mt7530_regmap_bus, priv->bus, | ||
mt7531_pcs_config[i]); | ||
if (IS_ERR(regmap)) { | ||
ret = PTR_ERR(regmap); | ||
break; | ||
} | ||
pcs = mtk_pcs_lynxi_create(priv->dev, regmap, | ||
MT7531_PHYA_CTRL_SIGNAL3, 0); | ||
if (!pcs) { | ||
ret = -ENXIO; | ||
break; | ||
} | ||
priv->ports[5 + i].sgmii_pcs = pcs; | ||
} | ||
|
||
if (ret && i) | ||
mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); | ||
|
||
return ret; | ||
} | ||
|
||
static const struct of_device_id mt7530_of_match[] = { | ||
{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, | ||
{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, | ||
{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, | ||
{ /* sentinel */ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, mt7530_of_match); | ||
|
||
static int | ||
mt7530_probe(struct mdio_device *mdiodev) | ||
{ | ||
static struct regmap_config *regmap_config; | ||
struct mt7530_priv *priv; | ||
struct device_node *dn; | ||
int ret; | ||
|
||
dn = mdiodev->dev.of_node; | ||
|
||
priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); | ||
if (!priv) | ||
return -ENOMEM; | ||
|
||
priv->bus = mdiodev->bus; | ||
priv->dev = &mdiodev->dev; | ||
|
||
ret = mt7530_probe_common(priv); | ||
if (ret) | ||
return ret; | ||
|
||
/* Use medatek,mcm property to distinguish hardware type that would | ||
* cause a little bit differences on power-on sequence. | ||
* Not MCM that indicates switch works as the remote standalone | ||
* integrated circuit so the GPIO pin would be used to complete | ||
* the reset, otherwise memory-mapped register accessing used | ||
* through syscon provides in the case of MCM. | ||
*/ | ||
priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); | ||
if (priv->mcm) { | ||
dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); | ||
|
||
priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); | ||
if (IS_ERR(priv->rstc)) { | ||
dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); | ||
return PTR_ERR(priv->rstc); | ||
} | ||
} else { | ||
priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", | ||
GPIOD_OUT_LOW); | ||
if (IS_ERR(priv->reset)) { | ||
dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); | ||
return PTR_ERR(priv->reset); | ||
} | ||
} | ||
|
||
if (priv->id == ID_MT7530) { | ||
priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); | ||
if (IS_ERR(priv->core_pwr)) | ||
return PTR_ERR(priv->core_pwr); | ||
|
||
priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); | ||
if (IS_ERR(priv->io_pwr)) | ||
return PTR_ERR(priv->io_pwr); | ||
} | ||
|
||
regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config), | ||
GFP_KERNEL); | ||
if (!regmap_config) | ||
return -ENOMEM; | ||
|
||
regmap_config->reg_bits = 16; | ||
regmap_config->val_bits = 32; | ||
regmap_config->reg_stride = 4; | ||
regmap_config->max_register = MT7530_CREV; | ||
regmap_config->disable_locking = true; | ||
priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, | ||
priv->bus, regmap_config); | ||
if (IS_ERR(priv->regmap)) | ||
return PTR_ERR(priv->regmap); | ||
|
||
if (priv->id == ID_MT7531) { | ||
ret = mt7531_create_sgmii(priv); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
return dsa_register_switch(priv->ds); | ||
} | ||
|
||
static void | ||
mt7530_remove(struct mdio_device *mdiodev) | ||
{ | ||
struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); | ||
int ret = 0, i; | ||
|
||
if (!priv) | ||
return; | ||
|
||
ret = regulator_disable(priv->core_pwr); | ||
if (ret < 0) | ||
dev_err(priv->dev, | ||
"Failed to disable core power: %d\n", ret); | ||
|
||
ret = regulator_disable(priv->io_pwr); | ||
if (ret < 0) | ||
dev_err(priv->dev, "Failed to disable io pwr: %d\n", | ||
ret); | ||
|
||
mt7530_remove_common(priv); | ||
|
||
for (i = 0; i < 2; ++i) | ||
mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); | ||
} | ||
|
||
static void mt7530_shutdown(struct mdio_device *mdiodev) | ||
{ | ||
struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); | ||
|
||
if (!priv) | ||
return; | ||
|
||
dsa_switch_shutdown(priv->ds); | ||
|
||
dev_set_drvdata(&mdiodev->dev, NULL); | ||
} | ||
|
||
static struct mdio_driver mt7530_mdio_driver = { | ||
.probe = mt7530_probe, | ||
.remove = mt7530_remove, | ||
.shutdown = mt7530_shutdown, | ||
.mdiodrv.driver = { | ||
.name = "mt7530-mdio", | ||
.of_match_table = mt7530_of_match, | ||
}, | ||
}; | ||
|
||
mdio_module_driver(mt7530_mdio_driver); | ||
|
||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); | ||
MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)"); | ||
MODULE_LICENSE("GPL"); |
Oops, something went wrong.