mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
net: dsa: initial support for MT7628 embedded switch
Add support for the MT7628 embedded switch. The switch has 5 built-in 100Mbps user ports (ports 0-4) and one 1Gbps port that is internally attached to the SoCs CPU MAC and serves as the CPU port. The switch hardware has a very limited 16 entry VLAN table. Configuring VLANs is the only way to control switch forwarding. Currently 6 entries are used by tag_8021q to isolate the ports. Double tag feature is enabled to force the switch to append the VLAN tag even if the incoming packet is already tagged, this simulates VLAN-unaware functionality and simplifies the tagger implementation. Signed-off-by: Joris Vaisvila <joey@tinyisr.com> Reviewed-by: Daniel Golle <daniel@makrotopia.org> Link: https://patch.msgid.link/20260813190241.789323-5-joey@tinyisr.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
44204fd425
commit
15062bb05e
|
|
@ -63,6 +63,16 @@ config NET_DSA_MT7530_MMIO
|
|||
are directly mapped into the SoCs register space rather than being
|
||||
accessible via MDIO.
|
||||
|
||||
config NET_DSA_MT7628
|
||||
tristate "MediaTek MT7628 Embedded Ethernet switch support"
|
||||
depends on HAS_IOMEM && (SOC_MT7620 || COMPILE_TEST)
|
||||
select NET_DSA_TAG_MT7628
|
||||
select MEDIATEK_FE_SOC_PHY
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This enables support for the built-in Ethernet switch found
|
||||
in the MT7628 SoC.
|
||||
|
||||
config NET_DSA_MV88E6060
|
||||
tristate "Marvell 88E6060 ethernet switch chip support"
|
||||
select NET_DSA_TAG_TRAILER
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ obj-$(CONFIG_NET_DSA_KS8995) += ks8995.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_MT7628) += mt7628.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
|
||||
|
|
|
|||
652
drivers/net/dsa/mt7628.c
Normal file
652
drivers/net/dsa/mt7628.c
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Mediatek MT7628 Embedded Switch (ESW) DSA driver
|
||||
* Copyright (C) 2026 Joris Vaisvila <joey@tinyisr.com>
|
||||
*
|
||||
* Portions derived from OpenWRT esw_rt3050 driver:
|
||||
* Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
* Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name>
|
||||
* Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
* Copyright (C) 2016 Vittorio Gambaletta <openwrt@vittgam.net>
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/dsa/8021q.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <net/dsa.h>
|
||||
|
||||
#define MT7628_ESW_REG_IMR 0x04
|
||||
#define MT7628_ESW_REG_FCT0 0x08
|
||||
#define MT7628_ESW_REG_PFC1 0x14
|
||||
#define MT7628_ESW_REG_PVIDC(port) (0x40 + 4 * ((port) / 2))
|
||||
#define MT7628_ESW_REG_VLANI(vlan) (0x50 + 4 * ((vlan) / 2))
|
||||
#define MT7628_ESW_REG_VMSC(vlan) (0x70 + 4 * ((vlan) / 4))
|
||||
#define MT7628_ESW_REG_VUB(vlan) (0x100 + 4 * ((vlan) / 4))
|
||||
#define MT7628_ESW_REG_SOCPC 0x8c
|
||||
#define MT7628_ESW_REG_POC0 0x90
|
||||
#define MT7628_ESW_REG_POC2 0x98
|
||||
#define MT7628_ESW_REG_SGC 0x9c
|
||||
#define MT7628_ESW_REG_PCR0 0xc0
|
||||
#define MT7628_ESW_REG_PCR1 0xc4
|
||||
#define MT7628_ESW_REG_FPA2 0xc8
|
||||
#define MT7628_ESW_REG_FCT2 0xcc
|
||||
#define MT7628_ESW_REG_SGC2 0xe4
|
||||
|
||||
#define MT7628_ESW_FCT0_DROP_SET_TH GENMASK(7, 0)
|
||||
#define MT7628_ESW_FCT0_DROP_RLS_TH GENMASK(15, 8)
|
||||
#define MT7628_ESW_FCT0_FC_SET_TH GENMASK(23, 16)
|
||||
#define MT7628_ESW_FCT0_FC_RLS_TH GENMASK(31, 24)
|
||||
|
||||
#define MT7628_ESW_PFC1_EN_VLAN GENMASK(22, 16)
|
||||
|
||||
#define MT7628_ESW_PVID_S 12
|
||||
#define MT7628_ESW_PVID_M GENMASK(11, 0)
|
||||
#define MT7628_ESW_PVID_SHIFT(port) \
|
||||
(MT7628_ESW_PVID_S * ((port) % 2))
|
||||
#define MT7628_ESW_PVID_MASK(port) \
|
||||
(MT7628_ESW_PVID_M << MT7628_ESW_PVID_SHIFT(port))
|
||||
#define MT7628_ESW_PVID_PREP(port, pvid) \
|
||||
(((pvid) & MT7628_ESW_PVID_M) << MT7628_ESW_PVID_SHIFT(port))
|
||||
|
||||
#define MT7628_ESW_VID_S 12
|
||||
#define MT7628_ESW_VID_M GENMASK(11, 0)
|
||||
#define MT7628_ESW_VID_SHIFT(vlan) \
|
||||
(MT7628_ESW_VID_S * ((vlan) % 2))
|
||||
#define MT7628_ESW_VID_MASK(vlan) \
|
||||
(MT7628_ESW_VID_M << MT7628_ESW_VID_SHIFT(vlan))
|
||||
#define MT7628_ESW_VID_PREP(vlan, vid) \
|
||||
(((vid) & MT7628_ESW_VID_M) << MT7628_ESW_VID_SHIFT(vlan))
|
||||
|
||||
#define MT7628_ESW_VMSC_S 8
|
||||
#define MT7628_ESW_VMSC_M GENMASK(7, 0)
|
||||
#define MT7628_ESW_VMSC_SHIFT(vlan) \
|
||||
(MT7628_ESW_VMSC_S * ((vlan) % 4))
|
||||
#define MT7628_ESW_VMSC_MASK(vlan) \
|
||||
(MT7628_ESW_VMSC_M << MT7628_ESW_VMSC_SHIFT(vlan))
|
||||
#define MT7628_ESW_VMSC_PREP(vlan, vmsc) \
|
||||
(((vmsc) & MT7628_ESW_VMSC_M) << MT7628_ESW_VMSC_SHIFT(vlan))
|
||||
|
||||
#define MT7628_ESW_VUB_S 7
|
||||
#define MT7628_ESW_VUB_M GENMASK(6, 0)
|
||||
#define MT7628_ESW_VUB_SHIFT(vlan) \
|
||||
(MT7628_ESW_VUB_S * ((vlan) % 4))
|
||||
#define MT7628_ESW_VUB_MASK(vlan) \
|
||||
(MT7628_ESW_VUB_M << MT7628_ESW_VUB_SHIFT(vlan))
|
||||
#define MT7628_ESW_VUB_PREP(vlan, vub) \
|
||||
(((vub) & MT7628_ESW_VUB_M) << MT7628_ESW_VUB_SHIFT(vlan))
|
||||
|
||||
#define MT7628_ESW_SOCPC_CRC_PADDING BIT(25)
|
||||
#define MT7628_ESW_SOCPC_DISBC2CPU GENMASK(22, 16)
|
||||
#define MT7628_ESW_SOCPC_DISMC2CPU GENMASK(14, 8)
|
||||
#define MT7628_ESW_SOCPC_DISUN2CPU GENMASK(6, 0)
|
||||
|
||||
#define MT7628_ESW_POC0_PORT_DISABLE GENMASK(29, 23)
|
||||
|
||||
#define MT7628_ESW_POC2_PER_VLAN_UNTAG_EN BIT(15)
|
||||
|
||||
#define MT7628_ESW_SGC_AGING_INTERVAL GENMASK(3, 0)
|
||||
#define MT7628_ESW_BC_STORM_PROT GENMASK(5, 4)
|
||||
#define MT7628_ESW_PKT_MAX_LEN GENMASK(7, 6)
|
||||
#define MT7628_ESW_DIS_PKT_ABORT BIT(8)
|
||||
#define MT7628_ESW_ADDRESS_HASH_ALG GENMASK(10, 9)
|
||||
#define MT7628_ESW_DISABLE_TX_BACKOFF BIT(11)
|
||||
#define MT7628_ESW_BP_JAM_CNT GENMASK(15, 12)
|
||||
#define MT7628_ESW_DISMIIPORT_WASTX GENMASK(17, 16)
|
||||
#define MT7628_ESW_BP_MODE GENMASK(19, 18)
|
||||
#define MT7628_ESW_BISH_DIS BIT(20)
|
||||
#define MT7628_ESW_BISH_TH GENMASK(22, 21)
|
||||
#define MT7628_ESW_LED_FLASH_TIME GENMASK(24, 23)
|
||||
#define MT7628_ESW_RMC_RULE GENMASK(26, 25)
|
||||
#define MT7628_ESW_IP_MULT_RULE GENMASK(28, 27)
|
||||
#define MT7628_ESW_LEN_ERR_CHK BIT(29)
|
||||
#define MT7628_ESW_BKOFF_ALG BIT(30)
|
||||
|
||||
#define MT7628_ESW_PCR0_WT_NWAY_DATA GENMASK(31, 16)
|
||||
#define MT7628_ESW_PCR0_RD_PHY_CMD BIT(14)
|
||||
#define MT7628_ESW_PCR0_WT_PHY_CMD BIT(13)
|
||||
#define MT7628_ESW_PCR0_CPU_PHY_REG GENMASK(12, 8)
|
||||
#define MT7628_ESW_PCR0_CPU_PHY_ADDR GENMASK(4, 0)
|
||||
|
||||
#define MT7628_ESW_PCR1_RD_DATA GENMASK(31, 16)
|
||||
#define MT7628_ESW_PCR1_RD_DONE BIT(1)
|
||||
#define MT7628_ESW_PCR1_WT_DONE BIT(0)
|
||||
|
||||
#define MT7628_ESW_FPA2_AP_EN BIT(29)
|
||||
#define MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE GENMASK(28, 24)
|
||||
#define MT7628_ESW_FPA2_FORCE_RGMII_LINK1 BIT(13)
|
||||
#define MT7628_ESW_FPA2_FORCE_RGMII_EN1 BIT(11)
|
||||
|
||||
#define MT7628_ESW_FCT2_MUST_DROP_RLS_TH GENMASK(17, 13)
|
||||
#define MT7628_ESW_FCT2_MUST_DROP_SET_TH GENMASK(12, 8)
|
||||
#define MT7628_ESW_FCT2_MC_PER_PORT_TH GENMASK(5, 0)
|
||||
|
||||
#define MT7628_ESW_SGC2_SPECIAL_TAG_EN BIT(23)
|
||||
#define MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP GENMASK(22, 16)
|
||||
#define MT7628_ESW_SGC2_DOUBLE_TAG_EN GENMASK(6, 0)
|
||||
|
||||
#define MT7628_ESW_PORTS_NOCPU GENMASK(5, 0)
|
||||
#define MT7628_ESW_PORTS_CPU BIT(6)
|
||||
#define MT7628_ESW_PORTS_ALL GENMASK(6, 0)
|
||||
|
||||
#define MT7628_ESW_NUM_PORTS 7
|
||||
#define MT7628_NUM_VLANS 16
|
||||
|
||||
static const struct regmap_config mt7628_esw_regmap_cfg = {
|
||||
.name = "mt7628-esw",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
struct mt7628_vlan {
|
||||
bool active;
|
||||
u8 members;
|
||||
u8 untag;
|
||||
u16 vid;
|
||||
};
|
||||
|
||||
struct mt7628_esw {
|
||||
struct reset_control *rst_ephy;
|
||||
struct reset_control *rst_esw;
|
||||
struct regmap *regmap;
|
||||
struct dsa_switch *ds;
|
||||
u16 tag_8021q_pvid[MT7628_ESW_NUM_PORTS];
|
||||
struct mt7628_vlan vlans[MT7628_NUM_VLANS];
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int mt7628_mii_read(struct mii_bus *bus, int port, int regnum)
|
||||
{
|
||||
struct mt7628_esw *esw = bus->priv;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* RD_DONE bit is read to clear. Read PCR1 once to acknowledge any
|
||||
* stale completion indicator before starting a new transaction.
|
||||
*/
|
||||
ret = regmap_read(esw->regmap, MT7628_ESW_REG_PCR1, &val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_write(esw->regmap, MT7628_ESW_REG_PCR0,
|
||||
FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG,
|
||||
regnum) |
|
||||
FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR,
|
||||
port) | MT7628_ESW_PCR0_RD_PHY_CMD);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
|
||||
(val & MT7628_ESW_PCR1_RD_DONE), 10,
|
||||
5000);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return FIELD_GET(MT7628_ESW_PCR1_RD_DATA, val);
|
||||
|
||||
out:
|
||||
dev_err(&bus->dev, "read failed. MDIO timeout?\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt7628_mii_write(struct mii_bus *bus, int port, int regnum, u16 dat)
|
||||
{
|
||||
struct mt7628_esw *esw = bus->priv;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* WT_DONE bit is read to clear. Read PCR1 once to acknowledge any
|
||||
* stale completion indicator before starting a new transaction.
|
||||
*/
|
||||
ret = regmap_read(esw->regmap, MT7628_ESW_REG_PCR1, &val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_write(esw->regmap, MT7628_ESW_REG_PCR0,
|
||||
FIELD_PREP(MT7628_ESW_PCR0_WT_NWAY_DATA, dat) |
|
||||
FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG,
|
||||
regnum) |
|
||||
FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR,
|
||||
port) | MT7628_ESW_PCR0_WT_PHY_CMD);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
|
||||
(val & MT7628_ESW_PCR1_WT_DONE), 10,
|
||||
5000);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
dev_err(&bus->dev, "write failed. MDIO timeout?\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt7628_setup_internal_mdio(struct dsa_switch *ds)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
struct device *dev = ds->dev;
|
||||
struct mii_bus *bus;
|
||||
|
||||
bus = devm_mdiobus_alloc(dev);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
bus->name = "MT7628 internal MDIO bus";
|
||||
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
|
||||
bus->priv = esw;
|
||||
bus->read = mt7628_mii_read;
|
||||
bus->write = mt7628_mii_write;
|
||||
bus->parent = dev;
|
||||
|
||||
ds->user_mii_bus = bus;
|
||||
bus->phy_mask = ~ds->phys_mii_mask;
|
||||
|
||||
return devm_mdiobus_register(dev, bus);
|
||||
}
|
||||
|
||||
static void mt7628_switch_init(struct dsa_switch *ds)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_FCT0,
|
||||
FIELD_PREP(MT7628_ESW_FCT0_DROP_SET_TH, 0x50) |
|
||||
FIELD_PREP(MT7628_ESW_FCT0_DROP_RLS_TH, 0x78) |
|
||||
FIELD_PREP(MT7628_ESW_FCT0_FC_SET_TH, 0xa0) |
|
||||
FIELD_PREP(MT7628_ESW_FCT0_FC_RLS_TH, 0xc8));
|
||||
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_FCT2,
|
||||
FIELD_PREP(MT7628_ESW_FCT2_MC_PER_PORT_TH, 0xc) |
|
||||
FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_SET_TH, 0x10) |
|
||||
FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_RLS_TH, 0x12));
|
||||
|
||||
/*
|
||||
* general switch configuration:
|
||||
* 300s aging interval
|
||||
* broadcast storm prevention disabled
|
||||
* max packet length 1536 bytes
|
||||
* disable collision 16 packet abort and late collision abort
|
||||
* use xor48 for address hashing
|
||||
* disable tx backoff
|
||||
* 10 packet back pressure jam
|
||||
* disable was_transmit
|
||||
* jam until BP condition released
|
||||
* 30ms LED flash
|
||||
* rmc tb fault to all ports
|
||||
* unmatched IGMP as broadcast
|
||||
*/
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_SGC,
|
||||
FIELD_PREP(MT7628_ESW_SGC_AGING_INTERVAL, 1) |
|
||||
FIELD_PREP(MT7628_ESW_BC_STORM_PROT, 0) |
|
||||
FIELD_PREP(MT7628_ESW_PKT_MAX_LEN, 0) |
|
||||
MT7628_ESW_DIS_PKT_ABORT |
|
||||
FIELD_PREP(MT7628_ESW_ADDRESS_HASH_ALG, 1) |
|
||||
MT7628_ESW_DISABLE_TX_BACKOFF |
|
||||
FIELD_PREP(MT7628_ESW_BP_JAM_CNT, 10) |
|
||||
FIELD_PREP(MT7628_ESW_DISMIIPORT_WASTX, 0) |
|
||||
FIELD_PREP(MT7628_ESW_BP_MODE, 0b10) |
|
||||
FIELD_PREP(MT7628_ESW_LED_FLASH_TIME, 0) |
|
||||
FIELD_PREP(MT7628_ESW_RMC_RULE, 0) |
|
||||
FIELD_PREP(MT7628_ESW_IP_MULT_RULE, 0));
|
||||
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_SOCPC,
|
||||
MT7628_ESW_SOCPC_CRC_PADDING |
|
||||
FIELD_PREP(MT7628_ESW_SOCPC_DISUN2CPU,
|
||||
MT7628_ESW_PORTS_CPU) |
|
||||
FIELD_PREP(MT7628_ESW_SOCPC_DISMC2CPU,
|
||||
MT7628_ESW_PORTS_CPU) |
|
||||
FIELD_PREP(MT7628_ESW_SOCPC_DISBC2CPU,
|
||||
MT7628_ESW_PORTS_CPU));
|
||||
|
||||
regmap_set_bits(esw->regmap, MT7628_ESW_REG_FPA2,
|
||||
MT7628_ESW_FPA2_FORCE_RGMII_EN1 |
|
||||
MT7628_ESW_FPA2_FORCE_RGMII_LINK1 |
|
||||
MT7628_ESW_FPA2_AP_EN);
|
||||
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_FPA2,
|
||||
MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE,
|
||||
FIELD_PREP(MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE, 31));
|
||||
|
||||
/* disable all interrupts */
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_IMR, 0);
|
||||
|
||||
/* enable MT7628 DSA tag on CPU port */
|
||||
regmap_write(esw->regmap, MT7628_ESW_REG_SGC2,
|
||||
MT7628_ESW_SGC2_SPECIAL_TAG_EN |
|
||||
FIELD_PREP(MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP,
|
||||
MT7628_ESW_PORTS_CPU));
|
||||
|
||||
/*
|
||||
* Double tag feature allows switch to always append the port PVID VLAN tag
|
||||
* regardless of if the incoming packet already has a VLAN tag.
|
||||
* This is enabled to simulate VLAN unawareness.
|
||||
*/
|
||||
regmap_set_bits(esw->regmap, MT7628_ESW_REG_SGC2,
|
||||
FIELD_PREP(MT7628_ESW_SGC2_DOUBLE_TAG_EN,
|
||||
MT7628_ESW_PORTS_NOCPU));
|
||||
|
||||
regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC2,
|
||||
MT7628_ESW_POC2_PER_VLAN_UNTAG_EN);
|
||||
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_PFC1,
|
||||
MT7628_ESW_PFC1_EN_VLAN,
|
||||
FIELD_PREP(MT7628_ESW_PFC1_EN_VLAN,
|
||||
MT7628_ESW_PORTS_ALL));
|
||||
}
|
||||
|
||||
static void mt7628_esw_set_pvid(struct mt7628_esw *esw, unsigned int port,
|
||||
unsigned int pvid)
|
||||
{
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_PVIDC(port),
|
||||
MT7628_ESW_PVID_MASK(port),
|
||||
MT7628_ESW_PVID_PREP(port, pvid));
|
||||
}
|
||||
|
||||
static void mt7628_esw_set_vlan_id(struct mt7628_esw *esw, unsigned int vlan,
|
||||
unsigned int vid)
|
||||
{
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_VLANI(vlan),
|
||||
MT7628_ESW_VID_MASK(vlan),
|
||||
MT7628_ESW_VID_PREP(vlan, vid));
|
||||
}
|
||||
|
||||
static void mt7628_esw_set_vmsc(struct mt7628_esw *esw, unsigned int vlan,
|
||||
unsigned int msc)
|
||||
{
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_VMSC(vlan),
|
||||
MT7628_ESW_VMSC_MASK(vlan),
|
||||
MT7628_ESW_VMSC_PREP(vlan, msc));
|
||||
}
|
||||
|
||||
static void mt7628_esw_set_vub(struct mt7628_esw *esw, unsigned int vlan,
|
||||
unsigned int vub)
|
||||
{
|
||||
regmap_update_bits(esw->regmap, MT7628_ESW_REG_VUB(vlan),
|
||||
MT7628_ESW_VUB_MASK(vlan),
|
||||
MT7628_ESW_VUB_PREP(vlan, vub));
|
||||
}
|
||||
|
||||
static void mt7628_vlan_sync(struct dsa_switch *ds)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MT7628_NUM_VLANS; i++) {
|
||||
struct mt7628_vlan *vlan = &esw->vlans[i];
|
||||
|
||||
mt7628_esw_set_vmsc(esw, i, vlan->members);
|
||||
mt7628_esw_set_vlan_id(esw, i, vlan->vid);
|
||||
mt7628_esw_set_vub(esw, i, vlan->untag);
|
||||
}
|
||||
|
||||
for (i = 0; i < ds->num_ports; i++)
|
||||
mt7628_esw_set_pvid(esw, i, esw->tag_8021q_pvid[i]);
|
||||
}
|
||||
|
||||
static int mt7628_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(esw->rst_esw);
|
||||
if (ret)
|
||||
return ret;
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
ret = reset_control_reset(esw->rst_ephy);
|
||||
if (ret)
|
||||
return ret;
|
||||
usleep_range(1000, 2000);
|
||||
/*
|
||||
* all MMIO reads hang if esw is not out of reset
|
||||
* ephy needs extra time to get out of reset or it ends up misconfigured
|
||||
*/
|
||||
|
||||
mt7628_switch_init(ds);
|
||||
|
||||
ret = mt7628_setup_internal_mdio(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtnl_lock();
|
||||
ret = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));
|
||||
rtnl_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt7628_port_enable(struct dsa_switch *ds, int port,
|
||||
struct phy_device *phy)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
|
||||
/*
|
||||
* All switch ports are disabled by esw reset and enabled here by DSA.
|
||||
*/
|
||||
regmap_clear_bits(esw->regmap, MT7628_ESW_REG_POC0,
|
||||
FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7628_port_disable(struct dsa_switch *ds, int port)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
|
||||
regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC0,
|
||||
FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
|
||||
}
|
||||
|
||||
static enum dsa_tag_protocol
|
||||
mt7628_get_tag_proto(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp)
|
||||
{
|
||||
return DSA_TAG_PROTO_MT7628;
|
||||
}
|
||||
|
||||
static void mt7628_phylink_get_caps(struct dsa_switch *ds, int port,
|
||||
struct phylink_config *config)
|
||||
{
|
||||
switch (port) {
|
||||
case 6:
|
||||
config->mac_capabilities |= MAC_1000;
|
||||
fallthrough;
|
||||
case 0 ... 4:
|
||||
config->mac_capabilities |= MAC_100 | MAC_10;
|
||||
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
|
||||
config->supported_interfaces);
|
||||
break;
|
||||
default:
|
||||
break; /* port 5 does not exist on MT7628 */
|
||||
}
|
||||
}
|
||||
|
||||
static int mt7628_dsa_8021q_vlan_add(struct dsa_switch *ds, int port,
|
||||
u16 vid, u16 flags)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
struct mt7628_vlan *vlan = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MT7628_NUM_VLANS; i++) {
|
||||
struct mt7628_vlan *check_vlan = &esw->vlans[i];
|
||||
|
||||
if (!check_vlan->active && !vlan)
|
||||
vlan = check_vlan;
|
||||
|
||||
if (check_vlan->active && check_vlan->vid == vid) {
|
||||
vlan = check_vlan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vlan)
|
||||
return -ENOSPC;
|
||||
|
||||
vlan->vid = vid;
|
||||
vlan->active = true;
|
||||
vlan->members |= BIT(port);
|
||||
|
||||
if (flags & BRIDGE_VLAN_INFO_PVID)
|
||||
esw->tag_8021q_pvid[port] = vid;
|
||||
|
||||
if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
|
||||
vlan->untag |= BIT(port);
|
||||
|
||||
mt7628_vlan_sync(ds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt7628_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
|
||||
{
|
||||
struct mt7628_esw *esw = ds->priv;
|
||||
struct mt7628_vlan *vlan = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MT7628_NUM_VLANS; i++) {
|
||||
struct mt7628_vlan *check_vlan = &esw->vlans[i];
|
||||
|
||||
if (!check_vlan->active || check_vlan->vid != vid)
|
||||
continue;
|
||||
vlan = check_vlan;
|
||||
break;
|
||||
}
|
||||
if (!vlan)
|
||||
return -ENOENT;
|
||||
|
||||
if (esw->tag_8021q_pvid[port] == vid)
|
||||
esw->tag_8021q_pvid[port] = 0;
|
||||
|
||||
vlan->members &= ~BIT(port);
|
||||
vlan->untag &= ~BIT(port);
|
||||
|
||||
if (!vlan->members) {
|
||||
vlan->active = false;
|
||||
vlan->vid = 0;
|
||||
}
|
||||
|
||||
mt7628_vlan_sync(ds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7628_teardown(struct dsa_switch *ds)
|
||||
{
|
||||
rtnl_lock();
|
||||
dsa_tag_8021q_unregister(ds);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static const struct dsa_switch_ops mt7628_switch_ops = {
|
||||
.get_tag_protocol = mt7628_get_tag_proto,
|
||||
.setup = mt7628_setup,
|
||||
.teardown = mt7628_teardown,
|
||||
.port_enable = mt7628_port_enable,
|
||||
.port_disable = mt7628_port_disable,
|
||||
.phylink_get_caps = mt7628_phylink_get_caps,
|
||||
.tag_8021q_vlan_add = mt7628_dsa_8021q_vlan_add,
|
||||
.tag_8021q_vlan_del = mt7628_dsa_8021q_vlan_del,
|
||||
};
|
||||
|
||||
static int mt7628_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mt7628_esw *esw;
|
||||
struct dsa_switch *ds;
|
||||
void __iomem *base;
|
||||
|
||||
ds = devm_kzalloc(&pdev->dev, sizeof(*ds), GFP_KERNEL);
|
||||
if (!ds)
|
||||
return -ENOMEM;
|
||||
|
||||
esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
|
||||
if (!esw)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
esw->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&mt7628_esw_regmap_cfg);
|
||||
if (IS_ERR(esw->regmap))
|
||||
return PTR_ERR(esw->regmap);
|
||||
|
||||
esw->rst_ephy = devm_reset_control_get_exclusive(&pdev->dev, "ephy");
|
||||
if (IS_ERR(esw->rst_ephy))
|
||||
return dev_err_probe(dev, PTR_ERR(esw->rst_ephy),
|
||||
"failed to get EPHY reset\n");
|
||||
|
||||
esw->rst_esw = devm_reset_control_get_exclusive(&pdev->dev, "esw");
|
||||
if (IS_ERR(esw->rst_esw))
|
||||
return dev_err_probe(dev, PTR_ERR(esw->rst_esw),
|
||||
"failed to get ESW reset\n");
|
||||
|
||||
ds->dev = dev;
|
||||
ds->num_ports = MT7628_ESW_NUM_PORTS;
|
||||
ds->ops = &mt7628_switch_ops;
|
||||
ds->priv = esw;
|
||||
esw->ds = ds;
|
||||
esw->dev = dev;
|
||||
dev_set_drvdata(dev, esw);
|
||||
|
||||
return dsa_register_switch(ds);
|
||||
}
|
||||
|
||||
static void mt7628_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mt7628_esw *esw = platform_get_drvdata(pdev);
|
||||
|
||||
if (!esw)
|
||||
return;
|
||||
|
||||
dsa_unregister_switch(esw->ds);
|
||||
}
|
||||
|
||||
static void mt7628_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct mt7628_esw *esw = platform_get_drvdata(pdev);
|
||||
|
||||
if (!esw)
|
||||
return;
|
||||
|
||||
dsa_switch_shutdown(esw->ds);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
static const struct of_device_id mt7628_of_match[] = {
|
||||
{ .compatible = "mediatek,mt7628-esw" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, mt7628_of_match);
|
||||
|
||||
static struct platform_driver mt7628_driver = {
|
||||
.driver = {
|
||||
.name = "mt7628-esw",
|
||||
.of_match_table = mt7628_of_match,
|
||||
},
|
||||
.probe = mt7628_probe,
|
||||
.remove = mt7628_remove,
|
||||
.shutdown = mt7628_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(mt7628_driver);
|
||||
|
||||
MODULE_AUTHOR("Joris Vaisvila <joey@tinyisr.com>");
|
||||
MODULE_DESCRIPTION("Driver for Mediatek MT7628 embedded switch");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in New Issue
Block a user