diff options
45 files changed, 1256 insertions, 647 deletions
diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml index 9f7d131bbcef..84985f53bffd 100644 --- a/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml +++ b/Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml @@ -21,6 +21,8 @@ properties: - microchip,ksz8765 - microchip,ksz8794 - microchip,ksz8795 + - microchip,ksz8863 + - microchip,ksz8873 - microchip,ksz9477 - microchip,ksz9897 - microchip,ksz9896 diff --git a/Documentation/devicetree/bindings/net/mdio-gpio.txt b/Documentation/devicetree/bindings/net/mdio-gpio.txt index 8dbcf8295c6c..4d91a36c5cf5 100644 --- a/Documentation/devicetree/bindings/net/mdio-gpio.txt +++ b/Documentation/devicetree/bindings/net/mdio-gpio.txt @@ -2,6 +2,7 @@ MDIO on GPIOs Currently defined compatibles: - virtual,gpio-mdio +- microchip,mdio-smi0 MDC and MDIO lines connected to GPIO controllers are listed in the gpios property as described in section VIII.1 in the following order: diff --git a/Documentation/networking/timestamping.rst b/Documentation/networking/timestamping.rst index f682e88fa87e..7db3985359bc 100644 --- a/Documentation/networking/timestamping.rst +++ b/Documentation/networking/timestamping.rst @@ -630,30 +630,45 @@ hardware timestamping on it. This is because the SO_TIMESTAMPING API does not allow the delivery of multiple hardware timestamps for the same packet, so anybody else except for the DSA switch port must be prevented from doing so. -In code, DSA provides for most of the infrastructure for timestamping already, -in generic code: a BPF classifier (``ptp_classify_raw``) is used to identify -PTP event messages (any other packets, including PTP general messages, are not -timestamped), and provides two hooks to drivers: - -- ``.port_txtstamp()``: The driver is passed a clone of the timestampable skb - to be transmitted, before actually transmitting it. Typically, a switch will - have a PTP TX timestamp register (or sometimes a FIFO) where the timestamp - becomes available. There may be an IRQ that is raised upon this timestamp's - availability, or the driver might have to poll after invoking - ``dev_queue_xmit()`` towards the host interface. Either way, in the - ``.port_txtstamp()`` method, the driver only needs to save the clone for - later use (when the timestamp becomes available). Each skb is annotated with - a pointer to its clone, in ``DSA_SKB_CB(skb)->clone``, to ease the driver's - job of keeping track of which clone belongs to which skb. - -- ``.port_rxtstamp()``: The original (and only) timestampable skb is provided - to the driver, for it to annotate it with a timestamp, if that is immediately - available, or defer to later. On reception, timestamps might either be - available in-band (through metadata in the DSA header, or attached in other - ways to the packet), or out-of-band (through another RX timestamping FIFO). - Deferral on RX is typically necessary when retrieving the timestamp needs a - sleepable context. In that case, it is the responsibility of the DSA driver - to call ``netif_rx_ni()`` on the freshly timestamped skb. +In the generic layer, DSA provides the following infrastructure for PTP +timestamping: + +- ``.port_txtstamp()``: a hook called prior to the transmission of + packets with a hardware TX timestamping request from user space. + This is required for two-step timestamping, since the hardware + timestamp becomes available after the actual MAC transmission, so the + driver must be prepared to correlate the timestamp with the original + packet so that it can re-enqueue the packet back into the socket's + error queue. To save the packet for when the timestamp becomes + available, the driver can call ``skb_clone_sk`` , save the clone pointer + in skb->cb and enqueue a tx skb queue. Typically, a switch will have a + PTP TX timestamp register (or sometimes a FIFO) where the timestamp + becomes available. In case of a FIFO, the hardware might store + key-value pairs of PTP sequence ID/message type/domain number and the + actual timestamp. To perform the correlation correctly between the + packets in a queue waiting for timestamping and the actual timestamps, + drivers can use a BPF classifier (``ptp_classify_raw``) to identify + the PTP transport type, and ``ptp_parse_header`` to interpret the PTP + header fields. There may be an IRQ that is raised upon this + timestamp's availability, or the driver might have to poll after + invoking ``dev_queue_xmit()`` towards the host interface. + One-step TX timestamping do not require packet cloning, since there is + no follow-up message required by the PTP protocol (because the + TX timestamp is embedded into the packet by the MAC), and therefore + user space does not expect the packet annotated with the TX timestamp + to be re-enqueued into its socket's error queue. + +- ``.port_rxtstamp()``: On RX, the BPF classifier is run by DSA to + identify PTP event messages (any other packets, including PTP general + messages, are not timestamped). The original (and only) timestampable + skb is provided to the driver, for it to annotate it with a timestamp, + if that is immediately available, or defer to later. On reception, + timestamps might either be available in-band (through metadata in the + DSA header, or attached in other ways to the packet), or out-of-band + (through another RX timestamping FIFO). Deferral on RX is typically + necessary when retrieving the timestamp needs a sleepable context. In + that case, it is the responsibility of the DSA driver to call + ``netif_rx_ni()`` on the freshly timestamped skb. 3.2.2 Ethernet PHYs ^^^^^^^^^^^^^^^^^^^ diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c index 69dd9a2e8bb6..40b41c794dfa 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c @@ -373,30 +373,38 @@ long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp) return restart ? 1 : -1; } -bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type) +void hellcreek_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb) { struct hellcreek *hellcreek = ds->priv; struct hellcreek_port_hwtstamp *ps; struct ptp_header *hdr; + struct sk_buff *clone; + unsigned int type; ps = &hellcreek->ports[port].port_hwtstamp; - /* Check if the driver is expected to do HW timestamping */ - if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) - return false; + type = ptp_classify_raw(skb); + if (type == PTP_CLASS_NONE) + return; /* Make sure the message is a PTP message that needs to be timestamped * and the interaction with the HW timestamping is enabled. If not, stop * here */ - hdr = hellcreek_should_tstamp(hellcreek, port, clone, type); + hdr = hellcreek_should_tstamp(hellcreek, port, skb, type); if (!hdr) - return false; + return; + + clone = skb_clone_sk(skb); + if (!clone) + return; if (test_and_set_bit_lock(HELLCREEK_HWTSTAMP_TX_IN_PROGRESS, - &ps->state)) - return false; + &ps->state)) { + kfree_skb(clone); + return; + } ps->tx_skb = clone; @@ -406,8 +414,6 @@ bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, ps->tx_tstamp_start = jiffies; ptp_schedule_worker(hellcreek->ptp_clock, 0); - - return true; } bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h index c0745ffa1ebb..71af77efb28b 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h @@ -44,8 +44,8 @@ int hellcreek_port_hwtstamp_get(struct dsa_switch *ds, int port, bool hellcreek_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *clone, unsigned int type); -bool hellcreek_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type); +void hellcreek_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb); int hellcreek_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *info); diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 4ec6a47b7f72..c9e2a8989556 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -29,7 +29,7 @@ menuconfig NET_DSA_MICROCHIP_KSZ8795 depends on NET_DSA select NET_DSA_MICROCHIP_KSZ_COMMON help - This driver adds support for Microchip KSZ8795 switch chips. + This driver adds support for Microchip KSZ8795/KSZ88X3 switch chips. config NET_DSA_MICROCHIP_KSZ8795_SPI tristate "KSZ8795 series SPI connected switch driver" @@ -40,3 +40,11 @@ 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 + 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 929caa81e782..2a03b21a3386 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 000000000000..9d611895d3cf --- /dev/null +++ b/drivers/net/dsa/microchip/ksz8.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Microchip KSZ8XXX series register access + * + * Copyright (C) 2020 Pengutronix, Michael Grzeschik <kernel@pengutronix.de> + */ + +#ifndef __KSZ8XXX_H +#define __KSZ8XXX_H +#include <linux/kernel.h> + +enum ksz_regs { + REG_IND_CTRL_0, + REG_IND_DATA_8, + REG_IND_DATA_CHECK, + REG_IND_DATA_HI, + REG_IND_DATA_LO, + REG_IND_MIB_CHECK, + P_FORCE_CTRL, + P_LINK_STATUS, + P_LOCAL_CTRL, + P_NEG_RESTART_CTRL, + P_REMOTE_STATUS, + P_SPEED_STATUS, + S_TAIL_TAG_CTRL, +}; + +enum ksz_masks { + PORT_802_1P_REMAPPING, + SW_TAIL_TAG_ENABLE, + MIB_COUNTER_OVERFLOW, + MIB_COUNTER_VALID, + VLAN_TABLE_FID, + VLAN_TABLE_MEMBERSHIP, + VLAN_TABLE_VALID, + STATIC_MAC_TABLE_VALID, + STATIC_MAC_TABLE_USE_FID, + STATIC_MAC_TABLE_FID, + STATIC_MAC_TABLE_OVERRIDE, + STATIC_MAC_TABLE_FWD_PORTS, + DYNAMIC_MAC_TABLE_ENTRIES_H, + DYNAMIC_MAC_TABLE_MAC_EMPTY, + DYNAMIC_MAC_TABLE_NOT_READY, + DYNAMIC_MAC_TABLE_ENTRIES, + DYNAMIC_MAC_TABLE_FID, + DYNAMIC_MAC_TABLE_SRC_PORT, + DYNAMIC_MAC_TABLE_TIMESTAMP, +}; + +enum ksz_shifts { + VLAN_TABLE_MEMBERSHIP_S, + VLAN_TABLE, + STATIC_MAC_FWD_PORTS, + STATIC_MAC_FID, + DYNAMIC_MAC_ENTRIES_H, + DYNAMIC_MAC_ENTRIES, + DYNAMIC_MAC_FID, + DYNAMIC_MAC_TIMESTAMP, + DYNAMIC_MAC_SRC_PORT, +}; + +struct ksz8 { + const u8 *regs; + const u32 *masks; + const u8 *shifts; + void *priv; +}; + +#endif diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index b4b7de63ca79..ad509a57a945 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -20,10 +20,112 @@ #include "ksz_common.h" #include "ksz8795_reg.h" +#include "ksz8.h" + +static const u8 ksz8795_regs[] = { + [REG_IND_CTRL_0] = 0x6E, + [REG_IND_DATA_8] = 0x70, + [REG_IND_DATA_CHECK] = 0x72, + [REG_IND_DATA_HI] = 0x71, + [REG_IND_DATA_LO] = 0x75, + [REG_IND_MIB_CHECK] = 0x74, + [P_FORCE_CTRL] = 0x0C, + [P_LINK_STATUS] = 0x0E, + [P_LOCAL_CTRL] = 0x07, + [P_NEG_RESTART_CTRL] = 0x0D, + [P_REMOTE_STATUS] = 0x08, + [P_SPEED_STATUS] = 0x09, + [S_TAIL_TAG_CTRL] = 0x0C, +}; + +static const u32 ksz8795_masks[] = { + [PORT_802_1P_REMAPPING] = BIT(7), + [SW_TAIL_TAG_ENABLE] = BIT(1), + [MIB_COUNTER_OVERFLOW] = BIT(6), + [MIB_COUNTER_VALID] = BIT(5), + [VLAN_TABLE_FID] = GENMASK(6, 0), + [VLAN_TABLE_MEMBERSHIP] = GENMASK(11, 7), + [VLAN_TABLE_VALID] = BIT(12), + [STATIC_MAC_TABLE_VALID] = BIT(21), + [STATIC_MAC_TABLE_USE_FID] = BIT(23), + [STATIC_MAC_TABLE_FID] = GENMASK(30, 24), + [STATIC_MAC_TABLE_OVERRIDE] = BIT(26), + [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(24, 20), + [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(6, 0), + [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(8), + [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), + [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 29), + [DYNAMIC_MAC_TABLE_FID] = GENMASK(26, 20), + [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(26, 24), + [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(28, 27), +}; + +static const u8 ksz8795_shifts[] = { + [VLAN_TABLE_MEMBERSHIP_S] = 7, + [VLAN_TABLE] = 16, + [STATIC_MAC_FWD_PORTS] = 16, + [STATIC_MAC_FID] = 24, + [DYNAMIC_MAC_ENTRIES_H] = 3, + [DYNAMIC_MAC_ENTRIES] = 29, + [DYNAMIC_MAC_FID] = 16, + [DYNAMIC_MAC_TIMESTAMP] = 27, + [DYNAMIC_MAC_SRC_PORT] = 24, +}; + +static const u8 ksz8863_regs[] = { + [REG_IND_CTRL_0] = 0x79, + [REG_IND_DATA_8] = 0x7B, + [REG_IND_DATA_CHECK] = 0x7B, + [REG_IND_DATA_HI] = 0x7C, + [REG_IND_DATA_LO] = 0x80, + [REG_IND_MIB_CHECK] = 0x80, + [P_FORCE_CTRL] = 0x0C, + [P_LINK_STATUS] = 0x0E, + [P_LOCAL_CTRL] = 0x0C, + [P_NEG_RESTART_CTRL] = 0x0D, + [P_REMOTE_STATUS] = 0x0E, + [P_SPEED_STATUS] = 0x0F, + [S_TAIL_TAG_CTRL] = 0x03, +}; + +static const u32 ksz8863_masks[] = { + [PORT_802_1P_REMAPPING] = BIT(3), + [SW_TAIL_TAG_ENABLE] = BIT(6), + [MIB_COUNTER_OVERFLOW] = BIT(7), + [MIB_COUNTER_VALID] = BIT(6), + [VLAN_TABLE_FID] = GENMASK(15, 12), + [VLAN_TABLE_MEMBERSHIP] = GENMASK(18, 16), + [VLAN_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_USE_FID] = BIT(21), + [STATIC_MAC_TABLE_FID] = GENMASK(29, 26), + [STATIC_MAC_TABLE_OVERRIDE] = BIT(20), + [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16), + [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(5, 0), + [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(7), + [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), + [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 28), + [DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16), + [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20), + [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22), +}; -static const struct { +static u8 ksz8863_shifts[] = { + [VLAN_TABLE_MEMBERSHIP_S] = 16, + [STATIC_MAC_FWD_PORTS] = 16, + [STATIC_MAC_FID] = 22, + [DYNAMIC_MAC_ENTRIES_H] = 3, + [DYNAMIC_MAC_ENTRIES] = 24, + [DYNAMIC_MAC_FID] = 16, + [DYNAMIC_MAC_TIMESTAMP] = 24, + [DYNAMIC_MAC_SRC_PORT] = 20, +}; + +struct mib_names { char string[ETH_GSTRING_LEN]; -} mib_names[] = { +}; + +static const struct mib_names ksz87xx_mib_names[] = { { "rx_hi" }, { "rx_undersize" }, { "rx_fragments" }, @@ -62,6 +164,48 @@ static const struct { { "tx_discards" }, }; +static const struct mib_names ksz88xx_mib_names[] = { + { "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 bool ksz_is_ksz88x3(struct ksz_device *dev) +{ + return dev->chip_id == 0x8830; +} + 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,12 +218,20 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, bits, set ? bits : 0); } -static int ksz8795_reset_switch(struct ksz_device *dev) +static int ksz8_reset_switch(struct ksz_device *dev) { - /* reset switch */ - ksz_write8(dev, REG_POWER_MANAGEMENT_1, - SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S); - ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0); + if (ksz_is_ksz88x3(dev)) { + /* reset switch */ + ksz_cfg(dev, KSZ8863_REG_SW_RESET, + KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true); + ksz_cfg(dev, KSZ8863_REG_SW_RESET, + KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false); + } else { + /* reset switch */ + ksz_write8(dev, REG_POWER_MANAGEMENT_1, + SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S); + ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0); + } return 0; } @@ -117,29 +269,34 @@ 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; + const u32 *masks; + const u8 *regs; u16 ctrl_addr; u32 data; u8 check; int loop; + masks = ksz8->masks; + regs = ksz8->regs; + 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[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); + ksz_read8(dev, regs[REG_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[REG_IND_DATA_LO], &data); + if (check & masks[MIB_COUNTER_OVERFLOW]) *cnt += MIB_COUNTER_VALUE + 1; *cnt += data & MIB_COUNTER_VALUE; break; @@ -151,27 +308,33 @@ static void ksz8795_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { + struct ksz8 *ksz8 = dev->priv; + const u32 *masks; + const u8 *regs; u16 ctrl_addr; u32 data; u8 check; int loop; + masks = ksz8->masks; + regs = ksz8->regs; + addr -= dev->reg_mib_cnt; - ctrl_addr = (KS_MIB_TOTAL_RX_1 - KS_MIB_TOTAL_RX_0) * port; - ctrl_addr += addr + KS_MIB_TOTAL_RX_0; + 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[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); + ksz_read8(dev, regs[REG_IND_MIB_CHECK], &check); - if (check & MIB_COUNTER_VALID) { - ksz_read32(dev, REG_IND_DATA_LO, &data); + if (check & masks[MIB_COUNTER_VALID]) { + ksz_read32(dev, regs[REG_IND_DATA_LO], &data); if (addr < 2) { u64 total; @@ -179,13 +342,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 & masks[MIB_COUNTER_OVERFLOW]) { total = MIB_TOTAL_BYTES_H + 1; total <<= 32; *cnt += total; } } else { - if (check & MIB_COUNTER_OVERFLOW) + if (check & masks[MIB_COUNTER_OVERFLOW]) *cnt += MIB_PACKET_DROPPED + 1; *cnt += data & MIB_PACKET_DROPPED; } @@ -195,8 +358,52 @@ static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, mutex_unlock(&dev->alu_mutex); } -static void ksz8795_freeze_mib(struct ksz_device *dev, int port, bool freeze) +static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) +{ + struct ksz8 *ksz8 = dev->priv; + const u8 *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[REG_IND_CTRL_0], ctrl_addr); + ksz_read32(dev, regs[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 ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) { + if (ksz_is_ksz88x3(dev)) + ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt); + else + ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt); +} + +static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) +{ + if (ksz_is_ksz88x3(dev)) + return; + /* enable the port for flush/freeze function */ if (freeze) ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true); @@ -207,14 +414,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_ksz88x3(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,80 +435,99 @@ 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; + const u8 *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[REG_IND_CTRL_0], ctrl_addr); + ksz_read64(dev, regs[REG_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; + const u8 *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[REG_IND_DATA_HI], data); + ksz_write16(dev, regs[REG_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; int timeout = 100; + const u32 *masks; + const u8 *regs; + + masks = ksz8->masks; + regs = ksz8->regs; do { - ksz_read8(dev, REG_IND_DATA_CHECK, data); + ksz_read8(dev, regs[REG_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[REG_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; u32 data_hi, data_lo; + const u8 *shifts; + const u32 *masks; + const u8 *regs; u16 ctrl_addr; u8 data; int rc; + shifts = ksz8->shifts; + masks = ksz8->masks; + regs = ksz8->regs; + 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[REG_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 +538,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[REG_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,91 +570,128 @@ 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; u32 data_hi, data_lo; + const u8 *shifts; + const u32 *masks; u64 data; - ksz8795_r_table(dev, TABLE_STATIC_MAC, addr, &data); + shifts = ksz8->shifts; + masks = ksz8->masks; + + 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; u32 data_hi, data_lo; + const u8 *shifts; + const u32 *masks; u64 data; + shifts = ksz8->shifts; + masks = ksz8->masks; + 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; + 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; + const u8 *shifts; + const u32 *masks; + + shifts = ksz8->shifts; + masks = ksz8->masks; + + *fid = vlan & masks[VLAN_TABLE_FID]; + *member = (vlan & masks[VLAN_TABLE_MEMBERSHIP]) >> + shifts[VLAN_TABLE_MEMBERSHIP_S]; + *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, + u16 *vlan) { + struct ksz8 *ksz8 = dev->priv; + const u8 *shifts; + const u32 *masks; + + shifts = ksz8->shifts; + masks = ksz8->masks; + *vlan = fid; - *vlan |= (u16)member << VLAN_TABLE_MEMBERSHIP_S; + *vlan |= (u16)member << shifts[VLAN_TABLE_MEMBERSHIP_S]; 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; + const u8 *shifts; u64 data; int i; - ksz8795_r_table(dev, TABLE_VLAN, addr, &data); + shifts = ksz8->shifts; + + ksz8_r_table(dev, TABLE_VLAN, addr, &data); addr *= dev->phy_port_cnt; for (i = 0; i < dev->phy_port_cnt; i++) { dev->vlan_cache[addr + i].table[0] = (u16)data; - data >>= VLAN_TABLE_S; + data >>= shifts[VLAN_TABLE]; } } -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, u16 *vlan) { int index; u16 *data; @@ -435,11 +701,11 @@ static void ksz8795_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan) data = (u16 *)&buf; addr = vid / dev->phy_port_cnt; index = vid & 3; - ksz8795_r_table(dev, TABLE_VLAN, addr, &buf); + ksz8_r_table(dev, TABLE_VLAN, addr, &buf); *vlan = data[index]; } -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, u16 vlan) { int index; u16 *data; @@ -449,30 +715,37 @@ static void ksz8795_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) data = (u16 *)&buf; addr = vid / dev->phy_port_cnt; index = vid & 3; - ksz8795_r_table(dev, TABLE_VLAN, addr, &buf); + ksz8_r_table(dev, TABLE_VLAN, addr, &buf); data[index] = 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; u8 restart, speed, ctrl, link; + const u8 *regs = ksz8->regs; int processed = true; u16 data = 0; u8 p = phy; 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_ksz88x3(dev)) { + if ((ctrl & PORT_AUTO_NEG_ENABLE)) + data |= PHY_AUTO_NEG_ENABLE; + } else { + if (!(ctrl & PORT_AUTO_NEG_DISABLE)) + data |= PHY_AUTO_NEG_ENABLE; + } if (restart & PORT_POWER_DOWN) data |= PHY_POWER_DOWN; if (restart & PORT_AUTO_NEG_RESTART) @@ -491,7 +764,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 | @@ -506,10 +779,13 @@ static void ksz8795_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data = KSZ8795_ID_HI; break; case PHY_REG_ID_2: - data = KSZ8795_ID_LO; + if (ksz_is_ksz88x3(dev)) + data = KSZ8863_ID_LO; + else + data = KSZ8795_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 +799,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 +822,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; u8 restart, speed, ctrl, data; + const u8 *regs = ksz8->regs; + u8 p = phy; switch (reg) { case PHY_REG_CTRL: @@ -557,24 +835,32 @@ 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; + if (ksz_is_ksz88x3(dev)) { + if ((val & PHY_AUTO_NEG_ENABLE)) + data |= PORT_AUTO_NEG_ENABLE; + else + data &= ~PORT_AUTO_NEG_ENABLE; + } else { + 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; + } - /* Fiber port does not support auto-negotiation. */ - if (dev->ports[p].fiber) - data |= PORT_AUTO_NEG_DISABLE; if (val & PHY_SPEED_100MBIT) data |= PORT_FORCE_100_MBIT; else @@ -584,8 +870,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 +902,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,34 +923,37 @@ 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; + + /* ksz88x3 uses the same tag schema as KSZ9893 */ + return ksz_is_ksz88x3(dev) ? + DSA_TAG_PROTO_KSZ9893 : DSA_TAG_PROTO_KSZ8795; } -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) { struct ksz_device *dev = ds->priv; int i; for (i = 0; i < dev->mib_cnt; i++) { - memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string, - ETH_GSTRING_LEN); + memcpy(buf + i * ETH_GSTRING_LEN, + dev->mib_names[i].string, ETH_GSTRING_LEN); } } -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) { u8 data; @@ -675,8 +964,7 @@ static void ksz8795_cfg_port_member(struct ksz_device *dev, int port, 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; @@ -734,7 +1022,7 @@ static void ksz8795_port_stp_state_set(struct dsa_switch *ds, int port, p->stp_state = state; /* 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) { @@ -749,7 +1037,7 @@ 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[DSA_MAX_PORTS]; int first, index, cnt; @@ -782,30 +1070,35 @@ static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port) } } -static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port, - bool flag, - struct netlink_ext_ack *extack) +static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, + struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; + if (ksz_is_ksz88x3(dev)) + return -ENOTSUPP; + ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag); return 0; } -static int ksz8795_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) +static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; struct ksz_device *dev = ds->priv; u16 data, new_pvid = 0; u8 fid, member, valid; + if (ksz_is_ksz88x3(dev)) + return -ENOTSUPP; + ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); - ksz8795_r_vlan_table(dev, vlan->vid, &data); - ksz8795_from_vlan(data, &fid, &member, &valid); + ksz8_r_vlan_table(dev, vlan->vid, &data); + ksz8_from_vlan(dev, data, &fid, &member, &valid); /* First time to setup the VLAN entry. */ if (!valid) { @@ -815,8 +1108,8 @@ static int 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, vlan->vid, data); + ksz8_to_vlan(dev, fid, member, valid, &data); + ksz8_w_vlan_table(dev, vlan->vid, data); /* change PVID */ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) @@ -834,21 +1127,24 @@ static int ksz8795_port_vlan_add(struct dsa_switch *ds, int port, return 0; } -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, pvid, new_pvid = 0; u8 fid, member, valid; + if (ksz_is_ksz88x3(dev)) + return -ENOTSUPP; + ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); pvid = pvid & 0xFFF; ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); - ksz8795_r_vlan_table(dev, vlan->vid, &data); - ksz8795_from_vlan(data, &fid, &member, &valid); + ksz8_r_vlan_table(dev, vlan->vid, &data); + ksz8_from_vlan(dev, data, &fid, &member, &valid); member &= ~BIT(port); @@ -861,8 +1157,8 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port, if (pvid == vlan->vid) new_pvid = 1; - ksz8795_to_vlan(fid, member, valid, &data); - ksz8795_w_vlan_table(dev, vlan->vid, data); + ksz8_to_vlan(dev, fid, member, valid, &data); + ksz8_w_vlan_table(dev, vlan->vid, data); if (new_pvid != pvid) ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid); @@ -870,9 +1166,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; @@ -894,8 +1190,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; @@ -915,91 +1211,111 @@ 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; + + if (!p->interface && dev->compat_interface) { + dev_warn(dev->dev, + "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. " + "Please update your device tree.\n", + port); + p->interface = dev->compat_interface; + } + + /* 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 (p->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 (p->interface == PHY_INTERFACE_MODE_RGMII_ID || + p->interface == PHY_INTERFACE_MODE_RGMII_RXID) + data8 |= PORT_RGMII_ID_IN_ENABLE; + if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || + p->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]; - u8 data8, member; + struct ksz8 *ksz8 = dev->priv; + const u32 *masks; + u8 member; + + masks = ksz8->masks; /* 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_ksz88x3(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) { - if (!p->interface && dev->compat_interface) { - dev_warn(dev->dev, - "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. " - "Please update your device tree.\n", - port); - p->interface = dev->compat_interface; - } - - /* 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 (p->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 (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_RXID) - data8 |= PORT_RGMII_ID_IN_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->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_ksz88x3(dev)) + ksz8795_cpu_interface_select(dev, port); member = dev->port_mask; } else { member = dev->host_mask | p->vid_member; } - 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; + const u8 *regs = ksz8->regs; struct ksz_port *p; + const u32 *masks; u8 remote; int i; + masks = ksz8->masks; + /* 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 < dev->phy_port_cnt; i++) { @@ -1010,7 +1326,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->phy_port_cnt) @@ -1022,9 +1338,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_ksz88x3(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); @@ -1034,7 +1352,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; @@ -1045,7 +1363,7 @@ static int ksz8795_setup(struct dsa_switch *ds) if (!dev->vlan_cache) return -ENOMEM; - ret = ksz8795_reset_switch(dev); + ret = ksz8_reset_switch(dev); if (ret) { dev_err(ds->dev, "failed to reset switch\n"); return ret; @@ -1068,7 +1386,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); @@ -1083,7 +1401,7 @@ static int ksz8795_setup(struct dsa_switch *ds) BROADCAST_STORM_PROT_RATE) / 100); for (i = 0; i < (dev->num_vlans / 4); i++) - ksz8795_r_vlan_entries(dev, i); + ksz8_r_vlan_entries(dev, i); /* Setup STP address for STP operation. */ memset(&alu, 0, sizeof(alu)); @@ -1092,7 +1410,7 @@ 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); @@ -1101,36 +1419,36 @@ static int ksz8795_setup(struct dsa_switch *ds) 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, .phylink_mac_link_down = ksz_mac_link_down, .port_enable = ksz_enable_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_add = ksz8795_port_vlan_add, - .port_vlan_del = ksz8795_port_vlan_del, + .port_vlan_filtering = ksz8_port_vlan_filtering, + .port_vlan_add = ksz8_port_vlan_add, + .port_vlan_del = ksz8_port_vlan_del, .port_fdb_dump = ksz_port_fdb_dump, .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; @@ -1143,19 +1461,30 @@ 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; - if (id2 == CHIP_ID_95) { - u8 val; + switch (id1) { + case KSZ87_FAMILY_ID: + if ((id2 != CHIP_ID_94 && id2 != CHIP_ID_95)) + return -ENODEV; + + 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) { - id2 = 0x94; + id2 = 0x95; + ksz_read8(dev, REG_PORT_STATUS_0, &val); + if (val & PORT_FIBER_MODE) + id2 = 0x65; + } else if (id2 == CHIP_ID_94) { + id2 = 0x94; + } + break; + case KSZ88_FAMILY_ID: + if (id2 != CHIP_ID_63) + return -ENODEV; + break; + default: + dev_err(dev->dev, "invalid family id: %d\n", id1); + return -ENODEV; } id16 &= ~0xff; id16 |= id2; @@ -1174,7 +1503,7 @@ struct ksz_chip_data { int port_cnt; }; -static const struct ksz_chip_data ksz8795_switch_chips[] = { +static const struct ksz_chip_data ksz8_switch_chips[] = { { .chip_id = 0x8795, .dev_name = "KSZ8795", @@ -1216,16 +1545,26 @@ static const struct ksz_chip_data ksz8795_switch_chips[] = { .cpu_ports = 0x10, /* can be configured as cpu port */ .port_cnt = 5, /* total cpu and user ports */ }, + { + .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 = 3, + }, }; -static int ksz8795_switch_init(struct ksz_device *dev) +static int ksz8_switch_init(struct ksz_device *dev) { + struct ksz8 *ksz8 = dev->priv; int i; - 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]; + for (i = 0; i < ARRAY_SIZE(ksz8_switch_chips); i++) { + const struct ksz_chip_data *chip = &ksz8_switch_chips[i]; if (dev->chip_id == chip->chip_id) { dev->name = chip->dev_name; @@ -1247,8 +1586,21 @@ static int ksz8795_switch_init(struct ksz_device *dev) if (!dev->cpu_ports) return -ENODEV; - dev->reg_mib_cnt = KSZ8795_COUNTER_NUM; - dev->mib_cnt = ARRAY_SIZE(mib_names); + if (ksz_is_ksz88x3(dev)) { + ksz8->regs = ksz8863_regs; + ksz8->masks = ksz8863_masks; + ksz8->shifts = ksz8863_shifts; + dev->mib_cnt = ARRAY_SIZE(ksz88xx_mib_names); + dev->mib_names = ksz88xx_mib_names; + } else { + ksz8->regs = ksz8795_regs; + ksz8->masks = ksz8795_masks; + ksz8->shifts = ksz8795_shifts; + dev->mib_cnt = ARRAY_SIZE(ksz87xx_mib_names); + dev->mib_names = ksz87xx_mib_names; + } + + dev->reg_mib_cnt = MIB_COUNTER_NUM; dev->ports = devm_kzalloc(dev->dev, dev->port_cnt * sizeof(struct ksz_port), @@ -1272,36 +1624,36 @@ 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); + ksz8_reset_switch(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, - .r_mib_pkt = ksz8795_r_mib_pkt, - .freeze_mib = ksz8795_freeze_mib, - .port_init_cnt = ksz8795_port_init_cnt, - .shutdown = ksz8795_reset_switch, - .detect = ksz8795_switch_detect, - .init = ksz8795_switch_init, - .exit = ksz8795_switch_exit, +static const struct ksz_dev_ops ksz8_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 = ksz8_r_mib_pkt, + .freeze_mib = ksz8_freeze_mib, + .port_init_cnt = ksz8_port_init_cnt, + .shutdown = ksz8_reset_switch, + .detect = ksz8_switch_detect, + .init = ksz8_switch_init, + .exit = ksz8_switch_exit, }; -int ksz8795_switch_register(struct ksz_device *dev) +int ksz8_switch_register(struct ksz_device *dev) { - return ksz_switch_register(dev, &ksz8795_dev_ops); + return ksz_switch_register(dev, &ksz8_dev_ops); } -EXPORT_SYMBOL(ksz8795_switch_register); +EXPORT_SYMBOL(ksz8_switch_register); MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver"); diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index 40372047d40d..c2e52c40a54c 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -16,7 +16,8 @@ #define REG_CHIP_ID0 0x00 -#define FAMILY_ID 0x87 +#define KSZ87_FAMILY_ID 0x87 +#define KSZ88_FAMILY_ID 0x88 #define REG_CHIP_ID1 0x01 @@ -28,6 +29,12 @@ #define CHIP_ID_94 0x60 #define CHIP_ID_95 0x90 +#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 REG_SW_CTRL_0 0x02 @@ -98,7 +105,6 @@ #define REG_SW_CTRL_10 0x0C -#define SW_TAIL_TAG_ENABLE BIT(1) #define SW_PASS_PAUSE BIT(0) #define REG_SW_CTRL_11 0x0D @@ -150,7 +156,6 @@ #define REG_PORT_4_CTRL_2 0x42 #define REG_PORT_5_CTRL_2 0x52 -#define 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 +274,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) @@ -319,14 +325,12 @@ #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 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 @@ -356,8 +360,6 @@ #define REG_SW_MAC_ADDR_4 0x6C #define REG_SW_MAC_ADDR_5 0x6D -#define REG_IND_CTRL_0 0x6E - #define TABLE_EXT_SELECT_S 5 #define TABLE_EEE_V 1 #define TABLE_ACL_V 2 @@ -383,23 +385,13 @@ #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 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 REG_INT_STATUS 0x7C #define REG_INT_ENABLE 0x7D @@ -816,6 +808,7 @@ #define KSZ8795_ID_HI 0x0022 #define KSZ8795_ID_LO 0x1550 +#define KSZ8863_ID_LO 0x1430 #define KSZ8795_SW_ID 0x8795 @@ -846,7 +839,7 @@ #define KS_PRIO_IN_REG 4 -#define KSZ8795_COUNTER_NUM 0x20 +#define MIB_COUNTER_NUM 0x20 /* Common names used by other drivers */ @@ -856,12 +849,6 @@ #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 @@ -876,7 +863,6 @@ #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 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 @@ -890,65 +876,6 @@ #define BROADCAST_STORM_VALUE 9969 /** - * STATIC_MAC_TABLE_ADDR 00-0000FFFF-FFFFFFFF - * STATIC_MAC_TABLE_FWD_PORTS 00-001F0000-00000000 - * STATIC_MAC_TABLE_VALID 00-00200000-00000000 - * STATIC_MAC_TABLE_OVERRIDE 00-00400000-00000000 - * STATIC_MAC_TABLE_USE_FID 00-00800000-00000000 - * 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 STATIC_MAC_FWD_PORTS_S 16 -#define STATIC_MAC_FID_S 24 - -/** - * VLAN_TABLE_FID 00-007F007F-007F007F - * VLAN_TABLE_MEMBERSHIP 00-0F800F80-0F800F80 - * VLAN_TABLE_VALID 00-10001000-10001000 - */ - -#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 16 - -/** - * DYNAMIC_MAC_TABLE_ADDR 00-0000FFFF-FFFFFFFF - * DYNAMIC_MAC_TABLE_FID 00-007F0000-00000000 - * DYNAMIC_MAC_TABLE_NOT_READY 00-00800000-00000000 - * DYNAMIC_MAC_TABLE_SRC_PORT 00-07000000-00000000 - * DYNAMIC_MAC_TABLE_TIMESTAMP 00-18000000-00000000 - * DYNAMIC_MAC_TABLE_ENTRIES 7F-E0000000-00000000 - * 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 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 - -/** * MIB_COUNTER_VALUE 00-00000000-3FFFFFFF * MIB_TOTAL_BYTES 00-0000000F-FFFFFFFF * MIB_PACKET_DROPPED 00-00000000-0000FFFF @@ -956,31 +883,15 @@ * MIB_COUNTER_OVERFLOW 00-00000040-00000000 */ -#define MIB_COUNTER_OVERFLOW BIT(6) -#define 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_TOTAL_RX_1 0x104 +#define KSZ8795_MIB_TOTAL_TX_1 0x105 + +#define KSZ8863_MIB_PACKET_DROPPED_TX_0 0x100 +#define KSZ8863_MIB_PACKET_DROPPED_RX_0 0x105 #define MIB_PACKET_DROPPED 0x0000FFFF diff --git a/drivers/net/dsa/microchip/ksz8795_spi.c b/drivers/net/dsa/microchip/ksz8795_spi.c index f98432a3e2b5..85ba12aa82d8 100644 --- a/drivers/net/dsa/microchip/ksz8795_spi.c +++ b/drivers/net/dsa/microchip/ksz8795_spi.c @@ -14,34 +14,52 @@ #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 int ksz8795_spi_probe(struct spi_device *spi) { + const struct regmap_config *regmap_config; + struct device *ddev = &spi->dev; struct regmap_config rc; struct ksz_device *dev; - int i, ret; + struct ksz8 *ksz8; + 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 = device_get_match_data(ddev); + if (!regmap_config) + return -EINVAL; + 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; } } @@ -55,7 +73,7 @@ static int ksz8795_spi_probe(struct spi_device *spi) if (ret) return ret; - ret = ksz8795_switch_register(dev); + ret = ksz8_switch_register(dev); /* Main DSA driver may not be started yet. */ if (ret) @@ -85,9 +103,11 @@ static void ksz8795_spi_shutdown(struct spi_device *spi) } static const struct of_device_id ksz8795_dt_ids[] = { - { .compatible = "microchip,ksz8765" }, - { .compatible = "microchip,ksz8794" }, - { .compatible = "microchip,ksz8795" }, + { .compatible = "microchip,ksz8765", .data = &ksz8795_regmap_config }, + { .compatible = "microchip,ksz8794", .data = &ksz8795_regmap_config }, + { .compatible = "microchip,ksz8795", .data = &ksz8795_regmap_config }, + { .compatible = "microchip,ksz8863", .data = &ksz8863_regmap_config }, + { .compatible = "microchip,ksz8873", .data = &ksz8863_regmap_config }, {}, }; MODULE_DEVICE_TABLE(of, ksz8795_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c new file mode 100644 index 000000000000..30d97ea7a949 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip KSZ8863 series register access through SMI + * + * Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@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 + * + */ + +#define SMI_KSZ88XX_READ_PHY BIT(4) + +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 = ctx; + struct mdio_device *mdev; + u8 reg = *(u8 *)reg_buf; + u8 *val = val_buf; + struct ksz8 *ksz8; + int i, ret = 0; + + ksz8 = dev->priv; + mdev = ksz8->priv; + + mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); + for (i = 0; i < val_len; i++) { + int tmp = reg + i; + + ret = __mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) | + SMI_KSZ88XX_READ_PHY, tmp); + if (ret < 0) + goto out; + + val[i] = ret; + } + ret = 0; + + out: + mutex_unlock(&mdev->bus->mdio_lock); + + return ret; +} + +static int ksz8863_mdio_write(void *ctx, const void *data, size_t count) +{ + struct ksz_device *dev = ctx; + struct mdio_device *mdev; + struct ksz8 *ksz8; + int i, ret = 0; + u32 reg; + u8 *val; + + ksz8 = dev->priv; + mdev = ksz8->priv; + + val = (u8 *)(data + 4); + reg = *(u32 *)data; + + mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); + 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) + goto out; + } + + out: + mutex_unlock(&mdev->bus->mdio_lock); + + return ret; +} + +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, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + }, + { + .name = "#16", + .reg_bits = 8, + .pad_bits = 24, + .val_bits = 16, + .cache_type = REGCACHE_NONE, + .use_single_read = 1, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + }, + { + .name = "#32", + .reg_bits = 8, + .pad_bits = 24, + .val_bits = 32, + .cache_type = REGCACHE_NONE, + .use_single_read = 1, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + } +}; + +static int ksz8863_smi_probe(struct mdio_device *mdiodev) +{ + struct regmap_config rc; + struct ksz_device *dev; + struct ksz8 *ksz8; + 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++) { + rc = ksz8863_regmap_config[i]; + rc.lock_arg = &dev->regmap_mutex; + dev->regmap[i] = devm_regmap_init(&mdiodev->dev, + ®map_smi[i], dev, + &rc); + 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 = ksz8_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_common.h b/drivers/net/dsa/microchip/ksz_common.h index f212775372ce..e0bbdca64375 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -71,6 +71,7 @@ struct ksz_device { int port_cnt; int reg_mib_cnt; int mib_cnt; + const struct mib_names *mib_names; phy_interface_t compat_interface; u32 regs_size; bool phy_errata_9477; @@ -142,7 +143,7 @@ int ksz_switch_register(struct ksz_device *dev, const struct ksz_dev_ops *ops); void ksz_switch_remove(struct ksz_device *dev); -int ksz8795_switch_register(struct ksz_device *dev); +int ksz8_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); diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index 094d17a1d037..8f74ffc7a279 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -468,30 +468,38 @@ long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) return restart ? 1 : -1; } -bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type) +void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb) { struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; struct ptp_header *hdr; + struct sk_buff *clone; + unsigned int type; - if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) - return false; + type = ptp_classify_raw(skb); + if (type == PTP_CLASS_NONE) + return; - hdr = mv88e6xxx_should_tstamp(chip, port, clone, type); + hdr = mv88e6xxx_should_tstamp(chip, port, skb, type); if (!hdr) - return false; + return; + + clone = skb_clone_sk(skb); + if (!clone) + return; if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, - &ps->state)) - return false; + &ps->state)) { + kfree_skb(clone); + return; + } ps->tx_skb = clone; ps->tx_tstamp_start = jiffies; ps->tx_seq_id = be16_to_cpu(hdr->sequence_id); ptp_schedule_worker(chip->ptp_clock, 0); - return true; } int mv88e6165_global_disable(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h index 9da9f197ba02..cf7fb6d660b1 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h @@ -117,8 +117,8 @@ int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port, bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *clone, unsigned int type); -bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type); +void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb); int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *info); @@ -151,11 +151,9 @@ static inline bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, return false; } -static inline bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, - unsigned int type) +static inline void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb) { - return false; } static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 470856bcd2f3..e4fbef81bc52 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -443,15 +443,15 @@ int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { /* There are no configurable serdes lanes on this switch chip but we - * need to return non-zero so that callers of + * need to return a non-negative lane number so that callers of * mv88e6xxx_serdes_get_lane() know this is a serdes port. */ switch (chip->ports[port].cmode) { case MV88E6185_PORT_STS_CMODE_SERDES: case MV88E6185_PORT_STS_CMODE_1000BASE_X: - return 0xff; - default: return 0; + default: + return -ENODEV; } } diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 6b5442be0230..ce607fbaaa3a 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1395,19 +1395,20 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port, return false; } -static bool felix_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type) +static void felix_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb) { struct ocelot *ocelot = ds->priv; - struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct sk_buff *clone = NULL; - if (ocelot->ptp && (skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP) && - ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { - ocelot_port_add_txtstamp_skb(ocelot, port, clone); - return true; - } + if (!ocelot->ptp) + return; - return false; + if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) + return; + + if (clone) + OCELOT_SKB_CB(skb)->clone = clone; } static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index d9c198ca0197..405024b637d6 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -3137,7 +3137,7 @@ static void sja1105_port_deferred_xmit(struct kthread_work *work) struct sk_buff *skb; while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { - struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone; mutex_lock(&priv->mgmt_lock); diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 1b90570b257b..0bc566b9e958 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -431,20 +431,24 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, return true; } -/* Called from dsa_skb_tx_timestamp. This callback is just to make DSA clone - * the skb and have it available in DSA_SKB_CB in the .port_deferred_xmit +/* Called from dsa_skb_tx_timestamp. This callback is just to clone + * the skb and have it available in SJA1105_SKB_CB in the .port_deferred_xmit * callback, where we will timestamp it synchronously. */ -bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *skb, unsigned int type) +void sja1105_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) { struct sja1105_private *priv = ds->priv; struct sja1105_port *sp = &priv->ports[port]; + struct sk_buff *clone; if (!sp->hwts_tx_en) - return false; + return; - return true; + clone = skb_clone_sk(skb); + if (!clone) + return; + + SJA1105_SKB_CB(skb)->clone = clone; } static int sja1105_ptp_reset(struct dsa_switch *ds) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 3daa33e98e77..34f97f58a355 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -104,8 +104,8 @@ void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot, bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb, unsigned int type); -bool sja1105_port_txtstamp(struct dsa_switch *ds, int port, - struct sk_buff *skb, unsigned int type); +void sja1105_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb); int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr); diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 8d06ffaf318a..0c4283319d7f 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -6,6 +6,7 @@ */ #include <linux/dsa/ocelot.h> #include <linux/if_bridge.h> +#include <linux/ptp_classify.h> #include <soc/mscc/ocelot_vcap.h> #include "ocelot.h" #include "ocelot_vcap.h" @@ -530,22 +531,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_port_disable); -void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, - struct sk_buff *clone) +static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, + struct sk_buff *clone) { struct ocelot_port *ocelot_port = ocelot->ports[port]; spin_lock(&ocelot_port->ts_id_lock); skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; - /* Store timestamp ID in cb[0] of sk_buff */ - clone->cb[0] = ocelot_port->ts_id; + /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ + OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id; ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4; skb_queue_tail(&ocelot_port->tx_skbs, clone); spin_unlock(&ocelot_port->ts_id_lock); } -EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); + +u32 ocelot_ptp_rew_op(struct sk_buff *skb) +{ + struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone; + u8 ptp_cmd = OCELOT_SKB_CB(skb)->ptp_cmd; + u32 rew_op = 0; + + if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP && clone) { + rew_op = ptp_cmd; + rew_op |= OCELOT_SKB_CB(clone)->ts_id << 3; + } else if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { + rew_op = ptp_cmd; + } + + return rew_op; +} +EXPORT_SYMBOL(ocelot_ptp_rew_op); + +static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb) +{ + struct ptp_header *hdr; + unsigned int ptp_class; + u8 msgtype, twostep; + + ptp_class = ptp_classify_raw(skb); + if (ptp_class == PTP_CLASS_NONE) + return false; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + return false; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + twostep = hdr->flag_field[0] & 0x2; + + if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0) + return true; + + return false; +} + +int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port, + struct sk_buff *skb, + struct sk_buff **clone) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 ptp_cmd = ocelot_port->ptp_cmd; + + /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */ + if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { + if (ocelot_ptp_is_onestep_sync(skb)) { + OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; + return 0; + } + + /* Fall back to two-step timestamping */ + ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + } + + if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { + *clone = skb_clone_sk(skb); + if (!(*clone)) + return -ENOMEM; + + ocelot_port_add_txtstamp_skb(ocelot, port, *clone); + OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; + } + + return 0; +} +EXPORT_SYMBOL(ocelot_port_txtstamp_request); static void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts) @@ -604,7 +675,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) spin_lock_irqsave(&port->tx_skbs.lock, flags); skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { - if (skb->cb[0] != id) + if (OCELOT_SKB_CB(skb)->ts_id != id) continue; __skb_unlink(skb, &port->tx_skbs); skb_match = skb; diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 36f32a4d9b0f..aad33d22c33f 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -507,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) /* Check if timestamping is needed */ if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { - rew_op = ocelot_port->ptp_cmd; + struct sk_buff *clone = NULL; - if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { - struct sk_buff *clone; - - clone = skb_clone_sk(skb); - if (!clone) { - kfree_skb(skb); - return NETDEV_TX_OK; - } + if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } - ocelot_port_add_txtstamp_skb(ocelot, port, clone); + if (clone) + OCELOT_SKB_CB(skb)->clone = clone; - rew_op |= clone->cb[0] << 3; - } + rew_op = ocelot_ptp_rew_op(skb); } ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); diff --git a/drivers/net/fddi/skfp/h/smc.h b/drivers/net/fddi/skfp/h/smc.h index 706fa619b703..3814a2ff64ae 100644 --- a/drivers/net/fddi/skfp/h/smc.h +++ b/drivers/net/fddi/skfp/h/smc.h @@ -228,7 +228,7 @@ struct s_phy { u_char timer1_exp ; u_char timer2_exp ; u_char pcm_pad1[1] ; - int cem_pst ; /* CEM privae state; used for dual homing */ + int cem_pst ; /* CEM private state; used for dual homing */ struct lem_counter lem ; #ifdef AMDPLC struct s_plc plc ; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 7427b989607e..1b998aa481f8 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -272,25 +272,22 @@ static void macvlan_broadcast(struct sk_buff *skb, if (skb->protocol == htons(ETH_P_PAUSE)) return; - for (i = 0; i < MACVLAN_HASH_SIZE; i++) { - hlist_for_each_entry_rcu(vlan, &port->vlan_hash[i], hlist) { - if (vlan->dev == src || !(vlan->mode & mode)) - continue; + hash_for_each_rcu(port->vlan_hash, i, vlan, hlist) { + if (vlan->dev == src || !(vlan->mode & mode)) + continue; - hash = mc_hash(vlan, eth->h_dest); - if (!test_bit(hash, vlan->mc_filter)) - continue; + hash = mc_hash(vlan, eth->h_dest); + if (!test_bit(hash, vlan->mc_filter)) + continue; - err = NET_RX_DROP; - nskb = skb_clone(skb, GFP_ATOMIC); - if (likely(nskb)) - err = macvlan_broadcast_one( - nskb, vlan, eth, + err = NET_RX_DROP; + nskb = skb_clone(skb, GFP_ATOMIC); + if (likely(nskb)) + err = macvlan_broadcast_one(nskb, vlan, eth, mode == MACVLAN_MODE_BRIDGE) ?: - netif_rx_ni(nskb); - macvlan_count_rx(vlan, skb->len + ETH_HLEN, - err == NET_RX_SUCCESS, true); - } + netif_rx_ni(nskb); + macvlan_count_rx(vlan, skb->len + ETH_HLEN, + err == NET_RX_SUCCESS, true); } } @@ -380,20 +377,14 @@ err: static void macvlan_flush_sources(struct macvlan_port *port, struct macvlan_dev *vlan) { + struct macvlan_source_entry *entry; + struct hlist_node *next; int i; - for (i = 0; i < MACVLAN_HASH_SIZE; i++) { - struct hlist_node *h, *n; - - hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) { - struct macvlan_source_entry *entry; + hash_for_each_safe(port->vlan_source_hash, i, next, entry, hlist) + if (entry->vlan == vlan) + macvlan_hash_del_source(entry); - entry = hlist_entry(h, struct macvlan_source_entry, - hlist); - if (entry->vlan == vlan) - macvlan_hash_del_source(entry); - } - } vlan->macaddr_count = 0; } diff --git a/drivers/net/mdio/mdio-bitbang.c b/drivers/net/mdio/mdio-bitbang.c index 0f457c436335..07609114a26b 100644 --- a/drivers/net/mdio/mdio-bitbang.c +++ b/drivers/net/mdio/mdio-bitbang.c @@ -158,7 +158,7 @@ int mdiobb_read(struct mii_bus *bus, int phy, int reg) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg); ctrl->ops->set_mdio_dir(ctrl, 0); @@ -190,7 +190,7 @@ int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg); /* send the turnaround (10) */ mdiobb_send_bit(ctrl, 1); @@ -217,6 +217,10 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) bus->read = mdiobb_read; bus->write = mdiobb_write; bus->priv = ctrl; + if (!ctrl->override_op_c22) { + ctrl->op_c22_read = MDIO_READ; + ctrl->op_c22_write = MDIO_WRITE; + } return bus; } diff --git a/drivers/net/mdio/mdio-gpio.c b/drivers/net/mdio/mdio-gpio.c index 56c8f914f893..0fb3c2de0845 100644 --- a/drivers/net/mdio/mdio-gpio.c +++ b/drivers/net/mdio/mdio-gpio.c @@ -132,6 +132,13 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask; } + if (dev->of_node && + of_device_is_compatible(dev->of_node, "microchip,mdio-smi0")) { + bitbang->ctrl.op_c22_read = 0; + bitbang->ctrl.op_c22_write = 0; + bitbang->ctrl.override_op_c22 = 1; + } + dev_set_drvdata(dev, new_bus); return new_bus; @@ -196,6 +203,7 @@ static int mdio_gpio_remove(struct platform_device *pdev) static const struct of_device_id mdio_gpio_of_match[] = { { .compatible = "virtual,mdio-gpio", }, + { .compatible = "microchip,mdio-smi0" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mdio_gpio_of_match); diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c index 9b9ac3ef735d..d8b31d4d2a73 100644 --- a/drivers/net/phy/marvell-88x2222.c +++ b/drivers/net/phy/marvell-88x2222.c @@ -473,8 +473,6 @@ static int mv2222_config_init(struct phy_device *phydev) if (phydev->interface != PHY_INTERFACE_MODE_XAUI) return -EINVAL; - phydev->autoneg = AUTONEG_DISABLE; - return 0; } diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h index dd93735ae228..1eb84562b311 100644 --- a/include/linux/dsa/sja1105.h +++ b/include/linux/dsa/sja1105.h @@ -47,11 +47,12 @@ struct sja1105_tagger_data { }; struct sja1105_skb_cb { + struct sk_buff *clone; u32 meta_tstamp; }; #define SJA1105_SKB_CB(skb) \ - ((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb)) + ((struct sja1105_skb_cb *)((skb)->cb)) struct sja1105_port { u16 subvlan_map[DSA_8021Q_N_SUBVLAN]; diff --git a/include/linux/mdio-bitbang.h b/include/linux/mdio-bitbang.h index aca4dc037b70..373630fe5c28 100644 --- a/include/linux/mdio-bitbang.h +++ b/include/linux/mdio-bitbang.h @@ -33,6 +33,9 @@ struct mdiobb_ops { struct mdiobb_ctrl { const struct mdiobb_ops *ops; + unsigned int override_op_c22; + u8 op_c22_read; + u8 op_c22_write; }; int mdiobb_read(struct mii_bus *bus, int phy, int reg); diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 18f783dcd55f..78ea3e332688 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -233,7 +233,6 @@ void ipv6_mc_unmap(struct inet6_dev *idev); void ipv6_mc_remap(struct inet6_dev *idev); void ipv6_mc_init_dev(struct inet6_dev *idev); void ipv6_mc_destroy_dev(struct inet6_dev *idev); -int ipv6_mc_check_icmpv6(struct sk_buff *skb); int ipv6_mc_check_mld(struct sk_buff *skb); void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp); diff --git a/include/net/dsa.h b/include/net/dsa.h index 507082959aa4..e1a2610a0e06 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -117,20 +117,6 @@ struct dsa_netdevice_ops { #define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \ MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE)) -struct dsa_skb_cb { - struct sk_buff *clone; -}; - -struct __dsa_skb_cb { - struct dsa_skb_cb cb; - u8 priv[48 - sizeof(struct dsa_skb_cb)]; -}; - -#define DSA_SKB_CB(skb) ((struct dsa_skb_cb *)((skb)->cb)) - -#define DSA_SKB_CB_PRIV(skb) \ - ((void *)(skb)->cb + offsetof(struct __dsa_skb_cb, priv)) - struct dsa_switch_tree { struct list_head list; @@ -740,8 +726,8 @@ struct dsa_switch_ops { struct ifreq *ifr); int (*port_hwtstamp_set)(struct dsa_switch *ds, int port, struct ifreq *ifr); - bool (*port_txtstamp)(struct dsa_switch *ds, int port, - struct sk_buff *clone, unsigned int type); + void (*port_txtstamp)(struct dsa_switch *ds, int port, + struct sk_buff *skb); bool (*port_rxtstamp)(struct dsa_switch *ds, int port, struct sk_buff *skb, unsigned int type); diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 68cdc7ceaf4d..2f5ce4d4fdbf 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -689,6 +689,15 @@ struct ocelot_policer { u32 burst; /* bytes */ }; +struct ocelot_skb_cb { + struct sk_buff *clone; + u8 ptp_cmd; + u8 ts_id; +}; + +#define OCELOT_SKB_CB(skb) \ + ((struct ocelot_skb_cb *)((skb)->cb)) + #define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) #define ocelot_read_gix(ocelot, reg, gi) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi)) #define ocelot_read_rix(ocelot, reg, ri) __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri)) @@ -740,15 +749,16 @@ u32 __ocelot_target_read_ix(struct ocelot *ocelot, enum ocelot_target target, void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target, u32 val, u32 reg, u32 offset); -/* Packet I/O */ #if IS_ENABLED(CONFIG_MSCC_OCELOT_SWITCH_LIB) +/* Packet I/O */ bool ocelot_can_inject(struct ocelot *ocelot, int grp); void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp, u32 rew_op, struct sk_buff *skb); int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb); void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp); +u32 ocelot_ptp_rew_op(struct sk_buff *skb); #else static inline bool ocelot_can_inject(struct ocelot *ocelot, int grp) @@ -772,6 +782,10 @@ static inline void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp) { } +static inline u32 ocelot_ptp_rew_op(struct sk_buff *skb) +{ + return 0; +} #endif /* Hardware initialization */ @@ -820,8 +834,9 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid); int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr); int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr); -void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, - struct sk_buff *clone); +int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port, + struct sk_buff *skb, + struct sk_buff **clone); void ocelot_get_txtstamp(struct ocelot *ocelot); void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu); int ocelot_get_max_mtu(struct ocelot *ocelot, int port); diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 2883601d5c8b..226bb05c3b42 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -3159,25 +3159,14 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, } #if IS_ENABLED(CONFIG_IPV6) -static int br_ip6_multicast_mrd_rcv(struct net_bridge *br, - struct net_bridge_port *port, - struct sk_buff *skb) +static void br_ip6_multicast_mrd_rcv(struct net_bridge *br, + struct net_bridge_port *port, + struct sk_buff *skb) { - int ret; - - if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6) - return -ENOMSG; - - ret = ipv6_mc_check_icmpv6(skb); - if (ret < 0) - return ret; - if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV) - return -ENOMSG; + return; br_multicast_mark_router(br, port); - - return 0; } static int br_multicast_ipv6_rcv(struct net_bridge *br, @@ -3191,18 +3180,12 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, err = ipv6_mc_check_mld(skb); - if (err == -ENOMSG) { + if (err == -ENOMSG || err == -ENODATA) { if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr)) BR_INPUT_SKB_CB(skb)->mrouters_only = 1; - - if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) { - err = br_ip6_multicast_mrd_rcv(br, port, skb); - - if (err < 0 && err != -ENOMSG) { - br_multicast_err_count(br, port, skb->protocol); - return err; - } - } + if (err == -ENODATA && + ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) + br_ip6_multicast_mrd_rcv(br, port, skb); return 0; } else if (err < 0) { diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index cbc2bd643ab2..5baba7021427 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -111,6 +111,8 @@ config NET_DSA_TAG_RTL4_A config NET_DSA_TAG_OCELOT tristate "Tag driver for Ocelot family of switches, using NPI port" + depends on MSCC_OCELOT_SWITCH_LIB || \ + (MSCC_OCELOT_SWITCH_LIB=n && COMPILE_TEST) select PACKING help Say Y or M if you want to enable NPI tagging for the Ocelot switches diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 77b33bd161b8..8c0f3c6ab365 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -20,7 +20,6 @@ #include <linux/if_bridge.h> #include <linux/if_hsr.h> #include <linux/netpoll.h> -#include <linux/ptp_classify.h> #include "dsa_priv.h" @@ -556,26 +555,14 @@ static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p, struct sk_buff *skb) { struct dsa_switch *ds = p->dp->ds; - struct sk_buff *clone; - unsigned int type; - type = ptp_classify_raw(skb); - if (type == PTP_CLASS_NONE) + if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) return; if (!ds->ops->port_txtstamp) return; - clone = skb_clone_sk(skb); - if (!clone) - return; - - if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type)) { - DSA_SKB_CB(skb)->clone = clone; - return; - } - - kfree_skb(clone); + ds->ops->port_txtstamp(ds, p->dp->index, skb); } netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev) @@ -627,11 +614,9 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) dev_sw_netstats_tx_add(dev, 1, skb->len); - DSA_SKB_CB(skb)->clone = NULL; + memset(skb->cb, 0, sizeof(skb->cb)); - /* Identify PTP protocol packets, clone them, and pass them to the - * switch driver - */ + /* Handle tx timestamp if any */ dsa_skb_tx_timestamp(p, skb); if (dsa_realloc_skb(skb, dev)) { diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index f9df9cac81c5..91f0fd1242cd 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -5,33 +5,14 @@ #include <soc/mscc/ocelot.h> #include "dsa_priv.h" -static void ocelot_xmit_ptp(struct dsa_port *dp, void *injection, - struct sk_buff *clone) -{ - struct ocelot *ocelot = dp->ds->priv; - struct ocelot_port *ocelot_port; - u64 rew_op; - - ocelot_port = ocelot->ports[dp->index]; - rew_op = ocelot_port->ptp_cmd; - - /* Retrieve timestamp ID populated inside skb->cb[0] of the - * clone by ocelot_port_add_txtstamp_skb - */ - if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) - rew_op |= clone->cb[0] << 3; - - ocelot_ifh_set_rew_op(injection, rew_op); -} - static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev, __be32 ifh_prefix, void **ifh) { struct dsa_port *dp = dsa_slave_to_port(netdev); - struct sk_buff *clone = DSA_SKB_CB(skb)->clone; struct dsa_switch *ds = dp->ds; void *injection; __be32 *prefix; + u32 rew_op = 0; injection = skb_push(skb, OCELOT_TAG_LEN); prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN); @@ -42,9 +23,9 @@ static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev, ocelot_ifh_set_src(injection, ds->num_ports); ocelot_ifh_set_qos_class(injection, skb->priority); - /* TX timestamping was requested */ - if (clone) - ocelot_xmit_ptp(dp, injection, clone); + rew_op = ocelot_ptp_rew_op(skb); + if (rew_op) + ocelot_ifh_set_rew_op(injection, rew_op); *ifh = injection; } diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index 5f3e8e124a82..62a93303bd63 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -13,32 +13,6 @@ #include <soc/mscc/ocelot_ptp.h> #include "dsa_priv.h" -static struct sk_buff *ocelot_xmit_ptp(struct dsa_port *dp, - struct sk_buff *skb, - struct sk_buff *clone) -{ - struct ocelot *ocelot = dp->ds->priv; - struct ocelot_port *ocelot_port; - int port = dp->index; - u32 rew_op; - - if (!ocelot_can_inject(ocelot, 0)) - return NULL; - - ocelot_port = ocelot->ports[port]; - rew_op = ocelot_port->ptp_cmd; - - /* Retrieve timestamp ID populated inside skb->cb[0] of the - * clone by ocelot_port_add_txtstamp_skb - */ - if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) - rew_op |= clone->cb[0] << 3; - - ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); - - return NULL; -} - static struct sk_buff *ocelot_xmit(struct sk_buff *skb, struct net_device *netdev) { @@ -46,11 +20,18 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb, u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index); u16 queue_mapping = skb_get_queue_mapping(skb); u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); - struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + struct ocelot *ocelot = dp->ds->priv; + int port = dp->index; + u32 rew_op = 0; + + rew_op = ocelot_ptp_rew_op(skb); + if (rew_op) { + if (!ocelot_can_inject(ocelot, 0)) + return NULL; - /* TX timestamping was requested, so inject through MMIO */ - if (clone) - return ocelot_xmit_ptp(dp, skb, clone); + ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); + return NULL; + } return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q, ((pcp << VLAN_PRIO_SHIFT) | tx_vid)); diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c index d3d6b6a66e5f..04d5fcdfa6e0 100644 --- a/net/ipv6/mcast_snoop.c +++ b/net/ipv6/mcast_snoop.c @@ -109,7 +109,7 @@ static int ipv6_mc_check_mld_msg(struct sk_buff *skb) struct mld_msg *mld; if (!ipv6_mc_may_pull(skb, len)) - return -EINVAL; + return -ENODATA; mld = (struct mld_msg *)skb_transport_header(skb); @@ -122,7 +122,7 @@ static int ipv6_mc_check_mld_msg(struct sk_buff *skb) case ICMPV6_MGM_QUERY: return ipv6_mc_check_mld_query(skb); default: - return -ENOMSG; + return -ENODATA; } } @@ -131,7 +131,7 @@ static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); } -int ipv6_mc_check_icmpv6(struct sk_buff *skb) +static int ipv6_mc_check_icmpv6(struct sk_buff *skb) { unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); unsigned int transport_len = ipv6_transport_len(skb); @@ -150,7 +150,6 @@ int ipv6_mc_check_icmpv6(struct sk_buff *skb) return 0; } -EXPORT_SYMBOL(ipv6_mc_check_icmpv6); /** * ipv6_mc_check_mld - checks whether this is a sane MLD packet @@ -161,7 +160,10 @@ EXPORT_SYMBOL(ipv6_mc_check_icmpv6); * * -EINVAL: A broken packet was detected, i.e. it violates some internet * standard - * -ENOMSG: IP header validation succeeded but it is not an MLD packet. + * -ENOMSG: IP header validation succeeded but it is not an ICMPv6 packet + * with a hop-by-hop option. + * -ENODATA: IP+ICMPv6 header with hop-by-hop option validation succeeded + * but it is not an MLD packet. * -ENOMEM: A memory allocation failure happened. * * Caller needs to set the skb network header and free any returned skb if it diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 2ee20743cb41..53486b162f01 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1478,11 +1478,15 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net, tunnel->l2tp_net = net; pn = l2tp_pernet(net); + sk = sock->sk; + sock_hold(sk); + tunnel->sock = sk; + spin_lock_bh(&pn->l2tp_tunnel_list_lock); list_for_each_entry(tunnel_walk, &pn->l2tp_tunnel_list, list) { if (tunnel_walk->tunnel_id == tunnel->tunnel_id) { spin_unlock_bh(&pn->l2tp_tunnel_list_lock); - + sock_put(sk); ret = -EEXIST; goto err_sock; } @@ -1490,10 +1494,6 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net, list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list); spin_unlock_bh(&pn->l2tp_tunnel_list_lock); - sk = sock->sk; - sock_hold(sk); - tunnel->sock = sk; - if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { struct udp_tunnel_sock_cfg udp_cfg = { .sk_user_data = tunnel, diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index c29170e767a8..05c6ae092053 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -54,7 +54,6 @@ static int llc_station_ac_send_xid_r(struct sk_buff *skb) if (!nskb) goto out; - rc = 0; llc_pdu_decode_sa(skb, mac_da); llc_pdu_decode_ssap(skb, &dsap); llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); @@ -83,7 +82,6 @@ static int llc_station_ac_send_test_r(struct sk_buff *skb) if (!nskb) goto out; - rc = 0; llc_pdu_decode_sa(skb, mac_da); llc_pdu_decode_ssap(skb, &dsap); llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 47bab701555f..05a21dd072df 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1019,7 +1019,6 @@ static int mpls_route_add(struct mpls_route_config *cfg, goto errout; } - err = -ENOMEM; rt = mpls_rt_alloc(nhs, max_via_alen, max_labels); if (IS_ERR(rt)) { err = PTR_ERR(rt); diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c index 92b4a8689aae..4190b90ff3b1 100644 --- a/net/rds/ib_send.c +++ b/net/rds/ib_send.c @@ -294,7 +294,6 @@ void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc) rds_ib_ring_free(&ic->i_send_ring, completed); rds_ib_sub_signaled(ic, nr_sig); - nr_sig = 0; if (test_and_clear_bit(RDS_LL_SEND_FULL, &conn->c_flags) || test_bit(0, &conn->c_map_queued)) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index e2e9e9b0a6d7..08aab5c01437 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -482,7 +482,6 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, RXKADDATALEN); goto protocol_error; } - offset += sizeof(sechdr); len -= sizeof(sechdr); buf = ntohl(sechdr.data_size); @@ -576,7 +575,6 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, RXKADDATALEN); goto protocol_error; } - offset += sizeof(sechdr); len -= sizeof(sechdr); buf = ntohl(sechdr.data_size); diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 47340b3b514f..be3e80b3e27f 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1076,7 +1076,6 @@ static int smc_connect(struct socket *sock, struct sockaddr *addr, rc = -EISCONN; goto out; case SMC_INIT: - rc = 0; break; } diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 790c6b7ecb26..76a6f8c2eec4 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -418,7 +418,7 @@ static int tls_push_data(struct sock *sk, struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_prot_info *prot = &tls_ctx->prot_info; struct tls_offload_context_tx *ctx = tls_offload_ctx_tx(tls_ctx); - struct tls_record_info *record = ctx->open_record; + struct tls_record_info *record; int tls_push_record_flags; struct page_frag *pfrag; size_t orig_size = size; |