* [RFC 0/3] microchip: add support for ksz8863 driver family
@ 2019-05-08 21:13 Michael Grzeschik
2019-05-08 21:13 ` [RFC 1/3] mdio-bitbang: add SMI0 mode support Michael Grzeschik
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-08 21:13 UTC (permalink / raw)
To: Tristram.Ha; +Cc: kernel, UNGLinuxDriver, netdev
This series adds support for the ksz8863 driver family to the
dsa based ksz drivers. For now the ksz8863 nad ksz8873 are compatible.
The driver is based on the ksz8895 RFC patch from Tristam Ha:
https://patchwork.ozlabs.org/patch/822712/
And the latest version of the ksz8863.h from Microchip:
https://raw.githubusercontent.com/Microchip-Ethernet/UNG8071_old_1.10/master/KSZ/linux-drivers/ksz8863/linux-3.14/drivers/net/ethernet/micrel/ksz8863.h
The driver works as expected on the machine including the switch. The
clients on the other hand behind the switch see some bandwidth issues
after the stack was bootet and configured appropriate via:
ip link add name br0 type bridge
ip link set dev eth1 master br0
ip link set dev eth2 master br0
@Tristam: Could you have a look if something obious was misconfigured?
Michael Grzeschik (3):
mdio-bitbang: add SMI0 mode support
ksz: Add Microchip KSZ8873 SMI-DSA driver
dt-bindings: net: dsa: document additional Microchip KSZ8863 family
switches
.../devicetree/bindings/net/dsa/ksz.txt | 44 +
drivers/net/dsa/microchip/Kconfig | 16 +
drivers/net/dsa/microchip/Makefile | 2 +
drivers/net/dsa/microchip/ksz8863.c | 1026 +++++++++++++++++
drivers/net/dsa/microchip/ksz8863_reg.h | 605 ++++++++++
drivers/net/dsa/microchip/ksz8863_smi.c | 105 ++
drivers/net/dsa/microchip/ksz_priv.h | 3 +
drivers/net/phy/mdio-bitbang.c | 10 +
include/linux/phy.h | 12 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 7 +
net/dsa/tag_ksz.c | 45 +
12 files changed, 1877 insertions(+)
create mode 100644 drivers/net/dsa/microchip/ksz8863.c
create mode 100644 drivers/net/dsa/microchip/ksz8863_reg.h
create mode 100644 drivers/net/dsa/microchip/ksz8863_smi.c
--
2.20.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC 1/3] mdio-bitbang: add SMI0 mode support
2019-05-08 21:13 [RFC 0/3] microchip: add support for ksz8863 driver family Michael Grzeschik
@ 2019-05-08 21:13 ` Michael Grzeschik
2019-05-09 14:29 ` Andrew Lunn
2019-05-08 21:13 ` [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver Michael Grzeschik
2019-05-08 21:13 ` [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches Michael Grzeschik
2 siblings, 1 reply; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-08 21:13 UTC (permalink / raw)
To: Tristram.Ha; +Cc: kernel, UNGLinuxDriver, netdev
Some microchip phys support the Serial Management Interface Protocol
(SMI) for the configuration of the extended register set. We add
MII_ADDR_SMI0 as an availabe interface to the mdiobb write and read
functions, as this interface can be easy realized using the bitbang mdio
driver.
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
drivers/net/phy/mdio-bitbang.c | 10 ++++++++++
include/linux/phy.h | 12 ++++++++++++
2 files changed, 22 insertions(+)
diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
index 5136275c8e739..a978f8a9a172b 100644
--- a/drivers/net/phy/mdio-bitbang.c
+++ b/drivers/net/phy/mdio-bitbang.c
@@ -22,6 +22,10 @@
#define MDIO_READ 2
#define MDIO_WRITE 1
+#define SMI0_RW_OPCODE 0
+#define SMI0_READ_PHY (1 << 4)
+#define SMI0_WRITE_PHY (0 << 4)
+
#define MDIO_C45 (1<<15)
#define MDIO_C45_ADDR (MDIO_C45 | 0)
#define MDIO_C45_READ (MDIO_C45 | 3)
@@ -157,6 +161,9 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
if (reg & MII_ADDR_C45) {
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
+ } else if (reg & MII_ADDR_SMI0) {
+ mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
+ (reg & 0xE0) >> 5 | SMI0_READ_PHY, reg);
} else
mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
@@ -188,6 +195,9 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
if (reg & MII_ADDR_C45) {
reg = mdiobb_cmd_addr(ctrl, phy, reg);
mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
+ } else if (reg & MII_ADDR_SMI0) {
+ mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
+ (reg & 0xE0) >> 5 | SMI0_WRITE_PHY, reg);
} else
mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 073fb151b5a99..f011722fbd5c2 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -199,6 +199,18 @@ static inline const char *phy_modes(phy_interface_t interface)
IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */
#define MII_ADDR_C45 (1<<30)
+/* 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
+ *
+ * The register number is encoded with the 5 least significant bits in REG
+ * and the 3 most significant bits in PHY
+ */
+#define MII_ADDR_SMI0 (1<<31)
+
struct device;
struct phylink;
struct sk_buff;
--
2.20.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver
2019-05-08 21:13 [RFC 0/3] microchip: add support for ksz8863 driver family Michael Grzeschik
2019-05-08 21:13 ` [RFC 1/3] mdio-bitbang: add SMI0 mode support Michael Grzeschik
@ 2019-05-08 21:13 ` Michael Grzeschik
2019-05-09 14:48 ` Andrew Lunn
2019-05-08 21:13 ` [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches Michael Grzeschik
2 siblings, 1 reply; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-08 21:13 UTC (permalink / raw)
To: Tristram.Ha; +Cc: kernel, UNGLinuxDriver, netdev
Cc: Tristram.Ha@microchip.com
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
drivers/net/dsa/microchip/Kconfig | 16 +
drivers/net/dsa/microchip/Makefile | 2 +
drivers/net/dsa/microchip/ksz8863.c | 1026 +++++++++++++++++++++++
drivers/net/dsa/microchip/ksz8863_reg.h | 605 +++++++++++++
drivers/net/dsa/microchip/ksz8863_smi.c | 105 +++
drivers/net/dsa/microchip/ksz_priv.h | 3 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 7 +
net/dsa/tag_ksz.c | 45 +
9 files changed, 1811 insertions(+)
create mode 100644 drivers/net/dsa/microchip/ksz8863.c
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 bea29fde9f3d1..a6fa6ae972951 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -14,3 +14,19 @@ config NET_DSA_MICROCHIP_KSZ9477_SPI
depends on NET_DSA_MICROCHIP_KSZ9477 && SPI
help
Select to enable support for registering switches configured through SPI.
+
+menuconfig NET_DSA_MICROCHIP_KSZ8863
+ tristate "Microchip KSZ8863 series switch support"
+ depends on NET_DSA
+ select NET_DSA_TAG_KSZ8863
+ select NET_DSA_MICROCHIP_KSZ_COMMON
+ help
+ This driver adds support for Microchip KSZ8863 switch chips.
+
+config NET_DSA_MICROCHIP_KSZ8863_SMI
+ tristate "KSZ series SMI connected switch driver"
+ depends on NET_DSA_MICROCHIP_KSZ8863
+ default y
+ help
+ Select to enable support for registering switches configured through SMI.
+ Supports the KSZ8863 Switch.
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 3142c18b8f573..73fd58052a9d6 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -1,3 +1,5 @@
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863) += ksz8863.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
diff --git a/drivers/net/dsa/microchip/ksz8863.c b/drivers/net/dsa/microchip/ksz8863.c
new file mode 100644
index 0000000000000..ed1a775eb5ab7
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8863.c
@@ -0,0 +1,1026 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ8863 switch driver main logic
+ *
+ * Copyright (C) 2017-2019 Microchip Technology Inc.
+ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "ksz_priv.h"
+#include "ksz_common.h"
+#include "ksz8863_reg.h"
+
+static const struct {
+ char string[ETH_GSTRING_LEN];
+} mib_names[TOTAL_SWITCH_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 int ksz8863_reset_switch(struct ksz_device *dev)
+{
+ /* reset switch */
+ ksz_cfg(dev, REG_SWITCH_RESET, GLOBAL_SOFTWARE_RESET | PCS_RESET, true);
+
+ ksz_cfg(dev, REG_POWER_MANAGEMENT, SWITCH_SOFTWARE_POWER_DOWN, true);
+ ksz_cfg(dev, REG_POWER_MANAGEMENT, SWITCH_SOFTWARE_POWER_DOWN, false);
+
+ return 0;
+}
+
+static void ksz8863_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
+ u64 *cnt)
+{
+ u32 data;
+ u16 ctrl_addr;
+ u8 check;
+ int loop;
+
+ ctrl_addr = addr + SWITCH_COUNTER_NUM * port;
+ ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
+
+ mutex_lock(&dev->alu_mutex);
+ ksz_write16(dev, REG_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);
+
+ if (check & MIB_COUNTER_VALID) {
+ ksz_read32(dev, REG_IND_DATA_LO, &data);
+ if (check & MIB_COUNTER_OVERFLOW)
+ *cnt += MIB_COUNTER_VALUE + 1;
+ *cnt += data & MIB_COUNTER_VALUE;
+ break;
+ }
+ }
+ mutex_unlock(&dev->alu_mutex);
+}
+
+static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
+ u64 *dropped, u64 *cnt)
+{
+ u32 cur;
+ u32 data;
+ u16 ctrl_addr;
+ u32 *last = (u32 *)dropped;
+
+ addr -= SWITCH_COUNTER_NUM;
+ ctrl_addr = addr ? KS_MIB_PACKET_DROPPED_TX_0 :
+ KS_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, REG_IND_CTRL_0, ctrl_addr);
+ ksz_read32(dev, REG_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 ksz8863_port_init_cnt(struct ksz_device *dev, int port)
+{
+ struct ksz_port_mib *mib = &dev->ports[port].mib;
+ u64 *dropped;
+
+ mib->cnt_ptr = 0;
+
+ /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */
+ while (mib->cnt_ptr < dev->reg_mib_cnt) {
+ dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr,
+ &mib->counters[mib->cnt_ptr]);
+ ++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,
+ dropped, &mib->counters[mib->cnt_ptr]);
+ ++mib->cnt_ptr;
+ }
+ mib->cnt_ptr = 0;
+ memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
+}
+
+static void ksz8863_r_table(struct ksz_device *dev, int table, u16 addr,
+ u64 *data)
+{
+ 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_get(dev, REG_IND_DATA_HI, data, sizeof(u64));
+ mutex_unlock(&dev->alu_mutex);
+ *data = be64_to_cpu(*data);
+}
+
+static void ksz8863_w_table(struct ksz_device *dev, int table, u16 addr,
+ u64 data)
+{
+ u16 ctrl_addr;
+
+ ctrl_addr = IND_ACC_TABLE(table) | addr;
+ data = cpu_to_be64(data);
+
+ mutex_lock(&dev->alu_mutex);
+ ksz_set(dev, REG_IND_DATA_HI, &data, sizeof(u64));
+ ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);
+ mutex_unlock(&dev->alu_mutex);
+}
+
+static int ksz8863_valid_dyn_entry(struct ksz_device *dev, u8 *data)
+{
+ int timeout = 100;
+
+ do {
+ ksz_read8(dev, REG_IND_DATA_CHECK, data);
+ timeout--;
+ } while ((*data & DYNAMIC_MAC_TABLE_NOT_READY) && timeout);
+
+ /* Entry is not ready for accessing. */
+ if (*data & DYNAMIC_MAC_TABLE_NOT_READY) {
+ return -EAGAIN;
+ /* Entry is ready for accessing. */
+ } else {
+ ksz_read8(dev, REG_IND_DATA_8, data);
+
+ /* There is no valid entry in the table. */
+ if (*data & DYNAMIC_MAC_TABLE_MAC_EMPTY)
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int ksz8863_r_dyn_mac_table(struct ksz_device *dev, u16 addr,
+ u8 *mac_addr, u8 *fid, u8 *src_port,
+ u8 *timestamp, u16 *entries)
+{
+ u32 data_hi;
+ u32 data_lo;
+ u16 ctrl_addr;
+ int rc;
+ u8 data;
+
+ 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);
+
+ rc = ksz8863_valid_dyn_entry(dev, &data);
+ if (rc == -EAGAIN) {
+ if (addr == 0)
+ *entries = 0;
+ } else if (rc == -ENXIO) {
+ *entries = 0;
+ /* At least one valid entry in the table. */
+ } else {
+ u64 buf;
+ int cnt;
+
+ ksz_get(dev, REG_IND_DATA_HI, &buf, sizeof(buf));
+ buf = be64_to_cpu(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;
+ *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;
+
+ mac_addr[5] = (u8)data_lo;
+ mac_addr[4] = (u8)(data_lo >> 8);
+ mac_addr[3] = (u8)(data_lo >> 16);
+ mac_addr[2] = (u8)(data_lo >> 24);
+
+ mac_addr[1] = (u8)data_hi;
+ mac_addr[0] = (u8)(data_hi >> 8);
+ rc = 0;
+ }
+ mutex_unlock(&dev->alu_mutex);
+
+ return rc;
+}
+
+static int ksz8863_r_sta_mac_table(struct ksz_device *dev, u16 addr,
+ struct alu_struct *alu)
+{
+ u64 data;
+ u32 data_hi;
+ u32 data_lo;
+
+ ksz8863_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)) {
+ 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->is_override =
+ (data_hi & STATIC_MAC_TABLE_OVERRIDE) ? 1 : 0;
+ data_hi >>= 1;
+ alu->is_static = true;
+ 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;
+
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+static void ksz8863_w_sta_mac_table(struct ksz_device *dev, u16 addr,
+ struct alu_struct *alu)
+{
+ u64 data;
+ u32 data_hi;
+ u32 data_lo;
+
+ data_lo = ((u32)alu->mac[2] << 24) |
+ ((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;
+
+ if (alu->is_override)
+ data_hi |= 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;
+ }
+
+ if (alu->is_static)
+ data_hi |= STATIC_MAC_TABLE_VALID;
+ else
+ data_hi &= ~STATIC_MAC_TABLE_OVERRIDE;
+
+ data = (u64)data_hi << 32 | data_lo;
+ ksz8863_w_table(dev, TABLE_STATIC_MAC, addr, data);
+}
+
+static inline void ksz8863_from_vlan(u16 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);
+}
+
+static inline void ksz8863_to_vlan(u8 fid, u8 member, u8 valid, u16 *vlan)
+{
+ *vlan = fid;
+ *vlan |= (u16)member << VLAN_TABLE_MEMBERSHIP_S;
+ if (valid)
+ *vlan |= VLAN_TABLE_VALID;
+}
+
+static void ksz8863_r_vlan_entries(struct ksz_device *dev, u16 addr)
+{
+ u64 data;
+ int i;
+
+ ksz8863_r_table(dev, TABLE_VLAN, addr, &data);
+ addr *= 2;
+ for (i = 0; i < 2; i++) {
+ dev->vlan_cache[addr + i].table[0] = data & VLAN_TABLE_M;
+ data >>= VLAN_TABLE_S;
+ }
+}
+
+static void ksz8863_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan)
+{
+ u64 buf;
+ u16 addr;
+ int index;
+
+ addr = vid / 2;
+ index = vid & 3;
+ ksz8863_r_table(dev, TABLE_VLAN, addr, &buf);
+ buf >>= VLAN_TABLE_S * index;
+ *vlan = buf & VLAN_TABLE_M;
+}
+
+static void ksz8863_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan)
+{
+ u64 buf;
+ u16 addr;
+ int index;
+
+ addr = vid / 2;
+ index = vid & 3;
+ ksz8863_r_table(dev, TABLE_VLAN, addr, &buf);
+ index *= VLAN_TABLE_S;
+ buf &= ~(VLAN_TABLE_M << index);
+ buf |= (u64)vlan << index;
+ dev->vlan_cache[vid].table[0] = vlan;
+ ksz8863_w_table(dev, TABLE_VLAN, addr, buf);
+}
+
+static enum dsa_tag_protocol ksz8863_get_tag_protocol(struct dsa_switch *ds,
+ int port)
+{
+ /* Use KSZ8795 tail tagging code. */
+ return DSA_TAG_PROTO_KSZ8863;
+}
+
+static void ksz8863_get_strings(struct dsa_switch *ds, int port,
+ u32 stringset, uint8_t *buf)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+ memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
+ ETH_GSTRING_LEN);
+ }
+ break;
+ }
+}
+
+static const u8 stp_multicast_addr[] = {
+ 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00
+};
+
+static void ksz8863_cfg_port_member(struct ksz_device *dev, int port,
+ u8 member)
+{
+ u8 data;
+
+ ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+ data &= ~PORT_VLAN_MEMBERSHIP;
+ data |= (member & dev->port_mask);
+ ksz_pwrite8(dev, port, P_MIRROR_CTRL, data);
+ dev->ports[port].member = member;
+}
+
+static void ksz8863_port_stp_state_set(struct dsa_switch *ds, int port,
+ u8 state)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *p = &dev->ports[port];
+ u8 data;
+ int member = -1;
+
+ ksz_pread8(dev, port, P_STP_CTRL, &data);
+ data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ data |= PORT_LEARN_DISABLE;
+ if (port < SWITCH_PORT_NUM)
+ member = 0;
+ break;
+ case BR_STATE_LISTENING:
+ data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+ if (port < SWITCH_PORT_NUM &&
+ p->stp_state == BR_STATE_DISABLED)
+ member = dev->host_mask | p->vid_member;
+ break;
+ case BR_STATE_LEARNING:
+ data |= PORT_RX_ENABLE;
+ break;
+ case BR_STATE_FORWARDING:
+ data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+
+ /* This function is also used internally. */
+ if (port == dev->cpu_port)
+ break;
+
+ /* Port is a member of a bridge. */
+ if (dev->br_member & (1 << port)) {
+ dev->member |= (1 << port);
+ member = dev->member;
+ } else {
+ member = dev->host_mask | p->vid_member;
+ }
+ break;
+ case BR_STATE_BLOCKING:
+ data |= PORT_LEARN_DISABLE;
+ if (port < SWITCH_PORT_NUM &&
+ p->stp_state == BR_STATE_DISABLED)
+ member = dev->host_mask | p->vid_member;
+ break;
+ default:
+ dev_err(ds->dev, "invalid STP state: %d\n", state);
+ return;
+ }
+
+ ksz_pwrite8(dev, port, P_STP_CTRL, data);
+ p->stp_state = state;
+ if (data & PORT_RX_ENABLE)
+ dev->rx_ports |= (1 << port);
+ else
+ dev->rx_ports &= ~(1 << port);
+ if (data & PORT_TX_ENABLE)
+ dev->tx_ports |= (1 << port);
+ else
+ dev->tx_ports &= ~(1 << port);
+
+ /* Port membership may share register with STP state. */
+ if (member >= 0 && member != p->member)
+ ksz8863_cfg_port_member(dev, port, (u8)member);
+
+ /* Check if forwarding needs to be updated. */
+ if (state != BR_STATE_FORWARDING) {
+ if (dev->br_member & (1 << port))
+ dev->member &= ~(1 << port);
+ }
+
+ /* When topology has changed the function ksz_update_port_member
+ * should be called to modify port forwarding behavior. However
+ * as the offload_fwd_mark indication cannot be reported here
+ * the switch forwarding function is not enabled.
+ */
+}
+
+static void ksz8863_flush_dyn_mac_table(struct ksz_device *dev, int port)
+{
+ int cnt;
+ int first;
+ int index;
+ u8 learn[TOTAL_PORT_NUM];
+
+ if ((uint)port < TOTAL_PORT_NUM) {
+ first = port;
+ cnt = port + 1;
+ } else {
+ /* Flush all ports. */
+ first = 0;
+ cnt = dev->mib_port_cnt;
+ }
+ for (index = first; index < cnt; index++) {
+ ksz_pread8(dev, index, P_STP_CTRL, &learn[index]);
+ if (!(learn[index] & PORT_LEARN_DISABLE))
+ ksz_pwrite8(dev, index, P_STP_CTRL,
+ learn[index] | PORT_LEARN_DISABLE);
+ }
+ ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SWITCH_FLUSH_DYN_MAC_TABLE, true);
+ for (index = first; index < cnt; index++) {
+ if (!(learn[index] & PORT_LEARN_DISABLE))
+ ksz_pwrite8(dev, index, P_STP_CTRL, learn[index]);
+ }
+}
+
+static int ksz8863_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool flag)
+{
+ struct ksz_device *dev = ds->priv;
+
+ ksz_cfg(dev, S_MIRROR_CTRL, SWITCH_VLAN_ENABLE, flag);
+
+ return 0;
+}
+
+static void ksz8863_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct ksz_device *dev = ds->priv;
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ u16 data;
+ u16 vid;
+ u8 fid;
+ u8 member;
+ u8 valid;
+ u16 new_pvid = 0;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ ksz8863_r_vlan_table(dev, vid, &data);
+ ksz8863_from_vlan(data, &fid, &member, &valid);
+
+ /* First time to setup the VLAN entry. */
+ if (!valid) {
+ /* Need to find a way to map VID to FID. */
+ fid = 1;
+ valid = 1;
+ }
+ member |= BIT(port);
+
+ ksz8863_to_vlan(fid, member, valid, &data);
+ ksz8863_w_vlan_table(dev, vid, data);
+
+ /* change PVID */
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+ new_pvid = vid;
+ }
+
+ if (new_pvid) {
+ ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid);
+ vid &= 0xfff;
+ vid |= new_pvid;
+ ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid);
+ }
+
+ mutex_unlock(&dev->vlan_mutex);
+}
+
+static int ksz8863_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct ksz_device *dev = ds->priv;
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ u16 data;
+ u16 vid;
+ u16 pvid;
+ u8 fid;
+ u8 member;
+ u8 valid;
+ u16 new_pvid = 0;
+
+ mutex_lock(&dev->vlan_mutex);
+
+ ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
+ pvid = pvid & 0xFFF;
+
+ ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
+
+ for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+ ksz8863_r_vlan_table(dev, vid, &data);
+ ksz8863_from_vlan(data, &fid, &member, &valid);
+
+ member &= ~BIT(port);
+
+ /* Invalidate the entry if no more member. */
+ if (!member) {
+ fid = 0;
+ valid = 0;
+ }
+
+ if (pvid == vid)
+ new_pvid = 1;
+
+ ksz8863_to_vlan(fid, member, valid, &data);
+ ksz8863_w_vlan_table(dev, vid, data);
+ }
+
+ if (new_pvid != pvid)
+ ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid);
+
+ mutex_unlock(&dev->vlan_mutex);
+
+ return 0;
+}
+
+static int ksz8863_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (ingress) {
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
+ dev->mirror_rx |= (1 << port);
+ } else {
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
+ dev->mirror_tx |= (1 << port);
+ }
+
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
+
+ /* configure mirror port */
+ if (dev->mirror_rx || dev->mirror_tx)
+ ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, true);
+
+ return 0;
+}
+
+static void ksz8863_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct ksz_device *dev = ds->priv;
+ u8 data;
+
+ if (mirror->ingress) {
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
+ dev->mirror_rx &= ~(1 << port);
+ } else {
+ ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
+ dev->mirror_tx &= ~(1 << port);
+ }
+
+ ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+
+ if (!dev->mirror_rx && !dev->mirror_tx)
+ ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+ PORT_MIRROR_SNIFFER, false);
+}
+
+static void ksz8863_phy_setup(struct ksz_device *dev, int port,
+ struct phy_device *phy)
+{
+ /* SUPPORTED_Pause can be removed to disable flow control when
+ * rate limiting is used.
+ */
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phy->supported);
+ linkmode_copy(phy->advertising, phy->supported);
+}
+
+static void ksz8863_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+ u8 member;
+ struct ksz_port *p = &dev->ports[port];
+
+ /* enable broadcast storm limit */
+ ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
+
+ /* 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);
+
+ /* enable 802.1p priority */
+ ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);
+
+ if (cpu_port) {
+ member = dev->port_mask;
+ dev->on_ports = dev->host_mask;
+ dev->live_ports = dev->host_mask;
+ } else {
+ member = dev->host_mask | p->vid_member;
+ dev->on_ports |= (1 << port);
+
+ /* Link was detected before port is enabled. */
+ if (p->phydev.link)
+ dev->live_ports |= (1 << port);
+ }
+ ksz8863_cfg_port_member(dev, port, member);
+}
+
+static void ksz8863_config_cpu_port(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *p;
+ int i;
+
+ ds->num_ports = dev->port_cnt + 1;
+
+ ksz_cfg(dev, S_TAIL_TAG_CTRL, SWITCH_TAIL_TAG_ENABLE, true);
+
+ p = &dev->ports[dev->cpu_port];
+ p->vid_member = dev->port_mask;
+ p->on = 1;
+
+ ksz8863_port_setup(dev, dev->cpu_port, true);
+ dev->member = dev->host_mask;
+
+ for (i = 0; i < SWITCH_PORT_NUM; i++) {
+ p = &dev->ports[i];
+
+ /* Initialize to non-zero so that ksz_cfg_port_member() will
+ * be called.
+ */
+ p->vid_member = (1 << i);
+ p->member = dev->port_mask;
+ ksz8863_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+
+ p->on = 1;
+ p->phy = 1;
+ }
+
+ for (i = 0; i < dev->phy_port_cnt; i++) {
+ p = &dev->ports[i];
+ if (!p->on)
+ continue;
+ ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL,
+ false);
+ }
+}
+
+static int ksz8863_setup(struct dsa_switch *ds)
+{
+ u8 data8;
+ u16 data16;
+ u32 value;
+ int i;
+ struct alu_struct alu;
+ struct ksz_device *dev = ds->priv;
+ int ret = 0;
+
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+
+ ret = ksz8863_reset_switch(dev);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+ return ret;
+ }
+
+ ksz_cfg(dev, S_REPLACE_VID_CTRL, SWITCH_FLOW_CTRL, true);
+
+ /* Enable automatic fast aging when link changed detected. */
+ ksz_cfg(dev, S_LINK_AGING_CTRL, SWITCH_LINK_AUTO_AGING, true);
+
+ ksz_read8(dev, REG_SWITCH_CTRL_1, &data8);
+
+ /* Enable aggressive back off algorithm in half duplex mode. */
+ data8 |= SWITCH_AGGR_BACKOFF;
+ ksz_write8(dev, REG_SWITCH_CTRL_1, data8);
+
+ ksz_read8(dev, REG_SWITCH_CTRL_2, &data8);
+
+ /* Make sure unicast VLAN boundary is set as default. */
+ data8 |= UNICAST_VLAN_BOUNDARY;
+
+ /* Enable no excessive collision drop. */
+ data8 |= NO_EXC_COLLISION_DROP;
+ ksz_write8(dev, REG_SWITCH_CTRL_2, data8);
+
+ ksz8863_config_cpu_port(ds);
+
+ ksz_cfg(dev, REG_SWITCH_CTRL_2, MULTICAST_STORM_DISABLE, true);
+
+ ksz_cfg(dev, S_REPLACE_VID_CTRL, SWITCH_REPLACE_VID, false);
+
+ ksz_cfg(dev, S_MIRROR_CTRL, SWITCH_MIRROR_RX_TX, false);
+
+ /* set broadcast storm protection 10% rate */
+ data8 = BROADCAST_STORM_PROT_RATE;
+ value = ((u32)BROADCAST_STORM_VALUE * data8) / 100;
+ if (value > BROADCAST_STORM_RATE)
+ value = BROADCAST_STORM_RATE;
+ ksz_read16(dev, S_REPLACE_VID_CTRL, &data16);
+ data16 &= ~BROADCAST_STORM_RATE;
+ data16 |= value;
+ ksz_write16(dev, S_REPLACE_VID_CTRL, data16);
+
+ for (i = 0; i < VLAN_TABLE_ENTRIES; i++)
+ ksz8863_r_vlan_entries(dev, i);
+
+ /* Setup STP address for STP operation. */
+ memset(&alu, 0, sizeof(alu));
+ memcpy(alu.mac, stp_multicast_addr, ETH_ALEN);
+ alu.is_static = true;
+ alu.is_override = true;
+ alu.port_forward = dev->host_mask;
+
+ ksz8863_w_sta_mac_table(dev, 0, &alu);
+
+ ksz_write8(dev, REG_CHIP_ID1, SWITCH_START);
+
+ ksz_init_mib_timer(dev);
+
+ return 0;
+}
+
+static const struct dsa_switch_ops ksz8863_switch_ops = {
+ .get_tag_protocol = ksz8863_get_tag_protocol,
+ .setup = ksz8863_setup,
+ .adjust_link = ksz_adjust_link,
+ .port_enable = ksz_enable_port,
+ .port_disable = ksz_disable_port,
+ .get_strings = ksz8863_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 = ksz8863_port_stp_state_set,
+ .port_fast_age = ksz_port_fast_age,
+ .port_vlan_filtering = ksz8863_port_vlan_filtering,
+ .port_vlan_prepare = ksz_port_vlan_prepare,
+ .port_vlan_add = ksz8863_port_vlan_add,
+ .port_vlan_del = ksz8863_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 = ksz8863_port_mirror_add,
+ .port_mirror_del = ksz8863_port_mirror_del,
+};
+
+static u32 ksz8863_get_port_addr(int port, int reg)
+{
+ int offset;
+
+ PORT_CTRL_ADDR(port, offset);
+
+ return reg + offset;
+}
+
+static int ksz8863_switch_detect(struct ksz_device *dev)
+{
+ u16 id16;
+ u8 id1;
+ u8 id2;
+ int ret;
+
+ /* read chip id */
+ ret = ksz_read16(dev, REG_CHIP_ID0, &id16);
+ if (ret)
+ return ret;
+
+ id1 = id16 >> 8;
+ id2 = id16 & SW_CHIP_ID_M;
+ if (id1 != FAMILY_ID ||
+ id2 != CHIP_ID_63)
+ return -ENODEV;
+
+ dev->mib_port_cnt = TOTAL_PORT_NUM;
+ dev->phy_port_cnt = SWITCH_PORT_NUM;
+
+ id16 = 0x8800;
+ id16 |= 0x73;
+ dev->chip_id = id16;
+
+ dev->cpu_port = dev->mib_port_cnt - 1;
+ dev->host_mask = (1 << dev->cpu_port);
+
+ return 0;
+}
+
+struct ksz_chip_data {
+ u16 chip_id;
+ const char *dev_name;
+ int num_vlans;
+ int num_alus;
+ int num_statics;
+ int cpu_ports;
+ int port_cnt;
+};
+
+static const struct ksz_chip_data ksz8863_switch_chips[] = {
+ {
+ .chip_id = 0x8863,
+ .dev_name = "KSZ8863",
+ .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 */
+ },
+ {
+ .chip_id = 0x8873,
+ .dev_name = "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 ksz8863_switch_init(struct ksz_device *dev)
+{
+ int i;
+
+ dev->ds->ops = &ksz8863_switch_ops;
+
+ for (i = 0; i < ARRAY_SIZE(ksz8863_switch_chips); i++) {
+ const struct ksz_chip_data *chip = &ksz8863_switch_chips[i];
+
+ if (dev->chip_id == chip->chip_id) {
+ dev->name = chip->dev_name;
+ dev->num_vlans = chip->num_vlans;
+ dev->num_alus = chip->num_alus;
+ dev->num_statics = chip->num_statics;
+ dev->port_cnt = chip->port_cnt;
+ dev->cpu_ports = chip->cpu_ports;
+
+ break;
+ }
+ }
+
+ /* no switch found */
+ if (!dev->cpu_ports)
+ return -ENODEV;
+
+ dev->port_mask = (1 << 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,
+ GFP_KERNEL);
+ if (!dev->ports)
+ return -ENOMEM;
+ for (i = 0; i < dev->mib_port_cnt; i++) {
+ mutex_init(&dev->ports[i].mib.cnt_mutex);
+ dev->ports[i].mib.counters =
+ devm_kzalloc(dev->dev,
+ sizeof(u64) *
+ (TOTAL_SWITCH_COUNTER_NUM + 1),
+ GFP_KERNEL);
+ if (!dev->ports[i].mib.counters)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ksz8863_switch_exit(struct ksz_device *dev)
+{
+ ksz8863_reset_switch(dev);
+ ksz_write8(dev, REG_CHIP_ID1, SWITCH_START);
+}
+
+static const struct ksz_dev_ops ksz8863_dev_ops = {
+ .get_port_addr = ksz8863_get_port_addr,
+ .cfg_port_member = ksz8863_cfg_port_member,
+ .flush_dyn_mac_table = ksz8863_flush_dyn_mac_table,
+ .phy_setup = ksz8863_phy_setup,
+ .port_setup = ksz8863_port_setup,
+ .r_dyn_mac_table = ksz8863_r_dyn_mac_table,
+ .r_sta_mac_table = ksz8863_r_sta_mac_table,
+ .w_sta_mac_table = ksz8863_w_sta_mac_table,
+ .r_mib_cnt = ksz8863_r_mib_cnt,
+ .r_mib_pkt = ksz8863_r_mib_pkt,
+ .port_init_cnt = ksz8863_port_init_cnt,
+ .shutdown = ksz8863_reset_switch,
+ .detect = ksz8863_switch_detect,
+ .init = ksz8863_switch_init,
+ .exit = ksz8863_switch_exit,
+};
+
+int ksz8863_switch_register(struct ksz_device *dev)
+{
+ return ksz_switch_register(dev, &ksz8863_dev_ops);
+}
+EXPORT_SYMBOL(ksz8863_switch_register);
+
+MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>");
+MODULE_DESCRIPTION("Micrel KSZ8863 SMI Switch driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/microchip/ksz8863_reg.h b/drivers/net/dsa/microchip/ksz8863_reg.h
new file mode 100644
index 0000000000000..6921890ae5509
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8863_reg.h
@@ -0,0 +1,605 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Microchip KSZ8863 definition file
+ *
+ * Copyright (c) 2015-2016 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ8863_REGS_H
+#define __KSZ8863_REGS_H
+
+#define REG_CHIP_ID0 0x00
+
+#define FAMILY_ID 0x88
+
+#define REG_CHIP_ID1 0x01
+#define SW_CHIP_ID_M 0xF0
+
+#define SWITCH_CHIP_ID_MASK 0xF0
+#define SWITCH_CHIP_ID_SHIFT 4
+#define SWITCH_REVISION_MASK 0x0E
+#define SWITCH_REVISION_SHIFT 1
+#define SWITCH_START 0x01
+
+#define CHIP_ID_63 0x30
+
+#define REG_SWITCH_CTRL_0 0x02
+
+#define SWITCH_NEW_BACKOFF BIT(7)
+#define SWITCH_FLUSH_DYN_MAC_TABLE BIT(5)
+#define SWITCH_FLUSH_STA_MAC_TABLE BIT(4)
+#define SWITCH_PASS_PAUSE BIT(3)
+#define SWITCH_LINK_AUTO_AGING BIT(0)
+
+#define REG_SWITCH_CTRL_1 0x03
+
+#define SWITCH_PASS_ALL BIT(7)
+#define SWITCH_TAIL_TAG_ENABLE BIT(6)
+#define SWITCH_TX_FLOW_CTRL BIT(5)
+#define SWITCH_RX_FLOW_CTRL BIT(4)
+#define SWITCH_CHECK_LENGTH BIT(3)
+#define SWITCH_AGING_ENABLE BIT(2)
+#define SWITCH_FAST_AGING BIT(1)
+#define SWITCH_AGGR_BACKOFF BIT(0)
+
+#define REG_SWITCH_CTRL_2 0x04
+
+#define UNICAST_VLAN_BOUNDARY BIT(7)
+#define MULTICAST_STORM_DISABLE BIT(6)
+#define SWITCH_BACK_PRESSURE BIT(5)
+#define FAIR_FLOW_CTRL BIT(4)
+#define NO_EXC_COLLISION_DROP BIT(3)
+#define SWITCH_HUGE_PACKET BIT(2)
+#define SWITCH_LEGAL_PACKET BIT(1)
+
+#define REG_SWITCH_CTRL_3 0x05
+
+#define SWITCH_VLAN_ENABLE BIT(7)
+#define SWITCH_IGMP_SNOOP BIT(6)
+#define WEIGHTED_FAIR_QUEUE_ENABLE BIT(3)
+#define SWITCH_MIRROR_RX_TX BIT(0)
+
+#define REG_SWITCH_CTRL_4 0x06
+
+#define SWITCH_HALF_DUPLEX BIT(6)
+#define SWITCH_FLOW_CTRL BIT(5)
+#define SWITCH_10_MBIT BIT(4)
+#define SWITCH_REPLACE_VID BIT(3)
+#define BROADCAST_STORM_RATE_HI 0x07
+
+#define REG_SWITCH_CTRL_5 0x07
+
+#define BROADCAST_STORM_RATE_LO 0xFF
+#define BROADCAST_STORM_RATE 0x07FF
+
+#define REG_SWITCH_CTRL_9 0x0B
+
+#define SPI_CLK_125_MHZ 0x80
+#define SPI_CLK_62_5_MHZ 0x40
+#define SPI_CLK_31_25_MHZ 0x00
+
+#define REG_SWITCH_CTRL_10 0x0C
+#define REG_SWITCH_CTRL_11 0x0D
+
+#define SWITCH_802_1P_MASK 3
+#define SWITCH_802_1P_BASE 3
+#define SWITCH_802_1P_SHIFT 2
+
+#define SWITCH_802_1P_MAP_MASK 3
+#define SWITCH_802_1P_MAP_SHIFT 2
+
+#define REG_SWITCH_CTRL_12 0x0E
+
+#define SWITCH_UNKNOWN_DA_ENABLE BIT(7)
+#define SWITCH_DRIVER_16MA BIT(6)
+#define SWITCH_UNKNOWN_DA_2_PORT3 BIT(2)
+#define SWITCH_UNKNOWN_DA_2_PORT2 BIT(1)
+#define SWITCH_UNKNOWN_DA_2_PORT1 BIT(0)
+
+#define REG_SWITCH_CTRL_13 0x0F
+
+#define SWITCH_PORT_PHY_ADDR_MASK 0x1F
+#define SWITCH_PORT_PHY_ADDR_SHIFT 3
+
+#define REG_PORT_1_CTRL_0 0x10
+#define REG_PORT_2_CTRL_0 0x20
+#define REG_PORT_3_CTRL_0 0x30
+
+#define PORT_BROADCAST_STORM BIT(7)
+#define PORT_DIFFSERV_ENABLE BIT(6)
+#define PORT_802_1P_ENABLE BIT(5)
+#define PORT_BASED_PRIORITY_MASK 0x18
+#define PORT_BASED_PRIORITY_BASE 0x03
+#define PORT_BASED_PRIORITY_SHIFT 3
+#define PORT_PORT_PRIO_0 0x00
+#define PORT_PORT_PRIO_1 0x08
+#define PORT_PORT_PRIO_2 0x10
+#define PORT_PORT_PRIO_3 0x18
+#define PORT_INSERT_TAG BIT(2)
+#define PORT_REMOVE_TAG BIT(1)
+#define PORT_4_QUEUES_ENABLE BIT(0)
+
+#define REG_PORT_1_CTRL_1 0x11
+#define REG_PORT_2_CTRL_1 0x21
+#define REG_PORT_3_CTRL_1 0x31
+
+#define PORT_MIRROR_SNIFFER BIT(7)
+#define PORT_MIRROR_RX BIT(6)
+#define PORT_MIRROR_TX BIT(5)
+#define PORT_DOUBLE_TAG BIT(4)
+#define PORT_802_1P_REMAPPING BIT(3)
+#define PORT_VLAN_MEMBERSHIP 0x07
+
+#define REG_PORT_1_CTRL_2 0x12
+#define REG_PORT_2_CTRL_2 0x22
+#define REG_PORT_3_CTRL_2 0x32
+
+#define PORT_2_QUEUES_ENABLE BIT(7)
+#define PORT_INGRESS_FILTER BIT(6)
+#define PORT_DISCARD_NON_VID BIT(5)
+#define PORT_FORCE_FLOW_CTRL BIT(4)
+#define PORT_BACK_PRESSURE BIT(3)
+#define PORT_TX_ENABLE BIT(2)
+#define PORT_RX_ENABLE BIT(1)
+#define PORT_LEARN_DISABLE BIT(0)
+
+#define REG_PORT_1_CTRL_3 0x13
+#define REG_PORT_2_CTRL_3 0x23
+#define REG_PORT_3_CTRL_3 0x33
+#define REG_PORT_1_CTRL_4 0x14
+#define REG_PORT_2_CTRL_4 0x24
+#define REG_PORT_3_CTRL_4 0x34
+
+#define PORT_DEFAULT_VID 0x0001
+
+#define REG_PORT_1_CTRL_5 0x15
+#define REG_PORT_2_CTRL_5 0x25
+#define REG_PORT_3_CTRL_5 0x35
+
+#define PORT_3_MII_MAC_MODE BIT(7)
+#define PORT_SA_MAC2 BIT(6)
+#define PORT_SA_MAC1 BIT(5)
+#define PORT_DROP_TAG BIT(4)
+#define PORT_INGRESS_LIMIT_MODE 0x0C
+#define PORT_INGRESS_ALL 0x00
+#define PORT_INGRESS_UNICAST 0x04
+#define PORT_INGRESS_MULTICAST 0x08
+#define PORT_INGRESS_BROADCAST 0x0C
+#define PORT_COUNT_IFG BIT(1)
+#define PORT_COUNT_PREAMBLE BIT(0)
+
+#define REG_PORT_1_IN_RATE_0 0x16
+#define REG_PORT_2_IN_RATE_0 0x26
+#define REG_PORT_3_IN_RATE_0 0x36
+
+#define PORT_3_INVERTED_REFCLK BIT(7)
+
+#define REG_PORT_1_IN_RATE_1 0x17
+#define REG_PORT_2_IN_RATE_1 0x27
+#define REG_PORT_3_IN_RATE_1 0x37
+#define REG_PORT_1_IN_RATE_2 0x18
+#define REG_PORT_2_IN_RATE_2 0x28
+#define REG_PORT_3_IN_RATE_2 0x38
+#define REG_PORT_1_IN_RATE_3 0x19
+#define REG_PORT_2_IN_RATE_3 0x29
+#define REG_PORT_3_IN_RATE_3 0x39
+
+#define PORT_PRIO_RATE 0x0F
+#define PORT_PRIO_RATE_SHIFT 4
+
+#define REG_PORT_1_LINK_MD_CTRL 0x1A
+#define REG_PORT_2_LINK_MD_CTRL 0x2A
+
+#define PORT_CABLE_10M_SHORT BIT(7)
+#define PORT_CABLE_DIAG_RESULT 0x60
+#define PORT_CABLE_STAT_NORMAL 0x00
+#define PORT_CABLE_STAT_OPEN 0x20
+#define PORT_CABLE_STAT_SHORT 0x40
+#define PORT_CABLE_STAT_FAILED 0x60
+#define PORT_START_CABLE_DIAG BIT(4)
+#define PORT_FORCE_LINK BIT(3)
+#define PORT_POWER_SAVING 0x04
+#define PORT_PHY_REMOTE_LOOPBACK BIT(1)
+#define PORT_CABLE_FAULT_COUNTER_H 0x01
+
+#define REG_PORT_1_LINK_MD_RESULT 0x1B
+#define REG_PORT_2_LINK_MD_RESULT 0x2B
+
+#define PORT_CABLE_FAULT_COUNTER_L 0xFF
+#define PORT_CABLE_FAULT_COUNTER 0x1FF
+
+#define REG_PORT_1_CTRL_12 0x1C
+#define REG_PORT_2_CTRL_12 0x2C
+
+#define PORT_AUTO_NEG_ENABLE BIT(7)
+#define PORT_FORCE_100_MBIT BIT(6)
+#define PORT_FORCE_FULL_DUPLEX BIT(5)
+#define PORT_AUTO_NEG_SYM_PAUSE BIT(4)
+#define PORT_AUTO_NEG_100BTX_FD BIT(3)
+#define PORT_AUTO_NEG_100BTX BIT(2)
+#define PORT_AUTO_NEG_10BT_FD BIT(1)
+#define PORT_AUTO_NEG_10BT BIT(0)
+
+#define REG_PORT_1_CTRL_13 0x1D
+#define REG_PORT_2_CTRL_13 0x2D
+
+#define PORT_LED_OFF BIT(7)
+#define PORT_TX_DISABLE BIT(6)
+#define PORT_AUTO_NEG_RESTART BIT(5)
+#define PORT_REMOTE_FAULT_DISABLE BIT(4)
+#define PORT_POWER_DOWN BIT(3)
+#define PORT_AUTO_MDIX_DISABLE BIT(2)
+#define PORT_FORCE_MDIX BIT(1)
+#define PORT_LOOPBACK BIT(0)
+
+#define REG_PORT_1_STATUS_0 0x1E
+#define REG_PORT_2_STATUS_0 0x2E
+
+#define PORT_MDIX_STATUS BIT(7)
+#define PORT_AUTO_NEG_COMPLETE BIT(6)
+#define PORT_STATUS_LINK_GOOD BIT(5)
+#define PORT_REMOTE_SYM_PAUSE BIT(4)
+#define PORT_REMOTE_100BTX_FD BIT(3)
+#define PORT_REMOTE_100BTX BIT(2)
+#define PORT_REMOTE_10BT_FD BIT(1)
+#define PORT_REMOTE_10BT BIT(0)
+
+#define REG_PORT_1_STATUS_1 0x1F
+#define REG_PORT_2_STATUS_1 0x2F
+#define REG_PORT_3_STATUS_1 0x3F
+
+#define PORT_HP_MDIX BIT(7)
+#define PORT_REVERSED_POLARITY BIT(5)
+#define PORT_TX_FLOW_CTRL BIT(4)
+#define PORT_RX_FLOW_CTRL BIT(3)
+#define PORT_STAT_SPEED_100MBIT BIT(2)
+#define PORT_STAT_FULL_DUPLEX BIT(1)
+#define PORT_REMOTE_FAULT BIT(0)
+
+#define REG_PORT_CTRL_0 0x00
+#define REG_PORT_CTRL_1 0x01
+#define REG_PORT_CTRL_2 0x02
+#define REG_PORT_CTRL_VID 0x03
+#define REG_PORT_CTRL_5 0x05
+#define REG_PORT_IN_RATE_0 0x06
+#define REG_PORT_IN_RATE_1 0x07
+#define REG_PORT_IN_RATE_2 0x08
+#define REG_PORT_IN_RATE_3 0x09
+#define REG_PORT_LINK_MD_CTRL 0x0A
+#define REG_PORT_LINK_MD_RESULT 0x0B
+#define REG_PORT_CTRL_12 0x0C
+#define REG_PORT_CTRL_13 0x0D
+#define REG_PORT_STATUS_0 0x0E
+#define REG_PORT_STATUS_1 0x0F
+
+#define PORT_CTRL_ADDR(port, addr) \
+ (addr = REG_PORT_1_CTRL_0 + (port) * \
+ (REG_PORT_2_CTRL_0 - REG_PORT_1_CTRL_0))
+
+#define REG_SWITCH_RESET 0x43
+
+#define GLOBAL_SOFTWARE_RESET BIT(4)
+#define PCS_RESET BIT(0)
+
+#define REG_TOS_PRIO_CTRL_0 0x60
+#define REG_TOS_PRIO_CTRL_1 0x61
+#define REG_TOS_PRIO_CTRL_2 0x62
+#define REG_TOS_PRIO_CTRL_3 0x63
+#define REG_TOS_PRIO_CTRL_4 0x64
+#define REG_TOS_PRIO_CTRL_5 0x65
+#define REG_TOS_PRIO_CTRL_6 0x66
+#define REG_TOS_PRIO_CTRL_7 0x67
+#define REG_TOS_PRIO_CTRL_8 0x68
+#define REG_TOS_PRIO_CTRL_9 0x69
+#define REG_TOS_PRIO_CTRL_10 0x6A
+#define REG_TOS_PRIO_CTRL_11 0x6B
+#define REG_TOS_PRIO_CTRL_12 0x6C
+#define REG_TOS_PRIO_CTRL_13 0x6D
+#define REG_TOS_PRIO_CTRL_14 0x6E
+#define REG_TOS_PRIO_CTRL_15 0x6F
+
+#define TOS_PRIO_M 3
+#define TOS_PRIO_S 2
+
+#define REG_SWITCH_MAC_ADDR_0 0x70
+#define REG_SWITCH_MAC_ADDR_1 0x71
+#define REG_SWITCH_MAC_ADDR_2 0x72
+#define REG_SWITCH_MAC_ADDR_3 0x73
+#define REG_SWITCH_MAC_ADDR_4 0x74
+#define REG_SWITCH_MAC_ADDR_5 0x75
+
+#define REG_USER_DEFINED_1 0x76
+#define REG_USER_DEFINED_2 0x77
+#define REG_USER_DEFINED_3 0x78
+
+#define REG_IND_CTRL_0 0x79
+
+#define TABLE_READ BIT(4)
+#define TABLE_STATIC_MAC BIT(2)
+#define TABLE_VLAN BIT(2)
+#define TABLE_DYNAMIC_MAC BIT(2)
+#define TABLE_MIB BIT(2)
+
+#define REG_IND_CTRL_1 0x7A
+
+#define TABLE_ENTRY_MASK 0x03FF
+
+#define REG_IND_DATA_8 0x7B
+#define REG_IND_DATA_7 0x7C
+#define REG_IND_DATA_6 0x7D
+#define REG_IND_DATA_5 0x7E
+#define REG_IND_DATA_4 0x7F
+#define REG_IND_DATA_3 0x80
+#define REG_IND_DATA_2 0x81
+#define REG_IND_DATA_1 0x82
+#define REG_IND_DATA_0 0x83
+
+#define REG_IND_DATA_CHECK REG_IND_DATA_8
+#define REG_IND_MIB_CHECK REG_IND_DATA_3
+#define REG_IND_DATA_HI REG_IND_DATA_7
+#define REG_IND_DATA_LO REG_IND_DATA_3
+
+#define REG_PORT_0_MAC_ADDR_0 0x8E
+#define REG_PORT_0_MAC_ADDR_1 0x8F
+#define REG_PORT_0_MAC_ADDR_2 0x90
+#define REG_PORT_0_MAC_ADDR_3 0x91
+#define REG_PORT_0_MAC_ADDR_4 0x92
+#define REG_PORT_0_MAC_ADDR_5 0x93
+#define REG_PORT_1_MAC_ADDR_0 0x94
+#define REG_PORT_1_MAC_ADDR_1 0x95
+#define REG_PORT_1_MAC_ADDR_2 0x96
+#define REG_PORT_1_MAC_ADDR_3 0x97
+#define REG_PORT_1_MAC_ADDR_4 0x98
+#define REG_PORT_1_MAC_ADDR_5 0x99
+
+#define REG_PORT_1_OUT_RATE_0 0x9A
+#define REG_PORT_2_OUT_RATE_0 0x9E
+#define REG_PORT_3_OUT_RATE_0 0xA2
+
+#define SWITCH_OUT_RATE_ENABLE BIT(7)
+
+#define REG_PORT_1_OUT_RATE_1 0x9B
+#define REG_PORT_2_OUT_RATE_1 0x9F
+#define REG_PORT_3_OUT_RATE_1 0xA3
+#define REG_PORT_1_OUT_RATE_2 0x9C
+#define REG_PORT_2_OUT_RATE_2 0xA0
+#define REG_PORT_3_OUT_RATE_2 0xA4
+#define REG_PORT_1_OUT_RATE_3 0x9D
+#define REG_PORT_2_OUT_RATE_3 0xA1
+#define REG_PORT_3_OUT_RATE_3 0xA5
+
+#define REG_PORT_OUT_RATE_0 0x00
+#define REG_PORT_OUT_RATE_1 0x01
+#define REG_PORT_OUT_RATE_2 0x02
+#define REG_PORT_OUT_RATE_3 0x03
+
+#define REG_MODE_INDICATOR 0xA6
+
+#define MODE_2_MII BIT(7)
+#define MODE_2_PHY BIT(6)
+#define PORT_1_RMII BIT(5)
+#define PORT_3_RMII BIT(4)
+#define PORT_1_MAC_MII BIT(3)
+#define PORT_3_MAC_MII BIT(2)
+#define PORT_1_FIBER BIT(1)
+#define PORT_2_FIBER BIT(0)
+
+#define MODE_MLL 0x03
+#define MODE_RLL 0x13
+#define MODE_FLL 0x01
+
+#define REG_BUF_RESERVED_Q3 0xA7
+#define REG_BUF_RESERVED_Q2 0xA8
+#define REG_BUF_RESERVED_Q1 0xA9
+#define REG_BUF_RESERVED_Q0 0xAA
+#define REG_PM_FLOW_CTRL_SELECT_1 0xAB
+#define REG_PM_FLOW_CTRL_SELECT_2 0xAC
+#define REG_PM_FLOW_CTRL_SELECT_3 0xAD
+#define REG_PM_FLOW_CTRL_SELECT_4 0xAE
+
+#define REG_PORT1_TXQ3_RATE_CTRL 0xAF
+#define REG_PORT1_TXQ2_RATE_CTRL 0xB0
+#define REG_PORT1_TXQ1_RATE_CTRL 0xB1
+#define REG_PORT1_TXQ0_RATE_CTRL 0xB2
+#define REG_PORT2_TXQ3_RATE_CTRL 0xB3
+#define REG_PORT2_TXQ2_RATE_CTRL 0xB4
+#define REG_PORT2_TXQ1_RATE_CTRL 0xB5
+#define REG_PORT2_TXQ0_RATE_CTRL 0xB6
+#define REG_PORT3_TXQ3_RATE_CTRL 0xB7
+#define REG_PORT3_TXQ2_RATE_CTRL 0xB8
+#define REG_PORT3_TXQ1_RATE_CTRL 0xB9
+#define REG_PORT3_TXQ0_RATE_CTRL 0xBA
+
+#define RATE_CTRL_ENABLE BIT(7)
+
+#define REG_INT_ENABLE 0xBB
+
+#define REG_INT_STATUS 0xBC
+
+#define INT_PORT_1_2_LINK_CHANGE BIT(7)
+#define INT_PORT_3_LINK_CHANGE BIT(2)
+#define INT_PORT_2_LINK_CHANGE BIT(1)
+#define INT_PORT_1_LINK_CHANGE BIT(0)
+
+#define REG_PAUSE_ITERATION_LIMIT 0xBD
+
+#define REG_INSERT_SRC_PVID 0xC2
+
+#define SWITCH_INS_TAG_1_PORT_2 BIT(5)
+#define SWITCH_INS_TAG_1_PORT_3 BIT(4)
+#define SWITCH_INS_TAG_2_PORT_1 BIT(3)
+#define SWITCH_INS_TAG_2_PORT_3 BIT(2)
+#define SWITCH_INS_TAG_3_PORT_1 BIT(1)
+#define SWITCH_INS_TAG_3_PORT_2 BIT(0)
+
+#define REG_POWER_MANAGEMENT 0xC3
+
+#define SWITCH_CPU_CLK_POWER_DOWN BIT(7)
+#define SWITCH_CLK_POWER_DOWN BIT(6)
+#define SWITCH_LED_SELECTION 0x30
+#define SWITCH_LED_LINK_ACT_SPEED 0x00
+#define SWITCH_LED_LINK_ACT 0x20
+#define SWITCH_LED_LINK_ACT_DUPLEX 0x10
+#define SWITCH_LED_LINK_DUPLEX 0x30
+#define SWITCH_LED_OUTPUT_MODE BIT(3)
+#define SWITCH_PLL_POWER_DOWN BIT(2)
+#define SWITCH_POWER_MANAGEMENT_MODE 0x03
+#define SWITCH_NORMAL 0x00
+#define SWITCH_ENERGY_DETECTION 0x01
+#define SWITCH_SOFTWARE_POWER_DOWN 0x02
+#define SWITCH_POWER_SAVING 0x03
+
+#define REG_FORWARD_INVALID_VID 0xC6
+
+#define SWITCH_FORWARD_INVALID_PORTS 0x70
+#define FORWARD_INVALID_PORT_SHIFT 4
+#define PORT_3_RMII_CLOCK_SELECTION BIT(3)
+#define PORT_1_RMII_CLOCK_SELECTION BIT(2)
+#define SWITCH_HOST_INTERFACE_MODE 0x03
+#define SWITCH_I2C_MASTER 0x00
+#define SWITCH_I2C_SLAVE 0x01
+#define SWITCH_SPI_SLAVE 0x02
+#define SWITCH_SMII 0x03
+
+#define KSZ8863_ID_HI 0x0022
+#define KSZ8863_ID_LO 0x1430
+
+#define PHY_REG_LINK_MD 29
+
+#define PHY_START_CABLE_DIAG BIT(15)
+#define PHY_CABLE_DIAG_RESULT 0x6000
+#define PHY_CABLE_STAT_NORMAL 0x0000
+#define PHY_CABLE_STAT_OPEN 0x2000
+#define PHY_CABLE_STAT_SHORT 0x4000
+#define PHY_CABLE_STAT_FAILED 0x6000
+#define PHY_CABLE_10M_SHORT BIT(12)
+#define PHY_CABLE_FAULT_COUNTER 0x01FF
+
+#define PHY_REG_PHY_CTRL 30
+
+#define PHY_STAT_REVERSED_POLARITY BIT(5)
+#define PHY_STAT_MDIX BIT(4)
+#define PHY_FORCE_LINK BIT(3)
+#define PHY_POWER_SAVING_DISABLE BIT(2)
+#define PHY_REMOTE_LOOPBACK BIT(1)
+
+/* Default values are used in ksz_sw.h if these are not defined. */
+#define PRIO_QUEUES 4
+
+#define KS_PRIO_IN_REG 4
+
+#define TOTAL_PORT_NUM 3
+
+/* Host port can only be last of them. */
+#define SWITCH_PORT_NUM (TOTAL_PORT_NUM - 1)
+
+#define KSZ8863_COUNTER_NUM 0x20
+#define TOTAL_KSZ8863_COUNTER_NUM (KSZ8863_COUNTER_NUM + 2)
+
+#define SWITCH_COUNTER_NUM KSZ8863_COUNTER_NUM
+#define TOTAL_SWITCH_COUNTER_NUM TOTAL_KSZ8863_COUNTER_NUM
+
+#define P_802_1P_CTRL REG_PORT_CTRL_2
+#define P_LOCAL_CTRL REG_PORT_CTRL_5
+#define P_REMOTE_STATUS REG_PORT_STATUS_1
+#define P_INS_SRC_PVID_CTRL REG_PORT_CTRL_8
+#define P_DROP_TAG_CTRL REG_PORT_CTRL_9
+
+#define S_HUGE_PACKET_CTRL REG_SW_CTRL_2
+#define S_PASS_PAUSE_CTRL REG_SW_CTRL_10
+#define S_IPV6_MLD_CTRL REG_SW_CTRL_21
+
+#define P_BCAST_STORM_CTRL REG_PORT_CTRL_0
+#define P_PRIO_CTRL REG_PORT_CTRL_0
+#define P_TAG_CTRL REG_PORT_CTRL_0
+#define P_MIRROR_CTRL REG_PORT_CTRL_1
+#define P_STP_CTRL REG_PORT_CTRL_2
+#define P_PHY_CTRL REG_PORT_CTRL_12
+#define P_FORCE_CTRL REG_PORT_CTRL_12
+#define P_NEG_RESTART_CTRL REG_PORT_CTRL_13
+#define P_LINK_STATUS REG_PORT_STATUS_0
+#define P_SPEED_STATUS REG_PORT_STATUS_1
+#define P_RATE_LIMIT_CTRL REG_PORT_CTRL_5
+#define P_SA_MAC_CTRL REG_PORT_CTRL_5
+#define P_4_QUEUE_CTRL REG_PORT_CTRL_0
+#define P_2_QUEUE_CTRL REG_PORT_CTRL_2
+
+#define S_LINK_AGING_CTRL REG_SWITCH_CTRL_0
+#define S_MIRROR_CTRL REG_SWITCH_CTRL_3
+#define S_REPLACE_VID_CTRL REG_SWITCH_CTRL_4
+#define S_802_1P_PRIO_CTRL REG_SWITCH_CTRL_10
+#define S_UNKNOWN_DA_CTRL REG_SWITCH_CTRL_12
+#define S_TOS_PRIO_CTRL REG_TOS_PRIO_CTRL_0
+#define S_FLUSH_TABLE_CTRL REG_SWITCH_CTRL_0
+#define S_TAIL_TAG_CTRL REG_SWITCH_CTRL_1
+#define S_FORWARD_INVALID_VID_CTRL REG_FORWARD_INVALID_VID
+#define S_INS_SRC_PVID_CTRL REG_INSERT_SRC_PVID
+
+#define IND_ACC_TABLE(table) ((table) << 8)
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROT_RATE 10
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE 9969
+
+#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 STATIC_MAC_FWD_PORTS_S 16
+#define STATIC_MAC_FID_S 24
+
+#define VLAN_TABLE_FID 0x007F
+#define VLAN_TABLE_MEMBERSHIP 0x0F80
+#define VLAN_TABLE_VALID 0x1000
+
+#define VLAN_TABLE_MEMBERSHIP_S 7
+#define VLAN_TABLE_S 13
+#define VLAN_TABLE_M (BIT(VLAN_TABLE_S) - 1)
+
+#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 DYNAMIC_MAC_TABLE_NOT_READY 0x80
+
+#define DYNAMIC_MAC_TABLE_ENTRIES_H 0x7F
+#define 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 MIB_COUNTER_OVERFLOW BIT(7)
+#define MIB_COUNTER_VALID BIT(6)
+
+#define MIB_COUNTER_VALUE 0x3FFFFFFF
+
+#define KS_MIB_PACKET_DROPPED_TX_0 0x100
+#define KS_MIB_PACKET_DROPPED_TX_1 0x101
+#define KS_MIB_PACKET_DROPPED_TX_2 0x102
+#define KS_MIB_PACKET_DROPPED_TX_3 0x103
+#define KS_MIB_PACKET_DROPPED_TX_4 0x104
+#define KS_MIB_PACKET_DROPPED_RX_0 0x105
+#define KS_MIB_PACKET_DROPPED_RX_1 0x106
+#define KS_MIB_PACKET_DROPPED_RX_2 0x107
+#define KS_MIB_PACKET_DROPPED_RX_3 0x108
+#define KS_MIB_PACKET_DROPPED_RX_4 0x109
+
+#define MIB_PACKET_DROPPED 0x0000FFFF
+
+#define TAIL_TAG_OVERRIDE BIT(6)
+#define TAIL_TAG_LOOKUP BIT(7)
+
+#define VLAN_TABLE_ENTRIES (16 / 2)
+#define FID_ENTRIES 16
+
+#endif
diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c
new file mode 100644
index 0000000000000..d9fa069ecfa37
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz8863_smi.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ8863 series register access through SMI
+ *
+ * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de>
+ */
+
+#include "ksz_priv.h"
+#include "ksz_spi.h"
+
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+ unsigned int len)
+{
+ int i = 0;
+
+ for (i = 0; i < len; i++)
+ data[i] = (u8)mdiobus_read(dev->bus, 0,
+ (reg + i) | MII_ADDR_SMI0);
+
+ return 0;
+}
+
+static int ksz_spi_write(struct ksz_device *dev, u32 reg, void *data,
+ unsigned int len)
+{
+ int i = 0;
+ int ret = 0;
+
+ u8 *val = data;
+
+ for (i = 0; i < len; i++) {
+ ret = mdiobus_write(dev->bus, 0,
+ (reg + i) | MII_ADDR_SMI0, val[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct ksz_io_ops ksz8863_smi_ops = {
+ .read8 = ksz_spi_read8,
+ .read16 = ksz_spi_read16,
+ .read32 = ksz_spi_read32,
+ .write8 = ksz_spi_write8,
+ .write16 = ksz_spi_write16,
+ .write32 = ksz_spi_write32,
+ .get = ksz_spi_get,
+ .set = ksz_spi_set,
+};
+
+static int ksz8863_smi_probe(struct mdio_device *mdiodev)
+{
+ struct ksz_device *dev;
+ int ret;
+
+ dev = ksz_switch_alloc(&mdiodev->dev, &ksz8863_smi_ops, mdiodev);
+ if (!dev)
+ return -EINVAL;
+
+ if (mdiodev->dev.platform_data)
+ dev->pdata = mdiodev->dev.platform_data;
+
+ dev->bus = mdiodev->bus;
+
+ 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.grzeschik@pengutronix.de>");
+MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/microchip/ksz_priv.h b/drivers/net/dsa/microchip/ksz_priv.h
index b52e5ca17ab49..4c7c43d694554 100644
--- a/drivers/net/dsa/microchip/ksz_priv.h
+++ b/drivers/net/dsa/microchip/ksz_priv.h
@@ -56,6 +56,8 @@ struct ksz_device {
const struct ksz_io_ops *ops;
const struct ksz_dev_ops *dev_ops;
+ struct mii_bus *bus;
+
struct device *dev;
void *priv;
@@ -167,6 +169,7 @@ int ksz_switch_register(struct ksz_device *dev,
const struct ksz_dev_ops *ops);
void ksz_switch_remove(struct ksz_device *dev);
+int ksz8863_switch_register(struct ksz_device *dev);
int ksz9477_switch_register(struct ksz_device *dev);
#endif
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6aaaadd6a413c..57fbf3e722362 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -44,6 +44,7 @@ struct phylink_link_state;
#define DSA_TAG_PROTO_TRAILER_VALUE 11
#define DSA_TAG_PROTO_8021Q_VALUE 12
#define DSA_TAG_PROTO_SJA1105_VALUE 13
+#define DSA_TAG_PROTO_KSZ8863_VALUE 14
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -60,6 +61,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE,
DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE,
DSA_TAG_PROTO_SJA1105 = DSA_TAG_PROTO_SJA1105_VALUE,
+ DSA_TAG_PROTO_KSZ8863 = DSA_TAG_PROTO_KSZ8863_VALUE,
};
struct packet_type;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index cf855352a440e..de641a62b814b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -83,6 +83,13 @@ config NET_DSA_TAG_KSZ
Say Y if you want to enable support for tagging frames for the
Microchip 9893 family of switches.
+config NET_DSA_TAG_KSZ8863
+ tristate "Tag driver for Microchip 8863 family of switches"
+ select NET_DSA_TAG_KSZ_COMMON
+ help
+ Say Y if you want to enable support for tagging frames for the
+ Microchip 8863 family of switches.
+
config NET_DSA_TAG_KSZ9477
tristate "Tag driver for Microchip 9477 family of switches"
select NET_DSA_TAG_KSZ_COMMON
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index b4872b87d4a6b..3ef7c3945f0a4 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -182,9 +182,54 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893);
+#define KSZ8863_INGRESS_TAG_LEN 1
+
+static struct sk_buff *ksz8863_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct sk_buff *nskb;
+ u16 *tag;
+
+ nskb = ksz_common_xmit(skb, dev, KSZ8863_INGRESS_TAG_LEN);
+ if (!nskb)
+ return NULL;
+
+ /* Tag encoding */
+ tag = skb_put(nskb, KSZ8863_INGRESS_TAG_LEN);
+
+ *tag = 1 << (dp->index); /* destination port */
+ *tag = cpu_to_be16(*tag);
+
+ return nskb;
+}
+
+static struct sk_buff *ksz8863_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt)
+{
+ /* Tag decoding */
+ u8 *tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+ unsigned int port = tag[0] & 1;
+ unsigned int len = KSZ_EGRESS_TAG_LEN;
+
+ return ksz_common_rcv(skb, dev, port, len);
+}
+
+static const struct dsa_device_ops ksz8863_netdev_ops = {
+ .name = "ksz8863",
+ .proto = DSA_TAG_PROTO_KSZ8863,
+ .xmit = ksz8863_xmit,
+ .rcv = ksz8863_rcv,
+ .overhead = KSZ8863_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8863_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8863);
+
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
+ &DSA_TAG_DRIVER_NAME(ksz8863_netdev_ops),
};
module_dsa_tag_drivers(dsa_tag_driver_array);
--
2.20.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches
2019-05-08 21:13 [RFC 0/3] microchip: add support for ksz8863 driver family Michael Grzeschik
2019-05-08 21:13 ` [RFC 1/3] mdio-bitbang: add SMI0 mode support Michael Grzeschik
2019-05-08 21:13 ` [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver Michael Grzeschik
@ 2019-05-08 21:13 ` Michael Grzeschik
2019-06-13 20:09 ` Rob Herring
2 siblings, 1 reply; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-08 21:13 UTC (permalink / raw)
To: Tristram.Ha; +Cc: kernel, UNGLinuxDriver, netdev, devicetree
Document additional Microchip KSZ8863 family switches.
Show how KSZ8863 switch should be configured as the host port is port 3.
Cc: devicetree@vger.kernel.org
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
.../devicetree/bindings/net/dsa/ksz.txt | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/dsa/ksz.txt b/Documentation/devicetree/bindings/net/dsa/ksz.txt
index e7db7268fd0fd..4ac576e1cc34e 100644
--- a/Documentation/devicetree/bindings/net/dsa/ksz.txt
+++ b/Documentation/devicetree/bindings/net/dsa/ksz.txt
@@ -5,6 +5,8 @@ Required properties:
- compatible: For external switch chips, compatible string must be exactly one
of the following:
+ - "microchip,ksz8863"
+ - "microchip,ksz8873"
- "microchip,ksz9477"
- "microchip,ksz9897"
- "microchip,ksz9896"
@@ -31,6 +33,48 @@ Ethernet switch connected via SPI to the host, CPU port wired to eth0:
};
};
+ mdio0: mdio-gpio {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mdio_1>;
+ compatible = "virtual,mdio-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpios = <&gpio1 31 0 &gpio1 22 0>;
+
+ ksz8863@3 {
+ compatible = "microchip,ksz8863";
+ interrupt-parrent = <&gpio3>;
+ interrupt = <30 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports@0 {
+ reg = <0>;
+ label = "lan1";
+ };
+
+ ports@1 {
+ reg = <1>;
+ label = "lan2";
+ };
+
+ ports@2 {
+ reg = <2>;
+ label = "cpu";
+ ethernet = <ð0>;
+
+ fixed-link {
+ speed = <100>;
+ full-duplex;
+ };
+ };
+ };
+ };
+ };
+
spi1: spi@f8008000 {
pinctrl-0 = <&pinctrl_spi_ksz>;
cs-gpios = <&pioC 25 0>;
--
2.20.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [RFC 1/3] mdio-bitbang: add SMI0 mode support
2019-05-08 21:13 ` [RFC 1/3] mdio-bitbang: add SMI0 mode support Michael Grzeschik
@ 2019-05-09 14:29 ` Andrew Lunn
2019-05-10 7:32 ` Michael Grzeschik
0 siblings, 1 reply; 10+ messages in thread
From: Andrew Lunn @ 2019-05-09 14:29 UTC (permalink / raw)
To: Michael Grzeschik; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev
On Wed, May 08, 2019 at 11:13:28PM +0200, Michael Grzeschik wrote:
> Some microchip phys support the Serial Management Interface Protocol
> (SMI) for the configuration of the extended register set. We add
> MII_ADDR_SMI0 as an availabe interface to the mdiobb write and read
> functions, as this interface can be easy realized using the bitbang mdio
> driver.
>
> Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> ---
> drivers/net/phy/mdio-bitbang.c | 10 ++++++++++
> include/linux/phy.h | 12 ++++++++++++
> 2 files changed, 22 insertions(+)
>
> diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
> index 5136275c8e739..a978f8a9a172b 100644
> --- a/drivers/net/phy/mdio-bitbang.c
> +++ b/drivers/net/phy/mdio-bitbang.c
> @@ -22,6 +22,10 @@
> #define MDIO_READ 2
> #define MDIO_WRITE 1
>
> +#define SMI0_RW_OPCODE 0
> +#define SMI0_READ_PHY (1 << 4)
> +#define SMI0_WRITE_PHY (0 << 4)
> +
> #define MDIO_C45 (1<<15)
> #define MDIO_C45_ADDR (MDIO_C45 | 0)
> #define MDIO_C45_READ (MDIO_C45 | 3)
> @@ -157,6 +161,9 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
> if (reg & MII_ADDR_C45) {
> reg = mdiobb_cmd_addr(ctrl, phy, reg);
> mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
> + } else if (reg & MII_ADDR_SMI0) {
> + mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
> + (reg & 0xE0) >> 5 | SMI0_READ_PHY, reg);
> } else
> mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
>
> @@ -188,6 +195,9 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
> if (reg & MII_ADDR_C45) {
> reg = mdiobb_cmd_addr(ctrl, phy, reg);
> mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
> + } else if (reg & MII_ADDR_SMI0) {
> + mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
> + (reg & 0xE0) >> 5 | SMI0_WRITE_PHY, reg);
> } else
> mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
>
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 073fb151b5a99..f011722fbd5c2 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -199,6 +199,18 @@ static inline const char *phy_modes(phy_interface_t interface)
> IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */
> #define MII_ADDR_C45 (1<<30)
>
> +/* 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
> + *
> + * The register number is encoded with the 5 least significant bits in REG
> + * and the 3 most significant bits in PHY
> + */
> +#define MII_ADDR_SMI0 (1<<31)
> +
Michael
This is a Micrel Proprietary protocol. So we should reflect this in
the name. MII_ADDR_MICREL_SMI? Why the 0? Are there different
versions? Maybe replace all SMI0 with MICREL_SMI in mdio-bitbang.c
When i look at this, i don't see how a normal MDIO bus driver is going
to support this. Only the bit banging driver, or maybe a Micrel MDIO
bus master hardware driver. So i think the diagram should be placed
into mdio-bitbang.c, and it would be nice to add a diagram of standard
SMI.
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver
2019-05-08 21:13 ` [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver Michael Grzeschik
@ 2019-05-09 14:48 ` Andrew Lunn
2019-05-10 7:37 ` Michael Grzeschik
0 siblings, 1 reply; 10+ messages in thread
From: Andrew Lunn @ 2019-05-09 14:48 UTC (permalink / raw)
To: Michael Grzeschik; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev
On Wed, May 08, 2019 at 11:13:29PM +0200, Michael Grzeschik wrote:
> Cc: Tristram.Ha@microchip.com
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> ---
> drivers/net/dsa/microchip/Kconfig | 16 +
> drivers/net/dsa/microchip/Makefile | 2 +
> drivers/net/dsa/microchip/ksz8863.c | 1026 +++++++++++++++++++++++
> drivers/net/dsa/microchip/ksz8863_reg.h | 605 +++++++++++++
> drivers/net/dsa/microchip/ksz8863_smi.c | 105 +++
> drivers/net/dsa/microchip/ksz_priv.h | 3 +
> include/net/dsa.h | 2 +
> net/dsa/Kconfig | 7 +
> net/dsa/tag_ksz.c | 45 +
> 9 files changed, 1811 insertions(+)
> create mode 100644 drivers/net/dsa/microchip/ksz8863.c
> 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 bea29fde9f3d1..a6fa6ae972951 100644
> --- a/drivers/net/dsa/microchip/Kconfig
> +++ b/drivers/net/dsa/microchip/Kconfig
> @@ -14,3 +14,19 @@ config NET_DSA_MICROCHIP_KSZ9477_SPI
> depends on NET_DSA_MICROCHIP_KSZ9477 && SPI
> help
> Select to enable support for registering switches configured through SPI.
> +
> +menuconfig NET_DSA_MICROCHIP_KSZ8863
> + tristate "Microchip KSZ8863 series switch support"
> + depends on NET_DSA
> + select NET_DSA_TAG_KSZ8863
> + select NET_DSA_MICROCHIP_KSZ_COMMON
> + help
> + This driver adds support for Microchip KSZ8863 switch chips.
> +
> +config NET_DSA_MICROCHIP_KSZ8863_SMI
> + tristate "KSZ series SMI connected switch driver"
> + depends on NET_DSA_MICROCHIP_KSZ8863
> + default y
> + help
> + Select to enable support for registering switches configured through SMI.
SMI is a synonym for MDIO. So we should make it clear, this is a
proprietary version. "... through Microchip SMI".
You might also want to either depend on or select mdio-bitbang, since
that is the other driver which supports Microchip SMI.
> +static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
> + unsigned int len)
> +{
> + int i = 0;
> +
> + for (i = 0; i < len; i++)
> + data[i] = (u8)mdiobus_read(dev->bus, 0,
> + (reg + i) | MII_ADDR_SMI0);
mdiobus_read() and mdiobus_write() can return an error, which is why
is returns an int. Please check for the error and return it.
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 6aaaadd6a413c..57fbf3e722362 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -44,6 +44,7 @@ struct phylink_link_state;
> #define DSA_TAG_PROTO_TRAILER_VALUE 11
> #define DSA_TAG_PROTO_8021Q_VALUE 12
> #define DSA_TAG_PROTO_SJA1105_VALUE 13
> +#define DSA_TAG_PROTO_KSZ8863_VALUE 14
>
> enum dsa_tag_protocol {
> DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
> @@ -60,6 +61,7 @@ enum dsa_tag_protocol {
> DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE,
> DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE,
> DSA_TAG_PROTO_SJA1105 = DSA_TAG_PROTO_SJA1105_VALUE,
> + DSA_TAG_PROTO_KSZ8863 = DSA_TAG_PROTO_KSZ8863_VALUE,
> };
Please put all the tag driver changes into a separate patch.
> +static struct sk_buff *ksz8863_rcv(struct sk_buff *skb, struct net_device *dev,
> + struct packet_type *pt)
> +{
> + /* Tag decoding */
> + u8 *tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
> + unsigned int port = tag[0] & 1;
Does this device only have 2 ports, 0 and 1?
> + unsigned int len = KSZ_EGRESS_TAG_LEN;
> +
> + return ksz_common_rcv(skb, dev, port, len);
> +}
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC 1/3] mdio-bitbang: add SMI0 mode support
2019-05-09 14:29 ` Andrew Lunn
@ 2019-05-10 7:32 ` Michael Grzeschik
2019-05-10 12:36 ` Andrew Lunn
0 siblings, 1 reply; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-10 7:32 UTC (permalink / raw)
To: Andrew Lunn; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev
[-- Attachment #1: Type: text/plain, Size: 4808 bytes --]
On Thu, May 09, 2019 at 04:29:25PM +0200, Andrew Lunn wrote:
> On Wed, May 08, 2019 at 11:13:28PM +0200, Michael Grzeschik wrote:
> > Some microchip phys support the Serial Management Interface Protocol
> > (SMI) for the configuration of the extended register set. We add
> > MII_ADDR_SMI0 as an availabe interface to the mdiobb write and read
> > functions, as this interface can be easy realized using the bitbang mdio
> > driver.
> >
> > Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
> > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> > ---
> > drivers/net/phy/mdio-bitbang.c | 10 ++++++++++
> > include/linux/phy.h | 12 ++++++++++++
> > 2 files changed, 22 insertions(+)
> >
> > diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
> > index 5136275c8e739..a978f8a9a172b 100644
> > --- a/drivers/net/phy/mdio-bitbang.c
> > +++ b/drivers/net/phy/mdio-bitbang.c
> > @@ -22,6 +22,10 @@
> > #define MDIO_READ 2
> > #define MDIO_WRITE 1
> >
> > +#define SMI0_RW_OPCODE 0
> > +#define SMI0_READ_PHY (1 << 4)
> > +#define SMI0_WRITE_PHY (0 << 4)
> > +
> > #define MDIO_C45 (1<<15)
> > #define MDIO_C45_ADDR (MDIO_C45 | 0)
> > #define MDIO_C45_READ (MDIO_C45 | 3)
> > @@ -157,6 +161,9 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
> > if (reg & MII_ADDR_C45) {
> > reg = mdiobb_cmd_addr(ctrl, phy, reg);
> > mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);
> > + } else if (reg & MII_ADDR_SMI0) {
> > + mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
> > + (reg & 0xE0) >> 5 | SMI0_READ_PHY, reg);
> > } else
> > mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
> >
> > @@ -188,6 +195,9 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
> > if (reg & MII_ADDR_C45) {
> > reg = mdiobb_cmd_addr(ctrl, phy, reg);
> > mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
> > + } else if (reg & MII_ADDR_SMI0) {
> > + mdiobb_cmd(ctrl, SMI0_RW_OPCODE,
> > + (reg & 0xE0) >> 5 | SMI0_WRITE_PHY, reg);
> > } else
> > mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
> >
> > diff --git a/include/linux/phy.h b/include/linux/phy.h
> > index 073fb151b5a99..f011722fbd5c2 100644
> > --- a/include/linux/phy.h
> > +++ b/include/linux/phy.h
> > @@ -199,6 +199,18 @@ static inline const char *phy_modes(phy_interface_t interface)
> > IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */
> > #define MII_ADDR_C45 (1<<30)
> >
> > +/* 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
> > + *
> > + * The register number is encoded with the 5 least significant bits in REG
> > + * and the 3 most significant bits in PHY
> > + */
> > +#define MII_ADDR_SMI0 (1<<31)
> > +
>
> Michael
>
> This is a Micrel Proprietary protocol. So we should reflect this in
> the name. MII_ADDR_MICREL_SMI? Why the 0? Are there different
> versions? Maybe replace all SMI0 with MICREL_SMI in mdio-bitbang.c
There are two variants of the SMI interface.
The KSZ8863/73/93 Products use the above Variant described as "SMI0".
The KSZ8864/95 Products use another layout:
preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle
|frame| OP code |address |address| | |
read | 32x1´s | 01 | 10 | RR11R | RRRRR |Z0| 00000000DDDDDDDD | Z
write| 32x1´s | 01 | 01 | RR11R | RRRRR |10| xxxxxxxxDDDDDDDD | Z
So they describe their write/read operation in the OP code rather then
the PHY address.
We could change the SMI index to SMI_KSZ88X3 for the current SMI0 to
give it a more descriptive name. Beside we never know if Microchip
will add the same protocol to other numbered switches. What naming
would you prefer?
> When i look at this, i don't see how a normal MDIO bus driver is going
> to support this. Only the bit banging driver, or maybe a Micrel MDIO
> bus master hardware driver. So i think the diagram should be placed
> into mdio-bitbang.c, and it would be nice to add a diagram of standard
> SMI.
Right. I will move the description to mdio-bitbang.c.
Thanks,
Michael
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver
2019-05-09 14:48 ` Andrew Lunn
@ 2019-05-10 7:37 ` Michael Grzeschik
0 siblings, 0 replies; 10+ messages in thread
From: Michael Grzeschik @ 2019-05-10 7:37 UTC (permalink / raw)
To: Andrew Lunn; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev
[-- Attachment #1: Type: text/plain, Size: 4664 bytes --]
On Thu, May 09, 2019 at 04:48:44PM +0200, Andrew Lunn wrote:
> On Wed, May 08, 2019 at 11:13:29PM +0200, Michael Grzeschik wrote:
> > Cc: Tristram.Ha@microchip.com
> > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> > ---
> > drivers/net/dsa/microchip/Kconfig | 16 +
> > drivers/net/dsa/microchip/Makefile | 2 +
> > drivers/net/dsa/microchip/ksz8863.c | 1026 +++++++++++++++++++++++
> > drivers/net/dsa/microchip/ksz8863_reg.h | 605 +++++++++++++
> > drivers/net/dsa/microchip/ksz8863_smi.c | 105 +++
> > drivers/net/dsa/microchip/ksz_priv.h | 3 +
> > include/net/dsa.h | 2 +
> > net/dsa/Kconfig | 7 +
> > net/dsa/tag_ksz.c | 45 +
> > 9 files changed, 1811 insertions(+)
> > create mode 100644 drivers/net/dsa/microchip/ksz8863.c
> > 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 bea29fde9f3d1..a6fa6ae972951 100644
> > --- a/drivers/net/dsa/microchip/Kconfig
> > +++ b/drivers/net/dsa/microchip/Kconfig
> > @@ -14,3 +14,19 @@ config NET_DSA_MICROCHIP_KSZ9477_SPI
> > depends on NET_DSA_MICROCHIP_KSZ9477 && SPI
> > help
> > Select to enable support for registering switches configured through SPI.
> > +
> > +menuconfig NET_DSA_MICROCHIP_KSZ8863
> > + tristate "Microchip KSZ8863 series switch support"
> > + depends on NET_DSA
> > + select NET_DSA_TAG_KSZ8863
> > + select NET_DSA_MICROCHIP_KSZ_COMMON
> > + help
> > + This driver adds support for Microchip KSZ8863 switch chips.
> > +
> > +config NET_DSA_MICROCHIP_KSZ8863_SMI
>
> > + tristate "KSZ series SMI connected switch driver"
> > + depends on NET_DSA_MICROCHIP_KSZ8863
> > + default y
> > + help
> > + Select to enable support for registering switches configured through SMI.
>
> SMI is a synonym for MDIO. So we should make it clear, this is a
> proprietary version. "... through Microchip SMI".
>
> You might also want to either depend on or select mdio-bitbang, since
> that is the other driver which supports Microchip SMI.
Right, I will add the comment and include the depend to mdio-bitbang.
> > +static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
> > + unsigned int len)
> > +{
> > + int i = 0;
> > +
> > + for (i = 0; i < len; i++)
> > + data[i] = (u8)mdiobus_read(dev->bus, 0,
> > + (reg + i) | MII_ADDR_SMI0);
>
> mdiobus_read() and mdiobus_write() can return an error, which is why
> is returns an int. Please check for the error and return it.
Thanks for pointing this out. I will fix it!
> > diff --git a/include/net/dsa.h b/include/net/dsa.h
> > index 6aaaadd6a413c..57fbf3e722362 100644
> > --- a/include/net/dsa.h
> > +++ b/include/net/dsa.h
> > @@ -44,6 +44,7 @@ struct phylink_link_state;
> > #define DSA_TAG_PROTO_TRAILER_VALUE 11
> > #define DSA_TAG_PROTO_8021Q_VALUE 12
> > #define DSA_TAG_PROTO_SJA1105_VALUE 13
> > +#define DSA_TAG_PROTO_KSZ8863_VALUE 14
> >
> > enum dsa_tag_protocol {
> > DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
> > @@ -60,6 +61,7 @@ enum dsa_tag_protocol {
> > DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE,
> > DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE,
> > DSA_TAG_PROTO_SJA1105 = DSA_TAG_PROTO_SJA1105_VALUE,
> > + DSA_TAG_PROTO_KSZ8863 = DSA_TAG_PROTO_KSZ8863_VALUE,
> > };
>
> Please put all the tag driver changes into a separate patch.
Right, I will ad that to another patch.
> > +static struct sk_buff *ksz8863_rcv(struct sk_buff *skb, struct net_device *dev,
> > + struct packet_type *pt)
> > +{
> > + /* Tag decoding */
> > + u8 *tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
> > + unsigned int port = tag[0] & 1;
>
> Does this device only have 2 ports, 0 and 1?
>
Yes this is a very basic "switch" only managing two physical ports.
Refer Page 28 Chapter 3.7 "Tail Tagging Mode"
in the Reference Manual of the switch:
http://ww1.microchip.com/downloads/en/DeviceDoc/00002335B.pdf
> > + unsigned int len = KSZ_EGRESS_TAG_LEN;
> > +
> > + return ksz_common_rcv(skb, dev, port, len);
> > +}
>
> Andrew
>
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC 1/3] mdio-bitbang: add SMI0 mode support
2019-05-10 7:32 ` Michael Grzeschik
@ 2019-05-10 12:36 ` Andrew Lunn
0 siblings, 0 replies; 10+ messages in thread
From: Andrew Lunn @ 2019-05-10 12:36 UTC (permalink / raw)
To: Michael Grzeschik; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev
> > > +/* 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
> > > + *
> > > + * The register number is encoded with the 5 least significant bits in REG
> > > + * and the 3 most significant bits in PHY
> > > + */
> > > +#define MII_ADDR_SMI0 (1<<31)
> > > +
> >
> > Michael
> >
> > This is a Micrel Proprietary protocol. So we should reflect this in
> > the name. MII_ADDR_MICREL_SMI? Why the 0? Are there different
> > versions? Maybe replace all SMI0 with MICREL_SMI in mdio-bitbang.c
>
> There are two variants of the SMI interface.
Hi Michael
O.K, that explains the 0.
>
> The KSZ8863/73/93 Products use the above Variant described as "SMI0".
>
> The KSZ8864/95 Products use another layout:
>
> preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle
> |frame| OP code |address |address| | |
> read | 32x1´s | 01 | 10 | RR11R | RRRRR |Z0| 00000000DDDDDDDD | Z
> write| 32x1´s | 01 | 01 | RR11R | RRRRR |10| xxxxxxxxDDDDDDDD | Z
>
> So they describe their write/read operation in the OP code rather then
> the PHY address.
At a first look, i think a standard MDIO bus controller can do this?
If so, we don't need a second define, just some code in the switch
driver which shuffles bits around.
>
> We could change the SMI index to SMI_KSZ88X3 for the current SMI0 to
> give it a more descriptive name.
That seems sensible. In the mv88e6xxx driver, we name things based on
the first device to introduce the feature.
Andrew
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches
2019-05-08 21:13 ` [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches Michael Grzeschik
@ 2019-06-13 20:09 ` Rob Herring
0 siblings, 0 replies; 10+ messages in thread
From: Rob Herring @ 2019-06-13 20:09 UTC (permalink / raw)
To: Michael Grzeschik; +Cc: Tristram.Ha, kernel, UNGLinuxDriver, netdev, devicetree
On Wed, May 08, 2019 at 11:13:30PM +0200, Michael Grzeschik wrote:
> Document additional Microchip KSZ8863 family switches.
>
> Show how KSZ8863 switch should be configured as the host port is port 3.
>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
> ---
> .../devicetree/bindings/net/dsa/ksz.txt | 44 +++++++++++++++++++
> 1 file changed, 44 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/dsa/ksz.txt b/Documentation/devicetree/bindings/net/dsa/ksz.txt
> index e7db7268fd0fd..4ac576e1cc34e 100644
> --- a/Documentation/devicetree/bindings/net/dsa/ksz.txt
> +++ b/Documentation/devicetree/bindings/net/dsa/ksz.txt
> @@ -5,6 +5,8 @@ Required properties:
>
> - compatible: For external switch chips, compatible string must be exactly one
> of the following:
> + - "microchip,ksz8863"
> + - "microchip,ksz8873"
> - "microchip,ksz9477"
> - "microchip,ksz9897"
> - "microchip,ksz9896"
> @@ -31,6 +33,48 @@ Ethernet switch connected via SPI to the host, CPU port wired to eth0:
> };
> };
>
> + mdio0: mdio-gpio {
Does this example show something new? Examples don't need to instantiate
every possible option.
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_mdio_1>;
> + compatible = "virtual,mdio-gpio";
> + #address-cells = <1>;
> + #size-cells = <0>;
> + gpios = <&gpio1 31 0 &gpio1 22 0>;
> +
> + ksz8863@3 {
> + compatible = "microchip,ksz8863";
> + interrupt-parrent = <&gpio3>;
> + interrupt = <30 IRQ_TYPE_LEVEL_HIGH>;
> + reg = <0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + ports@0 {
> + reg = <0>;
> + label = "lan1";
> + };
> +
> + ports@1 {
> + reg = <1>;
> + label = "lan2";
> + };
> +
> + ports@2 {
> + reg = <2>;
> + label = "cpu";
> + ethernet = <ð0>;
> +
> + fixed-link {
> + speed = <100>;
> + full-duplex;
> + };
> + };
> + };
> + };
> + };
> +
> spi1: spi@f8008000 {
> pinctrl-0 = <&pinctrl_spi_ksz>;
> cs-gpios = <&pioC 25 0>;
> --
> 2.20.1
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2019-06-13 20:09 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-05-08 21:13 [RFC 0/3] microchip: add support for ksz8863 driver family Michael Grzeschik
2019-05-08 21:13 ` [RFC 1/3] mdio-bitbang: add SMI0 mode support Michael Grzeschik
2019-05-09 14:29 ` Andrew Lunn
2019-05-10 7:32 ` Michael Grzeschik
2019-05-10 12:36 ` Andrew Lunn
2019-05-08 21:13 ` [RFC 2/3] ksz: Add Microchip KSZ8873 SMI-DSA driver Michael Grzeschik
2019-05-09 14:48 ` Andrew Lunn
2019-05-10 7:37 ` Michael Grzeschik
2019-05-08 21:13 ` [RFC 3/3] dt-bindings: net: dsa: document additional Microchip KSZ8863 family switches Michael Grzeschik
2019-06-13 20:09 ` Rob Herring
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).