Commit 7b600f8d authored by David S. Miller's avatar David S. Miller

Merge branch 'mt7988-support'

Daniel Golle says:

====================
net: dsa: add support for MT7988

The MediaTek MT7988 SoC comes with a built-in switch very similar to
previous MT7530 and MT7531. However, the switch address space is mapped
into the SoCs memory space rather than being connected via MDIO.
Using MMIO simplifies register access and also removes the need for a bus
lock, and for that reason also makes interrupt handling more light-weight.

Note that this is different from previous SoCs like MT7621 and MT7623N
which also came with an integrated MT7530-like switch which yet had to be
accessed via MDIO.

Split-off the part of the driver registering an MDIO driver, then add
another module acting as MMIO/platform driver.

The whole series has been tested on various MediaTek boards:
 * MT7623A + MT7530 (BPi-R2)
 * MT7986A + MT7531 (BPi-R3)
 * MT7988A reference board

Changes since v1:
 * use 'internal' PHY mode where appropriate
 * use regmap_update_bits in mt7530_rmw
 * improve dt-bindings

Changes since RFC v3:
 * WARN_ON_ONCE if register read fails
 * move probing of the reset GPIO and reset controller link out of
   common  probe function, as they are not actually common

Changes since RFC v2:
 * split into many small commits to ease review
 * introduce helper functions to reduce code duplication
 * use helpers for locking to make lock-skipping easier and less ugly
   to implement.
 * add dt-bindings for mediatek,mt7988-switch

