net: dsa: microchip: Add LAN9646 switch support to KSZ DSA driver
authorTristram Ha <tristram.ha@microchip.com>
Sat, 9 Nov 2024 01:57:05 +0000 (17:57 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 14 Nov 2024 03:54:58 +0000 (19:54 -0800)
LAN9646 switch is a 6-port switch with functions like KSZ9897.  It has
4 internal PHYs and 1 SGMII port.  The chip id read from hardware is
same as KSZ9477, so software driver needs to create a new chip id and
group allowable functions under its chip data structure to
differentiate the product.

Signed-off-by: Tristram Ha <tristram.ha@microchip.com>
Link: https://patch.msgid.link/20241109015705.82685-3-Tristram.Ha@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/microchip/ksz9477_i2c.c
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h
drivers/net/dsa/microchip/ksz_spi.c
include/linux/platform_data/microchip-ksz.h

index 0ba658a72d8fea92373893301480751de98f8d48..d16817e0476f2f66c6b474b489f61561be53bafe 100644 (file)
@@ -1131,6 +1131,10 @@ void ksz9477_config_cpu_port(struct dsa_switch *ds)
                if (i == dev->cpu_port)
                        continue;
                ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+
+               /* Power down the internal PHY if port is unused. */
+               if (dsa_is_unused_port(ds, i) && dev->info->internal_phy[i])
+                       ksz_pwrite16(dev, i, 0x100, BMCR_PDOWN);
        }
 }
 
index 7d7560f23a73b5868cbd0ce10000ba5e1410fbcb..1c6d7fc16772373e13726806c96e776750e6ec24 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Microchip KSZ9477 series register access through I2C
  *
- * Copyright (C) 2018-2019 Microchip Technology Inc.
+ * Copyright (C) 2018-2024 Microchip Technology Inc.
  */
 
 #include <linux/i2c.h>
@@ -16,6 +16,8 @@ KSZ_REGMAP_TABLE(ksz9477, not_used, 16, 0, 0);
 
 static int ksz9477_i2c_probe(struct i2c_client *i2c)
 {
+       const struct ksz_chip_data *chip;
+       struct device *ddev = &i2c->dev;
        struct regmap_config rc;
        struct ksz_device *dev;
        int i, ret;
@@ -24,6 +26,12 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c)
        if (!dev)
                return -ENOMEM;
 
+       chip = device_get_match_data(ddev);
+       if (!chip)
+               return -EINVAL;
+
+       /* Save chip id to do special initialization when probing. */
+       dev->chip_id = chip->chip_id;
        for (i = 0; i < __KSZ_NUM_REGMAPS; i++) {
                rc = ksz9477_regmap_config[i];
                rc.lock_arg = &dev->regmap_mutex;
@@ -111,6 +119,10 @@ static const struct of_device_id ksz9477_dt_ids[] = {
                .compatible = "microchip,ksz9567",
                .data = &ksz_switch_chips[KSZ9567]
        },
+       {
+               .compatible = "microchip,lan9646",
+               .data = &ksz_switch_chips[LAN9646]
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
index d57782cdda599ee53cd233974c67bfe305230181..920443ee8ffd02380da64cd9e14e867d6210e890 100644 (file)
@@ -1908,6 +1908,41 @@ const struct ksz_chip_data ksz_switch_chips[] = {
                .internal_phy   = {true, true, true, true,
                                   false, false, true, true},
        },
+
+       [LAN9646] = {
+               .chip_id = LAN9646_CHIP_ID,
+               .dev_name = "LAN9646",
+               .num_vlans = 4096,
+               .num_alus = 4096,
+               .num_statics = 16,
+               .cpu_ports = 0x7F,      /* can be configured as cpu port */
+               .port_cnt = 7,          /* total physical port count */
+               .port_nirqs = 4,
+               .num_tx_queues = 4,
+               .num_ipms = 8,
+               .ops = &ksz9477_dev_ops,
+               .phylink_mac_ops = &ksz9477_phylink_mac_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, true},
+               .supports_rmii  = {false, false, false, false,
+                                  false, true, true},
+               .supports_rgmii = {false, false, false, false,
+                                  false, true, true},
+               .internal_phy   = {true, true, true, true,
+                                  true, false, false},
+               .gbit_capable   = {true, true, true, true, true, true, true},
+               .wr_table = &ksz9477_register_set,
+               .rd_table = &ksz9477_register_set,
+       },
 };
 EXPORT_SYMBOL_GPL(ksz_switch_chips);
 
