Add KSZ88X3 driver support. We add support for the KXZ88X3 three port
switches using the Microchip SMI Interface. They are currently only
supported using the MDIO-Bitbang Interface. Which is making use of the
mdio_ll_read/write functions. The patch uses the common functions
from the ksz8795 driver.

Signed-off-by: Michael Grzeschik <m.grzesc...@pengutronix.de>
---
RFC -> v1: - added Microchip SMI to description
           - added select MDIO_BITBANG
           - added return error code handling in mdiobus_read/write
           - fixed the stp state handling
           - moved tag handling code to seperate patch

v1 -> v2:  - completely reworked the driver to use existing ksz8795 functions

v2 -> v3:  - fixed logic of PHY_AUTO_NEG_ENABLE/DISABLE in r/w_phy
           - removed mdio_ll_read/write and used direct SMI bus
           - added SPI based 8863 support

 drivers/net/dsa/microchip/Kconfig       |   9 +
 drivers/net/dsa/microchip/Makefile      |   1 +
 drivers/net/dsa/microchip/ksz8.h        |  68 ++
 drivers/net/dsa/microchip/ksz8795.c     | 909 ++++++++++++++++--------
 drivers/net/dsa/microchip/ksz8795_reg.h | 214 +++---
 drivers/net/dsa/microchip/ksz8795_spi.c |  83 ++-
 drivers/net/dsa/microchip/ksz8863_reg.h | 121 ++++
 drivers/net/dsa/microchip/ksz8863_smi.c | 186 +++++
 drivers/net/dsa/microchip/ksz_common.h  |   1 +
 9 files changed, 1187 insertions(+), 405 deletions(-)
 create mode 100644 drivers/net/dsa/microchip/ksz8.h
 create mode 100644 drivers/net/dsa/microchip/ksz8863_reg.h
 create mode 100644 drivers/net/dsa/microchip/ksz8863_smi.c

diff --git a/drivers/net/dsa/microchip/Kconfig 
b/drivers/net/dsa/microchip/Kconfig
index 4ec6a47b7f7284..c5819bd4121cc7 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -40,3 +40,12 @@ config NET_DSA_MICROCHIP_KSZ8795_SPI
 
          It is required to use the KSZ8795 switch driver as the only access
          is through SPI.
+
+config NET_DSA_MICROCHIP_KSZ8863_SMI
+       tristate "KSZ series SMI connected switch driver"
+       depends on NET_DSA_MICROCHIP_KSZ8795
+       select MDIO_BITBANG
+       default y
+       help
+         Select to enable support for registering switches configured through
+         Microchip SMI. It Supports the KSZ8863 and KSZ8873 Switch.
diff --git a/drivers/net/dsa/microchip/Makefile 
b/drivers/net/dsa/microchip/Makefile
index 929caa81e782ed..2a03b21a3386f5 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C)     += ksz9477_i2c.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI)    += ksz9477_spi.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795)                += ksz8795.o
 obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795_SPI)    += ksz8795_spi.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI)    += ksz8863_smi.o
diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h
new file mode 100644
index 00000000000000..a2fb6b9e2d1e5d
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Microchip KSZ8863 series register access through SMI
+ *
+ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <ker...@pengutronix.de>
+ */
+
+#ifndef __KSZ8XXX_H
+#define __KSZ8XXX_H
+
+struct ksz_regs {
+       int ind_ctrl_0;
+       int ind_data_8;
+       int ind_data_check;
+       int ind_data_hi;
+       int ind_data_lo;
+       int ind_mib_check;
+       int p_force_ctrl;
+       int p_link_status;
+       int p_local_ctrl;
+       int p_neg_restart_ctrl;
+       int p_remote_status;
+       int p_speed_status;
+       int s_tail_tag_ctrl;
+};
+
+struct ksz_masks {
+       int port_802_1p_remapping;
+       int sw_tail_tag_enable;
+       int mib_counter_overflow;
+       int mib_counter_valid;
+       int vlan_table_fid;
+       int vlan_table_membership;
+       int vlan_table_valid;
+       int static_mac_table_valid;
+       int static_mac_table_use_fid;
+       int static_mac_table_fid;
+       int static_mac_table_override;
+       int static_mac_table_fwd_ports;
+       int dynamic_mac_table_entries_h;
+       int dynamic_mac_table_mac_empty;
+       int dynamic_mac_table_not_ready;
+       int dynamic_mac_table_entries;
+       int dynamic_mac_table_fid;
+       int dynamic_mac_table_src_port;
+       int dynamic_mac_table_timestamp;
+};
+
+struct ksz_shifts {
+       int vlan_table_membership;
+       int vlan_table;
+       int static_mac_fwd_ports;
+       int static_mac_fid;
+       int dynamic_mac_entries_h;
+       int dynamic_mac_entries;
+       int dynamic_mac_fid;
+       int dynamic_mac_timestamp;
+       int dynamic_mac_src_port;
+};
+
+struct ksz8 {
+       struct ksz_regs *regs;
+       struct ksz_masks *masks;
+       struct ksz_shifts *shifts;
+       void *priv;
+};
+
+#endif
diff --git a/drivers/net/dsa/microchip/ksz8795.c 
b/drivers/net/dsa/microchip/ksz8795.c
index 47d65b77caf774..c9c01477eebc10 100644
--- a/drivers/net/dsa/microchip/ksz8795.c
+++ b/drivers/net/dsa/microchip/ksz8795.c
@@ -20,10 +20,109 @@
 
 #include "ksz_common.h"
 #include "ksz8795_reg.h"
+#include "ksz8863_reg.h"
+#include "ksz8.h"
+
+static struct ksz_regs ksz8795_regs = {
+       .ind_ctrl_0                     = KSZ8795_REG_IND_CTRL_0,
+       .ind_data_8                     = KSZ8795_REG_IND_DATA_8,
+       .ind_data_check                 = KSZ8795_REG_IND_DATA_CHECK,
+       .ind_data_hi                    = KSZ8795_REG_IND_DATA_HI,
+       .ind_data_lo                    = KSZ8795_REG_IND_DATA_LO,
+       .ind_mib_check                  = KSZ8795_REG_IND_MIB_CHECK,
+       .p_force_ctrl                   = KSZ8795_P_FORCE_CTRL,
+       .p_link_status                  = KSZ8795_P_LINK_STATUS,
+       .p_local_ctrl                   = KSZ8795_P_LOCAL_CTRL,
+       .p_neg_restart_ctrl             = KSZ8795_P_NEG_RESTART_CTRL,
+       .p_remote_status                = KSZ8795_P_REMOTE_STATUS,
+       .p_speed_status                 = KSZ8795_P_SPEED_STATUS,
+       .s_tail_tag_ctrl                = KSZ8795_S_TAIL_TAG_CTRL,
+};
+
+static struct ksz_masks ksz8795_masks = {
+       .port_802_1p_remapping          = KSZ8795_PORT_802_1P_REMAPPING,
+       .sw_tail_tag_enable             = KSZ8795_SW_TAIL_TAG_ENABLE,
+       .mib_counter_overflow           = KSZ8795_MIB_COUNTER_OVERFLOW,
+       .mib_counter_valid              = KSZ8795_MIB_COUNTER_VALID,
+       .vlan_table_fid                 = KSZ8795_VLAN_TABLE_FID,
+       .vlan_table_membership          = KSZ8795_VLAN_TABLE_MEMBERSHIP,
+       .vlan_table_valid               = KSZ8795_VLAN_TABLE_VALID,
+       .static_mac_table_valid         = KSZ8795_STATIC_MAC_TABLE_VALID,
+       .static_mac_table_use_fid       = KSZ8795_STATIC_MAC_TABLE_USE_FID,
+       .static_mac_table_fid           = KSZ8795_STATIC_MAC_TABLE_FID,
+       .static_mac_table_override      = KSZ8795_STATIC_MAC_TABLE_OVERRIDE,
+       .static_mac_table_fwd_ports     = KSZ8795_STATIC_MAC_TABLE_FWD_PORTS,
+       .dynamic_mac_table_entries_h    = KSZ8795_DYNAMIC_MAC_TABLE_ENTRIES_H,
+       .dynamic_mac_table_mac_empty    = KSZ8795_DYNAMIC_MAC_TABLE_MAC_EMPTY,
+       .dynamic_mac_table_not_ready    = KSZ8795_DYNAMIC_MAC_TABLE_NOT_READY,
+       .dynamic_mac_table_entries      = KSZ8795_DYNAMIC_MAC_TABLE_ENTRIES,
+       .dynamic_mac_table_fid          = KSZ8795_DYNAMIC_MAC_TABLE_FID,
+       .dynamic_mac_table_src_port     = KSZ8795_DYNAMIC_MAC_TABLE_SRC_PORT,
+       .dynamic_mac_table_timestamp    = KSZ8795_DYNAMIC_MAC_TABLE_TIMESTAMP,
+};
+
+static struct ksz_shifts ksz8795_shifts = {
+       .vlan_table_membership          = KSZ8795_VLAN_TABLE_MEMBERSHIP_S,
+       .vlan_table                     = KSZ8795_VLAN_TABLE_S,
+       .static_mac_fwd_ports           = KSZ8795_STATIC_MAC_FWD_PORTS_S,
+       .static_mac_fid                 = KSZ8795_STATIC_MAC_FID_S,
+       .dynamic_mac_entries            = KSZ8795_DYNAMIC_MAC_ENTRIES_S,
+       .dynamic_mac_fid                = KSZ8795_DYNAMIC_MAC_FID_S,
+       .dynamic_mac_timestamp          = KSZ8795_DYNAMIC_MAC_TIMESTAMP_S,
+       .dynamic_mac_src_port           = KSZ8795_DYNAMIC_MAC_SRC_PORT_S,
+};
+
+static struct ksz_regs ksz8863_regs = {
+       .ind_ctrl_0                     = KSZ8863_REG_IND_CTRL_0,
+       .ind_data_8                     = KSZ8863_REG_IND_DATA_8,
+       .ind_data_check                 = KSZ8863_REG_IND_DATA_CHECK,
+       .ind_data_hi                    = KSZ8863_REG_IND_DATA_HI,
+       .ind_data_lo                    = KSZ8863_REG_IND_DATA_LO,
+       .ind_mib_check                  = KSZ8863_REG_IND_MIB_CHECK,
+       .p_force_ctrl                   = KSZ8863_P_FORCE_CTRL,
+       .p_link_status                  = KSZ8863_P_LINK_STATUS,
+       .p_local_ctrl                   = KSZ8863_P_LOCAL_CTRL,
+       .p_neg_restart_ctrl             = KSZ8863_P_NEG_RESTART_CTRL,
+       .p_remote_status                = KSZ8863_P_REMOTE_STATUS,
+       .p_speed_status                 = KSZ8863_P_SPEED_STATUS,
+       .s_tail_tag_ctrl                = KSZ8863_S_TAIL_TAG_CTRL,
+};
+
+static struct ksz_masks ksz8863_masks = {
+       .port_802_1p_remapping          = KSZ8863_PORT_802_1P_REMAPPING,
+       .sw_tail_tag_enable             = KSZ8863_SW_TAIL_TAG_ENABLE,
+       .mib_counter_overflow           = KSZ8863_MIB_COUNTER_OVERFLOW,
+       .mib_counter_valid              = KSZ8863_MIB_COUNTER_VALID,
+       .vlan_table_fid                 = KSZ8863_VLAN_TABLE_FID,
+       .vlan_table_membership          = KSZ8863_VLAN_TABLE_MEMBERSHIP,
+       .vlan_table_valid               = KSZ8863_VLAN_TABLE_VALID,
+       .static_mac_table_valid         = KSZ8863_STATIC_MAC_TABLE_VALID,
+       .static_mac_table_use_fid       = KSZ8863_STATIC_MAC_TABLE_USE_FID,
+       .static_mac_table_fid           = KSZ8863_STATIC_MAC_TABLE_FID,
+       .static_mac_table_override      = KSZ8863_STATIC_MAC_TABLE_OVERRIDE,
+       .static_mac_table_fwd_ports     = KSZ8863_STATIC_MAC_TABLE_FWD_PORTS,
+       .dynamic_mac_table_entries_h    = KSZ8863_DYNAMIC_MAC_TABLE_ENTRIES_H,
+       .dynamic_mac_table_mac_empty    = KSZ8863_DYNAMIC_MAC_TABLE_MAC_EMPTY,
+       .dynamic_mac_table_not_ready    = KSZ8863_DYNAMIC_MAC_TABLE_NOT_READY,
+       .dynamic_mac_table_entries      = KSZ8863_DYNAMIC_MAC_TABLE_ENTRIES,
+       .dynamic_mac_table_fid          = KSZ8863_DYNAMIC_MAC_TABLE_FID,
+       .dynamic_mac_table_src_port     = KSZ8863_DYNAMIC_MAC_TABLE_SRC_PORT,
+       .dynamic_mac_table_timestamp    = KSZ8863_DYNAMIC_MAC_TABLE_TIMESTAMP,
+};
+
+static struct ksz_shifts ksz8863_shifts = {
+       .vlan_table_membership          = KSZ8863_VLAN_TABLE_MEMBERSHIP_S,
+       .static_mac_fwd_ports           = KSZ8863_STATIC_MAC_FWD_PORTS_S,
+       .static_mac_fid                 = KSZ8863_STATIC_MAC_FID_S,
+       .dynamic_mac_entries            = KSZ8863_DYNAMIC_MAC_ENTRIES_S,
+       .dynamic_mac_fid                = KSZ8863_DYNAMIC_MAC_FID_S,
+       .dynamic_mac_timestamp          = KSZ8863_DYNAMIC_MAC_TIMESTAMP_S,
+       .dynamic_mac_src_port           = KSZ8863_DYNAMIC_MAC_SRC_PORT_S,
+};
 
 static const struct {
        char string[ETH_GSTRING_LEN];
-} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
+} ksz87xx_mib_names[TOTAL_KSZ8795_COUNTER_NUM] = {
        { "rx_hi" },
        { "rx_undersize" },
        { "rx_fragments" },
@@ -62,6 +161,45 @@ static const struct {
        { "tx_discards" },
 };
 
+static const struct {
+       char string[ETH_GSTRING_LEN];
+} ksz88xx_mib_names[TOTAL_KSZ8863_COUNTER_NUM] = {
+       { "rx" },
+       { "rx_hi" },
+       { "rx_undersize" },
+       { "rx_fragments" },
+       { "rx_oversize" },
+       { "rx_jabbers" },
+       { "rx_symbol_err" },
+       { "rx_crc_err" },
+       { "rx_align_err" },
+       { "rx_mac_ctrl" },
+       { "rx_pause" },
+       { "rx_bcast" },
+       { "rx_mcast" },
+       { "rx_ucast" },
+       { "rx_64_or_less" },
+       { "rx_65_127" },
+       { "rx_128_255" },
+       { "rx_256_511" },
+       { "rx_512_1023" },
+       { "rx_1024_1522" },
+       { "tx" },
+       { "tx_hi" },
+       { "tx_late_col" },
+       { "tx_pause" },
+       { "tx_bcast" },
+       { "tx_mcast" },
+       { "tx_ucast" },
+       { "tx_deferred" },
+       { "tx_total_col" },
+       { "tx_exc_col" },
+       { "tx_single_col" },
+       { "tx_mult_col" },
+       { "rx_discards" },
+       { "tx_discards" },
+};
+
 static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
 {
        regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
@@ -74,6 +212,11 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, 
int offset, u8 bits,
                           bits, set ? bits : 0);
 }
 