Changes since initial RFC:
 * use regmap for register access and move register access to bus-
   specific driver
 * move initialization of MT7531 SGMII PCS to MDIO driver
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 56b029dd 386f5fc9
...@@ -11,16 +11,23 @@ maintainers: ...@@ -11,16 +11,23 @@ maintainers:
- Landen Chao <Landen.Chao@mediatek.com> - Landen Chao <Landen.Chao@mediatek.com>
- DENG Qingfang <dqfext@gmail.com> - DENG Qingfang <dqfext@gmail.com>
- Sean Wang <sean.wang@mediatek.com> - Sean Wang <sean.wang@mediatek.com>
- Daniel Golle <daniel@makrotopia.org>
description: | description: |
There are two versions of MT7530, standalone and in a multi-chip module. There are three versions of MT7530, standalone, in a multi-chip module and
built-into a SoC.
MT7530 is a part of the multi-chip module in MT7620AN, MT7620DA, MT7620DAN, MT7530 is a part of the multi-chip module in MT7620AN, MT7620DA, MT7620DAN,
MT7620NN, MT7621AT, MT7621DAT, MT7621ST and MT7623AI SoCs. MT7620NN, MT7621AT, MT7621DAT, MT7621ST and MT7623AI SoCs.
The MT7988 SoC comes with a built-in switch similar to MT7531 as well as four
Gigabit Ethernet PHYs. The switch registers are directly mapped into the SoC's
memory map rather than using MDIO. The switch got an internally connected 10G
CPU port and 4 user ports connected to the built-in Gigabit Ethernet PHYs.
MT7530 in MT7620AN, MT7620DA, MT7620DAN and MT7620NN SoCs has got 10/100 PHYs MT7530 in MT7620AN, MT7620DA, MT7620DAN and MT7620NN SoCs has got 10/100 PHYs
and the switch registers are directly mapped into SoC's memory map rather than and the switch registers are directly mapped into SoC's memory map rather than
using MDIO. The DSA driver currently doesn't support this. using MDIO. The DSA driver currently doesn't support MT7620 variants.
There is only the standalone version of MT7531. There is only the standalone version of MT7531.
...@@ -81,6 +88,10 @@ properties: ...@@ -81,6 +88,10 @@ properties:
Multi-chip module MT7530 in MT7621AT, MT7621DAT and MT7621ST SoCs Multi-chip module MT7530 in MT7621AT, MT7621DAT and MT7621ST SoCs
const: mediatek,mt7621 const: mediatek,mt7621
- description:
Built-in switch of the MT7988 SoC
const: mediatek,mt7988-switch
reg: reg:
maxItems: 1 maxItems: 1
...@@ -268,6 +279,17 @@ allOf: ...@@ -268,6 +279,17 @@ allOf:
required: required:
- mediatek,mcm - mediatek,mcm
- if:
properties:
compatible:
const: mediatek,mt7988-switch
then:
$ref: "#/$defs/mt7530-dsa-port"
properties:
gpio-controller: false
mediatek,mcm: false
reset-names: false
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:
......
...@@ -13175,8 +13175,11 @@ MEDIATEK SWITCH DRIVER ...@@ -13175,8 +13175,11 @@ MEDIATEK SWITCH DRIVER
M: Sean Wang <sean.wang@mediatek.com> M: Sean Wang <sean.wang@mediatek.com>
M: Landen Chao <Landen.Chao@mediatek.com> M: Landen Chao <Landen.Chao@mediatek.com>
M: DENG Qingfang <dqfext@gmail.com> M: DENG Qingfang <dqfext@gmail.com>
M: Daniel Golle <daniel@makrotopia.org>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
S: Maintained S: Maintained
F: drivers/net/dsa/mt7530-mdio.c
F: drivers/net/dsa/mt7530-mmio.c
F: drivers/net/dsa/mt7530.* F: drivers/net/dsa/mt7530.*
F: net/dsa/tag_mtk.c F: net/dsa/tag_mtk.c
......
...@@ -38,11 +38,34 @@ config NET_DSA_MT7530 ...@@ -38,11 +38,34 @@ config NET_DSA_MT7530
tristate "MediaTek MT7530 and MT7531 Ethernet switch support" tristate "MediaTek MT7530 and MT7531 Ethernet switch support"
select NET_DSA_TAG_MTK select NET_DSA_TAG_MTK
select MEDIATEK_GE_PHY select MEDIATEK_GE_PHY
select PCS_MTK_LYNXI imply NET_DSA_MT7530_MDIO
imply NET_DSA_MT7530_MMIO
help help
This enables support for the MediaTek MT7530 and MT7531 Ethernet This enables support for the MediaTek MT7530 and MT7531 Ethernet
switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT, switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
MT7621ST and MT7623AI SoCs is supported. MT7621ST and MT7623AI SoCs, and built-in switch in MT7988 SoC are
supported as well.
config NET_DSA_MT7530_MDIO
tristate "MediaTek MT7530 MDIO interface driver"
depends on NET_DSA_MT7530
select PCS_MTK_LYNXI
help
This enables support for the MediaTek MT7530 and MT7531 switch
chips which are connected via MDIO, as well as multi-chip
module MT7530 which can be found in the MT7621AT, MT7621DAT,
MT7621ST and MT7623AI SoCs.
config NET_DSA_MT7530_MMIO
tristate "MediaTek MT7530 MMIO interface driver"
depends on NET_DSA_MT7530
depends on HAS_IOMEM
help
This enables support for the built-in Ethernet switch found
in the MediaTek MT7988 SoC.
The switch is a similar design as MT7531, but the switch registers
are directly mapped into the SoCs register space rather than being
accessible via MDIO.
config NET_DSA_MV88E6060 config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support" tristate "Marvell 88E6060 ethernet switch chip support"
......
...@@ -7,6 +7,8 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o ...@@ -7,6 +7,8 @@ obj-$(CONFIG_FIXED_PHY) += dsa_loop_bdinfo.o
endif endif
obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
......
// 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");
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <net/dsa.h>
#include "mt7530.h"
static const struct of_device_id mt7988_of_match[] = {
{ .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mt7988_of_match);
static int
mt7988_probe(struct platform_device *pdev)
{
static struct regmap_config *sw_regmap_config;
struct mt7530_priv *priv;
void __iomem *base_addr;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = NULL;
priv->dev = &pdev->dev;
ret = mt7530_probe_common(priv);
if (ret)
return ret;
priv->rstc = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(priv->rstc)) {
dev_err(&pdev->dev, "Couldn't get our reset line\n");
return PTR_ERR(priv->rstc);
}
base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base_addr)) {
dev_err(&pdev->dev, "cannot request I/O memory space\n");
return -ENXIO;
}
sw_regmap_config = devm_kzalloc(&pdev->dev, sizeof(*sw_regmap_config), GFP_KERNEL);
if (!sw_regmap_config)
return -ENOMEM;
sw_regmap_config->name = "switch";
sw_regmap_config->reg_bits = 16;
sw_regmap_config->val_bits = 32;
sw_regmap_config->reg_stride = 4;
sw_regmap_config->max_register = MT7530_CREV;
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base_addr, sw_regmap_config);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
return dsa_register_switch(priv->ds);
}
static int
mt7988_remove(struct platform_device *pdev)
{
struct mt7530_priv *priv = platform_get_drvdata(pdev);
if (priv)
mt7530_remove_common(priv);
return 0;
}
static void mt7988_shutdown(struct platform_device *pdev)
{
struct mt7530_priv *priv = platform_get_drvdata(pdev);
if (!priv)
return;
dsa_switch_shutdown(priv->ds);
dev_set_drvdata(&pdev->dev, NULL);
}
static struct platform_driver mt7988_platform_driver = {
.probe = mt7988_probe,
.remove = mt7988_remove,
.shutdown = mt7988_shutdown,
.driver = {
.name = "mt7530-mmio",
.of_match_table = mt7988_of_match,
},
};
module_platform_driver(mt7988_platform_driver);
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MMIO)");
MODULE_LICENSE("GPL");
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pcs/pcs-mtk-lynxi.h>
#include <linux/phylink.h> #include <linux/phylink.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -143,31 +142,42 @@ core_write_mmd_indirect(struct mt7530_priv *priv, int prtad, ...@@ -143,31 +142,42 @@ core_write_mmd_indirect(struct mt7530_priv *priv, int prtad,
} }
static void static void
core_write(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_mutex_lock(struct mt7530_priv *priv)
{ {
struct mii_bus *bus = priv->bus; if (priv->bus)
mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
}
static void
mt7530_mutex_unlock(struct mt7530_priv *priv)
{
if (priv->bus)
mutex_unlock(&priv->bus->mdio_lock);
}
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); static void
core_write(struct mt7530_priv *priv, u32 reg, u32 val)
{
mt7530_mutex_lock(priv);
core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
} }
static void static void
core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
{ {
struct mii_bus *bus = priv->bus;
u32 val; u32 val;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2); val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
val &= ~mask; val &= ~mask;
val |= set; val |= set;
core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
} }
static void static void
...@@ -185,66 +195,42 @@ core_clear(struct mt7530_priv *priv, u32 reg, u32 val) ...@@ -185,66 +195,42 @@ core_clear(struct mt7530_priv *priv, u32 reg, u32 val)
static int static int
mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val)
{ {
struct mii_bus *bus = priv->bus;
u16 page, r, lo, hi;
int ret; int ret;
page = (reg >> 6) & 0x3ff; ret = regmap_write(priv->regmap, reg, val);
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)
goto err;
ret = bus->write(bus, 0x1f, r, lo);
if (ret < 0)
goto err;
ret = bus->write(bus, 0x1f, 0x10, hi);
err:
if (ret < 0) if (ret < 0)
dev_err(&bus->dev, dev_err(priv->dev,
"failed to write mt7530 register\n"); "failed to write mt7530 register\n");
return ret; return ret;
} }
static u32 static u32
mt7530_mii_read(struct mt7530_priv *priv, u32 reg) mt7530_mii_read(struct mt7530_priv *priv, u32 reg)
{ {
struct mii_bus *bus = priv->bus;
u16 page, r, lo, hi;
int ret; int ret;
u32 val;
page = (reg >> 6) & 0x3ff; ret = regmap_read(priv->regmap, reg, &val);
r = (reg >> 2) & 0xf; if (ret) {
WARN_ON_ONCE(1);
/* MT7530 uses 31 as the pseudo port */ dev_err(priv->dev,
ret = bus->write(bus, 0x1f, 0x1f, page);
if (ret < 0) {
dev_err(&bus->dev,
"failed to read mt7530 register\n"); "failed to read mt7530 register\n");
return ret; return 0;
} }
lo = bus->read(bus, 0x1f, r); return val;
hi = bus->read(bus, 0x1f, 0x10);
return (hi << 16) | (lo & 0xffff);
} }
static void static void
mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)
{ {
struct mii_bus *bus = priv->bus; mt7530_mutex_lock(priv);
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
mt7530_mii_write(priv, reg, val); mt7530_mii_write(priv, reg, val);
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
} }
static u32 static u32
...@@ -256,14 +242,13 @@ _mt7530_unlocked_read(struct mt7530_dummy_poll *p) ...@@ -256,14 +242,13 @@ _mt7530_unlocked_read(struct mt7530_dummy_poll *p)
static u32 static u32
_mt7530_read(struct mt7530_dummy_poll *p) _mt7530_read(struct mt7530_dummy_poll *p)
{ {
struct mii_bus *bus = p->priv->bus;
u32 val; u32 val;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(p->priv);
val = mt7530_mii_read(p->priv, p->reg); val = mt7530_mii_read(p->priv, p->reg);
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(p->priv);
return val; return val;
} }
...@@ -281,23 +266,17 @@ static void ...@@ -281,23 +266,17 @@ static void
mt7530_rmw(struct mt7530_priv *priv, u32 reg, mt7530_rmw(struct mt7530_priv *priv, u32 reg,
u32 mask, u32 set) u32 mask, u32 set)
{ {
struct mii_bus *bus = priv->bus; mt7530_mutex_lock(priv);
u32 val;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); regmap_update_bits(priv->regmap, reg, mask, set);
val = mt7530_mii_read(priv, reg); mt7530_mutex_unlock(priv);
val &= ~mask;
val |= set;
mt7530_mii_write(priv, reg, val);
mutex_unlock(&bus->mdio_lock);
} }
static void static void
mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val)
{ {
mt7530_rmw(priv, reg, 0, val); mt7530_rmw(priv, reg, val, val);
} }
static void static void
...@@ -635,14 +614,13 @@ static int ...@@ -635,14 +614,13 @@ static int
mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
int regnum) int regnum)
{ {
struct mii_bus *bus = priv->bus;
struct mt7530_dummy_poll p; struct mt7530_dummy_poll p;
u32 reg, val; u32 reg, val;
int ret; int ret;
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000); !(val & MT7531_PHY_ACS_ST), 20, 100000);
...@@ -675,7 +653,7 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, ...@@ -675,7 +653,7 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
ret = val & MT7531_MDIO_RW_DATA_MASK; ret = val & MT7531_MDIO_RW_DATA_MASK;
out: out:
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
return ret; return ret;
} }
...@@ -684,14 +662,13 @@ static int ...@@ -684,14 +662,13 @@ static int
mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
int regnum, u16 data) int regnum, u16 data)
{ {
struct mii_bus *bus = priv->bus;
struct mt7530_dummy_poll p; struct mt7530_dummy_poll p;
u32 val, reg; u32 val, reg;
int ret; int ret;
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000); !(val & MT7531_PHY_ACS_ST), 20, 100000);
...@@ -723,7 +700,7 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, ...@@ -723,7 +700,7 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
} }
out: out:
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
return ret; return ret;
} }
...@@ -731,14 +708,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, ...@@ -731,14 +708,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
static int static int
mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
{ {
struct mii_bus *bus = priv->bus;
struct mt7530_dummy_poll p; struct mt7530_dummy_poll p;
int ret; int ret;
u32 val; u32 val;
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000); !(val & MT7531_PHY_ACS_ST), 20, 100000);
...@@ -761,7 +737,7 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) ...@@ -761,7 +737,7 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
ret = val & MT7531_MDIO_RW_DATA_MASK; ret = val & MT7531_MDIO_RW_DATA_MASK;
out: out:
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
return ret; return ret;
} }
...@@ -770,14 +746,13 @@ static int ...@@ -770,14 +746,13 @@ static int
mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
u16 data) u16 data)
{ {
struct mii_bus *bus = priv->bus;
struct mt7530_dummy_poll p; struct mt7530_dummy_poll p;
int ret; int ret;
u32 reg; u32 reg;
INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC);
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg,
!(reg & MT7531_PHY_ACS_ST), 20, 100000); !(reg & MT7531_PHY_ACS_ST), 20, 100000);
...@@ -799,7 +774,7 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, ...@@ -799,7 +774,7 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
} }
out: out:
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
return ret; return ret;
} }
...@@ -921,6 +896,24 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ...@@ -921,6 +896,24 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
return 0; return 0;
} }
static const char *p5_intf_modes(unsigned int p5_interface)
{
switch (p5_interface) {
case P5_DISABLED:
return "DISABLED";
case P5_INTF_SEL_PHY_P0:
return "PHY P0";
case P5_INTF_SEL_PHY_P4:
return "PHY P4";
case P5_INTF_SEL_GMAC5:
return "GMAC5";
case P5_INTF_SEL_GMAC5_SGMII:
return "GMAC5_SGMII";
default:
return "unknown";
}
}
static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
...@@ -1080,7 +1073,6 @@ static int ...@@ -1080,7 +1073,6 @@ static int
mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
struct mii_bus *bus = priv->bus;
int length; int length;
u32 val; u32 val;
...@@ -1091,7 +1083,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ...@@ -1091,7 +1083,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
if (!dsa_is_cpu_port(ds, port)) if (!dsa_is_cpu_port(ds, port))
return 0; return 0;
mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
val = mt7530_mii_read(priv, MT7530_GMACCR); val = mt7530_mii_read(priv, MT7530_GMACCR);
val &= ~MAX_RX_PKT_LEN_MASK; val &= ~MAX_RX_PKT_LEN_MASK;
...@@ -1112,7 +1104,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ...@@ -1112,7 +1104,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
mt7530_mii_write(priv, MT7530_GMACCR, val); mt7530_mii_write(priv, MT7530_GMACCR, val);
mutex_unlock(&bus->mdio_lock); mt7530_mutex_unlock(priv);
return 0; return 0;
} }
...@@ -1913,10 +1905,10 @@ mt7530_irq_thread_fn(int irq, void *dev_id) ...@@ -1913,10 +1905,10 @@ mt7530_irq_thread_fn(int irq, void *dev_id)
u32 val; u32 val;
int p; int p;
mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
val = mt7530_mii_read(priv, MT7530_SYS_INT_STS); val = mt7530_mii_read(priv, MT7530_SYS_INT_STS);
mt7530_mii_write(priv, MT7530_SYS_INT_STS, val); mt7530_mii_write(priv, MT7530_SYS_INT_STS, val);
mutex_unlock(&priv->bus->mdio_lock); mt7530_mutex_unlock(priv);
for (p = 0; p < MT7530_NUM_PHYS; p++) { for (p = 0; p < MT7530_NUM_PHYS; p++) {
if (BIT(p) & val) { if (BIT(p) & val) {
...@@ -1952,7 +1944,7 @@ mt7530_irq_bus_lock(struct irq_data *d) ...@@ -1952,7 +1944,7 @@ mt7530_irq_bus_lock(struct irq_data *d)
{ {
struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); mt7530_mutex_lock(priv);
} }
static void static void
...@@ -1961,7 +1953,7 @@ mt7530_irq_bus_sync_unlock(struct irq_data *d) ...@@ -1961,7 +1953,7 @@ mt7530_irq_bus_sync_unlock(struct irq_data *d)
struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
mutex_unlock(&priv->bus->mdio_lock); mt7530_mutex_unlock(priv);
} }
static struct irq_chip mt7530_irq_chip = { static struct irq_chip mt7530_irq_chip = {
...@@ -1989,6 +1981,47 @@ static const struct irq_domain_ops mt7530_irq_domain_ops = { ...@@ -1989,6 +1981,47 @@ static const struct irq_domain_ops mt7530_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell, .xlate = irq_domain_xlate_onecell,
}; };
static void
mt7988_irq_mask(struct irq_data *d)
{
struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
priv->irq_enable &= ~BIT(d->hwirq);
mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
}
static void
mt7988_irq_unmask(struct irq_data *d)
{
struct mt7530_priv *priv = irq_data_get_irq_chip_data(d);
priv->irq_enable |= BIT(d->hwirq);
mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable);
}
static struct irq_chip mt7988_irq_chip = {
.name = KBUILD_MODNAME,
.irq_mask = mt7988_irq_mask,
.irq_unmask = mt7988_irq_unmask,
};
static int
mt7988_irq_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, domain->host_data);
irq_set_chip_and_handler(irq, &mt7988_irq_chip, handle_simple_irq);
irq_set_nested_thread(irq, true);
irq_set_noprobe(irq);
return 0;
}
static const struct irq_domain_ops mt7988_irq_domain_ops = {
.map = mt7988_irq_map,
.xlate = irq_domain_xlate_onecell,
};
static void static void
mt7530_setup_mdio_irq(struct mt7530_priv *priv) mt7530_setup_mdio_irq(struct mt7530_priv *priv)
{ {
...@@ -2023,8 +2056,15 @@ mt7530_setup_irq(struct mt7530_priv *priv) ...@@ -2023,8 +2056,15 @@ mt7530_setup_irq(struct mt7530_priv *priv)
return priv->irq ? : -EINVAL; return priv->irq ? : -EINVAL;
} }
if (priv->id == ID_MT7988)
priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
&mt7988_irq_domain_ops,
priv);
else
priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS,
&mt7530_irq_domain_ops, priv); &mt7530_irq_domain_ops,
priv);
if (!priv->irq_domain) { if (!priv->irq_domain) {
dev_err(dev, "failed to create IRQ domain\n"); dev_err(dev, "failed to create IRQ domain\n");
return -ENOMEM; return -ENOMEM;
...@@ -2308,12 +2348,65 @@ mt7530_setup(struct dsa_switch *ds) ...@@ -2308,12 +2348,65 @@ mt7530_setup(struct dsa_switch *ds)
return 0; return 0;
} }
static int
mt7531_setup_common(struct dsa_switch *ds)
{
struct mt7530_priv *priv = ds->priv;
struct dsa_port *cpu_dp;
int ret, i;
/* BPDU to CPU port */
dsa_switch_for_each_cpu_port(cpu_dp, ds) {
mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
BIT(cpu_dp->index));
break;
}
mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK,
MT753X_BPDU_CPU_ONLY);
/* Enable and reset MIB counters */
mt7530_mib_reset(ds);
for (i = 0; i < MT7530_NUM_PORTS; i++) {
/* Disable forwarding by default on all ports */
mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
PCR_MATRIX_CLR);
/* Disable learning by default on all ports */
mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
if (dsa_is_cpu_port(ds, i)) {
ret = mt753x_cpu_port_enable(ds, i);
if (ret)
return ret;
} else {
mt7530_port_disable(ds, i);
/* Set default PVID to 0 on all user ports */
mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
G0_PORT_VID_DEF);
}
/* Enable consistent egress tag */
mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
}
/* Flush the FDB table */
ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
if (ret < 0)
return ret;
return 0;
}
static int static int
mt7531_setup(struct dsa_switch *ds) mt7531_setup(struct dsa_switch *ds)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
struct mt7530_dummy_poll p; struct mt7530_dummy_poll p;
struct dsa_port *cpu_dp;
u32 val, id; u32 val, id;
int ret, i; int ret, i;
...@@ -2391,44 +2484,7 @@ mt7531_setup(struct dsa_switch *ds) ...@@ -2391,44 +2484,7 @@ mt7531_setup(struct dsa_switch *ds)
mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2, mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2,
CORE_PLL_GROUP4, val); CORE_PLL_GROUP4, val);
/* BPDU to CPU port */ mt7531_setup_common(ds);
dsa_switch_for_each_cpu_port(cpu_dp, ds) {
mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
BIT(cpu_dp->index));
break;
}
mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK,
MT753X_BPDU_CPU_ONLY);
/* Enable and reset MIB counters */
mt7530_mib_reset(ds);
for (i = 0; i < MT7530_NUM_PORTS; i++) {
/* Disable forwarding by default on all ports */
mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
PCR_MATRIX_CLR);
/* Disable learning by default on all ports */
mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
if (dsa_is_cpu_port(ds, i)) {
ret = mt753x_cpu_port_enable(ds, i);
if (ret)
return ret;
} else {
mt7530_port_disable(ds, i);
/* Set default PVID to 0 on all user ports */
mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK,
G0_PORT_VID_DEF);
}
/* Enable consistent egress tag */
mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
}
/* Setup VLAN ID 0 for VLAN-unaware bridges */ /* Setup VLAN ID 0 for VLAN-unaware bridges */
ret = mt7530_setup_vlan0(priv); ret = mt7530_setup_vlan0(priv);
...@@ -2438,11 +2494,6 @@ mt7531_setup(struct dsa_switch *ds) ...@@ -2438,11 +2494,6 @@ mt7531_setup(struct dsa_switch *ds)
ds->assisted_learning_on_cpu_port = true; ds->assisted_learning_on_cpu_port = true;
ds->mtu_enforcement_ingress = true; ds->mtu_enforcement_ingress = true;
/* Flush the FDB table */
ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
if (ret < 0)
return ret;
return 0; return 0;
} }
...@@ -2508,6 +2559,25 @@ static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, ...@@ -2508,6 +2559,25 @@ static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,
} }
} }
static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
phy_interface_zero(config->supported_interfaces);
switch (port) {
case 0 ... 4: /* Internal phy */
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break;
case 6:
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10000FD;
}
}
static int static int
mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
{ {
...@@ -2583,6 +2653,17 @@ static bool mt753x_is_mac_port(u32 port) ...@@ -2583,6 +2653,17 @@ static bool mt753x_is_mac_port(u32 port)
return (port == 5 || port == 6); return (port == 5 || port == 6);
} }
static int
mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
phy_interface_t interface)
{
if (dsa_is_cpu_port(ds, port) &&
interface == PHY_INTERFACE_MODE_INTERNAL)
return 0;
return -EINVAL;
}
static int static int
mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
phy_interface_t interface) phy_interface_t interface)
...@@ -2653,7 +2734,8 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, ...@@ -2653,7 +2734,8 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
switch (port) { switch (port) {
case 0 ... 4: /* Internal phy */ case 0 ... 4: /* Internal phy */
if (state->interface != PHY_INTERFACE_MODE_GMII) if (state->interface != PHY_INTERFACE_MODE_GMII &&
state->interface != PHY_INTERFACE_MODE_INTERNAL)
goto unsupported; goto unsupported;
break; break;
case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
...@@ -2731,7 +2813,8 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, ...@@ -2731,7 +2813,8 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
/* MT753x MAC works in 1G full duplex mode for all up-clocked /* MT753x MAC works in 1G full duplex mode for all up-clocked
* variants. * variants.
*/ */
if (interface == PHY_INTERFACE_MODE_TRGMII || if (interface == PHY_INTERFACE_MODE_INTERNAL ||
interface == PHY_INTERFACE_MODE_TRGMII ||
(phy_interface_mode_is_8023z(interface))) { (phy_interface_mode_is_8023z(interface))) {
speed = SPEED_1000; speed = SPEED_1000;
duplex = DUPLEX_FULL; duplex = DUPLEX_FULL;
...@@ -2811,6 +2894,21 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port) ...@@ -2811,6 +2894,21 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port)
return 0; return 0;
} }
static int
mt7988_cpu_port_config(struct dsa_switch *ds, int port)
{
struct mt7530_priv *priv = ds->priv;
mt7530_write(priv, MT7530_PMCR_P(port),
PMCR_CPU_PORT_SETTING(priv->id));
mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED,
PHY_INTERFACE_MODE_INTERNAL, NULL,
SPEED_10000, DUPLEX_FULL, true, true);
return 0;
}
static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config) struct phylink_config *config)
{ {
...@@ -2895,57 +2993,10 @@ static const struct phylink_pcs_ops mt7530_pcs_ops = { ...@@ -2895,57 +2993,10 @@ static const struct phylink_pcs_ops mt7530_pcs_ops = {
.pcs_an_restart = mt7530_pcs_an_restart, .pcs_an_restart = mt7530_pcs_an_restart,
}; };
static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
{
struct mt7530_priv *priv = context;
*val = mt7530_read(priv, reg);
return 0;
};
static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
{
struct mt7530_priv *priv = context;
mt7530_write(priv, reg, val);
return 0;
};
static int mt7530_regmap_update_bits(void *context, unsigned int reg,
unsigned int mask, unsigned int val)
{
struct mt7530_priv *priv = context;
mt7530_rmw(priv, reg, mask, val);
return 0;
};
static const struct regmap_bus mt7531_regmap_bus = {
.reg_write = mt7530_regmap_write,
.reg_read = mt7530_regmap_read,
.reg_update_bits = mt7530_regmap_update_bits,
};
#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
{ \
.name = _name, \
.reg_bits = 16, \
.val_bits = 32, \
.reg_stride = 4, \
.reg_base = _reg_base, \
.max_register = 0x17c, \
}
static const struct regmap_config mt7531_pcs_config[] = {
MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
};
static int static int
mt753x_setup(struct dsa_switch *ds) mt753x_setup(struct dsa_switch *ds)
{ {
struct mt7530_priv *priv = ds->priv; struct mt7530_priv *priv = ds->priv;
struct regmap *regmap;
int i, ret; int i, ret;
/* Initialise the PCS devices */ /* Initialise the PCS devices */
...@@ -2967,16 +3018,6 @@ mt753x_setup(struct dsa_switch *ds) ...@@ -2967,16 +3018,6 @@ mt753x_setup(struct dsa_switch *ds)
if (ret && priv->irq) if (ret && priv->irq)
mt7530_free_irq_common(priv); mt7530_free_irq_common(priv);
if (priv->id == ID_MT7531)
for (i = 0; i < 2; i++) {
regmap = devm_regmap_init(ds->dev,
&mt7531_regmap_bus, priv,
&mt7531_pcs_config[i]);
priv->ports[5 + i].sgmii_pcs =
mtk_pcs_lynxi_create(ds->dev, regmap,
MT7531_PHYA_CTRL_SIGNAL3, 0);
}
return ret; return ret;
} }
...@@ -3010,7 +3051,28 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, ...@@ -3010,7 +3051,28 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
return 0; return 0;
} }
static const struct dsa_switch_ops mt7530_switch_ops = { static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
{
return 0;
}
static int mt7988_setup(struct dsa_switch *ds)
{
struct mt7530_priv *priv = ds->priv;
/* Reset the switch */
reset_control_assert(priv->rstc);
usleep_range(20, 50);
reset_control_deassert(priv->rstc);
usleep_range(20, 50);
/* Reset the switch PHYs */
mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST);
return mt7531_setup_common(ds);
}
const struct dsa_switch_ops mt7530_switch_ops = {
.get_tag_protocol = mtk_get_tag_protocol, .get_tag_protocol = mtk_get_tag_protocol,
.setup = mt753x_setup, .setup = mt753x_setup,
.get_strings = mt7530_get_strings, .get_strings = mt7530_get_strings,
...@@ -3044,8 +3106,9 @@ static const struct dsa_switch_ops mt7530_switch_ops = { ...@@ -3044,8 +3106,9 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
.get_mac_eee = mt753x_get_mac_eee, .get_mac_eee = mt753x_get_mac_eee,
.set_mac_eee = mt753x_set_mac_eee, .set_mac_eee = mt753x_set_mac_eee,
}; };
EXPORT_SYMBOL_GPL(mt7530_switch_ops);
static const struct mt753x_info mt753x_table[] = { const struct mt753x_info mt753x_table[] = {
[ID_MT7621] = { [ID_MT7621] = {
.id = ID_MT7621, .id = ID_MT7621,
.pcs_ops = &mt7530_pcs_ops, .pcs_ops = &mt7530_pcs_ops,
...@@ -3083,53 +3146,38 @@ static const struct mt753x_info mt753x_table[] = { ...@@ -3083,53 +3146,38 @@ static const struct mt753x_info mt753x_table[] = {
.mac_port_get_caps = mt7531_mac_port_get_caps, .mac_port_get_caps = mt7531_mac_port_get_caps,
.mac_port_config = mt7531_mac_config, .mac_port_config = mt7531_mac_config,
}, },
[ID_MT7988] = {
.id = ID_MT7988,
.pcs_ops = &mt7530_pcs_ops,
.sw_setup = mt7988_setup,
.phy_read_c22 = mt7531_ind_c22_phy_read,
.phy_write_c22 = mt7531_ind_c22_phy_write,
.phy_read_c45 = mt7531_ind_c45_phy_read,
.phy_write_c45 = mt7531_ind_c45_phy_write,
.pad_setup = mt7988_pad_setup,
.cpu_port_config = mt7988_cpu_port_config,
.mac_port_get_caps = mt7988_mac_port_get_caps,
.mac_port_config = mt7988_mac_config,
},
}; };
EXPORT_SYMBOL_GPL(mt753x_table);
static const struct of_device_id mt7530_of_match[] = { int
{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, mt7530_probe_common(struct mt7530_priv *priv)
{ .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)
{ {
struct mt7530_priv *priv; struct device *dev = priv->dev;
struct device_node *dn;
dn = mdiodev->dev.of_node;
priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL);
if (!priv->ds) if (!priv->ds)
return -ENOMEM; return -ENOMEM;
priv->ds->dev = &mdiodev->dev; priv->ds->dev = dev;
priv->ds->num_ports = MT7530_NUM_PORTS; priv->ds->num_ports = MT7530_NUM_PORTS;
/* Use medatek,mcm property to distinguish hardware type that would
* casues a little bit differences on power-on sequence.
*/
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);
}
}
/* Get the hardware identifier from the devicetree node. /* Get the hardware identifier from the devicetree node.
* We will need it for some of the clock and regulator setup. * We will need it for some of the clock and regulator setup.
*/ */
priv->info = of_device_get_match_data(&mdiodev->dev); priv->info = of_device_get_match_data(dev);
if (!priv->info) if (!priv->info)
return -EINVAL; return -EINVAL;
...@@ -3143,94 +3191,27 @@ mt7530_probe(struct mdio_device *mdiodev) ...@@ -3143,94 +3191,27 @@ mt7530_probe(struct mdio_device *mdiodev)
return -EINVAL; return -EINVAL;
priv->id = priv->info->id; priv->id = priv->info->id;
priv->dev = dev;
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);
}
/* 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.
*/
if (!priv->mcm) {
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);
}
}
priv->bus = mdiodev->bus;
priv->dev = &mdiodev->dev;
priv->ds->priv = priv; priv->ds->priv = priv;
priv->ds->ops = &mt7530_switch_ops; priv->ds->ops = &mt7530_switch_ops;
mutex_init(&priv->reg_mutex); mutex_init(&priv->reg_mutex);
dev_set_drvdata(&mdiodev->dev, priv); dev_set_drvdata(dev, priv);
return dsa_register_switch(priv->ds); return 0;
} }
EXPORT_SYMBOL_GPL(mt7530_probe_common);
static void void
mt7530_remove(struct mdio_device *mdiodev) mt7530_remove_common(struct mt7530_priv *priv)
{ {
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);
if (priv->irq) if (priv->irq)
mt7530_free_irq(priv); mt7530_free_irq(priv);
dsa_unregister_switch(priv->ds); dsa_unregister_switch(priv->ds);
for (i = 0; i < 2; ++i)
mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
mutex_destroy(&priv->reg_mutex); mutex_destroy(&priv->reg_mutex);
} }
EXPORT_SYMBOL_GPL(mt7530_remove_common);
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",
.of_match_table = mt7530_of_match,
},
};
mdio_module_driver(mt7530_mdio_driver);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch"); MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch");
......
...@@ -18,6 +18,7 @@ enum mt753x_id { ...@@ -18,6 +18,7 @@ enum mt753x_id {
ID_MT7530 = 0, ID_MT7530 = 0,
ID_MT7621 = 1, ID_MT7621 = 1,
ID_MT7531 = 2, ID_MT7531 = 2,
ID_MT7988 = 3,
}; };
#define NUM_TRGMII_CTRL 5 #define NUM_TRGMII_CTRL 5
...@@ -54,11 +55,11 @@ enum mt753x_id { ...@@ -54,11 +55,11 @@ enum mt753x_id {
#define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16) #define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16)
#define MT7531_CPU_PMAP_MASK GENMASK(7, 0) #define MT7531_CPU_PMAP_MASK GENMASK(7, 0)
#define MT753X_MIRROR_REG(id) (((id) == ID_MT7531) ? \ #define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
MT7531_CFC : MT7530_MFC) MT7531_CFC : MT7530_MFC)
#define MT753X_MIRROR_EN(id) (((id) == ID_MT7531) ? \ #define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
MT7531_MIRROR_EN : MIRROR_EN) MT7531_MIRROR_EN : MIRROR_EN)
#define MT753X_MIRROR_MASK(id) (((id) == ID_MT7531) ? \ #define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
MT7531_MIRROR_MASK : MIRROR_MASK) MT7531_MIRROR_MASK : MIRROR_MASK)
/* Registers for BPDU and PAE frame control*/ /* Registers for BPDU and PAE frame control*/
...@@ -295,9 +296,8 @@ enum mt7530_vlan_port_acc_frm { ...@@ -295,9 +296,8 @@ enum mt7530_vlan_port_acc_frm {
MT7531_FORCE_DPX | \ MT7531_FORCE_DPX | \
MT7531_FORCE_RX_FC | \ MT7531_FORCE_RX_FC | \
MT7531_FORCE_TX_FC) MT7531_FORCE_TX_FC)
#define PMCR_FORCE_MODE_ID(id) (((id) == ID_MT7531) ? \ #define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
MT7531_FORCE_MODE : \ MT7531_FORCE_MODE : PMCR_FORCE_MODE)
PMCR_FORCE_MODE)
#define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
...@@ -682,24 +682,6 @@ enum p5_interface_select { ...@@ -682,24 +682,6 @@ enum p5_interface_select {
P5_INTF_SEL_GMAC5_SGMII, P5_INTF_SEL_GMAC5_SGMII,
}; };
static const char *p5_intf_modes(unsigned int p5_interface)
{
switch (p5_interface) {
case P5_DISABLED:
return "DISABLED";
case P5_INTF_SEL_PHY_P0:
return "PHY P0";
case P5_INTF_SEL_PHY_P4:
return "PHY P4";
case P5_INTF_SEL_GMAC5:
return "GMAC5";
case P5_INTF_SEL_GMAC5_SGMII:
return "GMAC5_SGMII";
default:
return "unknown";
}
}
struct mt7530_priv; struct mt7530_priv;
struct mt753x_pcs { struct mt753x_pcs {
...@@ -754,6 +736,7 @@ struct mt753x_info { ...@@ -754,6 +736,7 @@ struct mt753x_info {
* @dev: The device pointer * @dev: The device pointer
* @ds: The pointer to the dsa core structure * @ds: The pointer to the dsa core structure
* @bus: The bus used for the device and built-in PHY * @bus: The bus used for the device and built-in PHY
* @regmap: The regmap instance representing all switch registers
* @rstc: The pointer to reset control used by MCM * @rstc: The pointer to reset control used by MCM
* @core_pwr: The power supplied into the core * @core_pwr: The power supplied into the core
* @io_pwr: The power supplied into the I/O * @io_pwr: The power supplied into the I/O
...@@ -774,6 +757,7 @@ struct mt7530_priv { ...@@ -774,6 +757,7 @@ struct mt7530_priv {
struct device *dev; struct device *dev;
struct dsa_switch *ds; struct dsa_switch *ds;
struct mii_bus *bus; struct mii_bus *bus;
struct regmap *regmap;
struct reset_control *rstc; struct reset_control *rstc;
struct regulator *core_pwr; struct regulator *core_pwr;
struct regulator *io_pwr; struct regulator *io_pwr;
...@@ -830,4 +814,10 @@ static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p, ...@@ -830,4 +814,10 @@ static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p,
p->reg = reg; p->reg = reg;
} }
int mt7530_probe_common(struct mt7530_priv *priv);
void mt7530_remove_common(struct mt7530_priv *priv);
extern const struct dsa_switch_ops mt7530_switch_ops;
extern const struct mt753x_info mt753x_table[];
#endif /* __MT7530_H */ #endif /* __MT7530_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment