From b449080956127410dbc94ba7497de066d68c7573 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 26 Aug 2022 12:56:18 +0200 Subject: net: dsa: microchip: add separate struct ksz_chip_data for KSZ8563 chip Add separate entry for the KSZ8563 chip. According to the documentation it can support Gbit only on RGMII port. So, we will need to be able to describe in the followup patch. Signed-off-by: Oleksij Rempel Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/dsa/microchip/ksz_spi.c') diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 05bd089795f8..746b725b09ec 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -160,7 +160,7 @@ static const struct of_device_id ksz_dt_ids[] = { }, { .compatible = "microchip,ksz8563", - .data = &ksz_switch_chips[KSZ9893] + .data = &ksz_switch_chips[KSZ8563] }, { .compatible = "microchip,ksz9567", -- cgit v1.2.3-59-g8ed1b From ec6ba50c65c1e30218f69055a556bdd133af6da5 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 26 Aug 2022 12:56:26 +0200 Subject: net: dsa: microchip: add support for regmap_access_tables This is complex driver with support for different chips with different layouts. To detect at least some bugs earlier, we should validate register accesses by using regmap_access_table support. Signed-off-by: Oleksij Rempel Reviewed-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.h | 46 +++++++++++++++++++++++++++++++--- drivers/net/dsa/microchip/ksz_spi.c | 3 +++ 2 files changed, 45 insertions(+), 4 deletions(-) (limited to 'drivers/net/dsa/microchip/ksz_spi.c') diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 62b9499afca5..4491cedd32c3 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -62,6 +62,8 @@ struct ksz_chip_data { bool supports_rgmii[KSZ_MAX_NUM_PORTS]; bool internal_phy[KSZ_MAX_NUM_PORTS]; bool gbit_capable[KSZ_MAX_NUM_PORTS]; + const struct regmap_access_table *wr_table; + const struct regmap_access_table *rd_table; }; struct ksz_port { @@ -333,6 +335,10 @@ static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val) unsigned int value; int ret = regmap_read(dev->regmap[0], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 8bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -342,6 +348,10 @@ static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val) unsigned int value; int ret = regmap_read(dev->regmap[1], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 16bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -351,6 +361,10 @@ static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val) unsigned int value; int ret = regmap_read(dev->regmap[2], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 32bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -361,7 +375,10 @@ static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val) int ret; ret = regmap_bulk_read(dev->regmap[2], reg, value, 2); - if (!ret) + if (ret) + dev_err(dev->dev, "can't read 64bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + else *val = (u64)value[0] << 32 | value[1]; return ret; @@ -369,17 +386,38 @@ static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val) static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value) { - return regmap_write(dev->regmap[0], reg, value); + int ret; + + ret = regmap_write(dev->regmap[0], reg, value); + if (ret) + dev_err(dev->dev, "can't write 8bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value) { - return regmap_write(dev->regmap[1], reg, value); + int ret; + + ret = regmap_write(dev->regmap[1], reg, value); + if (ret) + dev_err(dev->dev, "can't write 16bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value) { - return regmap_write(dev->regmap[2], reg, value); + int ret; + + ret = regmap_write(dev->regmap[2], reg, value); + if (ret) + dev_err(dev->dev, "can't write 32bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value) diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 746b725b09ec..44c2d9912406 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -66,7 +66,10 @@ static int ksz_spi_probe(struct spi_device *spi) for (i = 0; i < ARRAY_SIZE(ksz8795_regmap_config); i++) { rc = regmap_config[i]; rc.lock_arg = &dev->regmap_mutex; + rc.wr_table = chip->wr_table; + rc.rd_table = chip->rd_table; dev->regmap[i] = devm_regmap_init_spi(spi, &rc); + if (IS_ERR(dev->regmap[i])) { ret = PTR_ERR(dev->regmap[i]); dev_err(&spi->dev, -- cgit v1.2.3-59-g8ed1b From c9cd961c0d43a22eb704aa92e1f8fb33e3d286e8 Mon Sep 17 00:00:00 2001 From: Arun Ramadoss Date: Fri, 2 Sep 2022 16:02:10 +0530 Subject: net: dsa: microchip: lan937x: add interrupt support for port phy link This patch enables the interrupts for internal phy link detection for LAN937x. The interrupt enable bits are active low. There is global interrupt mask for each port. And each port has the individual interrupt mask for TAS. QCI, SGMII, PTP, PHY and ACL. The first level of interrupt domain is registered for global port interrupt and second level of interrupt domain for the individual port interrupts. The phy interrupt is enabled in the lan937x_mdio_register function. Interrupt from which port is raised will be detected based on the interrupt host data. Signed-off-by: Arun Ramadoss Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 10 + drivers/net/dsa/microchip/ksz_common.h | 14 ++ drivers/net/dsa/microchip/ksz_spi.c | 2 + drivers/net/dsa/microchip/lan937x.h | 1 + drivers/net/dsa/microchip/lan937x_main.c | 364 ++++++++++++++++++++++++++++++- drivers/net/dsa/microchip/lan937x_reg.h | 12 + 6 files changed, 399 insertions(+), 4 deletions(-) (limited to 'drivers/net/dsa/microchip/ksz_spi.c') diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 63b9faa89393..ec2896a23834 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -205,6 +205,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { static const struct ksz_dev_ops lan937x_dev_ops = { .setup = lan937x_setup, + .teardown = lan937x_teardown, .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, @@ -1444,6 +1445,14 @@ static int ksz_setup(struct dsa_switch *ds) return 0; } +static void ksz_teardown(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + + if (dev->dev_ops->teardown) + dev->dev_ops->teardown(ds); +} + static void port_r_cnt(struct ksz_device *dev, int port) { struct ksz_port_mib *mib = &dev->ports[port].mib; @@ -2193,6 +2202,7 @@ static const struct dsa_switch_ops ksz_switch_ops = { .get_tag_protocol = ksz_get_tag_protocol, .get_phy_flags = ksz_get_phy_flags, .setup = ksz_setup, + .teardown = ksz_teardown, .phy_read = ksz_phy_read16, .phy_write = ksz_phy_write16, .phylink_get_caps = ksz_phylink_get_caps, diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 3fa3e4731d58..35346b39ce54 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -13,6 +13,7 @@ #include #include #include +#include #define KSZ_MAX_NUM_PORTS 8 @@ -68,6 +69,14 @@ struct ksz_chip_data { const struct regmap_access_table *rd_table; }; +struct ksz_irq { + u16 masked; + struct irq_chip chip; + struct irq_domain *domain; + int nirqs; + char name[16]; +}; + struct ksz_port { bool remove_tag; /* Remove Tag flag set, for ksz8795 only */ bool learning; @@ -86,6 +95,7 @@ struct ksz_port { u32 rgmii_tx_val; u32 rgmii_rx_val; struct ksz_device *ksz_dev; + struct ksz_irq pirq; u8 num; }; @@ -104,6 +114,7 @@ struct ksz_device { struct regmap *regmap[3]; void *priv; + int irq; struct gpio_desc *reset_gpio; /* Optional reset GPIO */ @@ -124,6 +135,8 @@ struct ksz_device { u16 mirror_rx; u16 mirror_tx; u16 port_mask; + struct mutex lock_irq; /* IRQ Access */ + struct ksz_irq girq; }; /* List of supported models */ @@ -260,6 +273,7 @@ struct alu_struct { struct ksz_dev_ops { int (*setup)(struct dsa_switch *ds); + void (*teardown)(struct dsa_switch *ds); u32 (*get_port_addr)(int port, int offset); void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member); void (*flush_dyn_mac_table)(struct ksz_device *dev, int port); diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 44c2d9912406..126ed1c986a9 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -88,6 +88,8 @@ static int ksz_spi_probe(struct spi_device *spi) if (ret) return ret; + dev->irq = spi->irq; + ret = ksz_switch_register(dev); /* Main DSA driver may not be started yet. */ diff --git a/drivers/net/dsa/microchip/lan937x.h b/drivers/net/dsa/microchip/lan937x.h index 5d78d034a62f..1b7f077946f3 100644 --- a/drivers/net/dsa/microchip/lan937x.h +++ b/drivers/net/dsa/microchip/lan937x.h @@ -8,6 +8,7 @@ int lan937x_reset_switch(struct ksz_device *dev); int lan937x_setup(struct dsa_switch *ds); +void lan937x_teardown(struct dsa_switch *ds); void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port); void lan937x_config_cpu_port(struct dsa_switch *ds); int lan937x_switch_init(struct ksz_device *dev); diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 0466c4d0b10c..4867aa62dd4c 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -18,6 +20,8 @@ #include "ksz_common.h" #include "lan937x.h" +#define LAN937x_PNIRQS 6 + static int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { return regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -165,6 +169,45 @@ static int lan937x_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, return lan937x_internal_phy_write(dev, addr, regnum, val); } +static int lan937x_irq_phy_setup(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy, err_phy; + int irq; + int ret; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) { + if (BIT(phy) & ds->phys_mii_mask) { + irq = irq_find_mapping(dev->ports[phy].pirq.domain, + PORT_SRC_PHY_INT); + if (irq < 0) { + ret = irq; + goto out; + } + ds->slave_mii_bus->irq[phy] = irq; + } + } + return 0; +out: + err_phy = phy; + + for (phy = 0; phy < err_phy; phy++) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); + + return ret; +} + +static void lan937x_irq_phy_free(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); +} + static int lan937x_mdio_register(struct ksz_device *dev) { struct dsa_switch *ds = dev->ds; @@ -194,10 +237,15 @@ static int lan937x_mdio_register(struct ksz_device *dev) ds->slave_mii_bus = bus; + ret = lan937x_irq_phy_setup(dev); + if (ret) + return ret; + ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np); if (ret) { dev_err(ds->dev, "unable to register MDIO bus %s\n", bus->id); + lan937x_irq_phy_free(dev); } of_node_put(mdio_np); @@ -387,9 +435,289 @@ void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) } } +int lan937x_switch_init(struct ksz_device *dev) +{ + dev->port_mask = (1 << dev->info->port_cnt) - 1; + + return 0; +} + +static void lan937x_girq_mask(struct irq_data *d) +{ + struct ksz_device *dev = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + dev->girq.masked |= (1 << n); +} + +static void lan937x_girq_unmask(struct irq_data *d) +{ + struct ksz_device *dev = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + dev->girq.masked &= ~(1 << n); +} + +static void lan937x_girq_bus_lock(struct irq_data *d) +{ + struct ksz_device *dev = irq_data_get_irq_chip_data(d); + + mutex_lock(&dev->lock_irq); +} + +static void lan937x_girq_bus_sync_unlock(struct irq_data *d) +{ + struct ksz_device *dev = irq_data_get_irq_chip_data(d); + int ret; + + ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, dev->girq.masked); + if (ret) + dev_err(dev->dev, "failed to change IRQ mask\n"); + + mutex_unlock(&dev->lock_irq); +} + +static const struct irq_chip lan937x_girq_chip = { + .name = "lan937x-global", + .irq_mask = lan937x_girq_mask, + .irq_unmask = lan937x_girq_unmask, + .irq_bus_lock = lan937x_girq_bus_lock, + .irq_bus_sync_unlock = lan937x_girq_bus_sync_unlock, +}; + +static int lan937x_girq_domain_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hwirq) +{ + struct ksz_device *dev = d->host_data; + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &dev->girq.chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops lan937x_girq_domain_ops = { + .map = lan937x_girq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void lan937x_girq_free(struct ksz_device *dev) +{ + int irq, virq; + + free_irq(dev->irq, dev); + + for (irq = 0; irq < dev->girq.nirqs; irq++) { + virq = irq_find_mapping(dev->girq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(dev->girq.domain); +} + +static irqreturn_t lan937x_girq_thread_fn(int irq, void *dev_id) +{ + struct ksz_device *dev = dev_id; + unsigned int nhandled = 0; + unsigned int sub_irq; + unsigned int n; + u32 data; + int ret; + + /* Read global interrupt status register */ + ret = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data); + if (ret) + goto out; + + for (n = 0; n < dev->girq.nirqs; ++n) { + if (data & (1 << n)) { + sub_irq = irq_find_mapping(dev->girq.domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static int lan937x_girq_setup(struct ksz_device *dev) +{ + int ret, irq; + + dev->girq.nirqs = dev->info->port_cnt; + dev->girq.domain = irq_domain_add_simple(NULL, dev->girq.nirqs, 0, + &lan937x_girq_domain_ops, dev); + if (!dev->girq.domain) + return -ENOMEM; + + for (irq = 0; irq < dev->girq.nirqs; irq++) + irq_create_mapping(dev->girq.domain, irq); + + dev->girq.chip = lan937x_girq_chip; + dev->girq.masked = ~0; + + ret = request_threaded_irq(dev->irq, NULL, lan937x_girq_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + dev_name(dev->dev), dev); + if (ret) + goto out; + + return 0; + +out: + lan937x_girq_free(dev); + + return ret; +} + +static void lan937x_pirq_mask(struct irq_data *d) +{ + struct ksz_port *port = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + port->pirq.masked |= (1 << n); +} + +static void lan937x_pirq_unmask(struct irq_data *d) +{ + struct ksz_port *port = irq_data_get_irq_chip_data(d); + unsigned int n = d->hwirq; + + port->pirq.masked &= ~(1 << n); +} + +static void lan937x_pirq_bus_lock(struct irq_data *d) +{ + struct ksz_port *port = irq_data_get_irq_chip_data(d); + struct ksz_device *dev = port->ksz_dev; + + mutex_lock(&dev->lock_irq); +} + +static void lan937x_pirq_bus_sync_unlock(struct irq_data *d) +{ + struct ksz_port *port = irq_data_get_irq_chip_data(d); + struct ksz_device *dev = port->ksz_dev; + + ksz_pwrite8(dev, port->num, REG_PORT_INT_MASK, port->pirq.masked); + mutex_unlock(&dev->lock_irq); +} + +static const struct irq_chip lan937x_pirq_chip = { + .name = "lan937x-port", + .irq_mask = lan937x_pirq_mask, + .irq_unmask = lan937x_pirq_unmask, + .irq_bus_lock = lan937x_pirq_bus_lock, + .irq_bus_sync_unlock = lan937x_pirq_bus_sync_unlock, +}; + +static int lan937x_pirq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct ksz_port *port = d->host_data; + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &port->pirq.chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops lan937x_pirq_domain_ops = { + .map = lan937x_pirq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void lan937x_pirq_free(struct ksz_device *dev, u8 p) +{ + struct ksz_port *port = &dev->ports[p]; + int irq, virq; + int irq_num; + + irq_num = irq_find_mapping(dev->girq.domain, p); + if (irq_num < 0) + return; + + free_irq(irq_num, port); + + for (irq = 0; irq < port->pirq.nirqs; irq++) { + virq = irq_find_mapping(port->pirq.domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(port->pirq.domain); +} + +static irqreturn_t lan937x_pirq_thread_fn(int irq, void *dev_id) +{ + struct ksz_port *port = dev_id; + unsigned int nhandled = 0; + struct ksz_device *dev; + unsigned int sub_irq; + unsigned int n; + u8 data; + + dev = port->ksz_dev; + + /* Read port interrupt status register */ + ksz_pread8(dev, port->num, REG_PORT_INT_STATUS, &data); + + for (n = 0; n < port->pirq.nirqs; ++n) { + if (data & (1 << n)) { + sub_irq = irq_find_mapping(port->pirq.domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } + + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static int lan937x_pirq_setup(struct ksz_device *dev, u8 p) +{ + struct ksz_port *port = &dev->ports[p]; + int ret, irq; + int irq_num; + + port->pirq.nirqs = LAN937x_PNIRQS; + port->pirq.domain = irq_domain_add_simple(dev->dev->of_node, + port->pirq.nirqs, 0, + &lan937x_pirq_domain_ops, + port); + if (!port->pirq.domain) + return -ENOMEM; + + for (irq = 0; irq < port->pirq.nirqs; irq++) + irq_create_mapping(port->pirq.domain, irq); + + port->pirq.chip = lan937x_pirq_chip; + port->pirq.masked = ~0; + + irq_num = irq_find_mapping(dev->girq.domain, p); + if (irq_num < 0) + return irq_num; + + snprintf(port->pirq.name, sizeof(port->pirq.name), "port_irq-%d", p); + + ret = request_threaded_irq(irq_num, NULL, lan937x_pirq_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + port->pirq.name, port); + if (ret) + goto out; + + return 0; + +out: + lan937x_pirq_free(dev, p); + + return ret; +} + int lan937x_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; + struct dsa_port *dp; int ret; /* enable Indirect Access from SPI to the VPHY registers */ @@ -399,10 +727,22 @@ int lan937x_setup(struct dsa_switch *ds) return ret; } + if (dev->irq > 0) { + ret = lan937x_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = lan937x_pirq_setup(dev, dp->index); + if (ret) + goto out_girq; + } + } + ret = lan937x_mdio_register(dev); if (ret < 0) { dev_err(dev->dev, "failed to register the mdio"); - return ret; + goto out_pirq; } /* The VLAN aware is a global setting. Mixed vlan @@ -428,13 +768,29 @@ int lan937x_setup(struct dsa_switch *ds) (SW_CLK125_ENB | SW_CLK25_ENB), true); return 0; + +out_pirq: + if (dev->irq > 0) + dsa_switch_for_each_user_port(dp, dev->ds) + lan937x_pirq_free(dev, dp->index); +out_girq: + if (dev->irq > 0) + lan937x_girq_free(dev); + + return ret; } -int lan937x_switch_init(struct ksz_device *dev) +void lan937x_teardown(struct dsa_switch *ds) { - dev->port_mask = (1 << dev->info->port_cnt) - 1; + struct ksz_device *dev = ds->priv; + struct dsa_port *dp; - return 0; + if (dev->irq > 0) { + dsa_switch_for_each_user_port(dp, dev->ds) + lan937x_pirq_free(dev, dp->index); + + lan937x_girq_free(dev); + } } void lan937x_switch_exit(struct ksz_device *dev) diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h index ba4adaddb3ec..a3c669d86e51 100644 --- a/drivers/net/dsa/microchip/lan937x_reg.h +++ b/drivers/net/dsa/microchip/lan937x_reg.h @@ -118,6 +118,18 @@ /* Port Registers */ /* 0 - Operation */ +#define REG_PORT_INT_STATUS 0x001B +#define REG_PORT_INT_MASK 0x001F + +#define PORT_TAS_INT BIT(5) +#define PORT_QCI_INT BIT(4) +#define PORT_SGMII_INT BIT(3) +#define PORT_PTP_INT BIT(2) +#define PORT_PHY_INT BIT(1) +#define PORT_ACL_INT BIT(0) + +#define PORT_SRC_PHY_INT 1 + #define REG_PORT_CTRL_0 0x0020 #define PORT_MAC_LOOPBACK BIT(7) -- cgit v1.2.3-59-g8ed1b From 2eb3ff3c09082bb4792f2149cf6582e2626a5e30 Mon Sep 17 00:00:00 2001 From: Romain Naour Date: Fri, 2 Sep 2022 12:16:07 +0200 Subject: net: dsa: microchip: add KSZ9896 switch support Add support for the KSZ9896 6-port Gigabit Ethernet Switch to the ksz9477 driver. Although the KSZ9896 is already listed in the device tree binding documentation since a1c0ed24fe9b (dt-bindings: net: dsa: document additional Microchip KSZ9477 family switches) the chip id (0x00989600) is not recognized by ksz_switch_detect() and rejected by the driver. The KSZ9896 is similar to KSZ9897 but has only one configurable MII/RMII/RGMII/GMII cpu port. Signed-off-by: Romain Naour Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 31 +++++++++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz_common.h | 2 ++ drivers/net/dsa/microchip/ksz_spi.c | 6 ++++++ 3 files changed, 39 insertions(+) (limited to 'drivers/net/dsa/microchip/ksz_spi.c') diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index ec2896a23834..b19631d47058 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -966,6 +966,35 @@ const struct ksz_chip_data ksz_switch_chips[] = { .rd_table = &ksz9477_register_set, }, + [KSZ9896] = { + .chip_id = KSZ9896_CHIP_ID, + .dev_name = "KSZ9896", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x3F, /* can be configured as cpu port */ + .port_cnt = 6, /* total physical port count */ + .ops = &ksz9477_dev_ops, + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + false, true}, + .supports_rmii = {false, false, false, false, + false, true}, + .supports_rgmii = {false, false, false, false, + false, true}, + .internal_phy = {true, true, true, true, + true, false}, + .gbit_capable = {true, true, true, true, true, true}, + }, + [KSZ9897] = { .chip_id = KSZ9897_CHIP_ID, .dev_name = "KSZ9897", @@ -1807,6 +1836,7 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, proto = DSA_TAG_PROTO_KSZ9893; if (dev->chip_id == KSZ9477_CHIP_ID || + dev->chip_id == KSZ9896_CHIP_ID || dev->chip_id == KSZ9897_CHIP_ID || dev->chip_id == KSZ9567_CHIP_ID) proto = DSA_TAG_PROTO_KSZ9477; @@ -2168,6 +2198,7 @@ static int ksz_switch_detect(struct ksz_device *dev) switch (id32) { case KSZ9477_CHIP_ID: + case KSZ9896_CHIP_ID: case KSZ9897_CHIP_ID: case KSZ9567_CHIP_ID: case LAN9370_CHIP_ID: diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 35346b39ce54..7c63f900dfce 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -147,6 +147,7 @@ enum ksz_model { KSZ8765, KSZ8830, KSZ9477, + KSZ9896, KSZ9897, KSZ9893, KSZ9567, @@ -164,6 +165,7 @@ enum ksz_chip_id { KSZ8765_CHIP_ID = 0x8765, KSZ8830_CHIP_ID = 0x8830, KSZ9477_CHIP_ID = 0x00947700, + KSZ9896_CHIP_ID = 0x00989600, KSZ9897_CHIP_ID = 0x00989700, KSZ9893_CHIP_ID = 0x00989300, KSZ9567_CHIP_ID = 0x00956700, diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 126ed1c986a9..82e2352f55fa 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -151,6 +151,10 @@ static const struct of_device_id ksz_dt_ids[] = { .compatible = "microchip,ksz9477", .data = &ksz_switch_chips[KSZ9477] }, + { + .compatible = "microchip,ksz9896", + .data = &ksz_switch_chips[KSZ9896] + }, { .compatible = "microchip,ksz9897", .data = &ksz_switch_chips[KSZ9897] @@ -202,6 +206,7 @@ static const struct spi_device_id ksz_spi_ids[] = { { "ksz8863" }, { "ksz8873" }, { "ksz9477" }, + { "ksz9896" }, { "ksz9897" }, { "ksz9893" }, { "ksz9563" }, @@ -231,6 +236,7 @@ static struct spi_driver ksz_spi_driver = { module_spi_driver(ksz_spi_driver); MODULE_ALIAS("spi:ksz9477"); +MODULE_ALIAS("spi:ksz9896"); MODULE_ALIAS("spi:ksz9897"); MODULE_ALIAS("spi:ksz9893"); MODULE_ALIAS("spi:ksz9563"); -- cgit v1.2.3-59-g8ed1b From 3525ecc127d893f99671df041764aa4185c79e0b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 21 Sep 2022 22:05:13 +0800 Subject: net: dsa: microchip: remove unnecessary set_drvdata() Remove unnecessary set_drvdata(NULL) function in ->remove(), the driver_data will be set to NULL in device_unbind_cleanup() after calling ->remove(). Signed-off-by: Yang Yingliang Signed-off-by: Jakub Kicinski --- drivers/net/dsa/microchip/ksz8863_smi.c | 2 -- drivers/net/dsa/microchip/ksz_spi.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers/net/dsa/microchip/ksz_spi.c') diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index 5247fdfb964d..ddb40838181e 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -180,8 +180,6 @@ static void ksz8863_smi_remove(struct mdio_device *mdiodev) if (dev) ksz_switch_remove(dev); - - dev_set_drvdata(&mdiodev->dev, NULL); } static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 82e2352f55fa..1b6ab891b986 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -107,8 +107,6 @@ static void ksz_spi_remove(struct spi_device *spi) if (dev) ksz_switch_remove(dev); - - spi_set_drvdata(spi, NULL); } static void ksz_spi_shutdown(struct spi_device *spi) -- cgit v1.2.3-59-g8ed1b