+static int ksz_is_87(struct ksz_device *dev)
+{
+       return (((dev->chip_id) >> 8) == KSZ87_FAMILY_ID);
+}
+
 static int ksz8795_reset_switch(struct ksz_device *dev)
 {
        /* reset switch */
@@ -84,6 +227,15 @@ static int ksz8795_reset_switch(struct ksz_device *dev)
        return 0;
 }
 
+static int ksz8863_reset_switch(struct ksz_device *dev)
+{
+       /* reset switch */
+       ksz_cfg(dev, KSZ8863_REG_SW_RESET,
+               KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
+
+       return 0;
+}
+
 static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue)
 {
        u8 hi, lo;
@@ -117,29 +269,31 @@ static void ksz8795_set_prio_queue(struct ksz_device 
*dev, int port, int queue)
                        true);
 }
 
-static void ksz8795_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
-                             u64 *cnt)
+static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 
*cnt)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
+       struct ksz_masks *masks = ksz8->masks;
        u16 ctrl_addr;
        u32 data;
        u8 check;
        int loop;
 
-       ctrl_addr = addr + SWITCH_COUNTER_NUM * port;
+       ctrl_addr = addr + dev->reg_mib_cnt * port;
        ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
 
        mutex_lock(&dev->alu_mutex);
-       ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
 
        /* It is almost guaranteed to always read the valid bit because of
         * slow SPI speed.
         */
        for (loop = 2; loop > 0; loop--) {
-               ksz_read8(dev, REG_IND_MIB_CHECK, &check);
+               ksz_read8(dev, regs->ind_mib_check, &check);
 
-               if (check & MIB_COUNTER_VALID) {
-                       ksz_read32(dev, REG_IND_DATA_LO, &data);
-                       if (check & MIB_COUNTER_OVERFLOW)
+               if (check & masks->mib_counter_valid) {
+                       ksz_read32(dev, regs->ind_data_lo, &data);
+                       if (check & masks->mib_counter_overflow)
                                *cnt += MIB_COUNTER_VALUE + 1;
                        *cnt += data & MIB_COUNTER_VALUE;
                        break;
@@ -148,30 +302,64 @@ static void ksz8795_r_mib_cnt(struct ksz_device *dev, int 
port, u16 addr,
        mutex_unlock(&dev->alu_mutex);
 }
 
+static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+                             u64 *dropped, u64 *cnt)
+{
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
+       u32 *last = (u32 *)dropped;
+       u16 ctrl_addr;
+       u32 data;
+       u32 cur;
+
+       addr -= dev->reg_mib_cnt;
+       ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 :
+                          KSZ8863_MIB_PACKET_DROPPED_RX_0;
+       ctrl_addr += port;
+       ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
+
+       mutex_lock(&dev->alu_mutex);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
+       ksz_read32(dev, regs->ind_data_lo, &data);
+       mutex_unlock(&dev->alu_mutex);
+
+       data &= MIB_PACKET_DROPPED;
+       cur = last[addr];
+       if (data != cur) {
+               last[addr] = data;
+               if (data < cur)
+                       data += MIB_PACKET_DROPPED + 1;
+               data -= cur;
+               *cnt += data;
+       }
+}
+
 static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
                              u64 *dropped, u64 *cnt)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
        u16 ctrl_addr;
        u32 data;
        u8 check;
        int loop;
 
-       addr -= SWITCH_COUNTER_NUM;
-       ctrl_addr = (KS_MIB_TOTAL_RX_1 - KS_MIB_TOTAL_RX_0) * port;
-       ctrl_addr += addr + KS_MIB_TOTAL_RX_0;
+       addr -= dev->reg_mib_cnt;
+       ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port;
+       ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0;
        ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
 
        mutex_lock(&dev->alu_mutex);
-       ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
 
        /* It is almost guaranteed to always read the valid bit because of
         * slow SPI speed.
         */
        for (loop = 2; loop > 0; loop--) {
-               ksz_read8(dev, REG_IND_MIB_CHECK, &check);
+               ksz_read8(dev, regs->ind_mib_check, &check);
 
-               if (check & MIB_COUNTER_VALID) {
-                       ksz_read32(dev, REG_IND_DATA_LO, &data);
+               if (check & KSZ8795_MIB_COUNTER_VALID) {
+                       ksz_read32(dev, regs->ind_data_lo, &data);
                        if (addr < 2) {
                                u64 total;
 
@@ -179,13 +367,13 @@ static void ksz8795_r_mib_pkt(struct ksz_device *dev, int 
port, u16 addr,
                                total <<= 32;
                                *cnt += total;
                                *cnt += data;
-                               if (check & MIB_COUNTER_OVERFLOW) {
+                               if (check & KSZ8795_MIB_COUNTER_OVERFLOW) {
                                        total = MIB_TOTAL_BYTES_H + 1;
                                        total <<= 32;
                                        *cnt += total;
                                }
                        } else {
-                               if (check & MIB_COUNTER_OVERFLOW)
+                               if (check & KSZ8795_MIB_COUNTER_OVERFLOW)
                                        *cnt += MIB_PACKET_DROPPED + 1;
                                *cnt += data & MIB_PACKET_DROPPED;
                        }
@@ -207,14 +395,17 @@ static void ksz8795_freeze_mib(struct ksz_device *dev, 
int port, bool freeze)
                ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
 }
 
-static void ksz8795_port_init_cnt(struct ksz_device *dev, int port)
+static void ksz8_port_init_cnt(struct ksz_device *dev, int port)
 {
        struct ksz_port_mib *mib = &dev->ports[port].mib;
+       u64 *dropped;
 
-       /* flush all enabled port MIB counters */
-       ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
-       ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true);
-       ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
+       if (ksz_is_87(dev)) {
+               /* flush all enabled port MIB counters */
+               ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
+               ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true);
+               ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
+       }
 
        mib->cnt_ptr = 0;
 
@@ -225,69 +416,81 @@ static void ksz8795_port_init_cnt(struct ksz_device *dev, 
int port)
                ++mib->cnt_ptr;
        }
 
+       /* last one in storage */
+       dropped = &mib->counters[dev->mib_cnt];
+
        /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */
        while (mib->cnt_ptr < dev->mib_cnt) {
                dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr,
-                                       NULL, &mib->counters[mib->cnt_ptr]);
+                                       dropped, &mib->counters[mib->cnt_ptr]);
                ++mib->cnt_ptr;
        }
        mib->cnt_ptr = 0;
        memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
 }
 
-static void ksz8795_r_table(struct ksz_device *dev, int table, u16 addr,
-                           u64 *data)
+static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 
*data)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
        u16 ctrl_addr;
 
        ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr;
 
        mutex_lock(&dev->alu_mutex);
-       ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
-       ksz_read64(dev, REG_IND_DATA_HI, data);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
+       ksz_read64(dev, regs->ind_data_hi, data);
        mutex_unlock(&dev->alu_mutex);
 }
 
-static void ksz8795_w_table(struct ksz_device *dev, int table, u16 addr,
-                           u64 data)
+static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
        u16 ctrl_addr;
 
        ctrl_addr = IND_ACC_TABLE(table) | addr;
 
        mutex_lock(&dev->alu_mutex);
-       ksz_write64(dev, REG_IND_DATA_HI, data);
-       ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
+       ksz_write64(dev, regs->ind_data_hi, data);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
        mutex_unlock(&dev->alu_mutex);
 }
 
-static int ksz8795_valid_dyn_entry(struct ksz_device *dev, u8 *data)
+static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
+       struct ksz_masks *masks = ksz8->masks;
        int timeout = 100;
 
        do {
-               ksz_read8(dev, REG_IND_DATA_CHECK, data);
+               ksz_read8(dev, regs->ind_data_check, data);
                timeout--;
-       } while ((*data & DYNAMIC_MAC_TABLE_NOT_READY) && timeout);
+       } while ((*data & masks->dynamic_mac_table_not_ready) && timeout);
 
        /* Entry is not ready for accessing. */
-       if (*data & DYNAMIC_MAC_TABLE_NOT_READY) {
+       if (*data & masks->dynamic_mac_table_not_ready) {
                return -EAGAIN;
        /* Entry is ready for accessing. */
        } else {
-               ksz_read8(dev, REG_IND_DATA_8, data);
+               ksz_read8(dev, regs->ind_data_8, data);
 
                /* There is no valid entry in the table. */
-               if (*data & DYNAMIC_MAC_TABLE_MAC_EMPTY)
+               if (*data & masks->dynamic_mac_table_mac_empty)
                        return -ENXIO;
        }
        return 0;
 }
 
-static int ksz8795_r_dyn_mac_table(struct ksz_device *dev, u16 addr,
-                                  u8 *mac_addr, u8 *fid, u8 *src_port,
-                                  u8 *timestamp, u16 *entries)
+static int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr,
+                               u8 *mac_addr, u8 *fid, u8 *src_port,
+                               u8 *timestamp, u16 *entries)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
+       struct ksz_masks *masks = ksz8->masks;
+       struct ksz_shifts *shifts = ksz8->shifts;
        u32 data_hi, data_lo;
        u16 ctrl_addr;
        u8 data;
@@ -296,9 +499,9 @@ static int ksz8795_r_dyn_mac_table(struct ksz_device *dev, 
u16 addr,
        ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr;
 
        mutex_lock(&dev->alu_mutex);
-       ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
+       ksz_write16(dev, regs->ind_ctrl_0, ctrl_addr);
 
-       rc = ksz8795_valid_dyn_entry(dev, &data);
+       rc = ksz8_valid_dyn_entry(dev, &data);
        if (rc == -EAGAIN) {
                if (addr == 0)
                        *entries = 0;
@@ -309,23 +512,23 @@ static int ksz8795_r_dyn_mac_table(struct ksz_device 
*dev, u16 addr,
                u64 buf = 0;
                int cnt;
 
-               ksz_read64(dev, REG_IND_DATA_HI, &buf);
+               ksz_read64(dev, regs->ind_data_hi, &buf);
                data_hi = (u32)(buf >> 32);
                data_lo = (u32)buf;
 
                /* Check out how many valid entry in the table. */
-               cnt = data & DYNAMIC_MAC_TABLE_ENTRIES_H;
-               cnt <<= DYNAMIC_MAC_ENTRIES_H_S;
-               cnt |= (data_hi & DYNAMIC_MAC_TABLE_ENTRIES) >>
-                       DYNAMIC_MAC_ENTRIES_S;
+               cnt = data & masks->dynamic_mac_table_entries_h;
+               cnt <<= shifts->dynamic_mac_entries_h;
+               cnt |= (data_hi & masks->dynamic_mac_table_entries) >>
+                       shifts->dynamic_mac_entries;
                *entries = cnt + 1;
 
-               *fid = (data_hi & DYNAMIC_MAC_TABLE_FID) >>
-                       DYNAMIC_MAC_FID_S;
-               *src_port = (data_hi & DYNAMIC_MAC_TABLE_SRC_PORT) >>
-                       DYNAMIC_MAC_SRC_PORT_S;
-               *timestamp = (data_hi & DYNAMIC_MAC_TABLE_TIMESTAMP) >>
-                       DYNAMIC_MAC_TIMESTAMP_S;
+               *fid = (data_hi & masks->dynamic_mac_table_fid) >>
+                       shifts->dynamic_mac_fid;
+               *src_port = (data_hi & masks->dynamic_mac_table_src_port) >>
+                       shifts->dynamic_mac_src_port;
+               *timestamp = (data_hi & masks->dynamic_mac_table_timestamp) >>
+                       shifts->dynamic_mac_timestamp;
 
                mac_addr[5] = (u8)data_lo;
                mac_addr[4] = (u8)(data_lo >> 8);
@@ -341,38 +544,48 @@ static int ksz8795_r_dyn_mac_table(struct ksz_device 
*dev, u16 addr,
        return rc;
 }
 
-static int ksz8795_r_sta_mac_table(struct ksz_device *dev, u16 addr,
-                                  struct alu_struct *alu)
+static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr,
+                               struct alu_struct *alu)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_masks *masks = ksz8->masks;
+       struct ksz_shifts *shifts = ksz8->shifts;
        u32 data_hi, data_lo;
        u64 data;
 
-       ksz8795_r_table(dev, TABLE_STATIC_MAC, addr, &data);
+       ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data);
        data_hi = data >> 32;
        data_lo = (u32)data;
-       if (data_hi & (STATIC_MAC_TABLE_VALID | STATIC_MAC_TABLE_OVERRIDE)) {
+       if (data_hi & (masks->static_mac_table_valid |
+                       masks->static_mac_table_override)) {
                alu->mac[5] = (u8)data_lo;
                alu->mac[4] = (u8)(data_lo >> 8);
                alu->mac[3] = (u8)(data_lo >> 16);
                alu->mac[2] = (u8)(data_lo >> 24);
                alu->mac[1] = (u8)data_hi;
                alu->mac[0] = (u8)(data_hi >> 8);
-               alu->port_forward = (data_hi & STATIC_MAC_TABLE_FWD_PORTS) >>
-                       STATIC_MAC_FWD_PORTS_S;
+               alu->port_forward =
+                       (data_hi & masks->static_mac_table_fwd_ports) >>
+                               shifts->static_mac_fwd_ports;
                alu->is_override =
-                       (data_hi & STATIC_MAC_TABLE_OVERRIDE) ? 1 : 0;
+                       (data_hi & masks->static_mac_table_override) ? 1 : 0;
                data_hi >>= 1;
-               alu->is_use_fid = (data_hi & STATIC_MAC_TABLE_USE_FID) ? 1 : 0;
-               alu->fid = (data_hi & STATIC_MAC_TABLE_FID) >>
-                       STATIC_MAC_FID_S;
+               alu->is_static = true;
+               alu->is_use_fid =
+                       (data_hi & masks->static_mac_table_use_fid) ? 1 : 0;
+               alu->fid = (data_hi & masks->static_mac_table_fid) >>
+                               shifts->static_mac_fid;
                return 0;
        }
        return -ENXIO;
 }
 
-static void ksz8795_w_sta_mac_table(struct ksz_device *dev, u16 addr,
-                                   struct alu_struct *alu)
+static void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr,
+                                struct alu_struct *alu)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_masks *masks = ksz8->masks;
+       struct ksz_shifts *shifts = ksz8->shifts;
        u32 data_hi, data_lo;
        u64 data;
 
