diff --git a/MAINTAINERS b/MAINTAINERS index 4970a6c6a0a8..7812f0e251ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13175,9 +13175,11 @@ MEDIATEK SWITCH DRIVER M: Sean Wang M: Landen Chao M: DENG Qingfang +M: Daniel Golle L: netdev@vger.kernel.org S: Maintained F: drivers/net/dsa/mt7530-mdio.c +F: drivers/net/dsa/mt7530-mmio.c F: drivers/net/dsa/mt7530.* F: net/dsa/tag_mtk.c diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 941d020fe4b6..3ed5391bb18d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -39,6 +39,7 @@ config NET_DSA_MT7530 select NET_DSA_TAG_MTK select MEDIATEK_GE_PHY imply NET_DSA_MT7530_MDIO + imply NET_DSA_MT7530_MMIO help This enables support for the MediaTek MT7530 and MT7531 Ethernet switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT, @@ -55,6 +56,17 @@ config NET_DSA_MT7530_MDIO 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 tristate "Marvell 88E6060 ethernet switch chip support" select NET_DSA_TAG_TRAILER diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index c5da75cb0a6b..cb9a97340e58 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -8,6 +8,7 @@ endif obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.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_RZN1_A5PSW) += rzn1_a5psw.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c new file mode 100644 index 000000000000..1a3d4b692f34 --- /dev/null +++ b/drivers/net/dsa/mt7530-mmio.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MMIO)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 394fada7ddf1..e4bb5037d352 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1981,6 +1981,47 @@ static const struct irq_domain_ops mt7530_irq_domain_ops = { .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 mt7530_setup_mdio_irq(struct mt7530_priv *priv) { @@ -2015,8 +2056,15 @@ mt7530_setup_irq(struct mt7530_priv *priv) return priv->irq ? : -EINVAL; } - priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, - &mt7530_irq_domain_ops, priv); + 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, + &mt7530_irq_domain_ops, + priv); + if (!priv->irq_domain) { dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; @@ -2511,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 mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) { @@ -2586,6 +2653,17 @@ static bool mt753x_is_mac_port(u32 port) 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 mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) @@ -2656,7 +2734,8 @@ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, switch (port) { 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; break; case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ @@ -2734,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 * variants. */ - if (interface == PHY_INTERFACE_MODE_TRGMII || + if (interface == PHY_INTERFACE_MODE_INTERNAL || + interface == PHY_INTERFACE_MODE_TRGMII || (phy_interface_mode_is_8023z(interface))) { speed = SPEED_1000; duplex = DUPLEX_FULL; @@ -2814,6 +2894,21 @@ mt7531_cpu_port_config(struct dsa_switch *ds, int port) 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, struct phylink_config *config) { @@ -2956,6 +3051,27 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, return 0; } +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, .setup = mt753x_setup, @@ -3030,6 +3146,19 @@ const struct mt753x_info mt753x_table[] = { .mac_port_get_caps = mt7531_mac_port_get_caps, .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); diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index ce02aa592a7a..01db5c9724fa 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -18,6 +18,7 @@ enum mt753x_id { ID_MT7530 = 0, ID_MT7621 = 1, ID_MT7531 = 2, + ID_MT7988 = 3, }; #define NUM_TRGMII_CTRL 5 @@ -54,11 +55,11 @@ enum mt753x_id { #define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16) #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) -#define MT753X_MIRROR_EN(id) (((id) == ID_MT7531) ? \ +#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ 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) /* Registers for BPDU and PAE frame control*/ @@ -295,9 +296,8 @@ enum mt7530_vlan_port_acc_frm { MT7531_FORCE_DPX | \ MT7531_FORCE_RX_FC | \ MT7531_FORCE_TX_FC) -#define PMCR_FORCE_MODE_ID(id) (((id) == ID_MT7531) ? \ - MT7531_FORCE_MODE : \ - PMCR_FORCE_MODE) +#define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_FORCE_MODE : PMCR_FORCE_MODE) #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ PMCR_TX_FC_EN | PMCR_RX_FC_EN | \