@@ -2970,6 +3005,7 @@ static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port)
        case KSZ9896_CHIP_ID:
                /* KSZ9896C Errata DS80000757A Module 3 */
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                /* KSZ9897R Errata DS80000758C Module 4 */
                /* Energy Efficient Ethernet (EEE) feature select must be manually disabled
                 *   The EEE feature is enabled by default, but it is not fully
@@ -3230,6 +3266,7 @@ static void ksz_port_teardown(struct dsa_switch *ds, int port)
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                if (dsa_is_user_port(ds, port))
                        ksz9477_port_acl_free(dev, port);
        }
@@ -3286,7 +3323,8 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
            dev->chip_id == KSZ9477_CHIP_ID ||
            dev->chip_id == KSZ9896_CHIP_ID ||
            dev->chip_id == KSZ9897_CHIP_ID ||
-           dev->chip_id == KSZ9567_CHIP_ID)
+           dev->chip_id == KSZ9567_CHIP_ID ||
+           dev->chip_id == LAN9646_CHIP_ID)
                proto = DSA_TAG_PROTO_KSZ9477;
 
        if (is_lan937x(dev))
@@ -3405,6 +3443,7 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port)
        case LAN9372_CHIP_ID:
        case LAN9373_CHIP_ID:
        case LAN9374_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
        }
 
@@ -3427,6 +3466,7 @@ static int ksz_validate_eee(struct dsa_switch *ds, int port)
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return 0;
        }
 
@@ -3779,7 +3819,10 @@ static int ksz_switch_detect(struct ksz_device *dev)
                case LAN9372_CHIP_ID:
                case LAN9373_CHIP_ID:
                case LAN9374_CHIP_ID:
-                       dev->chip_id = id32;
+
+                       /* LAN9646 does not have its own chip id. */
+                       if (dev->chip_id != LAN9646_CHIP_ID)
+                               dev->chip_id = id32;
                        break;
                case KSZ9893_CHIP_ID:
                        ret = ksz_read8(dev, REG_CHIP_ID4,
@@ -3818,6 +3861,7 @@ static int ksz_cls_flower_add(struct dsa_switch *ds, int port,
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return ksz9477_cls_flower_add(ds, port, cls, ingress);
        }
 
@@ -3838,6 +3882,7 @@ static int ksz_cls_flower_del(struct dsa_switch *ds, int port,
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return ksz9477_cls_flower_del(ds, port, cls, ingress);
        }
 
@@ -4925,6 +4970,7 @@ static int ksz_parse_drive_strength(struct ksz_device *dev)
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return ksz9477_drive_strength_write(dev, of_props,
                                                    ARRAY_SIZE(of_props));
        default:
index bbb548af201efb38c6aee82d36cc0e8a6d0dc238..b3bb75ca0796d208f232455677d338209bdaa97d 100644 (file)
@@ -236,6 +236,7 @@ enum ksz_model {
        LAN9372,
        LAN9373,
        LAN9374,
+       LAN9646,
 };
 
 enum ksz_regs {
index 1c6652f2b9fee0ee7117a60fb412c998601ae443..108a958dc356f10ec5808b6ea4ab22f06614c61b 100644 (file)
@@ -54,6 +54,8 @@ static int ksz_spi_probe(struct spi_device *spi)
        if (!chip)
                return -EINVAL;
 
+       /* Save chip id to do special initialization when probing. */
+       dev->chip_id = chip->chip_id;
        if (chip->chip_id == KSZ88X3_CHIP_ID)
                regmap_config = ksz8863_regmap_config;
        else if (chip->chip_id == KSZ8795_CHIP_ID ||
@@ -203,6 +205,10 @@ static const struct of_device_id ksz_dt_ids[] = {
                .compatible = "microchip,lan9374",
                .data = &ksz_switch_chips[LAN9374]
        },
+       {
+               .compatible = "microchip,lan9646",
+               .data = &ksz_switch_chips[LAN9646]
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, ksz_dt_ids);
@@ -228,6 +234,7 @@ static const struct spi_device_id ksz_spi_ids[] = {
        { "lan9372" },
        { "lan9373" },
        { "lan9374" },
+       { "lan9646" },
        { },
 };
 MODULE_DEVICE_TABLE(spi, ksz_spi_ids);
index 2ee1a679e59204340ac79a6ae1fb8ca5d99ab679..0e0e8fe6975f353eadd9f9493f2351f02c75ebfe 100644 (file)
@@ -42,6 +42,7 @@ enum ksz_chip_id {
        LAN9372_CHIP_ID = 0x00937200,
        LAN9373_CHIP_ID = 0x00937300,
        LAN9374_CHIP_ID = 0x00937400,
+       LAN9646_CHIP_ID = 0x00964600,
 };
 
 struct ksz_platform_data {