@@ -380,83 +593,106 @@ static void ksz8795_w_sta_mac_table(struct ksz_device 
*dev, u16 addr,
                ((u32)alu->mac[3] << 16) |
                ((u32)alu->mac[4] << 8) | alu->mac[5];
        data_hi = ((u32)alu->mac[0] << 8) | alu->mac[1];
-       data_hi |= (u32)alu->port_forward << STATIC_MAC_FWD_PORTS_S;
+       data_hi |= (u32)alu->port_forward << shifts->static_mac_fwd_ports;
 
        if (alu->is_override)
-               data_hi |= STATIC_MAC_TABLE_OVERRIDE;
+               data_hi |= masks->static_mac_table_override;
        if (alu->is_use_fid) {
-               data_hi |= STATIC_MAC_TABLE_USE_FID;
-               data_hi |= (u32)alu->fid << STATIC_MAC_FID_S;
+               data_hi |= masks->static_mac_table_use_fid;
+               data_hi |= (u32)alu->fid << shifts->static_mac_fid;
        }
        if (alu->is_static)
-               data_hi |= STATIC_MAC_TABLE_VALID;
+               data_hi |= masks->static_mac_table_valid;
        else
-               data_hi &= ~STATIC_MAC_TABLE_OVERRIDE;
+               data_hi &= ~masks->static_mac_table_override;
 
        data = (u64)data_hi << 32 | data_lo;
-       ksz8795_w_table(dev, TABLE_STATIC_MAC, addr, data);
+       ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data);
 }
 
-static void ksz8795_from_vlan(u16 vlan, u8 *fid, u8 *member, u8 *valid)
+static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid,
+                          u8 *member, u8 *valid)
 {
-       *fid = vlan & VLAN_TABLE_FID;
-       *member = (vlan & VLAN_TABLE_MEMBERSHIP) >> VLAN_TABLE_MEMBERSHIP_S;
-       *valid = !!(vlan & VLAN_TABLE_VALID);
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_masks *masks = ksz8->masks;
+       struct ksz_shifts *shifts = ksz8->shifts;
+
+       *fid = vlan & masks->vlan_table_fid;
+       *member = (vlan & masks->vlan_table_membership) >>
+                       shifts->vlan_table_membership;
+       *valid = !!(vlan & masks->vlan_table_valid);
 }
 
-static void ksz8795_to_vlan(u8 fid, u8 member, u8 valid, u16 *vlan)
+static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid,
+                        u32 *vlan)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_masks *masks = ksz8->masks;
+       struct ksz_shifts *shifts = ksz8->shifts;
+
        *vlan = fid;
-       *vlan |= (u16)member << VLAN_TABLE_MEMBERSHIP_S;
+       *vlan |= (u16)member << shifts->vlan_table_membership;
        if (valid)
-               *vlan |= VLAN_TABLE_VALID;
+               *vlan |= masks->vlan_table_valid;
 }
 
-static void ksz8795_r_vlan_entries(struct ksz_device *dev, u16 addr)
+static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_shifts *shifts = ksz8->shifts;
        u64 data;
        int i;
 
-       ksz8795_r_table(dev, TABLE_VLAN, addr, &data);
-       addr *= 4;
-       for (i = 0; i < 4; i++) {
-               dev->vlan_cache[addr + i].table[0] = (u16)data;
-               data >>= VLAN_TABLE_S;
+       ksz8_r_table(dev, TABLE_VLAN, addr, &data);
+       addr *= dev->port_cnt;
+       for (i = 0; i < dev->port_cnt; i++) {
+               if (ksz_is_87(dev)) {
+                       dev->vlan_cache[addr + i].table[0] = (u16)data;
+                       data >>= shifts->vlan_table;
+               } else {
+                       dev->vlan_cache[addr + i].table[0] = (u32)data;
+               }
        }
 }
 
-static void ksz8795_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan)
+static void ksz8_r_vlan_table(struct ksz_device *dev, u16 vid, u32 *vlan)
 {
-       int index;
-       u16 *data;
-       u16 addr;
+       u16 addr = vid / dev->port_cnt;
        u64 buf;
 
-       data = (u16 *)&buf;
-       addr = vid / 4;
-       index = vid & 3;
-       ksz8795_r_table(dev, TABLE_VLAN, addr, &buf);
-       *vlan = data[index];
+       ksz8_r_table(dev, TABLE_VLAN, addr, &buf);
+       if (ksz_is_87(dev)) {
+               u16 *data = (u16 *)&buf;
+
+               *vlan = data[vid & 3];
+       } else {
+               *vlan = (u32)buf;
+       }
 }
 
-static void ksz8795_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan)
+static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u32 vlan)
 {
-       int index;
-       u16 *data;
-       u16 addr;
+       u16 addr = vid / dev->port_cnt;
        u64 buf;
 
-       data = (u16 *)&buf;
-       addr = vid / 4;
-       index = vid & 3;
-       ksz8795_r_table(dev, TABLE_VLAN, addr, &buf);
-       data[index] = vlan;
+       ksz8_r_table(dev, TABLE_VLAN, addr, &buf);
+
+       if (ksz_is_87(dev)) {
+               u16 *data = (u16 *)&buf;
+
+               data[vid & 3] = vlan;
+       } else {
+               buf = vlan;
+       }
+
        dev->vlan_cache[vid].table[0] = vlan;
-       ksz8795_w_table(dev, TABLE_VLAN, addr, buf);
+       ksz8_w_table(dev, TABLE_VLAN, addr, buf);
 }
 
-static void ksz8795_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
+static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
 {
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
        u8 restart, speed, ctrl, link;
        int processed = true;
        u16 data = 0;
@@ -464,15 +700,20 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 *val)
 
        switch (reg) {
        case PHY_REG_CTRL:
-               ksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart);
-               ksz_pread8(dev, p, P_SPEED_STATUS, &speed);
-               ksz_pread8(dev, p, P_FORCE_CTRL, &ctrl);
+               ksz_pread8(dev, p, regs->p_neg_restart_ctrl, &restart);
+               ksz_pread8(dev, p, regs->p_speed_status, &speed);
+               ksz_pread8(dev, p, regs->p_force_ctrl, &ctrl);
                if (restart & PORT_PHY_LOOPBACK)
                        data |= PHY_LOOPBACK;
                if (ctrl & PORT_FORCE_100_MBIT)
                        data |= PHY_SPEED_100MBIT;
-               if (!(ctrl & PORT_AUTO_NEG_DISABLE))
-                       data |= PHY_AUTO_NEG_ENABLE;
+               if (ksz_is_87(dev)) {
+                       if (!(ctrl & PORT_AUTO_NEG_DISABLE))
+                               data |= PHY_AUTO_NEG_ENABLE;
+               } else {
+                       if ((ctrl & PORT_AUTO_NEG_ENABLE))
+                               data |= PHY_AUTO_NEG_ENABLE;
+               }
                if (restart & PORT_POWER_DOWN)
                        data |= PHY_POWER_DOWN;
                if (restart & PORT_AUTO_NEG_RESTART)
@@ -491,7 +732,7 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 phy, 
u16 reg, u16 *val)
                        data |= PHY_LED_DISABLE;
                break;
        case PHY_REG_STATUS:
-               ksz_pread8(dev, p, P_LINK_STATUS, &link);
+               ksz_pread8(dev, p, regs->p_link_status, &link);
                data = PHY_100BTX_FD_CAPABLE |
                       PHY_100BTX_CAPABLE |
                       PHY_10BT_FD_CAPABLE |
@@ -503,13 +744,19 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 *val)
                        data |= PHY_LINK_STATUS;
                break;
        case PHY_REG_ID_1:
-               data = KSZ8795_ID_HI;
+               if (ksz_is_87(dev))
+                       data = KSZ8795_ID_HI;
+               else
+                       data = KSZ8863_ID_HI;
                break;
        case PHY_REG_ID_2:
-               data = KSZ8795_ID_LO;
+               if (ksz_is_87(dev))
+                       data = KSZ8795_ID_LO;
+               else
+                       data = KSZ8863_ID_LO;
                break;
        case PHY_REG_AUTO_NEGOTIATION:
-               ksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl);
+               ksz_pread8(dev, p, regs->p_local_ctrl, &ctrl);
                data = PHY_AUTO_NEG_802_3;
                if (ctrl & PORT_AUTO_NEG_SYM_PAUSE)
                        data |= PHY_AUTO_NEG_SYM_PAUSE;
@@ -523,7 +770,7 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 phy, 
u16 reg, u16 *val)
                        data |= PHY_AUTO_NEG_10BT;
                break;
        case PHY_REG_REMOTE_CAPABILITY:
-               ksz_pread8(dev, p, P_REMOTE_STATUS, &link);
+               ksz_pread8(dev, p, regs->p_remote_status, &link);
                data = PHY_AUTO_NEG_802_3;
                if (link & PORT_REMOTE_SYM_PAUSE)
                        data |= PHY_AUTO_NEG_SYM_PAUSE;
@@ -546,10 +793,12 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 *val)
                *val = data;
 }
 
-static void ksz8795_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
+static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
 {
-       u8 p = phy;
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
        u8 restart, speed, ctrl, data;
+       u8 p = phy;
 
        switch (reg) {
        case PHY_REG_CTRL:
@@ -557,24 +806,31 @@ static void ksz8795_w_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 val)
                /* Do not support PHY reset function. */
                if (val & PHY_RESET)
                        break;
-               ksz_pread8(dev, p, P_SPEED_STATUS, &speed);
+               ksz_pread8(dev, p, regs->p_speed_status, &speed);
                data = speed;
                if (val & PHY_HP_MDIX)
                        data |= PORT_HP_MDIX;
                else
                        data &= ~PORT_HP_MDIX;
                if (data != speed)
-                       ksz_pwrite8(dev, p, P_SPEED_STATUS, data);
-               ksz_pread8(dev, p, P_FORCE_CTRL, &ctrl);
+                       ksz_pwrite8(dev, p, regs->p_speed_status, data);
+               ksz_pread8(dev, p, regs->p_force_ctrl, &ctrl);
                data = ctrl;
-               if (!(val & PHY_AUTO_NEG_ENABLE))
-                       data |= PORT_AUTO_NEG_DISABLE;
-               else
-                       data &= ~PORT_AUTO_NEG_DISABLE;
-
-               /* Fiber port does not support auto-negotiation. */
-               if (dev->ports[p].fiber)
-                       data |= PORT_AUTO_NEG_DISABLE;
+               if (ksz_is_87(dev)) {
+                       if (!(val & PHY_AUTO_NEG_ENABLE))
+                               data |= PORT_AUTO_NEG_DISABLE;
+                       else
+                               data &= ~PORT_AUTO_NEG_DISABLE;
+
+                       /* Fiber port does not support auto-negotiation. */
+                       if (dev->ports[p].fiber)
+                               data |= PORT_AUTO_NEG_DISABLE;
+               } else {
+                       if ((val & PHY_AUTO_NEG_ENABLE))
+                               data |= PORT_AUTO_NEG_ENABLE;
+                       else
+                               data &= ~PORT_AUTO_NEG_ENABLE;
+               }
                if (val & PHY_SPEED_100MBIT)
                        data |= PORT_FORCE_100_MBIT;
                else
@@ -584,8 +840,8 @@ static void ksz8795_w_phy(struct ksz_device *dev, u16 phy, 
u16 reg, u16 val)
                else
                        data &= ~PORT_FORCE_FULL_DUPLEX;
                if (data != ctrl)
-                       ksz_pwrite8(dev, p, P_FORCE_CTRL, data);
-               ksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart);
+                       ksz_pwrite8(dev, p, regs->p_force_ctrl, data);
+               ksz_pread8(dev, p, regs->p_neg_restart_ctrl, &restart);
                data = restart;
                if (val & PHY_LED_DISABLE)
                        data |= PORT_LED_OFF;
@@ -616,10 +872,10 @@ static void ksz8795_w_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 val)
                else
                        data &= ~PORT_PHY_LOOPBACK;
                if (data != restart)
