/*
  * 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>
 
 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;
        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;
                .compatible = "microchip,ksz9567",
                .data = &ksz_switch_chips[KSZ9567]
        },
+       {
+               .compatible = "microchip,lan9646",
+               .data = &ksz_switch_chips[LAN9646]
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
 
                .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);
 
        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
        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);
        }
            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))
        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;
        }
 
        case KSZ9893_CHIP_ID:
        case KSZ9896_CHIP_ID:
        case KSZ9897_CHIP_ID:
+       case LAN9646_CHIP_ID:
                return 0;
        }
 
                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,
        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);
        }
 
        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);
        }
 
        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:
 
        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 ||
                .compatible = "microchip,lan9374",
                .data = &ksz_switch_chips[LAN9374]
        },
+       {
+               .compatible = "microchip,lan9646",
+               .data = &ksz_switch_chips[LAN9646]
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, ksz_dt_ids);
        { "lan9372" },
        { "lan9373" },
        { "lan9374" },
+       { "lan9646" },
        { },
 };
 MODULE_DEVICE_TABLE(spi, ksz_spi_ids);