blob: fb63f6f644b9ec93d797718b09ae76faab29a170 [file] [edit]
// 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");