-                       ksz_pwrite8(dev, p, P_NEG_RESTART_CTRL, data);
+                       ksz_pwrite8(dev, p, regs->p_neg_restart_ctrl, data);
                break;
        case PHY_REG_AUTO_NEGOTIATION:
-               ksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl);
+               ksz_pread8(dev, p, regs->p_local_ctrl, &ctrl);
                data = ctrl;
                data &= ~(PORT_AUTO_NEG_SYM_PAUSE |
                          PORT_AUTO_NEG_100BTX_FD |
@@ -637,45 +893,50 @@ static void ksz8795_w_phy(struct ksz_device *dev, u16 
phy, u16 reg, u16 val)
                if (val & PHY_AUTO_NEG_10BT)
                        data |= PORT_AUTO_NEG_10BT;
                if (data != ctrl)
-                       ksz_pwrite8(dev, p, P_LOCAL_CTRL, data);
+                       ksz_pwrite8(dev, p, regs->p_local_ctrl, data);
                break;
        default:
                break;
        }
 }
 
-static enum dsa_tag_protocol ksz8795_get_tag_protocol(struct dsa_switch *ds,
-                                                     int port,
-                                                     enum dsa_tag_protocol mp)
+static enum dsa_tag_protocol ksz8_get_tag_protocol(struct dsa_switch *ds,
+                                                  int port,
+                                                  enum dsa_tag_protocol mp)
 {
-       return DSA_TAG_PROTO_KSZ8795;
+       struct ksz_device *dev = ds->priv;
+
+       return ksz_is_87(dev) ?
+               DSA_TAG_PROTO_KSZ8795 : DSA_TAG_PROTO_KSZ8863;
 }
 
-static void ksz8795_get_strings(struct dsa_switch *ds, int port,
-                               u32 stringset, uint8_t *buf)
+static void ksz8_get_strings(struct dsa_switch *ds, int port,
+                            u32 stringset, uint8_t *buf)
 {
-       int i;
+       struct ksz_device *dev = ds->priv;
 
-       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
-               memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
-                      ETH_GSTRING_LEN);
-       }
+       if (ksz_is_87(dev))
+               memcpy(buf, ksz87xx_mib_names, sizeof(ksz87xx_mib_names));
+       else
+               memcpy(buf, ksz88xx_mib_names, sizeof(ksz88xx_mib_names));
 }
 
-static void ksz8795_cfg_port_member(struct ksz_device *dev, int port,
-                                   u8 member)
+static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member)
 {
+       int membership = PORT_VLAN_MEMBERSHIP;
        u8 data;
 
+       if (!ksz_is_87(dev))
+               membership = (membership >> 2);
+
        ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
-       data &= ~PORT_VLAN_MEMBERSHIP;
+       data &= ~(membership);
        data |= (member & dev->port_mask);
        ksz_pwrite8(dev, port, P_MIRROR_CTRL, data);
        dev->ports[port].member = member;
 }
 
-static void ksz8795_port_stp_state_set(struct dsa_switch *ds, int port,
-                                      u8 state)
+static void ksz8_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 {
        struct ksz_device *dev = ds->priv;
        int forward = dev->member;
@@ -691,12 +952,12 @@ static void ksz8795_port_stp_state_set(struct dsa_switch 
*ds, int port,
        switch (state) {
        case BR_STATE_DISABLED:
                data |= PORT_LEARN_DISABLE;
-               if (port < SWITCH_PORT_NUM)
+               if (port < dev->port_cnt)
                        member = 0;
                break;
        case BR_STATE_LISTENING:
                data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
-               if (port < SWITCH_PORT_NUM &&
+               if (port < dev->port_cnt &&
                    p->stp_state == BR_STATE_DISABLED)
                        member = dev->host_mask | p->vid_member;
                break;
@@ -720,7 +981,7 @@ static void ksz8795_port_stp_state_set(struct dsa_switch 
*ds, int port,
                break;
        case BR_STATE_BLOCKING:
                data |= PORT_LEARN_DISABLE;
-               if (port < SWITCH_PORT_NUM &&
+               if (port < dev->port_cnt &&
                    p->stp_state == BR_STATE_DISABLED)
                        member = dev->host_mask | p->vid_member;
                break;
@@ -742,7 +1003,7 @@ static void ksz8795_port_stp_state_set(struct dsa_switch 
*ds, int port,
 
        /* Port membership may share register with STP state. */
        if (member >= 0 && member != p->member)
-               ksz8795_cfg_port_member(dev, port, (u8)member);
+               ksz8_cfg_port_member(dev, port, (u8)member);
 
        /* Check if forwarding needs to be updated. */
        if (state != BR_STATE_FORWARDING) {
@@ -757,13 +1018,13 @@ static void ksz8795_port_stp_state_set(struct dsa_switch 
*ds, int port,
                ksz_update_port_member(dev, port);
 }
 
-static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port)
+static void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
 {
-       u8 learn[TOTAL_PORT_NUM];
+       u8 *learn = kzalloc(dev->mib_port_cnt, GFP_KERNEL);
        int first, index, cnt;
        struct ksz_port *p;
 
-       if ((uint)port < TOTAL_PORT_NUM) {
+       if ((uint)port < dev->mib_port_cnt) {
                first = port;
                cnt = port + 1;
        } else {
@@ -786,12 +1047,13 @@ static void ksz8795_flush_dyn_mac_table(struct 
ksz_device *dev, int port)
                if (!p->on)
                        continue;
                if (!(learn[index] & PORT_LEARN_DISABLE))
-                       ksz_pwrite8(dev, index, P_STP_CTRL, learn[index]);
+                       ksz_pwrite8(dev, index, P_STP_CTRL,
+                                   learn[index]);
        }
+       kfree(learn);
 }
 
-static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                      bool flag)
+static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)
 {
        struct ksz_device *dev = ds->priv;
 
@@ -800,19 +1062,20 @@ static int ksz8795_port_vlan_filtering(struct dsa_switch 
*ds, int port,
        return 0;
 }
 
-static void ksz8795_port_vlan_add(struct dsa_switch *ds, int port,
-                                 const struct switchdev_obj_port_vlan *vlan)
+static void ksz8_port_vlan_add(struct dsa_switch *ds, int port,
+                              const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct ksz_device *dev = ds->priv;
-       u16 data, vid, new_pvid = 0;
+       u16 vid, new_pvid = 0;
+       u32 data = 0;
        u8 fid, member, valid;
 
        ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
 
        for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               ksz8795_r_vlan_table(dev, vid, &data);
-               ksz8795_from_vlan(data, &fid, &member, &valid);
+               ksz8_r_vlan_table(dev, vid, &data);
+               ksz8_from_vlan(dev, data, &fid, &member, &valid);
 
                /* First time to setup the VLAN entry. */
                if (!valid) {
@@ -822,9 +1085,8 @@ static void ksz8795_port_vlan_add(struct dsa_switch *ds, 
int port,
                }
                member |= BIT(port);
 
-               ksz8795_to_vlan(fid, member, valid, &data);
-               ksz8795_w_vlan_table(dev, vid, data);
-
+               ksz8_to_vlan(dev, fid, member, valid, &data);
+               ksz8_w_vlan_table(dev, vid, data);
                /* change PVID */
                if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
                        new_pvid = vid;
@@ -838,12 +1100,13 @@ static void ksz8795_port_vlan_add(struct dsa_switch *ds, 
int port,
        }
 }
 
-static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_vlan *vlan)
+static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,
+                             const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct ksz_device *dev = ds->priv;
-       u16 data, vid, pvid, new_pvid = 0;
+       u32 data = 0;
+       u16 vid, pvid, new_pvid = 0;
        u8 fid, member, valid;
 
        ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
@@ -852,8 +1115,8 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, 
int port,
        ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
 
        for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               ksz8795_r_vlan_table(dev, vid, &data);
-               ksz8795_from_vlan(data, &fid, &member, &valid);
+               ksz8_r_vlan_table(dev, vid, &data);
+               ksz8_from_vlan(dev, data, &fid, &member, &valid);
 
                member &= ~BIT(port);
 
@@ -866,8 +1129,8 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, 
int port,
                if (pvid == vid)
                        new_pvid = 1;
 
-               ksz8795_to_vlan(fid, member, valid, &data);
-               ksz8795_w_vlan_table(dev, vid, data);
+               ksz8_to_vlan(dev, fid, member, valid, &data);
+               ksz8_w_vlan_table(dev, vid, data);
        }
 
        if (new_pvid != pvid)
@@ -876,9 +1139,9 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, 
int port,
        return 0;
 }
 
-static int ksz8795_port_mirror_add(struct dsa_switch *ds, int port,
-                                  struct dsa_mall_mirror_tc_entry *mirror,
-                                  bool ingress)
+static int ksz8_port_mirror_add(struct dsa_switch *ds, int port,
+                               struct dsa_mall_mirror_tc_entry *mirror,
+                               bool ingress)
 {
        struct ksz_device *dev = ds->priv;
 
@@ -900,8 +1163,8 @@ static int ksz8795_port_mirror_add(struct dsa_switch *ds, 
int port,
        return 0;
 }
 
-static void ksz8795_port_mirror_del(struct dsa_switch *ds, int port,
-                                   struct dsa_mall_mirror_tc_entry *mirror)
+static void ksz8_port_mirror_del(struct dsa_switch *ds, int port,
+                                struct dsa_mall_mirror_tc_entry *mirror)
 {
        struct ksz_device *dev = ds->priv;
        u8 data;
@@ -921,59 +1184,71 @@ static void ksz8795_port_mirror_del(struct dsa_switch 
*ds, int port,
                             PORT_MIRROR_SNIFFER, false);
 }
 
-static void ksz8795_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port)
 {
        struct ksz_port *p = &dev->ports[port];
-       u8 data8, member;
+       u8 data8;
+
+       /* Configure MII interface for proper network communication. */
+       ksz_read8(dev, REG_PORT_5_CTRL_6, &data8);
+       data8 &= ~PORT_INTERFACE_TYPE;
+       data8 &= ~PORT_GMII_1GPS_MODE;
+       switch (dev->interface) {
+       case PHY_INTERFACE_MODE_MII:
+               p->phydev.speed = SPEED_100;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               data8 |= PORT_INTERFACE_RMII;
+               p->phydev.speed = SPEED_100;
+               break;
+       case PHY_INTERFACE_MODE_GMII:
+               data8 |= PORT_GMII_1GPS_MODE;
+               data8 |= PORT_INTERFACE_GMII;
+               p->phydev.speed = SPEED_1000;
+               break;
+       default:
+               data8 &= ~PORT_RGMII_ID_IN_ENABLE;
+               data8 &= ~PORT_RGMII_ID_OUT_ENABLE;
+               if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                   dev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+                       data8 |= PORT_RGMII_ID_IN_ENABLE;
+               if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                   dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+                       data8 |= PORT_RGMII_ID_OUT_ENABLE;
+               data8 |= PORT_GMII_1GPS_MODE;
+               data8 |= PORT_INTERFACE_RGMII;
+               p->phydev.speed = SPEED_1000;
+               break;
+       }
+       ksz_write8(dev, REG_PORT_5_CTRL_6, data8);
+       p->phydev.duplex = 1;
+}
 
+static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+       struct ksz_port *p = &dev->ports[port];
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_masks *masks = ksz8->masks;
+       u8 member;
        /* enable broadcast storm limit */
        ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
 
-       ksz8795_set_prio_queue(dev, port, 4);
+       if (ksz_is_87(dev))
+               ksz8795_set_prio_queue(dev, port, 4);
 
        /* disable DiffServ priority */
        ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);
 
        /* replace priority */
-       ksz_port_cfg(dev, port, P_802_1P_CTRL, PORT_802_1P_REMAPPING, false);
+       ksz_port_cfg(dev, port, P_802_1P_CTRL,
+                    masks->port_802_1p_remapping, false);
 
        /* enable 802.1p priority */
        ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);
 
        if (cpu_port) {
-               /* Configure MII interface for proper network communication. */
-               ksz_read8(dev, REG_PORT_5_CTRL_6, &data8);
-               data8 &= ~PORT_INTERFACE_TYPE;
-               data8 &= ~PORT_GMII_1GPS_MODE;
-               switch (dev->interface) {
-               case PHY_INTERFACE_MODE_MII:
-                       p->phydev.speed = SPEED_100;
-                       break;
-               case PHY_INTERFACE_MODE_RMII:
-                       data8 |= PORT_INTERFACE_RMII;
-                       p->phydev.speed = SPEED_100;
-                       break;
-               case PHY_INTERFACE_MODE_GMII:
-                       data8 |= PORT_GMII_1GPS_MODE;
-                       data8 |= PORT_INTERFACE_GMII;
-                       p->phydev.speed = SPEED_1000;
-                       break;
-               default:
-                       data8 &= ~PORT_RGMII_ID_IN_ENABLE;
-                       data8 &= ~PORT_RGMII_ID_OUT_ENABLE;
-                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-                           dev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
-                               data8 |= PORT_RGMII_ID_IN_ENABLE;
-                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
-                           dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
-                               data8 |= PORT_RGMII_ID_OUT_ENABLE;
-                       data8 |= PORT_GMII_1GPS_MODE;
-                       data8 |= PORT_INTERFACE_RGMII;
-                       p->phydev.speed = SPEED_1000;
-                       break;
-               }
-               ksz_write8(dev, REG_PORT_5_CTRL_6, data8);
-               p->phydev.duplex = 1;
+               if (ksz_is_87(dev))
+                       ksz8795_cpu_interface_select(dev, port);
 
                member = dev->port_mask;
                dev->on_ports = dev->host_mask;
@@ -986,12 +1261,15 @@ static void ksz8795_port_setup(struct ksz_device *dev, 
int port, bool cpu_port)
                if (p->phydev.link)
                        dev->live_ports |= BIT(port);
        }
-       ksz8795_cfg_port_member(dev, port, member);
+       ksz8_cfg_port_member(dev, port, member);
 }
 
-static void ksz8795_config_cpu_port(struct dsa_switch *ds)
+static void ksz8_config_cpu_port(struct dsa_switch *ds)
 {
        struct ksz_device *dev = ds->priv;
+       struct ksz8 *ksz8 = dev->priv;
+       struct ksz_regs *regs = ksz8->regs;
+       struct ksz_masks *masks = ksz8->masks;
        struct ksz_port *p;
        u8 remote;
        int i;
@@ -1000,16 +1278,16 @@ static void ksz8795_config_cpu_port(struct dsa_switch 
*ds)
 
        /* Switch marks the maximum frame with extra byte as oversize. */
        ksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true);
-       ksz_cfg(dev, S_TAIL_TAG_CTRL, SW_TAIL_TAG_ENABLE, true);
+       ksz_cfg(dev, regs->s_tail_tag_ctrl, masks->sw_tail_tag_enable, true);
 
        p = &dev->ports[dev->cpu_port];
        p->vid_member = dev->port_mask;
        p->on = 1;
 
-       ksz8795_port_setup(dev, dev->cpu_port, true);
+       ksz8_port_setup(dev, dev->cpu_port, true);
        dev->member = dev->host_mask;
 
-       for (i = 0; i < SWITCH_PORT_NUM; i++) {
+       for (i = 0; i < dev->port_cnt; i++) {
                p = &dev->ports[i];
 
                /* Initialize to non-zero so that ksz_cfg_port_member() will
@@ -1017,7 +1295,7 @@ static void ksz8795_config_cpu_port(struct dsa_switch *ds)
                 */
                p->vid_member = BIT(i);
                p->member = dev->port_mask;
-               ksz8795_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+               ksz8_port_stp_state_set(ds, i, BR_STATE_DISABLED);
 
                /* Last port may be disabled. */
                if (i == dev->port_cnt)
@@ -1029,9 +1307,11 @@ static void ksz8795_config_cpu_port(struct dsa_switch 
*ds)
                p = &dev->ports[i];
                if (!p->on)
                        continue;
-               ksz_pread8(dev, i, P_REMOTE_STATUS, &remote);
-               if (remote & PORT_FIBER_MODE)
-                       p->fiber = 1;
+               if (ksz_is_87(dev)) {
+                       ksz_pread8(dev, i, regs->p_remote_status, &remote);
+                       if (remote & PORT_FIBER_MODE)
+                               p->fiber = 1;
+               }
                if (p->fiber)
                        ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL,
                                     true);
@@ -1041,7 +1321,7 @@ static void ksz8795_config_cpu_port(struct dsa_switch *ds)
        }
 }
 
-static int ksz8795_setup(struct dsa_switch *ds)
+static int ksz8_setup(struct dsa_switch *ds)
 {
        struct ksz_device *dev = ds->priv;
        struct alu_struct alu;
@@ -1052,7 +1332,7 @@ static int ksz8795_setup(struct dsa_switch *ds)
        if (!dev->vlan_cache)
                return -ENOMEM;
 
-       ret = ksz8795_reset_switch(dev);
+       dev->dev_ops->shutdown(dev);
        if (ret) {
                dev_err(ds->dev, "failed to reset switch\n");
                return ret;
@@ -1075,7 +1355,7 @@ static int ksz8795_setup(struct dsa_switch *ds)
                           UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP,
                           UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP);
 
-       ksz8795_config_cpu_port(ds);
+       ksz8_config_cpu_port(ds);
 
        ksz_cfg(dev, REG_SW_CTRL_2, MULTICAST_STORM_DISABLE, true);
 
@@ -1089,8 +1369,8 @@ static int ksz8795_setup(struct dsa_switch *ds)
                           (BROADCAST_STORM_VALUE *
                           BROADCAST_STORM_PROT_RATE) / 100);
 
-       for (i = 0; i < VLAN_TABLE_ENTRIES; i++)
-               ksz8795_r_vlan_entries(dev, i);
+       for (i = 0; i < (dev->num_vlans / 2); i++)
+               ksz8_r_vlan_entries(dev, i);
 
        /* Setup STP address for STP operation. */
        memset(&alu, 0, sizeof(alu));
@@ -1099,46 +1379,46 @@ static int ksz8795_setup(struct dsa_switch *ds)
        alu.is_override = true;
        alu.port_forward = dev->host_mask;
 
-       ksz8795_w_sta_mac_table(dev, 0, &alu);
+       ksz8_w_sta_mac_table(dev, 0, &alu);
 
        ksz_init_mib_timer(dev);
 
        return 0;
 }
 
-static const struct dsa_switch_ops ksz8795_switch_ops = {
-       .get_tag_protocol       = ksz8795_get_tag_protocol,
-       .setup                  = ksz8795_setup,
+static const struct dsa_switch_ops ksz8_switch_ops = {
+       .get_tag_protocol       = ksz8_get_tag_protocol,
+       .setup                  = ksz8_setup,
        .phy_read               = ksz_phy_read16,
        .phy_write              = ksz_phy_write16,
        .adjust_link            = ksz_adjust_link,
        .port_enable            = ksz_enable_port,
        .port_disable           = ksz_disable_port,
-       .get_strings            = ksz8795_get_strings,
+       .get_strings            = ksz8_get_strings,
        .get_ethtool_stats      = ksz_get_ethtool_stats,
        .get_sset_count         = ksz_sset_count,
        .port_bridge_join       = ksz_port_bridge_join,
        .port_bridge_leave      = ksz_port_bridge_leave,
-       .port_stp_state_set     = ksz8795_port_stp_state_set,
+       .port_stp_state_set     = ksz8_port_stp_state_set,
        .port_fast_age          = ksz_port_fast_age,
-       .port_vlan_filtering    = ksz8795_port_vlan_filtering,
+       .port_vlan_filtering    = ksz8_port_vlan_filtering,
        .port_vlan_prepare      = ksz_port_vlan_prepare,
-       .port_vlan_add          = ksz8795_port_vlan_add,
-       .port_vlan_del          = ksz8795_port_vlan_del,
+       .port_vlan_add          = ksz8_port_vlan_add,
+       .port_vlan_del          = ksz8_port_vlan_del,
        .port_fdb_dump          = ksz_port_fdb_dump,
        .port_mdb_prepare       = ksz_port_mdb_prepare,
        .port_mdb_add           = ksz_port_mdb_add,
        .port_mdb_del           = ksz_port_mdb_del,
-       .port_mirror_add        = ksz8795_port_mirror_add,
-       .port_mirror_del        = ksz8795_port_mirror_del,
+       .port_mirror_add        = ksz8_port_mirror_add,
+       .port_mirror_del        = ksz8_port_mirror_del,
 };
 
-static u32 ksz8795_get_port_addr(int port, int offset)
+static u32 ksz8_get_port_addr(int port, int offset)
 {
        return PORT_CTRL_ADDR(port, offset);
 }
 
-static int ksz8795_switch_detect(struct ksz_device *dev)
+static int ksz8_switch_detect(struct ksz_device *dev)
 {
        u8 id1, id2;
        u16 id16;
@@ -1151,26 +1431,41 @@ static int ksz8795_switch_detect(struct ksz_device *dev)
 
        id1 = id16 >> 8;
        id2 = id16 & SW_CHIP_ID_M;
-       if (id1 != FAMILY_ID ||
-           (id2 != CHIP_ID_94 && id2 != CHIP_ID_95))
-               return -ENODEV;
 
-       dev->mib_port_cnt = TOTAL_PORT_NUM;
-       dev->phy_port_cnt = SWITCH_PORT_NUM;
-       dev->port_cnt = SWITCH_PORT_NUM;
-
-       if (id2 == CHIP_ID_95) {
-               u8 val;
-
-               id2 = 0x95;
-               ksz_read8(dev, REG_PORT_1_STATUS_0, &val);
-               if (val & PORT_FIBER_MODE)
-                       id2 = 0x65;
-       } else if (id2 == CHIP_ID_94) {
-               dev->port_cnt--;
-               dev->last_port = dev->port_cnt;
-               id2 = 0x94;
+       switch (id1) {
+       case KSZ87_FAMILY_ID:
+               if ((id2 != CHIP_ID_94 && id2 != CHIP_ID_95))
+                       return -ENODEV;
+
+               dev->mib_port_cnt = KSZ8795_TOTAL_PORT_NUM;
+               dev->phy_port_cnt = KSZ8795_SWITCH_PORT_NUM;
+               dev->port_cnt = KSZ8795_SWITCH_PORT_NUM;
+               if (id2 == CHIP_ID_95) {
+                       u8 val;
+
+                       id2 = 0x95;
+                       ksz_read8(dev, KSZ8795_REG_PORT_STATUS_0, &val);
+                       if (val & PORT_FIBER_MODE)
+                               id2 = 0x65;
+               } else if (id2 == CHIP_ID_94) {
+                       dev->port_cnt--;
+                       dev->last_port = dev->port_cnt;
+                       id2 = 0x94;
+               }
+               break;
+       case KSZ88_FAMILY_ID:
+               if (id2 != CHIP_ID_63)
+                       return -ENODEV;
+
+               dev->mib_port_cnt = KSZ8863_TOTAL_PORT_NUM;
+               dev->phy_port_cnt = KSZ8863_SWITCH_PORT_NUM;
+               dev->port_cnt = KSZ8863_SWITCH_PORT_NUM;
+               break;
+       default:
+               dev_err(dev->dev, "invalid family id: %d\n", id1);
+               return -ENODEV;
        }
+
        id16 &= ~0xff;
        id16 |= id2;
        dev->chip_id = id16;
@@ -1189,6 +1484,7 @@ struct ksz_chip_data {
        int num_statics;
        int cpu_ports;
        int port_cnt;
+       int mib_cnt;
 };
 
 static const struct ksz_chip_data ksz8795_switch_chips[] = {
@@ -1219,13 +1515,23 @@ static const struct ksz_chip_data 
ksz8795_switch_chips[] = {
                .cpu_ports = 0x10,      /* can be configured as cpu port */
                .port_cnt = 4,          /* total physical port count */
        },
+       {
+               .chip_id = 0x8830,
+               .dev_name = "KSZ8863/KSZ8873",
+               .num_vlans = 16,
+               .num_alus = 0,
+               .num_statics = 8,
+               .cpu_ports = 0x4,       /* can be configured as cpu port */
+               .port_cnt = 2,          /* total physical port count */
+       },
 };
 
-static int ksz8795_switch_init(struct ksz_device *dev)
+static int ksz8_switch_init(struct ksz_device *dev)
 {
        int i;
+       struct ksz8 *ksz8 = dev->priv;
 
-       dev->ds->ops = &ksz8795_switch_ops;
+       dev->ds->ops = &ksz8_switch_ops;
 
        for (i = 0; i < ARRAY_SIZE(ksz8795_switch_chips); i++) {
                const struct ksz_chip_data *chip = &ksz8795_switch_chips[i];
@@ -1246,11 +1552,22 @@ static int ksz8795_switch_init(struct ksz_device *dev)
        if (!dev->cpu_ports)
                return -ENODEV;
 
+       if (ksz_is_87(dev)) {
+               ksz8->regs = &ksz8795_regs;
+               ksz8->masks = &ksz8795_masks;
+               ksz8->shifts = &ksz8795_shifts;
+               dev->mib_cnt = TOTAL_KSZ8795_COUNTER_NUM;
+       } else {
+               ksz8->regs = &ksz8863_regs;
+               ksz8->masks = &ksz8863_masks;
+               ksz8->shifts = &ksz8863_shifts;
+               dev->mib_cnt = TOTAL_KSZ8863_COUNTER_NUM;
+       }
+
        dev->port_mask = BIT(dev->port_cnt) - 1;
        dev->port_mask |= dev->host_mask;
 
        dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
-       dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM;
 
        i = dev->mib_port_cnt;
        dev->ports = devm_kzalloc(dev->dev, sizeof(struct ksz_port) * i,
@@ -1262,8 +1579,9 @@ static int ksz8795_switch_init(struct ksz_device *dev)
                dev->ports[i].mib.counters =
                        devm_kzalloc(dev->dev,
                                     sizeof(u64) *
-                                    (TOTAL_SWITCH_COUNTER_NUM + 1),
+                                    (dev->mib_cnt + 1),
                                     GFP_KERNEL);
+
                if (!dev->ports[i].mib.counters)
                        return -ENOMEM;
        }
@@ -1271,29 +1589,48 @@ static int ksz8795_switch_init(struct ksz_device *dev)
        return 0;
 }
 
-static void ksz8795_switch_exit(struct ksz_device *dev)
+static void ksz8_switch_exit(struct ksz_device *dev)
 {
-       ksz8795_reset_switch(dev);
-}
+       dev->dev_ops->shutdown(dev);
+};
 
 static const struct ksz_dev_ops ksz8795_dev_ops = {
-       .get_port_addr = ksz8795_get_port_addr,
-       .cfg_port_member = ksz8795_cfg_port_member,
-       .flush_dyn_mac_table = ksz8795_flush_dyn_mac_table,
-       .port_setup = ksz8795_port_setup,
-       .r_phy = ksz8795_r_phy,
-       .w_phy = ksz8795_w_phy,
-       .r_dyn_mac_table = ksz8795_r_dyn_mac_table,
-       .r_sta_mac_table = ksz8795_r_sta_mac_table,
-       .w_sta_mac_table = ksz8795_w_sta_mac_table,
-       .r_mib_cnt = ksz8795_r_mib_cnt,
+       .get_port_addr = ksz8_get_port_addr,
+       .cfg_port_member = ksz8_cfg_port_member,
+       .flush_dyn_mac_table = ksz8_flush_dyn_mac_table,
+       .port_setup = ksz8_port_setup,
+       .r_phy = ksz8_r_phy,
+       .w_phy = ksz8_w_phy,
+       .r_dyn_mac_table = ksz8_r_dyn_mac_table,
+       .r_sta_mac_table = ksz8_r_sta_mac_table,
+       .w_sta_mac_table = ksz8_w_sta_mac_table,
+       .r_mib_cnt = ksz8_r_mib_cnt,
        .r_mib_pkt = ksz8795_r_mib_pkt,
        .freeze_mib = ksz8795_freeze_mib,
-       .port_init_cnt = ksz8795_port_init_cnt,
+       .port_init_cnt = ksz8_port_init_cnt,
        .shutdown = ksz8795_reset_switch,
-       .detect = ksz8795_switch_detect,
-       .init = ksz8795_switch_init,
-       .exit = ksz8795_switch_exit,
+       .detect = ksz8_switch_detect,
+       .init = ksz8_switch_init,
+       .exit = ksz8_switch_exit,
+};
+
+static const struct ksz_dev_ops ksz8863_dev_ops = {
+       .get_port_addr = ksz8_get_port_addr,
+       .cfg_port_member = ksz8_cfg_port_member,
+       .flush_dyn_mac_table = ksz8_flush_dyn_mac_table,
+       .port_setup = ksz8_port_setup,
+       .r_phy = ksz8_r_phy,
+       .w_phy = ksz8_w_phy,
+       .r_dyn_mac_table = ksz8_r_dyn_mac_table,
+       .r_sta_mac_table = ksz8_r_sta_mac_table,
+       .w_sta_mac_table = ksz8_w_sta_mac_table,
+       .r_mib_cnt = ksz8_r_mib_cnt,
+       .r_mib_pkt = ksz8863_r_mib_pkt,
+       .port_init_cnt = ksz8_port_init_cnt,
+       .shutdown = ksz8863_reset_switch,
+       .detect = ksz8_switch_detect,
+       .init = ksz8_switch_init,
+       .exit = ksz8_switch_exit,
 };
 
 int ksz8795_switch_register(struct ksz_device *dev)
@@ -1302,6 +1639,12 @@ int ksz8795_switch_register(struct ksz_device *dev)
 }
 EXPORT_SYMBOL(ksz8795_switch_register);
 
+int ksz8863_switch_register(struct ksz_device *dev)
+{
+       return ksz_switch_register(dev, &ksz8863_dev_ops);
+}
+EXPORT_SYMBOL(ksz8863_switch_register);
+
 MODULE_AUTHOR("Tristram Ha <tristram...@microchip.com>");
 MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h 
b/drivers/net/dsa/microchip/ksz8795_reg.h
index 3a50462df8fa9d..ed543ceac6b7d7 100644
--- a/drivers/net/dsa/microchip/ksz8795_reg.h
+++ b/drivers/net/dsa/microchip/ksz8795_reg.h
@@ -16,7 +16,7 @@
 
 #define REG_CHIP_ID0                   0x00
 
-#define FAMILY_ID                      0x87
+#define KSZ87_FAMILY_ID                        0x87
 
 #define REG_CHIP_ID1                   0x01
 
@@ -32,7 +32,7 @@
 #define REG_SW_CTRL_0                  0x02
 
 #define SW_NEW_BACKOFF                 BIT(7)
-#define SW_GLOBAL_RESET                        BIT(6)
+#define KSZ8795_SW_GLOBAL_RESET                        BIT(6)
 #define SW_FLUSH_DYN_MAC_TABLE         BIT(5)
 #define SW_FLUSH_STA_MAC_TABLE         BIT(4)
 #define SW_LINK_AUTO_AGING             BIT(0)
@@ -98,7 +98,7 @@
 
 #define REG_SW_CTRL_10                 0x0C
 
-#define SW_TAIL_TAG_ENABLE             BIT(1)
+#define KSZ8795_SW_TAIL_TAG_ENABLE             BIT(1)
 #define SW_PASS_PAUSE                  BIT(0)
 
 #define REG_SW_CTRL_11                 0x0D
@@ -150,7 +150,7 @@
 #define REG_PORT_4_CTRL_2              0x42
 #define REG_PORT_5_CTRL_2              0x52
 
-#define PORT_802_1P_REMAPPING          BIT(7)
+#define KSZ8795_PORT_802_1P_REMAPPING          BIT(7)
 #define PORT_INGRESS_FILTER            BIT(6)
 #define PORT_DISCARD_NON_VID           BIT(5)
 #define PORT_FORCE_FLOW_CTRL           BIT(4)
@@ -269,6 +269,7 @@
 #define REG_PORT_3_CTRL_9              0x3C
 #define REG_PORT_4_CTRL_9              0x4C
 
+#define PORT_AUTO_NEG_ENABLE           BIT(7)
 #define PORT_AUTO_NEG_DISABLE          BIT(7)
 #define PORT_FORCE_100_MBIT            BIT(6)
 #define PORT_FORCE_FULL_DUPLEX         BIT(5)
@@ -320,30 +321,36 @@
 #define REG_PORT_CTRL_5                        0x05
 
 #define REG_PORT_CTRL_7                        0x07
-#define REG_PORT_STATUS_0              0x08
-#define REG_PORT_STATUS_1              0x09
+
+#define KSZ8795_REG_PORT_STATUS_0              0x08
+#define KSZ8795_REG_PORT_STATUS_1              0x09
+
 #define REG_PORT_LINK_MD_CTRL          0x0A
 #define REG_PORT_LINK_MD_RESULT                0x0B
-#define REG_PORT_CTRL_9                        0x0C
-#define REG_PORT_CTRL_10               0x0D
-#define REG_PORT_STATUS_2              0x0E
-#define REG_PORT_STATUS_3              0x0F
-
-#define REG_PORT_CTRL_12               0xA0
-#define REG_PORT_CTRL_13               0xA1
-#define REG_PORT_RATE_CTRL_3           0xA2
-#define REG_PORT_RATE_CTRL_2           0xA3
-#define REG_PORT_RATE_CTRL_1           0xA4
-#define REG_PORT_RATE_CTRL_0           0xA5
-#define REG_PORT_RATE_LIMIT            0xA6
-#define REG_PORT_IN_RATE_0             0xA7
-#define REG_PORT_IN_RATE_1             0xA8
-#define REG_PORT_IN_RATE_2             0xA9
-#define REG_PORT_IN_RATE_3             0xAA
-#define REG_PORT_OUT_RATE_0            0xAB
-#define REG_PORT_OUT_RATE_1            0xAC
-#define REG_PORT_OUT_RATE_2            0xAD
-#define REG_PORT_OUT_RATE_3            0xAE
+
+#define KSZ8795_REG_PORT_CTRL_9                        0x0C
+#define KSZ8795_REG_PORT_CTRL_10               0x0D
+#define KSZ8795_REG_PORT_STATUS_2              0x0E
+#define KSZ8795_REG_PORT_STATUS_3              0x0F
+
+#define REG_PORT_CTRL_12                       0x0C
+#define REG_PORT_CTRL_13                       0x0D
+
+#define KSZ8795_REG_PORT_CTRL_12               0xA0
+#define KSZ8795_REG_PORT_CTRL_13               0xA1
+#define KSZ8795_REG_PORT_RATE_CTRL_3           0xA2
+#define KSZ8795_REG_PORT_RATE_CTRL_2           0xA3
+#define KSZ8795_REG_PORT_RATE_CTRL_1           0xA4
+#define KSZ8795_REG_PORT_RATE_CTRL_0           0xA5
+#define KSZ8795_REG_PORT_RATE_LIMIT            0xA6
+#define KSZ8795_REG_PORT_IN_RATE_0             0xA7
+#define KSZ8795_REG_PORT_IN_RATE_1             0xA8
+#define KSZ8795_REG_PORT_IN_RATE_2             0xA9
+#define KSZ8795_REG_PORT_IN_RATE_3             0xAA
+#define KSZ8795_REG_PORT_OUT_RATE_0            0xAB
+#define KSZ8795_REG_PORT_OUT_RATE_1            0xAC
+#define KSZ8795_REG_PORT_OUT_RATE_2            0xAD
+#define KSZ8795_REG_PORT_OUT_RATE_3            0xAE
 
 #define PORT_CTRL_ADDR(port, addr)             \
        ((addr) + REG_PORT_1_CTRL_0 + (port) *  \
@@ -356,7 +363,7 @@
 #define REG_SW_MAC_ADDR_4              0x6C
 #define REG_SW_MAC_ADDR_5              0x6D
 
-#define REG_IND_CTRL_0                 0x6E
+#define KSZ8795_REG_IND_CTRL_0                 0x6E
 
 #define TABLE_EXT_SELECT_S             5
 #define TABLE_EEE_V                    1
@@ -378,27 +385,27 @@
 #define TABLE_DYNAMIC_MAC              (TABLE_DYNAMIC_MAC_V << TABLE_SELECT_S)
 #define TABLE_MIB                      (TABLE_MIB_V << TABLE_SELECT_S)
 
-#define REG_IND_CTRL_1                 0x6F
+#define KSZ8795_REG_IND_CTRL_1                 0x6F
 
 #define TABLE_ENTRY_MASK               0x03FF
 #define TABLE_EXT_ENTRY_MASK           0x0FFF
 
-#define REG_IND_DATA_8                 0x70
-#define REG_IND_DATA_7                 0x71
-#define REG_IND_DATA_6                 0x72
-#define REG_IND_DATA_5                 0x73
-#define REG_IND_DATA_4                 0x74
-#define REG_IND_DATA_3                 0x75
-#define REG_IND_DATA_2                 0x76
-#define REG_IND_DATA_1                 0x77
-#define REG_IND_DATA_0                 0x78
+#define KSZ8795_REG_IND_DATA_8         0x70
+#define KSZ8795_REG_IND_DATA_7         0x71
+#define KSZ8795_REG_IND_DATA_6         0x72
+#define KSZ8795_REG_IND_DATA_5         0x73
+#define KSZ8795_REG_IND_DATA_4         0x74
+#define KSZ8795_REG_IND_DATA_3         0x75
+#define KSZ8795_REG_IND_DATA_2         0x76
+#define KSZ8795_REG_IND_DATA_1         0x77
+#define KSZ8795_REG_IND_DATA_0         0x78
 
-#define REG_IND_DATA_PME_EEE_ACL       0xA0
+#define KSZ8795_REG_IND_DATA_PME_EEE_ACL       0xA0
 
-#define REG_IND_DATA_CHECK             REG_IND_DATA_6
-#define REG_IND_MIB_CHECK              REG_IND_DATA_4
-#define REG_IND_DATA_HI                        REG_IND_DATA_7
-#define REG_IND_DATA_LO                        REG_IND_DATA_3
+#define KSZ8795_REG_IND_DATA_CHECK     KSZ8795_REG_IND_DATA_6
+#define KSZ8795_REG_IND_MIB_CHECK      KSZ8795_REG_IND_DATA_4
+#define KSZ8795_REG_IND_DATA_HI                KSZ8795_REG_IND_DATA_7
+#define KSZ8795_REG_IND_DATA_LO                KSZ8795_REG_IND_DATA_3
 
 #define REG_INT_STATUS                 0x7C
 #define REG_INT_ENABLE                 0x7D
@@ -846,16 +853,15 @@
 
 #define KS_PRIO_IN_REG                 4
 
-#define TOTAL_PORT_NUM                 5
+#define KSZ8795_TOTAL_PORT_NUM         5
 
 /* Host port can only be last of them. */
-#define SWITCH_PORT_NUM                        (TOTAL_PORT_NUM - 1)
+#define KSZ8795_SWITCH_PORT_NUM                (KSZ8795_TOTAL_PORT_NUM - 1)
 
 #define KSZ8795_COUNTER_NUM            0x20
 #define TOTAL_KSZ8795_COUNTER_NUM      (KSZ8795_COUNTER_NUM + 4)
 
 #define SWITCH_COUNTER_NUM             KSZ8795_COUNTER_NUM
-#define TOTAL_SWITCH_COUNTER_NUM       TOTAL_KSZ8795_COUNTER_NUM
 
 /* Common names used by other drivers */
 
@@ -865,16 +871,16 @@
 #define P_MIRROR_CTRL                  REG_PORT_CTRL_1
 #define P_802_1P_CTRL                  REG_PORT_CTRL_2
 #define P_STP_CTRL                     REG_PORT_CTRL_2
-#define P_LOCAL_CTRL                   REG_PORT_CTRL_7
-#define P_REMOTE_STATUS                        REG_PORT_STATUS_0
-#define P_FORCE_CTRL                   REG_PORT_CTRL_9
-#define P_NEG_RESTART_CTRL             REG_PORT_CTRL_10
-#define P_SPEED_STATUS                 REG_PORT_STATUS_1
-#define P_LINK_STATUS                  REG_PORT_STATUS_2
-#define P_PASS_ALL_CTRL                        REG_PORT_CTRL_12
-#define P_INS_SRC_PVID_CTRL            REG_PORT_CTRL_12
-#define P_DROP_TAG_CTRL                        REG_PORT_CTRL_13
-#define P_RATE_LIMIT_CTRL              REG_PORT_RATE_LIMIT
+#define KSZ8795_P_LOCAL_CTRL                   REG_PORT_CTRL_7
+#define KSZ8795_P_REMOTE_STATUS                        
KSZ8795_REG_PORT_STATUS_0
+#define KSZ8795_P_FORCE_CTRL                   KSZ8795_REG_PORT_CTRL_9
+#define KSZ8795_P_NEG_RESTART_CTRL             KSZ8795_REG_PORT_CTRL_10
+#define KSZ8795_P_SPEED_STATUS                 KSZ8795_REG_PORT_STATUS_1
+#define KSZ8795_P_LINK_STATUS                  KSZ8795_REG_PORT_STATUS_2
+#define P_PASS_ALL_CTRL                        KSZ8795_REG_PORT_CTRL_12
+#define P_INS_SRC_PVID_CTRL            KSZ8795_REG_PORT_CTRL_12
+#define P_DROP_TAG_CTRL                        KSZ8795_REG_PORT_CTRL_13
+#define P_RATE_LIMIT_CTRL              KSZ8795_REG_PORT_RATE_LIMIT
 
 #define S_UNKNOWN_DA_CTRL              REG_SWITCH_CTRL_12
 #define S_FORWARD_INVALID_VID_CTRL     REG_FORWARD_INVALID_VID
@@ -885,7 +891,7 @@
 #define S_MIRROR_CTRL                  REG_SW_CTRL_3
 #define S_REPLACE_VID_CTRL             REG_SW_CTRL_4
 #define S_PASS_PAUSE_CTRL              REG_SW_CTRL_10
-#define S_TAIL_TAG_CTRL                        REG_SW_CTRL_10
+#define KSZ8795_S_TAIL_TAG_CTRL                        REG_SW_CTRL_10
 #define S_802_1P_PRIO_CTRL             REG_SW_CTRL_12
 #define S_TOS_PRIO_CTRL                        REG_TOS_PRIO_CTRL_0
 #define S_IPV6_MLD_CTRL                        REG_SW_CTRL_21
@@ -907,15 +913,15 @@
  * STATIC_MAC_TABLE_FID                        00-7F000000-00000000
  */
 
-#define STATIC_MAC_TABLE_ADDR          0x0000FFFF
-#define STATIC_MAC_TABLE_FWD_PORTS     0x001F0000
-#define STATIC_MAC_TABLE_VALID         0x00200000
-#define STATIC_MAC_TABLE_OVERRIDE      0x00400000
-#define STATIC_MAC_TABLE_USE_FID       0x00800000
-#define STATIC_MAC_TABLE_FID           0x7F000000
+#define KSZ8795_STATIC_MAC_TABLE_ADDR          0x0000FFFF
+#define KSZ8795_STATIC_MAC_TABLE_FWD_PORTS     0x001F0000
+#define KSZ8795_STATIC_MAC_TABLE_VALID         0x00200000
+#define KSZ8795_STATIC_MAC_TABLE_OVERRIDE      0x00400000
+#define KSZ8795_STATIC_MAC_TABLE_USE_FID       0x00800000
+#define KSZ8795_STATIC_MAC_TABLE_FID           0x7F000000
 
-#define STATIC_MAC_FWD_PORTS_S         16
-#define STATIC_MAC_FID_S               24
+#define KSZ8795_STATIC_MAC_FWD_PORTS_S         16
+#define KSZ8795_STATIC_MAC_FID_S               24
 
 /**
  * VLAN_TABLE_FID                      00-007F007F-007F007F
@@ -923,12 +929,12 @@
  * VLAN_TABLE_VALID                    00-10001000-10001000
  */
 
-#define VLAN_TABLE_FID                 0x007F
-#define VLAN_TABLE_MEMBERSHIP          0x0F80
-#define VLAN_TABLE_VALID               0x1000
+#define KSZ8795_VLAN_TABLE_FID                 0x007F
+#define KSZ8795_VLAN_TABLE_MEMBERSHIP          0x0F80
+#define KSZ8795_VLAN_TABLE_VALID               0x1000
 
-#define VLAN_TABLE_MEMBERSHIP_S                7
-#define VLAN_TABLE_S                   16
+#define KSZ8795_VLAN_TABLE_MEMBERSHIP_S                7
+#define KSZ8795_VLAN_TABLE_S                   16
 
 /**
  * DYNAMIC_MAC_TABLE_ADDR              00-0000FFFF-FFFFFFFF
@@ -940,22 +946,22 @@
  * DYNAMIC_MAC_TABLE_MAC_EMPTY         80-00000000-00000000
  */
 
-#define DYNAMIC_MAC_TABLE_ADDR         0x0000FFFF
-#define DYNAMIC_MAC_TABLE_FID          0x007F0000
-#define DYNAMIC_MAC_TABLE_SRC_PORT     0x07000000
-#define DYNAMIC_MAC_TABLE_TIMESTAMP    0x18000000
-#define DYNAMIC_MAC_TABLE_ENTRIES      0xE0000000
+#define KSZ8795_DYNAMIC_MAC_TABLE_ADDR         0x0000FFFF
+#define KSZ8795_DYNAMIC_MAC_TABLE_FID          0x007F0000
+#define KSZ8795_DYNAMIC_MAC_TABLE_SRC_PORT     0x07000000
+#define KSZ8795_DYNAMIC_MAC_TABLE_TIMESTAMP    0x18000000
+#define KSZ8795_DYNAMIC_MAC_TABLE_ENTRIES      0xE0000000
 
-#define DYNAMIC_MAC_TABLE_NOT_READY    0x80
+#define KSZ8795_DYNAMIC_MAC_TABLE_NOT_READY    0x80
 
-#define DYNAMIC_MAC_TABLE_ENTRIES_H    0x7F
-#define DYNAMIC_MAC_TABLE_MAC_EMPTY    0x80
+#define KSZ8795_DYNAMIC_MAC_TABLE_ENTRIES_H    0x7F
+#define KSZ8795_DYNAMIC_MAC_TABLE_MAC_EMPTY    0x80
 
-#define DYNAMIC_MAC_FID_S              16
-#define DYNAMIC_MAC_SRC_PORT_S         24
-#define DYNAMIC_MAC_TIMESTAMP_S                27
-#define DYNAMIC_MAC_ENTRIES_S          29
-#define DYNAMIC_MAC_ENTRIES_H_S                3
+#define KSZ8795_DYNAMIC_MAC_FID_S              16
+#define KSZ8795_DYNAMIC_MAC_SRC_PORT_S         24
+#define KSZ8795_DYNAMIC_MAC_TIMESTAMP_S                27
+#define KSZ8795_DYNAMIC_MAC_ENTRIES_S          29
+#define KSZ8795_DYNAMIC_MAC_ENTRIES_H_S                3
 
 /**
  * MIB_COUNTER_VALUE                   00-00000000-3FFFFFFF
@@ -965,31 +971,31 @@
  * MIB_COUNTER_OVERFLOW                        00-00000040-00000000
  */
 
-#define MIB_COUNTER_OVERFLOW           BIT(6)
-#define MIB_COUNTER_VALID              BIT(5)
+#define KSZ8795_MIB_COUNTER_OVERFLOW           BIT(6)
+#define KSZ8795_MIB_COUNTER_VALID              BIT(5)
 
 #define MIB_COUNTER_VALUE              0x3FFFFFFF
 
-#define KS_MIB_TOTAL_RX_0              0x100
-#define KS_MIB_TOTAL_TX_0              0x101
-#define KS_MIB_PACKET_DROPPED_RX_0     0x102
-#define KS_MIB_PACKET_DROPPED_TX_0     0x103
-#define KS_MIB_TOTAL_RX_1              0x104
-#define KS_MIB_TOTAL_TX_1              0x105
-#define KS_MIB_PACKET_DROPPED_TX_1     0x106
-#define KS_MIB_PACKET_DROPPED_RX_1     0x107
-#define KS_MIB_TOTAL_RX_2              0x108
-#define KS_MIB_TOTAL_TX_2              0x109
-#define KS_MIB_PACKET_DROPPED_TX_2     0x10A
-#define KS_MIB_PACKET_DROPPED_RX_2     0x10B
-#define KS_MIB_TOTAL_RX_3              0x10C
-#define KS_MIB_TOTAL_TX_3              0x10D
-#define KS_MIB_PACKET_DROPPED_TX_3     0x10E
-#define KS_MIB_PACKET_DROPPED_RX_3     0x10F
-#define KS_MIB_TOTAL_RX_4              0x110
-#define KS_MIB_TOTAL_TX_4              0x111
-#define KS_MIB_PACKET_DROPPED_TX_4     0x112
-#define KS_MIB_PACKET_DROPPED_RX_4     0x113
+#define KSZ8795_MIB_TOTAL_RX_0         0x100
+#define KSZ8795_MIB_TOTAL_TX_0         0x101
+#define KSZ8795_MIB_PACKET_DROPPED_RX_0        0x102
+#define KSZ8795_MIB_PACKET_DROPPED_TX_0        0x103
+#define KSZ8795_MIB_TOTAL_RX_1         0x104
+#define KSZ8795_MIB_TOTAL_TX_1         0x105
+#define KSZ8795_MIB_PACKET_DROPPED_TX_1        0x106
+#define KSZ8795_MIB_PACKET_DROPPED_RX_1        0x107
+#define KSZ8795_MIB_TOTAL_RX_2         0x108
+#define KSZ8795_MIB_TOTAL_TX_2         0x109
+#define KSZ8795_MIB_PACKET_DROPPED_TX_2        0x10A
+#define KSZ8795_MIB_PACKET_DROPPED_RX_2        0x10B
+#define KSZ8795_MIB_TOTAL_RX_3         0x10C
+#define KSZ8795_MIB_TOTAL_TX_3         0x10D
+#define KSZ8795_MIB_PACKET_DROPPED_TX_3        0x10E
+#define KSZ8795_MIB_PACKET_DROPPED_RX_3        0x10F
+#define KSZ8795_MIB_TOTAL_RX_4         0x110
+#define KSZ8795_MIB_TOTAL_TX_4         0x111
+#define KSZ8795_MIB_PACKET_DROPPED_TX_4        0x112
+#define KSZ8795_MIB_PACKET_DROPPED_RX_4        0x113
 
 #define MIB_PACKET_DROPPED             0x0000FFFF
 
diff --git a/drivers/net/dsa/microchip/ksz8795_spi.c 
b/drivers/net/dsa/microchip/ksz8795_spi.c
index 8b00f8e6c02f4f..415665dbbbc446 100644
--- a/drivers/net/dsa/microchip/ksz8795_spi.c
+++ b/drivers/net/dsa/microchip/ksz8795_spi.c
@@ -14,34 +14,86 @@
 #include <linux/regmap.h>
 #include <linux/spi/spi.h>
 
+#include "ksz8.h"
 #include "ksz_common.h"
 
-#define SPI_ADDR_SHIFT                 12
-#define SPI_ADDR_ALIGN                 3
-#define SPI_TURNAROUND_SHIFT           1
+#define KSZ8795_SPI_ADDR_SHIFT                 12
+#define KSZ8795_SPI_ADDR_ALIGN                 3
+#define KSZ8795_SPI_TURNAROUND_SHIFT           1
 
-KSZ_REGMAP_TABLE(ksz8795, 16, SPI_ADDR_SHIFT,
-                SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
+#define KSZ8863_SPI_ADDR_SHIFT                 8
+#define KSZ8863_SPI_ADDR_ALIGN                 8
+#define KSZ8863_SPI_TURNAROUND_SHIFT           0
+
+KSZ_REGMAP_TABLE(ksz8795, 16, KSZ8795_SPI_ADDR_SHIFT,
+                KSZ8795_SPI_TURNAROUND_SHIFT, KSZ8795_SPI_ADDR_ALIGN);
+
+KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT,
+                KSZ8863_SPI_TURNAROUND_SHIFT, KSZ8863_SPI_ADDR_ALIGN);
+
+static const struct ksz8_data {
+       const struct regmap_config *regmap_config;
+       int (*switch_register)(struct ksz_device *dev);
+} ksz8_data[] = {
+       { .regmap_config = ksz8795_regmap_config,
+         .switch_register = ksz8795_switch_register
+       },
+       { .regmap_config = ksz8863_regmap_config,
+         .switch_register = ksz8863_switch_register
+       },
+       {},
+};
+
+static const struct of_device_id ksz8795_dt_ids[] = {
+       { .compatible = "microchip,ksz8765", .data = &ksz8_data[0] },
+       { .compatible = "microchip,ksz8794", .data = &ksz8_data[0] },
+       { .compatible = "microchip,ksz8795", .data = &ksz8_data[0] },
+       { .compatible = "microchip,ksz8863", .data = &ksz8_data[1] },
+       { .compatible = "microchip,ksz8873", .data = &ksz8_data[1] },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ksz8795_dt_ids);
 
 static int ksz8795_spi_probe(struct spi_device *spi)
 {
+       const struct regmap_config *regmap_config;
+       const struct of_device_id *match;
+       struct device *ddev = &spi->dev;
+       const struct ksz8_data *data = NULL;
+       struct ksz8 *ksz8;
        struct regmap_config rc;
        struct ksz_device *dev;
-       int i, ret;
+       int i, ret = 0;
 
-       dev = ksz_switch_alloc(&spi->dev, spi);
+       ksz8 = devm_kzalloc(&spi->dev, sizeof(struct ksz8), GFP_KERNEL);
+       ksz8->priv = spi;
+
+       dev = ksz_switch_alloc(&spi->dev, ksz8);
        if (!dev)
                return -ENOMEM;
 
+       regmap_config = ksz8795_regmap_config;
+
+       if (ddev->of_node) {
+               match = of_match_node(ksz8795_dt_ids, ddev->of_node);
+               if (!match)
+                       return -ENOTSUPP;
+
+               if (match->data) {
+                       data = match->data;
+                       regmap_config = data->regmap_config;
+               }
+       }
+
        for (i = 0; i < ARRAY_SIZE(ksz8795_regmap_config); i++) {
-               rc = ksz8795_regmap_config[i];
+               rc = regmap_config[i];
                rc.lock_arg = &dev->regmap_mutex;
                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,
                                "Failed to initialize regmap%i: %d\n",
-                               ksz8795_regmap_config[i].val_bits, ret);
+                               regmap_config[i].val_bits, ret);
                        return ret;
                }
        }
@@ -49,7 +101,10 @@ static int ksz8795_spi_probe(struct spi_device *spi)
        if (spi->dev.platform_data)
                dev->pdata = spi->dev.platform_data;
 
-       ret = ksz8795_switch_register(dev);
+       if (ddev->of_node && data)
+               ret = data->switch_register(dev);
+       else
+               ret = ksz8795_switch_register(dev);
 
        /* Main DSA driver may not be started yet. */
        if (ret)
@@ -78,14 +133,6 @@ static void ksz8795_spi_shutdown(struct spi_device *spi)
                dev->dev_ops->shutdown(dev);
 }
 
-static const struct of_device_id ksz8795_dt_ids[] = {
-       { .compatible = "microchip,ksz8765" },
-       { .compatible = "microchip,ksz8794" },
-       { .compatible = "microchip,ksz8795" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, ksz8795_dt_ids);
-
 static struct spi_driver ksz8795_spi_driver = {
        .driver = {
                .name   = "ksz8795-switch",
diff --git a/drivers/net/dsa/microchip/ksz8863_reg.h 
b/drivers/net/dsa/microchip/ksz8863_reg.h
new file mode 100644
index 00000000000000..9262b5a0e37443
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8863_reg.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Microchip KSZ8863 register definitions
+ *
+ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <ker...@pengutronix.de>
+ */
+
+#ifndef __KSZ8863_REG_H
+#define __KSZ8863_REG_H
+
+#define KSZ88_FAMILY_ID                                0x88
+
+#define CHIP_ID_63                             0x30
+
+#define KSZ8863_REG_SW_RESET                   0x43
+
+#define KSZ8863_GLOBAL_SOFTWARE_RESET          BIT(4)
+#define KSZ8863_PCS_RESET                      BIT(0)
+
+#define KSZ8863_SW_TAIL_TAG_ENABLE             BIT(6)
+
+#define KSZ8863_REG_POWER_MANAGEMENT           0xC3
+
+#define KSZ8863_PORT_802_1P_REMAPPING          BIT(3)
+
+#define KSZ8863_REG_PORT_STATUS_0              0x0E
+#define KSZ8863_REG_PORT_STATUS_1              0x0F
+
+#define KSZ8863_REG_IND_CTRL_0                 0x79
+
+#define KSZ8863_REG_IND_CTRL_1                 0x7A
+
+#define KSZ8863_REG_IND_DATA_8                 0x7B
+#define KSZ8863_REG_IND_DATA_7                 0x7C
+#define KSZ8863_REG_IND_DATA_6                 0x7D
+#define KSZ8863_REG_IND_DATA_5                 0x7E
+#define KSZ8863_REG_IND_DATA_4                 0x7F
+#define KSZ8863_REG_IND_DATA_3                 0x80
+#define KSZ8863_REG_IND_DATA_2                 0x81
+#define KSZ8863_REG_IND_DATA_1                 0x82
+#define KSZ8863_REG_IND_DATA_0                 0x83
+
+#define KSZ8863_REG_IND_DATA_CHECK             KSZ8863_REG_IND_DATA_8
+#define KSZ8863_REG_IND_MIB_CHECK              KSZ8863_REG_IND_DATA_3
+#define KSZ8863_REG_IND_DATA_HI                        KSZ8863_REG_IND_DATA_7
+#define KSZ8863_REG_IND_DATA_LO                        KSZ8863_REG_IND_DATA_3
+
+#define KSZ8863_ID_HI                          0x0022
+#define KSZ8863_ID_LO                          0x1430
+
+#define KSZ8863_TOTAL_PORT_NUM                 3
+#define KSZ8863_SWITCH_PORT_NUM                        (KSZ8863_TOTAL_PORT_NUM 
- 1)
+
+#define KSZ8863_COUNTER_NUM                    0x20
+#define TOTAL_KSZ8863_COUNTER_NUM              (KSZ8863_COUNTER_NUM + 2)
+
+#define KSZ8863_P_LOCAL_CTRL                   REG_PORT_CTRL_12
+#define KSZ8863_P_REMOTE_STATUS                        
KSZ8863_REG_PORT_STATUS_1
+#define KSZ8863_P_FORCE_CTRL                   REG_PORT_CTRL_12
+#define KSZ8863_P_NEG_RESTART_CTRL             REG_PORT_CTRL_13
+#define KSZ8863_P_SPEED_STATUS                 KSZ8863_REG_PORT_STATUS_1
+#define KSZ8863_P_LINK_STATUS                  KSZ8863_REG_PORT_STATUS_0
+#define KSZ8863_S_TAIL_TAG_CTRL                        REG_SW_CTRL_1
+
+/**
+ * STATIC_MAC_TABLE_ADDR               00-0000FFFF-FFFFFFFF
+ * STATIC_MAC_TABLE_FWD_PORTS          00-00070000-00000000
+ * STATIC_MAC_TABLE_VALID              00-00080000-00000000
+ * STATIC_MAC_TABLE_OVERRIDE           00-00100000-00000000
+ * STATIC_MAC_TABLE_USE_FID            00-00200000-00000000
+ * STATIC_MAC_TABLE_FID                        00-3C000000-00000000
+ */
+
+#define KSZ8863_STATIC_MAC_TABLE_ADDR          0x0000FFFF
+#define KSZ8863_STATIC_MAC_TABLE_FWD_PORTS     0x00070000
+#define KSZ8863_STATIC_MAC_TABLE_VALID         0x00080000
+#define KSZ8863_STATIC_MAC_TABLE_OVERRIDE      0x00100000
+#define KSZ8863_STATIC_MAC_TABLE_USE_FID       0x00200000
+#define KSZ8863_STATIC_MAC_TABLE_FID           0x3C000000
+
+#define KSZ8863_STATIC_MAC_FWD_PORTS_S         16
+#define KSZ8863_STATIC_MAC_FID_S               22
+
+/**
+ * VLAN_TABLE_VID                      00-00000000-00000FFF
+ * VLAN_TABLE_FID                      00-00000000-0000F000
+ * VLAN_TABLE_MEMBERSHIP               00-00000000-00070000
+ * VLAN_TABLE_VALID                    00-00000000-00080000
+ */
+
+#define KSZ8863_VLAN_TABLE_VID                 0x00FFF
+#define KSZ8863_VLAN_TABLE_FID                 0x0F000
+#define KSZ8863_VLAN_TABLE_MEMBERSHIP          0x70000
+#define KSZ8863_VLAN_TABLE_VALID               0x80000
+
+#define KSZ8863_VLAN_TABLE_MEMBERSHIP_S                16
+
+#define KSZ8863_DYNAMIC_MAC_TABLE_ADDR         0x0000FFFF
+#define KSZ8863_DYNAMIC_MAC_TABLE_FID          0x000F0000
+#define KSZ8863_DYNAMIC_MAC_TABLE_SRC_PORT     0x00300000
+#define KSZ8863_DYNAMIC_MAC_TABLE_TIMESTAMP    0x00C00000
+#define KSZ8863_DYNAMIC_MAC_TABLE_ENTRIES      0xF0000000
+
+#define KSZ8863_DYNAMIC_MAC_TABLE_NOT_READY    0x80
+
+#define KSZ8863_DYNAMIC_MAC_TABLE_ENTRIES_H    0x3F
+#define KSZ8863_DYNAMIC_MAC_TABLE_MAC_EMPTY    0x80
+
+#define KSZ8863_DYNAMIC_MAC_FID_S              16
+#define KSZ8863_DYNAMIC_MAC_SRC_PORT_S         20
+#define KSZ8863_DYNAMIC_MAC_TIMESTAMP_S                24
+#define KSZ8863_DYNAMIC_MAC_ENTRIES_S          24
+#define KSZ8863_DYNAMIC_MAC_ENTRIES_H_S                3
+
+#define KSZ8863_MIB_COUNTER_OVERFLOW           BIT(7)
+#define KSZ8863_MIB_COUNTER_VALID              BIT(6)
+
+#define KSZ8863_MIB_PACKET_DROPPED_TX_0                0x100
+#define KSZ8863_MIB_PACKET_DROPPED_RX_0                0x105
+
+#endif
diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c 
b/drivers/net/dsa/microchip/ksz8863_smi.c
new file mode 100644
index 00000000000000..6e58dbad904a6e
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8863_smi.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ8863 series register access through SMI
+ *
+ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <ker...@pengutronix.de>
+ */
+
+#include "ksz8.h"
+#include "ksz_common.h"
+
+/* Serial Management Interface (SMI) uses the following frame format:
+ *
+ *       preamble|start|Read/Write|  PHY   |  REG  |TA|   Data bits      | Idle
+ *               |frame| OP code  |address |address|  |                  |
+ * read | 32x1´s | 01  |    00    | 1xRRR  | RRRRR |Z0| 00000000DDDDDDDD |  Z
+ * write| 32x1´s | 01  |    00    | 0xRRR  | RRRRR |10| xxxxxxxxDDDDDDDD |  Z
+ *
+ */
+
+static int ksz8863_mdio_read(void *ctx, const void *reg_buf, size_t reg_len,
+                            void *val_buf, size_t val_len)
+{
+       struct ksz_device *dev = (struct ksz_device *)ctx;
+       struct ksz8 *ksz8 = dev->priv;
+       struct mdio_device *mdev = ksz8->priv;
+       u8 reg = *(u8 *)reg_buf;
+       u8 *val = val_buf;
+       int ret;
+       int i;
+
+       for (i = 0; i < val_len; i++) {
+               int tmp = reg + i;
+
+               ret = mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) |
+                                  BIT(4), tmp);
+               if (ret < 0)
+                       return ret;
+
+               val[i] = ret;
+       }
+
+       return 0;
+}
+
+static int ksz8863_mdio_write(void *ctx, const void *data, size_t count)
+{
+       struct ksz_device *dev = (struct ksz_device *)ctx;
+       struct ksz8 *ksz8 = dev->priv;
+       struct mdio_device *mdev = ksz8->priv;
+       u8 *val = (u8 *)(data + 4);
+       u32 reg = *(u32 *)data;
+       int ret;
+       int i;
+
+       for (i = 0; i < (count - 4); i++) {
+               int tmp = reg + i;
+
+               ret = mdiobus_write(mdev->bus, ((tmp & 0xE0) >> 5),
+                                   tmp, val[i]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static const struct regmap_bus regmap_smi[] = {
+       {
+               .read = ksz8863_mdio_read,
+               .write = ksz8863_mdio_write,
+               .max_raw_read = 1,
+               .max_raw_write = 1,
+       },
+       {
+               .read = ksz8863_mdio_read,
+               .write = ksz8863_mdio_write,
+               .val_format_endian_default = REGMAP_ENDIAN_BIG,
+               .max_raw_read = 2,
+               .max_raw_write = 2,
+       },
+       {
+               .read = ksz8863_mdio_read,
+               .write = ksz8863_mdio_write,
+               .val_format_endian_default = REGMAP_ENDIAN_BIG,
+               .max_raw_read = 4,
+               .max_raw_write = 4,
+       }
+};
+
+static const struct regmap_config ksz8863_regmap_config[] = {
+       {
+               .name = "#8",
+               .reg_bits = 8,
+               .pad_bits = 24,
+               .val_bits = 8,
+               .cache_type = REGCACHE_NONE,
+               .use_single_read = 1,
+       },
+       {
+               .name = "#16",
+               .reg_bits = 8,
+               .pad_bits = 24,
+               .val_bits = 16,
+               .cache_type = REGCACHE_NONE,
+               .use_single_read = 1,
+       },
+       {
+               .name = "#32",
+               .reg_bits = 8,
+               .pad_bits = 24,
+               .val_bits = 32,
+               .cache_type = REGCACHE_NONE,
+               .use_single_read = 1,
+       }
+};
+
+static int ksz8863_smi_probe(struct mdio_device *mdiodev)
+{
+       struct ksz8 *ksz8;
+       struct ksz_device *dev;
+       int ret;
+       int i;
+
+       ksz8 = devm_kzalloc(&mdiodev->dev, sizeof(struct ksz8), GFP_KERNEL);
+       ksz8->priv = mdiodev;
+
+       dev = ksz_switch_alloc(&mdiodev->dev, ksz8);
+       if (!dev)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(ksz8863_regmap_config); i++) {
+               dev->regmap[i] = devm_regmap_init(&mdiodev->dev,
+                                                 &regmap_smi[i], dev,
+                                                 &ksz8863_regmap_config[i]);
+               if (IS_ERR(dev->regmap[i])) {
+                       ret = PTR_ERR(dev->regmap[i]);
+                       dev_err(&mdiodev->dev,
+                               "Failed to initialize regmap%i: %d\n",
+                               ksz8863_regmap_config[i].val_bits, ret);
+                       return ret;
+               }
+       }
+
+       if (mdiodev->dev.platform_data)
+               dev->pdata = mdiodev->dev.platform_data;
+
+       ret = ksz8863_switch_register(dev);
+
+       /* Main DSA driver may not be started yet. */
+       if (ret)
+               return ret;
+
+       dev_set_drvdata(&mdiodev->dev, dev);
+
+       return 0;
+}
+
+static void ksz8863_smi_remove(struct mdio_device *mdiodev)
+{
+       struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev);
+
+       if (dev)
+               ksz_switch_remove(dev);
+}
+
+static const struct of_device_id ksz8863_dt_ids[] = {
+       { .compatible = "microchip,ksz8863" },
+       { .compatible = "microchip,ksz8873" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ksz8863_dt_ids);
+
+static struct mdio_driver ksz8863_driver = {
+       .probe  = ksz8863_smi_probe,
+       .remove = ksz8863_smi_remove,
+       .mdiodrv.driver = {
+               .name   = "ksz8863-switch",
+               .of_match_table = ksz8863_dt_ids,
+       },
+};
+
+mdio_module_driver(ksz8863_driver);
+
+MODULE_AUTHOR("Michael Grzeschik <m.grzesc...@pengutronix.de>");
+MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/microchip/ksz_common.h 
b/drivers/net/dsa/microchip/ksz_common.h
index f2c9bb68fd3306..3200bea81091e2 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -150,6 +150,7 @@ int ksz_switch_register(struct ksz_device *dev,
 void ksz_switch_remove(struct ksz_device *dev);
 
 int ksz8795_switch_register(struct ksz_device *dev);
+int ksz8863_switch_register(struct ksz_device *dev);
 int ksz9477_switch_register(struct ksz_device *dev);
 
 void ksz_update_port_member(struct ksz_device *dev, int port);
-- 
2.26.2

Reply via email to