162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Realtek SMI subdriver for the Realtek RTL8365MB-VC ethernet switch. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> 562306a36Sopenharmony_ci * Copyright (C) 2021 Michael Rasmussen <mir@bang-olufsen.dk> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4 862306a36Sopenharmony_ci * integrated PHYs for the user facing ports, and an extension interface which 962306a36Sopenharmony_ci * can be connected to the CPU - or another PHY - via either MII, RMII, or 1062306a36Sopenharmony_ci * RGMII. The switch is configured via the Realtek Simple Management Interface 1162306a36Sopenharmony_ci * (SMI), which uses the MDIO/MDC lines. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Below is a simplified block diagram of the chip and its relevant interfaces. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * .-----------------------------------. 1662306a36Sopenharmony_ci * | | 1762306a36Sopenharmony_ci * UTP <---------------> Giga PHY <-> PCS <-> P0 GMAC | 1862306a36Sopenharmony_ci * UTP <---------------> Giga PHY <-> PCS <-> P1 GMAC | 1962306a36Sopenharmony_ci * UTP <---------------> Giga PHY <-> PCS <-> P2 GMAC | 2062306a36Sopenharmony_ci * UTP <---------------> Giga PHY <-> PCS <-> P3 GMAC | 2162306a36Sopenharmony_ci * | | 2262306a36Sopenharmony_ci * CPU/PHY <-MII/RMII/RGMII---> Extension <---> Extension | 2362306a36Sopenharmony_ci * | interface 1 GMAC 1 | 2462306a36Sopenharmony_ci * | | 2562306a36Sopenharmony_ci * SMI driver/ <-MDC/SCL---> Management ~~~~~~~~~~~~~~ | 2662306a36Sopenharmony_ci * EEPROM <-MDIO/SDA--> interface ~REALTEK ~~~~~ | 2762306a36Sopenharmony_ci * | ~RTL8365MB ~~~ | 2862306a36Sopenharmony_ci * | ~GXXXC TAIWAN~ | 2962306a36Sopenharmony_ci * GPIO <--------------> Reset ~~~~~~~~~~~~~~ | 3062306a36Sopenharmony_ci * | | 3162306a36Sopenharmony_ci * Interrupt <----------> Link UP/DOWN events | 3262306a36Sopenharmony_ci * controller | | 3362306a36Sopenharmony_ci * '-----------------------------------' 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * The driver uses DSA to integrate the 4 user and 1 extension ports into the 3662306a36Sopenharmony_ci * kernel. Netdevices are created for the user ports, as are PHY devices for 3762306a36Sopenharmony_ci * their integrated PHYs. The device tree firmware should also specify the link 3862306a36Sopenharmony_ci * partner of the extension port - either via a fixed-link or other phy-handle. 3962306a36Sopenharmony_ci * See the device tree bindings for more detailed information. Note that the 4062306a36Sopenharmony_ci * driver has only been tested with a fixed-link, but in principle it should not 4162306a36Sopenharmony_ci * matter. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * NOTE: Currently, only the RGMII interface is implemented in this driver. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * The interrupt line is asserted on link UP/DOWN events. The driver creates a 4662306a36Sopenharmony_ci * custom irqchip to handle this interrupt and demultiplex the events by reading 4762306a36Sopenharmony_ci * the status registers via SMI. Interrupts are then propagated to the relevant 4862306a36Sopenharmony_ci * PHY device. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * The EEPROM contains initial register values which the chip will read over I2C 5162306a36Sopenharmony_ci * upon hardware reset. It is also possible to omit the EEPROM. In both cases, 5262306a36Sopenharmony_ci * the driver will manually reprogram some registers using jam tables to reach 5362306a36Sopenharmony_ci * an initial state defined by the vendor driver. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * This Linux driver is written based on an OS-agnostic vendor driver from 5662306a36Sopenharmony_ci * Realtek. The reference GPL-licensed sources can be found in the OpenWrt 5762306a36Sopenharmony_ci * source tree under the name rtl8367c. The vendor driver claims to support a 5862306a36Sopenharmony_ci * number of similar switch controllers from Realtek, but the only hardware we 5962306a36Sopenharmony_ci * have is the RTL8365MB-VC. Moreover, there does not seem to be any chip under 6062306a36Sopenharmony_ci * the name RTL8367C. Although one wishes that the 'C' stood for some kind of 6162306a36Sopenharmony_ci * common hardware revision, there exist examples of chips with the suffix -VC 6262306a36Sopenharmony_ci * which are explicitly not supported by the rtl8367c driver and which instead 6362306a36Sopenharmony_ci * require the rtl8367d vendor driver. With all this uncertainty, the driver has 6462306a36Sopenharmony_ci * been modestly named rtl8365mb. Future implementors may wish to rename things 6562306a36Sopenharmony_ci * accordingly. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * In the same family of chips, some carry up to 8 user ports and up to 2 6862306a36Sopenharmony_ci * extension ports. Where possible this driver tries to make things generic, but 6962306a36Sopenharmony_ci * more work must be done to support these configurations. According to 7062306a36Sopenharmony_ci * documentation from Realtek, the family should include the following chips: 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * - RTL8363NB 7362306a36Sopenharmony_ci * - RTL8363NB-VB 7462306a36Sopenharmony_ci * - RTL8363SC 7562306a36Sopenharmony_ci * - RTL8363SC-VB 7662306a36Sopenharmony_ci * - RTL8364NB 7762306a36Sopenharmony_ci * - RTL8364NB-VB 7862306a36Sopenharmony_ci * - RTL8365MB-VC 7962306a36Sopenharmony_ci * - RTL8366SC 8062306a36Sopenharmony_ci * - RTL8367RB-VB 8162306a36Sopenharmony_ci * - RTL8367SB 8262306a36Sopenharmony_ci * - RTL8367S 8362306a36Sopenharmony_ci * - RTL8370MB 8462306a36Sopenharmony_ci * - RTL8310SR 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Some of the register logic for these additional chips has been skipped over 8762306a36Sopenharmony_ci * while implementing this driver. It is therefore not possible to assume that 8862306a36Sopenharmony_ci * things will work out-of-the-box for other chips, and a careful review of the 8962306a36Sopenharmony_ci * vendor driver may be needed to expand support. The RTL8365MB-VC seems to be 9062306a36Sopenharmony_ci * one of the simpler chips. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#include <linux/bitfield.h> 9462306a36Sopenharmony_ci#include <linux/bitops.h> 9562306a36Sopenharmony_ci#include <linux/interrupt.h> 9662306a36Sopenharmony_ci#include <linux/irqdomain.h> 9762306a36Sopenharmony_ci#include <linux/mutex.h> 9862306a36Sopenharmony_ci#include <linux/of_irq.h> 9962306a36Sopenharmony_ci#include <linux/regmap.h> 10062306a36Sopenharmony_ci#include <linux/if_bridge.h> 10162306a36Sopenharmony_ci#include <linux/if_vlan.h> 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#include "realtek.h" 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Family-specific data and limits */ 10662306a36Sopenharmony_ci#define RTL8365MB_PHYADDRMAX 7 10762306a36Sopenharmony_ci#define RTL8365MB_NUM_PHYREGS 32 10862306a36Sopenharmony_ci#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) 10962306a36Sopenharmony_ci#define RTL8365MB_MAX_NUM_PORTS 11 11062306a36Sopenharmony_ci#define RTL8365MB_MAX_NUM_EXTINTS 3 11162306a36Sopenharmony_ci#define RTL8365MB_LEARN_LIMIT_MAX 2112 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Chip identification registers */ 11462306a36Sopenharmony_ci#define RTL8365MB_CHIP_ID_REG 0x1300 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define RTL8365MB_CHIP_VER_REG 0x1301 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define RTL8365MB_MAGIC_REG 0x13C2 11962306a36Sopenharmony_ci#define RTL8365MB_MAGIC_VALUE 0x0249 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Chip reset register */ 12262306a36Sopenharmony_ci#define RTL8365MB_CHIP_RESET_REG 0x1322 12362306a36Sopenharmony_ci#define RTL8365MB_CHIP_RESET_SW_MASK 0x0002 12462306a36Sopenharmony_ci#define RTL8365MB_CHIP_RESET_HW_MASK 0x0001 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Interrupt polarity register */ 12762306a36Sopenharmony_ci#define RTL8365MB_INTR_POLARITY_REG 0x1100 12862306a36Sopenharmony_ci#define RTL8365MB_INTR_POLARITY_MASK 0x0001 12962306a36Sopenharmony_ci#define RTL8365MB_INTR_POLARITY_HIGH 0 13062306a36Sopenharmony_ci#define RTL8365MB_INTR_POLARITY_LOW 1 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Interrupt control/status register - enable/check specific interrupt types */ 13362306a36Sopenharmony_ci#define RTL8365MB_INTR_CTRL_REG 0x1101 13462306a36Sopenharmony_ci#define RTL8365MB_INTR_STATUS_REG 0x1102 13562306a36Sopenharmony_ci#define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000 13662306a36Sopenharmony_ci#define RTL8365MB_INTR_SLIENT_START_MASK 0x0800 13762306a36Sopenharmony_ci#define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200 13862306a36Sopenharmony_ci#define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100 13962306a36Sopenharmony_ci#define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080 14062306a36Sopenharmony_ci#define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040 14162306a36Sopenharmony_ci#define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020 14262306a36Sopenharmony_ci#define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010 14362306a36Sopenharmony_ci#define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008 14462306a36Sopenharmony_ci#define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004 14562306a36Sopenharmony_ci#define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002 14662306a36Sopenharmony_ci#define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001 14762306a36Sopenharmony_ci#define RTL8365MB_INTR_ALL_MASK \ 14862306a36Sopenharmony_ci (RTL8365MB_INTR_SLIENT_START_2_MASK | \ 14962306a36Sopenharmony_ci RTL8365MB_INTR_SLIENT_START_MASK | \ 15062306a36Sopenharmony_ci RTL8365MB_INTR_ACL_ACTION_MASK | \ 15162306a36Sopenharmony_ci RTL8365MB_INTR_CABLE_DIAG_FIN_MASK | \ 15262306a36Sopenharmony_ci RTL8365MB_INTR_INTERRUPT_8051_MASK | \ 15362306a36Sopenharmony_ci RTL8365MB_INTR_LOOP_DETECTION_MASK | \ 15462306a36Sopenharmony_ci RTL8365MB_INTR_GREEN_TIMER_MASK | \ 15562306a36Sopenharmony_ci RTL8365MB_INTR_SPECIAL_CONGEST_MASK | \ 15662306a36Sopenharmony_ci RTL8365MB_INTR_SPEED_CHANGE_MASK | \ 15762306a36Sopenharmony_ci RTL8365MB_INTR_LEARN_OVER_MASK | \ 15862306a36Sopenharmony_ci RTL8365MB_INTR_METER_EXCEEDED_MASK | \ 15962306a36Sopenharmony_ci RTL8365MB_INTR_LINK_CHANGE_MASK) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* Per-port interrupt type status registers */ 16262306a36Sopenharmony_ci#define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106 16362306a36Sopenharmony_ci#define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#define RTL8365MB_PORT_LINKUP_IND_REG 0x1107 16662306a36Sopenharmony_ci#define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* PHY indirect access registers */ 16962306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00 17062306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002 17162306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 17262306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 17362306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001 17462306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 17562306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01 17662306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02 17762306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0) 17862306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5) 17962306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8) 18062306a36Sopenharmony_ci#define RTL8365MB_PHY_BASE 0x2000 18162306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03 18262306a36Sopenharmony_ci#define RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG 0x1F04 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* PHY OCP address prefix register */ 18562306a36Sopenharmony_ci#define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15 18662306a36Sopenharmony_ci#define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0 18762306a36Sopenharmony_ci#define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* The PHY OCP addresses of PHY registers 0~31 start here */ 19062306a36Sopenharmony_ci#define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* External interface port mode values - used in DIGITAL_INTERFACE_SELECT */ 19362306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_DISABLE 0 19462306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_RGMII 1 19562306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 19662306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_MII_PHY 3 19762306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_TMII_MAC 4 19862306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_TMII_PHY 5 19962306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_GMII 6 20062306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_RMII_MAC 7 20162306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_RMII_PHY 8 20262306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_SGMII 9 20362306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_HSGMII 10 20462306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_1000X_100FX 11 20562306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_1000X 12 20662306a36Sopenharmony_ci#define RTL8365MB_EXT_PORT_MODE_100FX 13 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* External interface mode configuration registers 0~1 */ 20962306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ 21062306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ 21162306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ 21262306a36Sopenharmony_ci ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ 21362306a36Sopenharmony_ci (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 21462306a36Sopenharmony_ci 0x0) 21562306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ 21662306a36Sopenharmony_ci (0xF << (((_extint) % 2))) 21762306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ 21862306a36Sopenharmony_ci (((_extint) % 2) * 4) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* External interface RGMII TX/RX delay configuration registers 0~2 */ 22162306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ 22262306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ 22362306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ 22462306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_REG(_extint) \ 22562306a36Sopenharmony_ci ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ 22662306a36Sopenharmony_ci (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ 22762306a36Sopenharmony_ci (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ 22862306a36Sopenharmony_ci 0x0) 22962306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 23062306a36Sopenharmony_ci#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ 23362306a36Sopenharmony_ci#define RTL8365MB_PORT_SPEED_10M 0 23462306a36Sopenharmony_ci#define RTL8365MB_PORT_SPEED_100M 1 23562306a36Sopenharmony_ci#define RTL8365MB_PORT_SPEED_1000M 2 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* External interface force configuration registers 0~2 */ 23862306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ 23962306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ 24062306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ 24162306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ 24262306a36Sopenharmony_ci ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ 24362306a36Sopenharmony_ci (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ 24462306a36Sopenharmony_ci (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ 24562306a36Sopenharmony_ci 0x0) 24662306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 24762306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 24862306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 24962306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020 25062306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010 25162306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004 25262306a36Sopenharmony_ci#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* CPU port mask register - controls which ports are treated as CPU ports */ 25562306a36Sopenharmony_ci#define RTL8365MB_CPU_PORT_MASK_REG 0x1219 25662306a36Sopenharmony_ci#define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* CPU control register */ 25962306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_REG 0x121A 26062306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400 26162306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200 26262306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080 26362306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040 26462306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038 26562306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006 26662306a36Sopenharmony_ci#define RTL8365MB_CPU_CTRL_EN_MASK 0x0001 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* Maximum packet length register */ 26962306a36Sopenharmony_ci#define RTL8365MB_CFG0_MAX_LEN_REG 0x088C 27062306a36Sopenharmony_ci#define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF 27162306a36Sopenharmony_ci#define RTL8365MB_CFG0_MAX_LEN_MAX 0x3FFF 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* Port learning limit registers */ 27462306a36Sopenharmony_ci#define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20 27562306a36Sopenharmony_ci#define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \ 27662306a36Sopenharmony_ci (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport)) 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* Port isolation (forwarding mask) registers */ 27962306a36Sopenharmony_ci#define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 28062306a36Sopenharmony_ci#define RTL8365MB_PORT_ISOLATION_REG(_physport) \ 28162306a36Sopenharmony_ci (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) 28262306a36Sopenharmony_ci#define RTL8365MB_PORT_ISOLATION_MASK 0x07FF 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* MSTP port state registers - indexed by tree instance */ 28562306a36Sopenharmony_ci#define RTL8365MB_MSTI_CTRL_BASE 0x0A00 28662306a36Sopenharmony_ci#define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ 28762306a36Sopenharmony_ci (RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3)) 28862306a36Sopenharmony_ci#define RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(_physport) ((_physport) << 1) 28962306a36Sopenharmony_ci#define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ 29062306a36Sopenharmony_ci (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* MIB counter value registers */ 29362306a36Sopenharmony_ci#define RTL8365MB_MIB_COUNTER_BASE 0x1000 29462306a36Sopenharmony_ci#define RTL8365MB_MIB_COUNTER_REG(_x) (RTL8365MB_MIB_COUNTER_BASE + (_x)) 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* MIB counter address register */ 29762306a36Sopenharmony_ci#define RTL8365MB_MIB_ADDRESS_REG 0x1004 29862306a36Sopenharmony_ci#define RTL8365MB_MIB_ADDRESS_PORT_OFFSET 0x007C 29962306a36Sopenharmony_ci#define RTL8365MB_MIB_ADDRESS(_p, _x) \ 30062306a36Sopenharmony_ci (((RTL8365MB_MIB_ADDRESS_PORT_OFFSET) * (_p) + (_x)) >> 2) 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci#define RTL8365MB_MIB_CTRL0_REG 0x1005 30362306a36Sopenharmony_ci#define RTL8365MB_MIB_CTRL0_RESET_MASK 0x0002 30462306a36Sopenharmony_ci#define RTL8365MB_MIB_CTRL0_BUSY_MASK 0x0001 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* The DSA callback .get_stats64 runs in atomic context, so we are not allowed 30762306a36Sopenharmony_ci * to block. On the other hand, accessing MIB counters absolutely requires us to 30862306a36Sopenharmony_ci * block. The solution is thus to schedule work which polls the MIB counters 30962306a36Sopenharmony_ci * asynchronously and updates some private data, which the callback can then 31062306a36Sopenharmony_ci * fetch atomically. Three seconds should be a good enough polling interval. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci#define RTL8365MB_STATS_INTERVAL_JIFFIES (3 * HZ) 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cienum rtl8365mb_mib_counter_index { 31562306a36Sopenharmony_ci RTL8365MB_MIB_ifInOctets, 31662306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsFCSErrors, 31762306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsSymbolErrors, 31862306a36Sopenharmony_ci RTL8365MB_MIB_dot3InPauseFrames, 31962306a36Sopenharmony_ci RTL8365MB_MIB_dot3ControlInUnknownOpcodes, 32062306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsFragments, 32162306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsJabbers, 32262306a36Sopenharmony_ci RTL8365MB_MIB_ifInUcastPkts, 32362306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsDropEvents, 32462306a36Sopenharmony_ci RTL8365MB_MIB_ifInMulticastPkts, 32562306a36Sopenharmony_ci RTL8365MB_MIB_ifInBroadcastPkts, 32662306a36Sopenharmony_ci RTL8365MB_MIB_inMldChecksumError, 32762306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpChecksumError, 32862306a36Sopenharmony_ci RTL8365MB_MIB_inMldSpecificQuery, 32962306a36Sopenharmony_ci RTL8365MB_MIB_inMldGeneralQuery, 33062306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpSpecificQuery, 33162306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpGeneralQuery, 33262306a36Sopenharmony_ci RTL8365MB_MIB_inMldLeaves, 33362306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpLeaves, 33462306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsOctets, 33562306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsUnderSizePkts, 33662306a36Sopenharmony_ci RTL8365MB_MIB_etherOversizeStats, 33762306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts64Octets, 33862306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts65to127Octets, 33962306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts128to255Octets, 34062306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts256to511Octets, 34162306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts512to1023Octets, 34262306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsPkts1024to1518Octets, 34362306a36Sopenharmony_ci RTL8365MB_MIB_ifOutOctets, 34462306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsSingleCollisionFrames, 34562306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsMultipleCollisionFrames, 34662306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsDeferredTransmissions, 34762306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsLateCollisions, 34862306a36Sopenharmony_ci RTL8365MB_MIB_etherStatsCollisions, 34962306a36Sopenharmony_ci RTL8365MB_MIB_dot3StatsExcessiveCollisions, 35062306a36Sopenharmony_ci RTL8365MB_MIB_dot3OutPauseFrames, 35162306a36Sopenharmony_ci RTL8365MB_MIB_ifOutDiscards, 35262306a36Sopenharmony_ci RTL8365MB_MIB_dot1dTpPortInDiscards, 35362306a36Sopenharmony_ci RTL8365MB_MIB_ifOutUcastPkts, 35462306a36Sopenharmony_ci RTL8365MB_MIB_ifOutMulticastPkts, 35562306a36Sopenharmony_ci RTL8365MB_MIB_ifOutBroadcastPkts, 35662306a36Sopenharmony_ci RTL8365MB_MIB_outOampduPkts, 35762306a36Sopenharmony_ci RTL8365MB_MIB_inOampduPkts, 35862306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpJoinsSuccess, 35962306a36Sopenharmony_ci RTL8365MB_MIB_inIgmpJoinsFail, 36062306a36Sopenharmony_ci RTL8365MB_MIB_inMldJoinsSuccess, 36162306a36Sopenharmony_ci RTL8365MB_MIB_inMldJoinsFail, 36262306a36Sopenharmony_ci RTL8365MB_MIB_inReportSuppressionDrop, 36362306a36Sopenharmony_ci RTL8365MB_MIB_inLeaveSuppressionDrop, 36462306a36Sopenharmony_ci RTL8365MB_MIB_outIgmpReports, 36562306a36Sopenharmony_ci RTL8365MB_MIB_outIgmpLeaves, 36662306a36Sopenharmony_ci RTL8365MB_MIB_outIgmpGeneralQuery, 36762306a36Sopenharmony_ci RTL8365MB_MIB_outIgmpSpecificQuery, 36862306a36Sopenharmony_ci RTL8365MB_MIB_outMldReports, 36962306a36Sopenharmony_ci RTL8365MB_MIB_outMldLeaves, 37062306a36Sopenharmony_ci RTL8365MB_MIB_outMldGeneralQuery, 37162306a36Sopenharmony_ci RTL8365MB_MIB_outMldSpecificQuery, 37262306a36Sopenharmony_ci RTL8365MB_MIB_inKnownMulticastPkts, 37362306a36Sopenharmony_ci RTL8365MB_MIB_END, 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistruct rtl8365mb_mib_counter { 37762306a36Sopenharmony_ci u32 offset; 37862306a36Sopenharmony_ci u32 length; 37962306a36Sopenharmony_ci const char *name; 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci#define RTL8365MB_MAKE_MIB_COUNTER(_offset, _length, _name) \ 38362306a36Sopenharmony_ci [RTL8365MB_MIB_ ## _name] = { _offset, _length, #_name } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic struct rtl8365mb_mib_counter rtl8365mb_mib_counters[] = { 38662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(0, 4, ifInOctets), 38762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(4, 2, dot3StatsFCSErrors), 38862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(6, 2, dot3StatsSymbolErrors), 38962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(8, 2, dot3InPauseFrames), 39062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(10, 2, dot3ControlInUnknownOpcodes), 39162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(12, 2, etherStatsFragments), 39262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(14, 2, etherStatsJabbers), 39362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(16, 2, ifInUcastPkts), 39462306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(18, 2, etherStatsDropEvents), 39562306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(20, 2, ifInMulticastPkts), 39662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(22, 2, ifInBroadcastPkts), 39762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(24, 2, inMldChecksumError), 39862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(26, 2, inIgmpChecksumError), 39962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(28, 2, inMldSpecificQuery), 40062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(30, 2, inMldGeneralQuery), 40162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(32, 2, inIgmpSpecificQuery), 40262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(34, 2, inIgmpGeneralQuery), 40362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(36, 2, inMldLeaves), 40462306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(38, 2, inIgmpLeaves), 40562306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(40, 4, etherStatsOctets), 40662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(44, 2, etherStatsUnderSizePkts), 40762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(46, 2, etherOversizeStats), 40862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(48, 2, etherStatsPkts64Octets), 40962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(50, 2, etherStatsPkts65to127Octets), 41062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(52, 2, etherStatsPkts128to255Octets), 41162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(54, 2, etherStatsPkts256to511Octets), 41262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(56, 2, etherStatsPkts512to1023Octets), 41362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(58, 2, etherStatsPkts1024to1518Octets), 41462306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(60, 4, ifOutOctets), 41562306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(64, 2, dot3StatsSingleCollisionFrames), 41662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(66, 2, dot3StatsMultipleCollisionFrames), 41762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(68, 2, dot3StatsDeferredTransmissions), 41862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(70, 2, dot3StatsLateCollisions), 41962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(72, 2, etherStatsCollisions), 42062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(74, 2, dot3StatsExcessiveCollisions), 42162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(76, 2, dot3OutPauseFrames), 42262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(78, 2, ifOutDiscards), 42362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(80, 2, dot1dTpPortInDiscards), 42462306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(82, 2, ifOutUcastPkts), 42562306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(84, 2, ifOutMulticastPkts), 42662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(86, 2, ifOutBroadcastPkts), 42762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(88, 2, outOampduPkts), 42862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(90, 2, inOampduPkts), 42962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(92, 4, inIgmpJoinsSuccess), 43062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(96, 2, inIgmpJoinsFail), 43162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(98, 2, inMldJoinsSuccess), 43262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(100, 2, inMldJoinsFail), 43362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(102, 2, inReportSuppressionDrop), 43462306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(104, 2, inLeaveSuppressionDrop), 43562306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(106, 2, outIgmpReports), 43662306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(108, 2, outIgmpLeaves), 43762306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(110, 2, outIgmpGeneralQuery), 43862306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(112, 2, outIgmpSpecificQuery), 43962306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(114, 2, outMldReports), 44062306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(116, 2, outMldLeaves), 44162306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(118, 2, outMldGeneralQuery), 44262306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(120, 2, outMldSpecificQuery), 44362306a36Sopenharmony_ci RTL8365MB_MAKE_MIB_COUNTER(122, 2, inKnownMulticastPkts), 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic_assert(ARRAY_SIZE(rtl8365mb_mib_counters) == RTL8365MB_MIB_END); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistruct rtl8365mb_jam_tbl_entry { 44962306a36Sopenharmony_ci u16 reg; 45062306a36Sopenharmony_ci u16 val; 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* Lifted from the vendor driver sources */ 45462306a36Sopenharmony_cistatic const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_8365mb_vc[] = { 45562306a36Sopenharmony_ci { 0x13EB, 0x15BB }, { 0x1303, 0x06D6 }, { 0x1304, 0x0700 }, 45662306a36Sopenharmony_ci { 0x13E2, 0x003F }, { 0x13F9, 0x0090 }, { 0x121E, 0x03CA }, 45762306a36Sopenharmony_ci { 0x1233, 0x0352 }, { 0x1237, 0x00A0 }, { 0x123A, 0x0030 }, 45862306a36Sopenharmony_ci { 0x1239, 0x0084 }, { 0x0301, 0x1000 }, { 0x1349, 0x001F }, 45962306a36Sopenharmony_ci { 0x18E0, 0x4004 }, { 0x122B, 0x241C }, { 0x1305, 0xC000 }, 46062306a36Sopenharmony_ci { 0x13F0, 0x0000 }, 46162306a36Sopenharmony_ci}; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = { 46462306a36Sopenharmony_ci { 0x1200, 0x7FCB }, { 0x0884, 0x0003 }, { 0x06EB, 0x0001 }, 46562306a36Sopenharmony_ci { 0x03Fa, 0x0007 }, { 0x08C8, 0x00C0 }, { 0x0A30, 0x020E }, 46662306a36Sopenharmony_ci { 0x0800, 0x0000 }, { 0x0802, 0x0000 }, { 0x09DA, 0x0013 }, 46762306a36Sopenharmony_ci { 0x1D32, 0x0002 }, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cienum rtl8365mb_phy_interface_mode { 47162306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0, 47262306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0), 47362306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_MII = BIT(1), 47462306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_TMII = BIT(2), 47562306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_RMII = BIT(3), 47662306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_RGMII = BIT(4), 47762306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_SGMII = BIT(5), 47862306a36Sopenharmony_ci RTL8365MB_PHY_INTERFACE_MODE_HSGMII = BIT(6), 47962306a36Sopenharmony_ci}; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/** 48262306a36Sopenharmony_ci * struct rtl8365mb_extint - external interface info 48362306a36Sopenharmony_ci * @port: the port with an external interface 48462306a36Sopenharmony_ci * @id: the external interface ID, which is either 0, 1, or 2 48562306a36Sopenharmony_ci * @supported_interfaces: a bitmask of supported PHY interface modes 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Represents a mapping: port -> { id, supported_interfaces }. To be embedded 48862306a36Sopenharmony_ci * in &struct rtl8365mb_chip_info for every port with an external interface. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistruct rtl8365mb_extint { 49162306a36Sopenharmony_ci int port; 49262306a36Sopenharmony_ci int id; 49362306a36Sopenharmony_ci unsigned int supported_interfaces; 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** 49762306a36Sopenharmony_ci * struct rtl8365mb_chip_info - static chip-specific info 49862306a36Sopenharmony_ci * @name: human-readable chip name 49962306a36Sopenharmony_ci * @chip_id: chip identifier 50062306a36Sopenharmony_ci * @chip_ver: chip silicon revision 50162306a36Sopenharmony_ci * @extints: available external interfaces 50262306a36Sopenharmony_ci * @jam_table: chip-specific initialization jam table 50362306a36Sopenharmony_ci * @jam_size: size of the chip's jam table 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * These data are specific to a given chip in the family of switches supported 50662306a36Sopenharmony_ci * by this driver. When adding support for another chip in the family, a new 50762306a36Sopenharmony_ci * chip info should be added to the rtl8365mb_chip_infos array. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistruct rtl8365mb_chip_info { 51062306a36Sopenharmony_ci const char *name; 51162306a36Sopenharmony_ci u32 chip_id; 51262306a36Sopenharmony_ci u32 chip_ver; 51362306a36Sopenharmony_ci const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS]; 51462306a36Sopenharmony_ci const struct rtl8365mb_jam_tbl_entry *jam_table; 51562306a36Sopenharmony_ci size_t jam_size; 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* Chip info for each supported switch in the family */ 51962306a36Sopenharmony_ci#define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode) 52062306a36Sopenharmony_cistatic const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { 52162306a36Sopenharmony_ci { 52262306a36Sopenharmony_ci .name = "RTL8365MB-VC", 52362306a36Sopenharmony_ci .chip_id = 0x6367, 52462306a36Sopenharmony_ci .chip_ver = 0x0040, 52562306a36Sopenharmony_ci .extints = { 52662306a36Sopenharmony_ci { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | 52762306a36Sopenharmony_ci PHY_INTF(RMII) | PHY_INTF(RGMII) }, 52862306a36Sopenharmony_ci }, 52962306a36Sopenharmony_ci .jam_table = rtl8365mb_init_jam_8365mb_vc, 53062306a36Sopenharmony_ci .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 53162306a36Sopenharmony_ci }, 53262306a36Sopenharmony_ci { 53362306a36Sopenharmony_ci .name = "RTL8367S", 53462306a36Sopenharmony_ci .chip_id = 0x6367, 53562306a36Sopenharmony_ci .chip_ver = 0x00A0, 53662306a36Sopenharmony_ci .extints = { 53762306a36Sopenharmony_ci { 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) }, 53862306a36Sopenharmony_ci { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | 53962306a36Sopenharmony_ci PHY_INTF(RMII) | PHY_INTF(RGMII) }, 54062306a36Sopenharmony_ci }, 54162306a36Sopenharmony_ci .jam_table = rtl8365mb_init_jam_8365mb_vc, 54262306a36Sopenharmony_ci .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 54362306a36Sopenharmony_ci }, 54462306a36Sopenharmony_ci { 54562306a36Sopenharmony_ci .name = "RTL8367RB-VB", 54662306a36Sopenharmony_ci .chip_id = 0x6367, 54762306a36Sopenharmony_ci .chip_ver = 0x0020, 54862306a36Sopenharmony_ci .extints = { 54962306a36Sopenharmony_ci { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | 55062306a36Sopenharmony_ci PHY_INTF(RMII) | PHY_INTF(RGMII) }, 55162306a36Sopenharmony_ci { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | 55262306a36Sopenharmony_ci PHY_INTF(RMII) | PHY_INTF(RGMII) }, 55362306a36Sopenharmony_ci }, 55462306a36Sopenharmony_ci .jam_table = rtl8365mb_init_jam_8365mb_vc, 55562306a36Sopenharmony_ci .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 55662306a36Sopenharmony_ci }, 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cienum rtl8365mb_stp_state { 56062306a36Sopenharmony_ci RTL8365MB_STP_STATE_DISABLED = 0, 56162306a36Sopenharmony_ci RTL8365MB_STP_STATE_BLOCKING = 1, 56262306a36Sopenharmony_ci RTL8365MB_STP_STATE_LEARNING = 2, 56362306a36Sopenharmony_ci RTL8365MB_STP_STATE_FORWARDING = 3, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cienum rtl8365mb_cpu_insert { 56762306a36Sopenharmony_ci RTL8365MB_CPU_INSERT_TO_ALL = 0, 56862306a36Sopenharmony_ci RTL8365MB_CPU_INSERT_TO_TRAPPING = 1, 56962306a36Sopenharmony_ci RTL8365MB_CPU_INSERT_TO_NONE = 2, 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cienum rtl8365mb_cpu_position { 57362306a36Sopenharmony_ci RTL8365MB_CPU_POS_AFTER_SA = 0, 57462306a36Sopenharmony_ci RTL8365MB_CPU_POS_BEFORE_CRC = 1, 57562306a36Sopenharmony_ci}; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cienum rtl8365mb_cpu_format { 57862306a36Sopenharmony_ci RTL8365MB_CPU_FORMAT_8BYTES = 0, 57962306a36Sopenharmony_ci RTL8365MB_CPU_FORMAT_4BYTES = 1, 58062306a36Sopenharmony_ci}; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cienum rtl8365mb_cpu_rxlen { 58362306a36Sopenharmony_ci RTL8365MB_CPU_RXLEN_72BYTES = 0, 58462306a36Sopenharmony_ci RTL8365MB_CPU_RXLEN_64BYTES = 1, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/** 58862306a36Sopenharmony_ci * struct rtl8365mb_cpu - CPU port configuration 58962306a36Sopenharmony_ci * @enable: enable/disable hardware insertion of CPU tag in switch->CPU frames 59062306a36Sopenharmony_ci * @mask: port mask of ports that parse should parse CPU tags 59162306a36Sopenharmony_ci * @trap_port: forward trapped frames to this port 59262306a36Sopenharmony_ci * @insert: CPU tag insertion mode in switch->CPU frames 59362306a36Sopenharmony_ci * @position: position of CPU tag in frame 59462306a36Sopenharmony_ci * @rx_length: minimum CPU RX length 59562306a36Sopenharmony_ci * @format: CPU tag format 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * Represents the CPU tagging and CPU port configuration of the switch. These 59862306a36Sopenharmony_ci * settings are configurable at runtime. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_cistruct rtl8365mb_cpu { 60162306a36Sopenharmony_ci bool enable; 60262306a36Sopenharmony_ci u32 mask; 60362306a36Sopenharmony_ci u32 trap_port; 60462306a36Sopenharmony_ci enum rtl8365mb_cpu_insert insert; 60562306a36Sopenharmony_ci enum rtl8365mb_cpu_position position; 60662306a36Sopenharmony_ci enum rtl8365mb_cpu_rxlen rx_length; 60762306a36Sopenharmony_ci enum rtl8365mb_cpu_format format; 60862306a36Sopenharmony_ci}; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/** 61162306a36Sopenharmony_ci * struct rtl8365mb_port - private per-port data 61262306a36Sopenharmony_ci * @priv: pointer to parent realtek_priv data 61362306a36Sopenharmony_ci * @index: DSA port index, same as dsa_port::index 61462306a36Sopenharmony_ci * @stats: link statistics populated by rtl8365mb_stats_poll, ready for atomic 61562306a36Sopenharmony_ci * access via rtl8365mb_get_stats64 61662306a36Sopenharmony_ci * @stats_lock: protect the stats structure during read/update 61762306a36Sopenharmony_ci * @mib_work: delayed work for polling MIB counters 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_cistruct rtl8365mb_port { 62062306a36Sopenharmony_ci struct realtek_priv *priv; 62162306a36Sopenharmony_ci unsigned int index; 62262306a36Sopenharmony_ci struct rtnl_link_stats64 stats; 62362306a36Sopenharmony_ci spinlock_t stats_lock; 62462306a36Sopenharmony_ci struct delayed_work mib_work; 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/** 62862306a36Sopenharmony_ci * struct rtl8365mb - driver private data 62962306a36Sopenharmony_ci * @priv: pointer to parent realtek_priv data 63062306a36Sopenharmony_ci * @irq: registered IRQ or zero 63162306a36Sopenharmony_ci * @chip_info: chip-specific info about the attached switch 63262306a36Sopenharmony_ci * @cpu: CPU tagging and CPU port configuration for this chip 63362306a36Sopenharmony_ci * @mib_lock: prevent concurrent reads of MIB counters 63462306a36Sopenharmony_ci * @ports: per-port data 63562306a36Sopenharmony_ci * 63662306a36Sopenharmony_ci * Private data for this driver. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistruct rtl8365mb { 63962306a36Sopenharmony_ci struct realtek_priv *priv; 64062306a36Sopenharmony_ci int irq; 64162306a36Sopenharmony_ci const struct rtl8365mb_chip_info *chip_info; 64262306a36Sopenharmony_ci struct rtl8365mb_cpu cpu; 64362306a36Sopenharmony_ci struct mutex mib_lock; 64462306a36Sopenharmony_ci struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS]; 64562306a36Sopenharmony_ci}; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 val; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return regmap_read_poll_timeout(priv->map_nolock, 65262306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_STATUS_REG, 65362306a36Sopenharmony_ci val, !val, 10, 100); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, 65762306a36Sopenharmony_ci u32 ocp_addr) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci u32 val; 66062306a36Sopenharmony_ci int ret; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Set OCP prefix */ 66362306a36Sopenharmony_ci val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); 66462306a36Sopenharmony_ci ret = regmap_update_bits( 66562306a36Sopenharmony_ci priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG, 66662306a36Sopenharmony_ci RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, 66762306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); 66862306a36Sopenharmony_ci if (ret) 66962306a36Sopenharmony_ci return ret; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Set PHY register address */ 67262306a36Sopenharmony_ci val = RTL8365MB_PHY_BASE; 67362306a36Sopenharmony_ci val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); 67462306a36Sopenharmony_ci val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, 67562306a36Sopenharmony_ci ocp_addr >> 1); 67662306a36Sopenharmony_ci val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, 67762306a36Sopenharmony_ci ocp_addr >> 6); 67862306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, 67962306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); 68062306a36Sopenharmony_ci if (ret) 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, 68762306a36Sopenharmony_ci u32 ocp_addr, u16 *data) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci u32 val; 69062306a36Sopenharmony_ci int ret; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci mutex_lock(&priv->map_lock); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci ret = rtl8365mb_phy_poll_busy(priv); 69562306a36Sopenharmony_ci if (ret) 69662306a36Sopenharmony_ci goto out; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); 69962306a36Sopenharmony_ci if (ret) 70062306a36Sopenharmony_ci goto out; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Execute read operation */ 70362306a36Sopenharmony_ci val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, 70462306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | 70562306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, 70662306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); 70762306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, 70862306a36Sopenharmony_ci val); 70962306a36Sopenharmony_ci if (ret) 71062306a36Sopenharmony_ci goto out; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci ret = rtl8365mb_phy_poll_busy(priv); 71362306a36Sopenharmony_ci if (ret) 71462306a36Sopenharmony_ci goto out; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Get PHY register data */ 71762306a36Sopenharmony_ci ret = regmap_read(priv->map_nolock, 71862306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci goto out; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci *data = val & 0xFFFF; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ciout: 72562306a36Sopenharmony_ci mutex_unlock(&priv->map_lock); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return ret; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, 73162306a36Sopenharmony_ci u32 ocp_addr, u16 data) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci u32 val; 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci mutex_lock(&priv->map_lock); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = rtl8365mb_phy_poll_busy(priv); 73962306a36Sopenharmony_ci if (ret) 74062306a36Sopenharmony_ci goto out; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); 74362306a36Sopenharmony_ci if (ret) 74462306a36Sopenharmony_ci goto out; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Set PHY register data */ 74762306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, 74862306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); 74962306a36Sopenharmony_ci if (ret) 75062306a36Sopenharmony_ci goto out; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Execute write operation */ 75362306a36Sopenharmony_ci val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, 75462306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | 75562306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, 75662306a36Sopenharmony_ci RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); 75762306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, 75862306a36Sopenharmony_ci val); 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci goto out; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = rtl8365mb_phy_poll_busy(priv); 76362306a36Sopenharmony_ci if (ret) 76462306a36Sopenharmony_ci goto out; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciout: 76762306a36Sopenharmony_ci mutex_unlock(&priv->map_lock); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci u32 ocp_addr; 77562306a36Sopenharmony_ci u16 val; 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (phy > RTL8365MB_PHYADDRMAX) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (regnum > RTL8365MB_PHYREGMAX) 78262306a36Sopenharmony_ci return -EINVAL; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); 78762306a36Sopenharmony_ci if (ret) { 78862306a36Sopenharmony_ci dev_err(priv->dev, 78962306a36Sopenharmony_ci "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, 79062306a36Sopenharmony_ci regnum, ocp_addr, ret); 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", 79562306a36Sopenharmony_ci phy, regnum, ocp_addr, val); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return val; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, 80162306a36Sopenharmony_ci u16 val) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci u32 ocp_addr; 80462306a36Sopenharmony_ci int ret; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (phy > RTL8365MB_PHYADDRMAX) 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (regnum > RTL8365MB_PHYREGMAX) 81062306a36Sopenharmony_ci return -EINVAL; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); 81562306a36Sopenharmony_ci if (ret) { 81662306a36Sopenharmony_ci dev_err(priv->dev, 81762306a36Sopenharmony_ci "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, 81862306a36Sopenharmony_ci regnum, ocp_addr, ret); 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", 82362306a36Sopenharmony_ci phy, regnum, ocp_addr, val); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci return rtl8365mb_phy_read(ds->priv, phy, regnum); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, 83462306a36Sopenharmony_ci u16 val) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci return rtl8365mb_phy_write(ds->priv, phy, regnum, val); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic const struct rtl8365mb_extint * 84062306a36Sopenharmony_cirtl8365mb_get_port_extint(struct realtek_priv *priv, int port) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 84362306a36Sopenharmony_ci int i; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) { 84662306a36Sopenharmony_ci const struct rtl8365mb_extint *extint = 84762306a36Sopenharmony_ci &mb->chip_info->extints[i]; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!extint->supported_interfaces) 85062306a36Sopenharmony_ci continue; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (extint->port == port) 85362306a36Sopenharmony_ci return extint; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return NULL; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic enum dsa_tag_protocol 86062306a36Sopenharmony_cirtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, 86162306a36Sopenharmony_ci enum dsa_tag_protocol mp) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 86462306a36Sopenharmony_ci struct rtl8365mb_cpu *cpu; 86562306a36Sopenharmony_ci struct rtl8365mb *mb; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci mb = priv->chip_data; 86862306a36Sopenharmony_ci cpu = &mb->cpu; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) 87162306a36Sopenharmony_ci return DSA_TAG_PROTO_RTL8_4T; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return DSA_TAG_PROTO_RTL8_4; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, 87762306a36Sopenharmony_ci phy_interface_t interface) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci const struct rtl8365mb_extint *extint = 88062306a36Sopenharmony_ci rtl8365mb_get_port_extint(priv, port); 88162306a36Sopenharmony_ci struct device_node *dn; 88262306a36Sopenharmony_ci struct dsa_port *dp; 88362306a36Sopenharmony_ci int tx_delay = 0; 88462306a36Sopenharmony_ci int rx_delay = 0; 88562306a36Sopenharmony_ci u32 val; 88662306a36Sopenharmony_ci int ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (!extint) 88962306a36Sopenharmony_ci return -ENODEV; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci dp = dsa_to_port(priv->ds, port); 89262306a36Sopenharmony_ci dn = dp->dn; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Set the RGMII TX/RX delay 89562306a36Sopenharmony_ci * 89662306a36Sopenharmony_ci * The Realtek vendor driver indicates the following possible 89762306a36Sopenharmony_ci * configuration settings: 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * TX delay: 90062306a36Sopenharmony_ci * 0 = no delay, 1 = 2 ns delay 90162306a36Sopenharmony_ci * RX delay: 90262306a36Sopenharmony_ci * 0 = no delay, 7 = maximum delay 90362306a36Sopenharmony_ci * Each step is approximately 0.3 ns, so the maximum delay is about 90462306a36Sopenharmony_ci * 2.1 ns. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * The vendor driver also states that this must be configured *before* 90762306a36Sopenharmony_ci * forcing the external interface into a particular mode, which is done 90862306a36Sopenharmony_ci * in the rtl8365mb_phylink_mac_link_{up,down} functions. 90962306a36Sopenharmony_ci * 91062306a36Sopenharmony_ci * Only configure an RGMII TX (resp. RX) delay if the 91162306a36Sopenharmony_ci * tx-internal-delay-ps (resp. rx-internal-delay-ps) OF property is 91262306a36Sopenharmony_ci * specified. We ignore the detail of the RGMII interface mode 91362306a36Sopenharmony_ci * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only 91462306a36Sopenharmony_ci * property. 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) { 91762306a36Sopenharmony_ci val = val / 1000; /* convert to ns */ 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (val == 0 || val == 2) 92062306a36Sopenharmony_ci tx_delay = val / 2; 92162306a36Sopenharmony_ci else 92262306a36Sopenharmony_ci dev_warn(priv->dev, 92362306a36Sopenharmony_ci "RGMII TX delay must be 0 or 2 ns\n"); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { 92762306a36Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */ 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (val <= 7) 93062306a36Sopenharmony_ci rx_delay = val; 93162306a36Sopenharmony_ci else 93262306a36Sopenharmony_ci dev_warn(priv->dev, 93362306a36Sopenharmony_ci "RGMII RX delay must be 0 to 2.1 ns\n"); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ret = regmap_update_bits( 93762306a36Sopenharmony_ci priv->map, RTL8365MB_EXT_RGMXF_REG(extint->id), 93862306a36Sopenharmony_ci RTL8365MB_EXT_RGMXF_TXDELAY_MASK | 93962306a36Sopenharmony_ci RTL8365MB_EXT_RGMXF_RXDELAY_MASK, 94062306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) | 94162306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay)); 94262306a36Sopenharmony_ci if (ret) 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci ret = regmap_update_bits( 94662306a36Sopenharmony_ci priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id), 94762306a36Sopenharmony_ci RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id), 94862306a36Sopenharmony_ci RTL8365MB_EXT_PORT_MODE_RGMII 94962306a36Sopenharmony_ci << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET( 95062306a36Sopenharmony_ci extint->id)); 95162306a36Sopenharmony_ci if (ret) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, 95862306a36Sopenharmony_ci bool link, int speed, int duplex, 95962306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci const struct rtl8365mb_extint *extint = 96262306a36Sopenharmony_ci rtl8365mb_get_port_extint(priv, port); 96362306a36Sopenharmony_ci u32 r_tx_pause; 96462306a36Sopenharmony_ci u32 r_rx_pause; 96562306a36Sopenharmony_ci u32 r_duplex; 96662306a36Sopenharmony_ci u32 r_speed; 96762306a36Sopenharmony_ci u32 r_link; 96862306a36Sopenharmony_ci int val; 96962306a36Sopenharmony_ci int ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!extint) 97262306a36Sopenharmony_ci return -ENODEV; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (link) { 97562306a36Sopenharmony_ci /* Force the link up with the desired configuration */ 97662306a36Sopenharmony_ci r_link = 1; 97762306a36Sopenharmony_ci r_rx_pause = rx_pause ? 1 : 0; 97862306a36Sopenharmony_ci r_tx_pause = tx_pause ? 1 : 0; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (speed == SPEED_1000) { 98162306a36Sopenharmony_ci r_speed = RTL8365MB_PORT_SPEED_1000M; 98262306a36Sopenharmony_ci } else if (speed == SPEED_100) { 98362306a36Sopenharmony_ci r_speed = RTL8365MB_PORT_SPEED_100M; 98462306a36Sopenharmony_ci } else if (speed == SPEED_10) { 98562306a36Sopenharmony_ci r_speed = RTL8365MB_PORT_SPEED_10M; 98662306a36Sopenharmony_ci } else { 98762306a36Sopenharmony_ci dev_err(priv->dev, "unsupported port speed %s\n", 98862306a36Sopenharmony_ci phy_speed_to_str(speed)); 98962306a36Sopenharmony_ci return -EINVAL; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) { 99362306a36Sopenharmony_ci r_duplex = 1; 99462306a36Sopenharmony_ci } else if (duplex == DUPLEX_HALF) { 99562306a36Sopenharmony_ci r_duplex = 0; 99662306a36Sopenharmony_ci } else { 99762306a36Sopenharmony_ci dev_err(priv->dev, "unsupported duplex %s\n", 99862306a36Sopenharmony_ci phy_duplex_to_str(duplex)); 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci } else { 100262306a36Sopenharmony_ci /* Force the link down and reset any programmed configuration */ 100362306a36Sopenharmony_ci r_link = 0; 100462306a36Sopenharmony_ci r_tx_pause = 0; 100562306a36Sopenharmony_ci r_rx_pause = 0; 100662306a36Sopenharmony_ci r_speed = 0; 100762306a36Sopenharmony_ci r_duplex = 0; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) | 101162306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK, 101262306a36Sopenharmony_ci r_tx_pause) | 101362306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK, 101462306a36Sopenharmony_ci r_rx_pause) | 101562306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK, r_link) | 101662306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK, 101762306a36Sopenharmony_ci r_duplex) | 101862306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed); 101962306a36Sopenharmony_ci ret = regmap_write(priv->map, 102062306a36Sopenharmony_ci RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id), 102162306a36Sopenharmony_ci val); 102262306a36Sopenharmony_ci if (ret) 102362306a36Sopenharmony_ci return ret; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, 102962306a36Sopenharmony_ci struct phylink_config *config) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci const struct rtl8365mb_extint *extint = 103262306a36Sopenharmony_ci rtl8365mb_get_port_extint(ds->priv, port); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | 103562306a36Sopenharmony_ci MAC_10 | MAC_100 | MAC_1000FD; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (!extint) { 103862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_INTERNAL, 103962306a36Sopenharmony_ci config->supported_interfaces); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* GMII is the default interface mode for phylib, so 104262306a36Sopenharmony_ci * we have to support it for ports with integrated PHY. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_GMII, 104562306a36Sopenharmony_ci config->supported_interfaces); 104662306a36Sopenharmony_ci return; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Populate according to the modes supported by _this driver_, 105062306a36Sopenharmony_ci * not necessarily the modes supported by the hardware, some of 105162306a36Sopenharmony_ci * which remain unimplemented. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_RGMII) 105562306a36Sopenharmony_ci phy_interface_set_rgmii(config->supported_interfaces); 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic void rtl8365mb_phylink_mac_config(struct dsa_switch *ds, int port, 105962306a36Sopenharmony_ci unsigned int mode, 106062306a36Sopenharmony_ci const struct phylink_link_state *state) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 106362306a36Sopenharmony_ci int ret; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) { 106662306a36Sopenharmony_ci dev_err(priv->dev, 106762306a36Sopenharmony_ci "port %d supports only conventional PHY or fixed-link\n", 106862306a36Sopenharmony_ci port); 106962306a36Sopenharmony_ci return; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (phy_interface_mode_is_rgmii(state->interface)) { 107362306a36Sopenharmony_ci ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); 107462306a36Sopenharmony_ci if (ret) 107562306a36Sopenharmony_ci dev_err(priv->dev, 107662306a36Sopenharmony_ci "failed to configure RGMII mode on port %d: %d\n", 107762306a36Sopenharmony_ci port, ret); 107862306a36Sopenharmony_ci return; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also 108262306a36Sopenharmony_ci * supports 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic void rtl8365mb_phylink_mac_link_down(struct dsa_switch *ds, int port, 108762306a36Sopenharmony_ci unsigned int mode, 108862306a36Sopenharmony_ci phy_interface_t interface) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 109162306a36Sopenharmony_ci struct rtl8365mb_port *p; 109262306a36Sopenharmony_ci struct rtl8365mb *mb; 109362306a36Sopenharmony_ci int ret; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci mb = priv->chip_data; 109662306a36Sopenharmony_ci p = &mb->ports[port]; 109762306a36Sopenharmony_ci cancel_delayed_work_sync(&p->mib_work); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (phy_interface_mode_is_rgmii(interface)) { 110062306a36Sopenharmony_ci ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0, 110162306a36Sopenharmony_ci false, false); 110262306a36Sopenharmony_ci if (ret) 110362306a36Sopenharmony_ci dev_err(priv->dev, 110462306a36Sopenharmony_ci "failed to reset forced mode on port %d: %d\n", 110562306a36Sopenharmony_ci port, ret); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci return; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, 111262306a36Sopenharmony_ci unsigned int mode, 111362306a36Sopenharmony_ci phy_interface_t interface, 111462306a36Sopenharmony_ci struct phy_device *phydev, int speed, 111562306a36Sopenharmony_ci int duplex, bool tx_pause, 111662306a36Sopenharmony_ci bool rx_pause) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 111962306a36Sopenharmony_ci struct rtl8365mb_port *p; 112062306a36Sopenharmony_ci struct rtl8365mb *mb; 112162306a36Sopenharmony_ci int ret; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci mb = priv->chip_data; 112462306a36Sopenharmony_ci p = &mb->ports[port]; 112562306a36Sopenharmony_ci schedule_delayed_work(&p->mib_work, 0); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (phy_interface_mode_is_rgmii(interface)) { 112862306a36Sopenharmony_ci ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed, 112962306a36Sopenharmony_ci duplex, tx_pause, 113062306a36Sopenharmony_ci rx_pause); 113162306a36Sopenharmony_ci if (ret) 113262306a36Sopenharmony_ci dev_err(priv->dev, 113362306a36Sopenharmony_ci "failed to force mode on port %d: %d\n", port, 113462306a36Sopenharmony_ci ret); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci return; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic int rtl8365mb_port_change_mtu(struct dsa_switch *ds, int port, 114162306a36Sopenharmony_ci int new_mtu) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 114462306a36Sopenharmony_ci int frame_size; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* When a new MTU is set, DSA always sets the CPU port's MTU to the 114762306a36Sopenharmony_ci * largest MTU of the slave ports. Because the switch only has a global 114862306a36Sopenharmony_ci * RX length register, only allowing CPU port here is enough. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci if (!dsa_is_cpu_port(ds, port)) 115162306a36Sopenharmony_ci return 0; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci frame_size = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci dev_dbg(priv->dev, "changing mtu to %d (frame size: %d)\n", 115662306a36Sopenharmony_ci new_mtu, frame_size); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, 115962306a36Sopenharmony_ci RTL8365MB_CFG0_MAX_LEN_MASK, 116062306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 116162306a36Sopenharmony_ci frame_size)); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic int rtl8365mb_port_max_mtu(struct dsa_switch *ds, int port) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci return RTL8365MB_CFG0_MAX_LEN_MAX - VLAN_ETH_HLEN - ETH_FCS_LEN; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, 117062306a36Sopenharmony_ci u8 state) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 117362306a36Sopenharmony_ci enum rtl8365mb_stp_state val; 117462306a36Sopenharmony_ci int msti = 0; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci switch (state) { 117762306a36Sopenharmony_ci case BR_STATE_DISABLED: 117862306a36Sopenharmony_ci val = RTL8365MB_STP_STATE_DISABLED; 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci case BR_STATE_BLOCKING: 118162306a36Sopenharmony_ci case BR_STATE_LISTENING: 118262306a36Sopenharmony_ci val = RTL8365MB_STP_STATE_BLOCKING; 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci case BR_STATE_LEARNING: 118562306a36Sopenharmony_ci val = RTL8365MB_STP_STATE_LEARNING; 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci case BR_STATE_FORWARDING: 118862306a36Sopenharmony_ci val = RTL8365MB_STP_STATE_FORWARDING; 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci default: 119162306a36Sopenharmony_ci dev_err(priv->dev, "invalid STP state: %u\n", state); 119262306a36Sopenharmony_ci return; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port), 119662306a36Sopenharmony_ci RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port), 119762306a36Sopenharmony_ci val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, 120162306a36Sopenharmony_ci bool enable) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci /* Enable/disable learning by limiting the number of L2 addresses the 120462306a36Sopenharmony_ci * port can learn. Realtek documentation states that a limit of zero 120562306a36Sopenharmony_ci * disables learning. When enabling learning, set it to the chip's 120662306a36Sopenharmony_ci * maximum. 120762306a36Sopenharmony_ci */ 120862306a36Sopenharmony_ci return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), 120962306a36Sopenharmony_ci enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, 121362306a36Sopenharmony_ci u32 mask) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, 121962306a36Sopenharmony_ci u32 offset, u32 length, u64 *mibvalue) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci u64 tmpvalue = 0; 122262306a36Sopenharmony_ci u32 val; 122362306a36Sopenharmony_ci int ret; 122462306a36Sopenharmony_ci int i; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* The MIB address is an SRAM address. We request a particular address 122762306a36Sopenharmony_ci * and then poll the control register before reading the value from some 122862306a36Sopenharmony_ci * counter registers. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG, 123162306a36Sopenharmony_ci RTL8365MB_MIB_ADDRESS(port, offset)); 123262306a36Sopenharmony_ci if (ret) 123362306a36Sopenharmony_ci return ret; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* Poll for completion */ 123662306a36Sopenharmony_ci ret = regmap_read_poll_timeout(priv->map, RTL8365MB_MIB_CTRL0_REG, val, 123762306a36Sopenharmony_ci !(val & RTL8365MB_MIB_CTRL0_BUSY_MASK), 123862306a36Sopenharmony_ci 10, 100); 123962306a36Sopenharmony_ci if (ret) 124062306a36Sopenharmony_ci return ret; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* Presumably this indicates a MIB counter read failure */ 124362306a36Sopenharmony_ci if (val & RTL8365MB_MIB_CTRL0_RESET_MASK) 124462306a36Sopenharmony_ci return -EIO; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci /* There are four MIB counter registers each holding a 16 bit word of a 124762306a36Sopenharmony_ci * MIB counter. Depending on the offset, we should read from the upper 124862306a36Sopenharmony_ci * two or lower two registers. In case the MIB counter is 4 words, we 124962306a36Sopenharmony_ci * read from all four registers. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_ci if (length == 4) 125262306a36Sopenharmony_ci offset = 3; 125362306a36Sopenharmony_ci else 125462306a36Sopenharmony_ci offset = (offset + 1) % 4; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* Read the MIB counter 16 bits at a time */ 125762306a36Sopenharmony_ci for (i = 0; i < length; i++) { 125862306a36Sopenharmony_ci ret = regmap_read(priv->map, 125962306a36Sopenharmony_ci RTL8365MB_MIB_COUNTER_REG(offset - i), &val); 126062306a36Sopenharmony_ci if (ret) 126162306a36Sopenharmony_ci return ret; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci tmpvalue = ((tmpvalue) << 16) | (val & 0xFFFF); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* Only commit the result if no error occurred */ 126762306a36Sopenharmony_ci *mibvalue = tmpvalue; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return 0; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 127562306a36Sopenharmony_ci struct rtl8365mb *mb; 127662306a36Sopenharmony_ci int ret; 127762306a36Sopenharmony_ci int i; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci mb = priv->chip_data; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci mutex_lock(&mb->mib_lock); 128262306a36Sopenharmony_ci for (i = 0; i < RTL8365MB_MIB_END; i++) { 128362306a36Sopenharmony_ci struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, 128662306a36Sopenharmony_ci mib->length, &data[i]); 128762306a36Sopenharmony_ci if (ret) { 128862306a36Sopenharmony_ci dev_err(priv->dev, 128962306a36Sopenharmony_ci "failed to read port %d counters: %d\n", port, 129062306a36Sopenharmony_ci ret); 129162306a36Sopenharmony_ci break; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci mutex_unlock(&mb->mib_lock); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void rtl8365mb_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci int i; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (stringset != ETH_SS_STATS) 130262306a36Sopenharmony_ci return; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci for (i = 0; i < RTL8365MB_MIB_END; i++) { 130562306a36Sopenharmony_ci struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci strncpy(data + i * ETH_GSTRING_LEN, mib->name, ETH_GSTRING_LEN); 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic int rtl8365mb_get_sset_count(struct dsa_switch *ds, int port, int sset) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci if (sset != ETH_SS_STATS) 131462306a36Sopenharmony_ci return -EOPNOTSUPP; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci return RTL8365MB_MIB_END; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic void rtl8365mb_get_phy_stats(struct dsa_switch *ds, int port, 132062306a36Sopenharmony_ci struct ethtool_eth_phy_stats *phy_stats) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 132362306a36Sopenharmony_ci struct rtl8365mb_mib_counter *mib; 132462306a36Sopenharmony_ci struct rtl8365mb *mb; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci mb = priv->chip_data; 132762306a36Sopenharmony_ci mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3StatsSymbolErrors]; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci mutex_lock(&mb->mib_lock); 133062306a36Sopenharmony_ci rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, 133162306a36Sopenharmony_ci &phy_stats->SymbolErrorDuringCarrier); 133262306a36Sopenharmony_ci mutex_unlock(&mb->mib_lock); 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, 133662306a36Sopenharmony_ci struct ethtool_eth_mac_stats *mac_stats) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci u64 cnt[RTL8365MB_MIB_END] = { 133962306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutOctets] = 1, 134062306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutUcastPkts] = 1, 134162306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutMulticastPkts] = 1, 134262306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutBroadcastPkts] = 1, 134362306a36Sopenharmony_ci [RTL8365MB_MIB_dot3OutPauseFrames] = 1, 134462306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutDiscards] = 1, 134562306a36Sopenharmony_ci [RTL8365MB_MIB_ifInOctets] = 1, 134662306a36Sopenharmony_ci [RTL8365MB_MIB_ifInUcastPkts] = 1, 134762306a36Sopenharmony_ci [RTL8365MB_MIB_ifInMulticastPkts] = 1, 134862306a36Sopenharmony_ci [RTL8365MB_MIB_ifInBroadcastPkts] = 1, 134962306a36Sopenharmony_ci [RTL8365MB_MIB_dot3InPauseFrames] = 1, 135062306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsSingleCollisionFrames] = 1, 135162306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsMultipleCollisionFrames] = 1, 135262306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, 135362306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsDeferredTransmissions] = 1, 135462306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, 135562306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsExcessiveCollisions] = 1, 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci }; 135862306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 135962306a36Sopenharmony_ci struct rtl8365mb *mb; 136062306a36Sopenharmony_ci int ret; 136162306a36Sopenharmony_ci int i; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci mb = priv->chip_data; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci mutex_lock(&mb->mib_lock); 136662306a36Sopenharmony_ci for (i = 0; i < RTL8365MB_MIB_END; i++) { 136762306a36Sopenharmony_ci struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci /* Only fetch required MIB counters (marked = 1 above) */ 137062306a36Sopenharmony_ci if (!cnt[i]) 137162306a36Sopenharmony_ci continue; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, 137462306a36Sopenharmony_ci mib->length, &cnt[i]); 137562306a36Sopenharmony_ci if (ret) 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci mutex_unlock(&mb->mib_lock); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* The RTL8365MB-VC exposes MIB objects, which we have to translate into 138162306a36Sopenharmony_ci * IEEE 802.3 Managed Objects. This is not always completely faithful, 138262306a36Sopenharmony_ci * but we try out best. See RFC 3635 for a detailed treatment of the 138362306a36Sopenharmony_ci * subject. 138462306a36Sopenharmony_ci */ 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci mac_stats->FramesTransmittedOK = cnt[RTL8365MB_MIB_ifOutUcastPkts] + 138762306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutMulticastPkts] + 138862306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutBroadcastPkts] + 138962306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3OutPauseFrames] - 139062306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutDiscards]; 139162306a36Sopenharmony_ci mac_stats->SingleCollisionFrames = 139262306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3StatsSingleCollisionFrames]; 139362306a36Sopenharmony_ci mac_stats->MultipleCollisionFrames = 139462306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3StatsMultipleCollisionFrames]; 139562306a36Sopenharmony_ci mac_stats->FramesReceivedOK = cnt[RTL8365MB_MIB_ifInUcastPkts] + 139662306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInMulticastPkts] + 139762306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInBroadcastPkts] + 139862306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3InPauseFrames]; 139962306a36Sopenharmony_ci mac_stats->FrameCheckSequenceErrors = 140062306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3StatsFCSErrors]; 140162306a36Sopenharmony_ci mac_stats->OctetsTransmittedOK = cnt[RTL8365MB_MIB_ifOutOctets] - 140262306a36Sopenharmony_ci 18 * mac_stats->FramesTransmittedOK; 140362306a36Sopenharmony_ci mac_stats->FramesWithDeferredXmissions = 140462306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3StatsDeferredTransmissions]; 140562306a36Sopenharmony_ci mac_stats->LateCollisions = cnt[RTL8365MB_MIB_dot3StatsLateCollisions]; 140662306a36Sopenharmony_ci mac_stats->FramesAbortedDueToXSColls = 140762306a36Sopenharmony_ci cnt[RTL8365MB_MIB_dot3StatsExcessiveCollisions]; 140862306a36Sopenharmony_ci mac_stats->OctetsReceivedOK = cnt[RTL8365MB_MIB_ifInOctets] - 140962306a36Sopenharmony_ci 18 * mac_stats->FramesReceivedOK; 141062306a36Sopenharmony_ci mac_stats->MulticastFramesXmittedOK = 141162306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutMulticastPkts]; 141262306a36Sopenharmony_ci mac_stats->BroadcastFramesXmittedOK = 141362306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutBroadcastPkts]; 141462306a36Sopenharmony_ci mac_stats->MulticastFramesReceivedOK = 141562306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInMulticastPkts]; 141662306a36Sopenharmony_ci mac_stats->BroadcastFramesReceivedOK = 141762306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInBroadcastPkts]; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void rtl8365mb_get_ctrl_stats(struct dsa_switch *ds, int port, 142162306a36Sopenharmony_ci struct ethtool_eth_ctrl_stats *ctrl_stats) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 142462306a36Sopenharmony_ci struct rtl8365mb_mib_counter *mib; 142562306a36Sopenharmony_ci struct rtl8365mb *mb; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci mb = priv->chip_data; 142862306a36Sopenharmony_ci mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3ControlInUnknownOpcodes]; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci mutex_lock(&mb->mib_lock); 143162306a36Sopenharmony_ci rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, 143262306a36Sopenharmony_ci &ctrl_stats->UnsupportedOpcodesReceived); 143362306a36Sopenharmony_ci mutex_unlock(&mb->mib_lock); 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic void rtl8365mb_stats_update(struct realtek_priv *priv, int port) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci u64 cnt[RTL8365MB_MIB_END] = { 143962306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutOctets] = 1, 144062306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutUcastPkts] = 1, 144162306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutMulticastPkts] = 1, 144262306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutBroadcastPkts] = 1, 144362306a36Sopenharmony_ci [RTL8365MB_MIB_ifOutDiscards] = 1, 144462306a36Sopenharmony_ci [RTL8365MB_MIB_ifInOctets] = 1, 144562306a36Sopenharmony_ci [RTL8365MB_MIB_ifInUcastPkts] = 1, 144662306a36Sopenharmony_ci [RTL8365MB_MIB_ifInMulticastPkts] = 1, 144762306a36Sopenharmony_ci [RTL8365MB_MIB_ifInBroadcastPkts] = 1, 144862306a36Sopenharmony_ci [RTL8365MB_MIB_etherStatsDropEvents] = 1, 144962306a36Sopenharmony_ci [RTL8365MB_MIB_etherStatsCollisions] = 1, 145062306a36Sopenharmony_ci [RTL8365MB_MIB_etherStatsFragments] = 1, 145162306a36Sopenharmony_ci [RTL8365MB_MIB_etherStatsJabbers] = 1, 145262306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, 145362306a36Sopenharmony_ci [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, 145462306a36Sopenharmony_ci }; 145562306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 145662306a36Sopenharmony_ci struct rtnl_link_stats64 *stats; 145762306a36Sopenharmony_ci int ret; 145862306a36Sopenharmony_ci int i; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci stats = &mb->ports[port].stats; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci mutex_lock(&mb->mib_lock); 146362306a36Sopenharmony_ci for (i = 0; i < RTL8365MB_MIB_END; i++) { 146462306a36Sopenharmony_ci struct rtl8365mb_mib_counter *c = &rtl8365mb_mib_counters[i]; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* Only fetch required MIB counters (marked = 1 above) */ 146762306a36Sopenharmony_ci if (!cnt[i]) 146862306a36Sopenharmony_ci continue; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci ret = rtl8365mb_mib_counter_read(priv, port, c->offset, 147162306a36Sopenharmony_ci c->length, &cnt[i]); 147262306a36Sopenharmony_ci if (ret) 147362306a36Sopenharmony_ci break; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci mutex_unlock(&mb->mib_lock); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci /* Don't update statistics if there was an error reading the counters */ 147862306a36Sopenharmony_ci if (ret) 147962306a36Sopenharmony_ci return; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci spin_lock(&mb->ports[port].stats_lock); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci stats->rx_packets = cnt[RTL8365MB_MIB_ifInUcastPkts] + 148462306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInMulticastPkts] + 148562306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifInBroadcastPkts] - 148662306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutDiscards]; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci stats->tx_packets = cnt[RTL8365MB_MIB_ifOutUcastPkts] + 148962306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutMulticastPkts] + 149062306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutBroadcastPkts]; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* if{In,Out}Octets includes FCS - remove it */ 149362306a36Sopenharmony_ci stats->rx_bytes = cnt[RTL8365MB_MIB_ifInOctets] - 4 * stats->rx_packets; 149462306a36Sopenharmony_ci stats->tx_bytes = 149562306a36Sopenharmony_ci cnt[RTL8365MB_MIB_ifOutOctets] - 4 * stats->tx_packets; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci stats->rx_dropped = cnt[RTL8365MB_MIB_etherStatsDropEvents]; 149862306a36Sopenharmony_ci stats->tx_dropped = cnt[RTL8365MB_MIB_ifOutDiscards]; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci stats->multicast = cnt[RTL8365MB_MIB_ifInMulticastPkts]; 150162306a36Sopenharmony_ci stats->collisions = cnt[RTL8365MB_MIB_etherStatsCollisions]; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci stats->rx_length_errors = cnt[RTL8365MB_MIB_etherStatsFragments] + 150462306a36Sopenharmony_ci cnt[RTL8365MB_MIB_etherStatsJabbers]; 150562306a36Sopenharmony_ci stats->rx_crc_errors = cnt[RTL8365MB_MIB_dot3StatsFCSErrors]; 150662306a36Sopenharmony_ci stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci stats->tx_aborted_errors = cnt[RTL8365MB_MIB_ifOutDiscards]; 150962306a36Sopenharmony_ci stats->tx_window_errors = cnt[RTL8365MB_MIB_dot3StatsLateCollisions]; 151062306a36Sopenharmony_ci stats->tx_errors = stats->tx_aborted_errors + stats->tx_window_errors; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci spin_unlock(&mb->ports[port].stats_lock); 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic void rtl8365mb_stats_poll(struct work_struct *work) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct rtl8365mb_port *p = container_of(to_delayed_work(work), 151862306a36Sopenharmony_ci struct rtl8365mb_port, 151962306a36Sopenharmony_ci mib_work); 152062306a36Sopenharmony_ci struct realtek_priv *priv = p->priv; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci rtl8365mb_stats_update(priv, p->index); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci schedule_delayed_work(&p->mib_work, RTL8365MB_STATS_INTERVAL_JIFFIES); 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, 152862306a36Sopenharmony_ci struct rtnl_link_stats64 *s) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 153162306a36Sopenharmony_ci struct rtl8365mb_port *p; 153262306a36Sopenharmony_ci struct rtl8365mb *mb; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci mb = priv->chip_data; 153562306a36Sopenharmony_ci p = &mb->ports[port]; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci spin_lock(&p->stats_lock); 153862306a36Sopenharmony_ci memcpy(s, &p->stats, sizeof(*s)); 153962306a36Sopenharmony_ci spin_unlock(&p->stats_lock); 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic void rtl8365mb_stats_setup(struct realtek_priv *priv) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 154562306a36Sopenharmony_ci int i; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* Per-chip global mutex to protect MIB counter access, since doing 154862306a36Sopenharmony_ci * so requires accessing a series of registers in a particular order. 154962306a36Sopenharmony_ci */ 155062306a36Sopenharmony_ci mutex_init(&mb->mib_lock); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 155362306a36Sopenharmony_ci struct rtl8365mb_port *p = &mb->ports[i]; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci if (dsa_is_unused_port(priv->ds, i)) 155662306a36Sopenharmony_ci continue; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* Per-port spinlock to protect the stats64 data */ 155962306a36Sopenharmony_ci spin_lock_init(&p->stats_lock); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* This work polls the MIB counters and keeps the stats64 data 156262306a36Sopenharmony_ci * up-to-date. 156362306a36Sopenharmony_ci */ 156462306a36Sopenharmony_ci INIT_DELAYED_WORK(&p->mib_work, rtl8365mb_stats_poll); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistatic void rtl8365mb_stats_teardown(struct realtek_priv *priv) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 157162306a36Sopenharmony_ci int i; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 157462306a36Sopenharmony_ci struct rtl8365mb_port *p = &mb->ports[i]; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (dsa_is_unused_port(priv->ds, i)) 157762306a36Sopenharmony_ci continue; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci cancel_delayed_work_sync(&p->mib_work); 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci} 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_cistatic int rtl8365mb_get_and_clear_status_reg(struct realtek_priv *priv, u32 reg, 158462306a36Sopenharmony_ci u32 *val) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci int ret; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci ret = regmap_read(priv->map, reg, val); 158962306a36Sopenharmony_ci if (ret) 159062306a36Sopenharmony_ci return ret; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci return regmap_write(priv->map, reg, *val); 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic irqreturn_t rtl8365mb_irq(int irq, void *data) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct realtek_priv *priv = data; 159862306a36Sopenharmony_ci unsigned long line_changes = 0; 159962306a36Sopenharmony_ci u32 stat; 160062306a36Sopenharmony_ci int line; 160162306a36Sopenharmony_ci int ret; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci ret = rtl8365mb_get_and_clear_status_reg(priv, RTL8365MB_INTR_STATUS_REG, 160462306a36Sopenharmony_ci &stat); 160562306a36Sopenharmony_ci if (ret) 160662306a36Sopenharmony_ci goto out_error; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (stat & RTL8365MB_INTR_LINK_CHANGE_MASK) { 160962306a36Sopenharmony_ci u32 linkdown_ind; 161062306a36Sopenharmony_ci u32 linkup_ind; 161162306a36Sopenharmony_ci u32 val; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci ret = rtl8365mb_get_and_clear_status_reg( 161462306a36Sopenharmony_ci priv, RTL8365MB_PORT_LINKUP_IND_REG, &val); 161562306a36Sopenharmony_ci if (ret) 161662306a36Sopenharmony_ci goto out_error; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci linkup_ind = FIELD_GET(RTL8365MB_PORT_LINKUP_IND_MASK, val); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci ret = rtl8365mb_get_and_clear_status_reg( 162162306a36Sopenharmony_ci priv, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); 162262306a36Sopenharmony_ci if (ret) 162362306a36Sopenharmony_ci goto out_error; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci linkdown_ind = FIELD_GET(RTL8365MB_PORT_LINKDOWN_IND_MASK, val); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci line_changes = linkup_ind | linkdown_ind; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!line_changes) 163162306a36Sopenharmony_ci goto out_none; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci for_each_set_bit(line, &line_changes, priv->num_ports) { 163462306a36Sopenharmony_ci int child_irq = irq_find_mapping(priv->irqdomain, line); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci handle_nested_irq(child_irq); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return IRQ_HANDLED; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ciout_error: 164262306a36Sopenharmony_ci dev_err(priv->dev, "failed to read interrupt status: %d\n", ret); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ciout_none: 164562306a36Sopenharmony_ci return IRQ_NONE; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic struct irq_chip rtl8365mb_irq_chip = { 164962306a36Sopenharmony_ci .name = "rtl8365mb", 165062306a36Sopenharmony_ci /* The hardware doesn't support masking IRQs on a per-port basis */ 165162306a36Sopenharmony_ci}; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic int rtl8365mb_irq_map(struct irq_domain *domain, unsigned int irq, 165462306a36Sopenharmony_ci irq_hw_number_t hwirq) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci irq_set_chip_data(irq, domain->host_data); 165762306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &rtl8365mb_irq_chip, handle_simple_irq); 165862306a36Sopenharmony_ci irq_set_nested_thread(irq, 1); 165962306a36Sopenharmony_ci irq_set_noprobe(irq); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci return 0; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic void rtl8365mb_irq_unmap(struct irq_domain *d, unsigned int irq) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci irq_set_nested_thread(irq, 0); 166762306a36Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 166862306a36Sopenharmony_ci irq_set_chip_data(irq, NULL); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic const struct irq_domain_ops rtl8365mb_irqdomain_ops = { 167262306a36Sopenharmony_ci .map = rtl8365mb_irq_map, 167362306a36Sopenharmony_ci .unmap = rtl8365mb_irq_unmap, 167462306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 167562306a36Sopenharmony_ci}; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG, 168062306a36Sopenharmony_ci RTL8365MB_INTR_LINK_CHANGE_MASK, 168162306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK, 168262306a36Sopenharmony_ci enable ? 1 : 0)); 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cistatic int rtl8365mb_irq_enable(struct realtek_priv *priv) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci return rtl8365mb_set_irq_enable(priv, true); 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic int rtl8365mb_irq_disable(struct realtek_priv *priv) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci return rtl8365mb_set_irq_enable(priv, false); 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int rtl8365mb_irq_setup(struct realtek_priv *priv) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 169862306a36Sopenharmony_ci struct device_node *intc; 169962306a36Sopenharmony_ci u32 irq_trig; 170062306a36Sopenharmony_ci int virq; 170162306a36Sopenharmony_ci int irq; 170262306a36Sopenharmony_ci u32 val; 170362306a36Sopenharmony_ci int ret; 170462306a36Sopenharmony_ci int i; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); 170762306a36Sopenharmony_ci if (!intc) { 170862306a36Sopenharmony_ci dev_err(priv->dev, "missing child interrupt-controller node\n"); 170962306a36Sopenharmony_ci return -EINVAL; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* rtl8365mb IRQs cascade off this one */ 171362306a36Sopenharmony_ci irq = of_irq_get(intc, 0); 171462306a36Sopenharmony_ci if (irq <= 0) { 171562306a36Sopenharmony_ci if (irq != -EPROBE_DEFER) 171662306a36Sopenharmony_ci dev_err(priv->dev, "failed to get parent irq: %d\n", 171762306a36Sopenharmony_ci irq); 171862306a36Sopenharmony_ci ret = irq ? irq : -EINVAL; 171962306a36Sopenharmony_ci goto out_put_node; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci priv->irqdomain = irq_domain_add_linear(intc, priv->num_ports, 172362306a36Sopenharmony_ci &rtl8365mb_irqdomain_ops, priv); 172462306a36Sopenharmony_ci if (!priv->irqdomain) { 172562306a36Sopenharmony_ci dev_err(priv->dev, "failed to add irq domain\n"); 172662306a36Sopenharmony_ci ret = -ENOMEM; 172762306a36Sopenharmony_ci goto out_put_node; 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 173162306a36Sopenharmony_ci virq = irq_create_mapping(priv->irqdomain, i); 173262306a36Sopenharmony_ci if (!virq) { 173362306a36Sopenharmony_ci dev_err(priv->dev, 173462306a36Sopenharmony_ci "failed to create irq domain mapping\n"); 173562306a36Sopenharmony_ci ret = -EINVAL; 173662306a36Sopenharmony_ci goto out_remove_irqdomain; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci irq_set_parent(virq, irq); 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci /* Configure chip interrupt signal polarity */ 174362306a36Sopenharmony_ci irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); 174462306a36Sopenharmony_ci switch (irq_trig) { 174562306a36Sopenharmony_ci case IRQF_TRIGGER_RISING: 174662306a36Sopenharmony_ci case IRQF_TRIGGER_HIGH: 174762306a36Sopenharmony_ci val = RTL8365MB_INTR_POLARITY_HIGH; 174862306a36Sopenharmony_ci break; 174962306a36Sopenharmony_ci case IRQF_TRIGGER_FALLING: 175062306a36Sopenharmony_ci case IRQF_TRIGGER_LOW: 175162306a36Sopenharmony_ci val = RTL8365MB_INTR_POLARITY_LOW; 175262306a36Sopenharmony_ci break; 175362306a36Sopenharmony_ci default: 175462306a36Sopenharmony_ci dev_err(priv->dev, "unsupported irq trigger type %u\n", 175562306a36Sopenharmony_ci irq_trig); 175662306a36Sopenharmony_ci ret = -EINVAL; 175762306a36Sopenharmony_ci goto out_remove_irqdomain; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8365MB_INTR_POLARITY_REG, 176162306a36Sopenharmony_ci RTL8365MB_INTR_POLARITY_MASK, 176262306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val)); 176362306a36Sopenharmony_ci if (ret) 176462306a36Sopenharmony_ci goto out_remove_irqdomain; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci /* Disable the interrupt in case the chip has it enabled on reset */ 176762306a36Sopenharmony_ci ret = rtl8365mb_irq_disable(priv); 176862306a36Sopenharmony_ci if (ret) 176962306a36Sopenharmony_ci goto out_remove_irqdomain; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci /* Clear the interrupt status register */ 177262306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG, 177362306a36Sopenharmony_ci RTL8365MB_INTR_ALL_MASK); 177462306a36Sopenharmony_ci if (ret) 177562306a36Sopenharmony_ci goto out_remove_irqdomain; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, 177862306a36Sopenharmony_ci "rtl8365mb", priv); 177962306a36Sopenharmony_ci if (ret) { 178062306a36Sopenharmony_ci dev_err(priv->dev, "failed to request irq: %d\n", ret); 178162306a36Sopenharmony_ci goto out_remove_irqdomain; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* Store the irq so that we know to free it during teardown */ 178562306a36Sopenharmony_ci mb->irq = irq; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci ret = rtl8365mb_irq_enable(priv); 178862306a36Sopenharmony_ci if (ret) 178962306a36Sopenharmony_ci goto out_free_irq; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci of_node_put(intc); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci return 0; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ciout_free_irq: 179662306a36Sopenharmony_ci free_irq(mb->irq, priv); 179762306a36Sopenharmony_ci mb->irq = 0; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ciout_remove_irqdomain: 180062306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 180162306a36Sopenharmony_ci virq = irq_find_mapping(priv->irqdomain, i); 180262306a36Sopenharmony_ci irq_dispose_mapping(virq); 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci irq_domain_remove(priv->irqdomain); 180662306a36Sopenharmony_ci priv->irqdomain = NULL; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ciout_put_node: 180962306a36Sopenharmony_ci of_node_put(intc); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci return ret; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic void rtl8365mb_irq_teardown(struct realtek_priv *priv) 181562306a36Sopenharmony_ci{ 181662306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 181762306a36Sopenharmony_ci int virq; 181862306a36Sopenharmony_ci int i; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (mb->irq) { 182162306a36Sopenharmony_ci free_irq(mb->irq, priv); 182262306a36Sopenharmony_ci mb->irq = 0; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (priv->irqdomain) { 182662306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 182762306a36Sopenharmony_ci virq = irq_find_mapping(priv->irqdomain, i); 182862306a36Sopenharmony_ci irq_dispose_mapping(virq); 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci irq_domain_remove(priv->irqdomain); 183262306a36Sopenharmony_ci priv->irqdomain = NULL; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic int rtl8365mb_cpu_config(struct realtek_priv *priv) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 183962306a36Sopenharmony_ci struct rtl8365mb_cpu *cpu = &mb->cpu; 184062306a36Sopenharmony_ci u32 val; 184162306a36Sopenharmony_ci int ret; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG, 184462306a36Sopenharmony_ci RTL8365MB_CPU_PORT_MASK_MASK, 184562306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK, 184662306a36Sopenharmony_ci cpu->mask)); 184762306a36Sopenharmony_ci if (ret) 184862306a36Sopenharmony_ci return ret; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci val = FIELD_PREP(RTL8365MB_CPU_CTRL_EN_MASK, cpu->enable ? 1 : 0) | 185162306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_INSERTMODE_MASK, cpu->insert) | 185262306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) | 185362306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) | 185462306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) | 185562306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) | 185662306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK, 185762306a36Sopenharmony_ci cpu->trap_port >> 3 & 0x1); 185862306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val); 185962306a36Sopenharmony_ci if (ret) 186062306a36Sopenharmony_ci return ret; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci return 0; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, 186662306a36Sopenharmony_ci enum dsa_tag_protocol proto) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 186962306a36Sopenharmony_ci struct rtl8365mb_cpu *cpu; 187062306a36Sopenharmony_ci struct rtl8365mb *mb; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci mb = priv->chip_data; 187362306a36Sopenharmony_ci cpu = &mb->cpu; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci switch (proto) { 187662306a36Sopenharmony_ci case DSA_TAG_PROTO_RTL8_4: 187762306a36Sopenharmony_ci cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; 187862306a36Sopenharmony_ci cpu->position = RTL8365MB_CPU_POS_AFTER_SA; 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci case DSA_TAG_PROTO_RTL8_4T: 188162306a36Sopenharmony_ci cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; 188262306a36Sopenharmony_ci cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC; 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci /* The switch also supports a 4-byte format, similar to rtl4a but with 188562306a36Sopenharmony_ci * the same 0x04 8-bit version and probably 8-bit port source/dest. 188662306a36Sopenharmony_ci * There is no public doc about it. Not supported yet and it will probably 188762306a36Sopenharmony_ci * never be. 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_ci default: 189062306a36Sopenharmony_ci return -EPROTONOSUPPORT; 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci return rtl8365mb_cpu_config(priv); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic int rtl8365mb_switch_init(struct realtek_priv *priv) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 189962306a36Sopenharmony_ci const struct rtl8365mb_chip_info *ci; 190062306a36Sopenharmony_ci int ret; 190162306a36Sopenharmony_ci int i; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci ci = mb->chip_info; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* Do any chip-specific init jam before getting to the common stuff */ 190662306a36Sopenharmony_ci if (ci->jam_table) { 190762306a36Sopenharmony_ci for (i = 0; i < ci->jam_size; i++) { 190862306a36Sopenharmony_ci ret = regmap_write(priv->map, ci->jam_table[i].reg, 190962306a36Sopenharmony_ci ci->jam_table[i].val); 191062306a36Sopenharmony_ci if (ret) 191162306a36Sopenharmony_ci return ret; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* Common init jam */ 191662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { 191762306a36Sopenharmony_ci ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, 191862306a36Sopenharmony_ci rtl8365mb_init_jam_common[i].val); 191962306a36Sopenharmony_ci if (ret) 192062306a36Sopenharmony_ci return ret; 192162306a36Sopenharmony_ci } 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci return 0; 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_cistatic int rtl8365mb_reset_chip(struct realtek_priv *priv) 192762306a36Sopenharmony_ci{ 192862306a36Sopenharmony_ci u32 val; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, 193162306a36Sopenharmony_ci FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci /* Realtek documentation says the chip needs 1 second to reset. Sleep 193462306a36Sopenharmony_ci * for 100 ms before accessing any registers to prevent ACK timeouts. 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_ci msleep(100); 193762306a36Sopenharmony_ci return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, 193862306a36Sopenharmony_ci !(val & RTL8365MB_CHIP_RESET_HW_MASK), 193962306a36Sopenharmony_ci 20000, 1e6); 194062306a36Sopenharmony_ci} 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_cistatic int rtl8365mb_setup(struct dsa_switch *ds) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 194562306a36Sopenharmony_ci struct rtl8365mb_cpu *cpu; 194662306a36Sopenharmony_ci struct dsa_port *cpu_dp; 194762306a36Sopenharmony_ci struct rtl8365mb *mb; 194862306a36Sopenharmony_ci int ret; 194962306a36Sopenharmony_ci int i; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci mb = priv->chip_data; 195262306a36Sopenharmony_ci cpu = &mb->cpu; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci ret = rtl8365mb_reset_chip(priv); 195562306a36Sopenharmony_ci if (ret) { 195662306a36Sopenharmony_ci dev_err(priv->dev, "failed to reset chip: %d\n", ret); 195762306a36Sopenharmony_ci goto out_error; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci /* Configure switch to vendor-defined initial state */ 196162306a36Sopenharmony_ci ret = rtl8365mb_switch_init(priv); 196262306a36Sopenharmony_ci if (ret) { 196362306a36Sopenharmony_ci dev_err(priv->dev, "failed to initialize switch: %d\n", ret); 196462306a36Sopenharmony_ci goto out_error; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci /* Set up cascading IRQs */ 196862306a36Sopenharmony_ci ret = rtl8365mb_irq_setup(priv); 196962306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 197062306a36Sopenharmony_ci return ret; 197162306a36Sopenharmony_ci else if (ret) 197262306a36Sopenharmony_ci dev_info(priv->dev, "no interrupt support\n"); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* Configure CPU tagging */ 197562306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { 197662306a36Sopenharmony_ci cpu->mask |= BIT(cpu_dp->index); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) 197962306a36Sopenharmony_ci cpu->trap_port = cpu_dp->index; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci cpu->enable = cpu->mask > 0; 198262306a36Sopenharmony_ci ret = rtl8365mb_cpu_config(priv); 198362306a36Sopenharmony_ci if (ret) 198462306a36Sopenharmony_ci goto out_teardown_irq; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci /* Configure ports */ 198762306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) { 198862306a36Sopenharmony_ci struct rtl8365mb_port *p = &mb->ports[i]; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci if (dsa_is_unused_port(priv->ds, i)) 199162306a36Sopenharmony_ci continue; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci /* Forward only to the CPU */ 199462306a36Sopenharmony_ci ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); 199562306a36Sopenharmony_ci if (ret) 199662306a36Sopenharmony_ci goto out_teardown_irq; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* Disable learning */ 199962306a36Sopenharmony_ci ret = rtl8365mb_port_set_learning(priv, i, false); 200062306a36Sopenharmony_ci if (ret) 200162306a36Sopenharmony_ci goto out_teardown_irq; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* Set the initial STP state of all ports to DISABLED, otherwise 200462306a36Sopenharmony_ci * ports will still forward frames to the CPU despite being 200562306a36Sopenharmony_ci * administratively down by default. 200662306a36Sopenharmony_ci */ 200762306a36Sopenharmony_ci rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci /* Set up per-port private data */ 201062306a36Sopenharmony_ci p->priv = priv; 201162306a36Sopenharmony_ci p->index = i; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN); 201562306a36Sopenharmony_ci if (ret) 201662306a36Sopenharmony_ci goto out_teardown_irq; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci if (priv->setup_interface) { 201962306a36Sopenharmony_ci ret = priv->setup_interface(ds); 202062306a36Sopenharmony_ci if (ret) { 202162306a36Sopenharmony_ci dev_err(priv->dev, "could not set up MDIO bus\n"); 202262306a36Sopenharmony_ci goto out_teardown_irq; 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* Start statistics counter polling */ 202762306a36Sopenharmony_ci rtl8365mb_stats_setup(priv); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci return 0; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ciout_teardown_irq: 203262306a36Sopenharmony_ci rtl8365mb_irq_teardown(priv); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ciout_error: 203562306a36Sopenharmony_ci return ret; 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic void rtl8365mb_teardown(struct dsa_switch *ds) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci rtl8365mb_stats_teardown(priv); 204362306a36Sopenharmony_ci rtl8365mb_irq_teardown(priv); 204462306a36Sopenharmony_ci} 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_cistatic int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) 204762306a36Sopenharmony_ci{ 204862306a36Sopenharmony_ci int ret; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* For some reason we have to write a magic value to an arbitrary 205162306a36Sopenharmony_ci * register whenever accessing the chip ID/version registers. 205262306a36Sopenharmony_ci */ 205362306a36Sopenharmony_ci ret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE); 205462306a36Sopenharmony_ci if (ret) 205562306a36Sopenharmony_ci return ret; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci ret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id); 205862306a36Sopenharmony_ci if (ret) 205962306a36Sopenharmony_ci return ret; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci ret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver); 206262306a36Sopenharmony_ci if (ret) 206362306a36Sopenharmony_ci return ret; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci /* Reset magic register */ 206662306a36Sopenharmony_ci ret = regmap_write(map, RTL8365MB_MAGIC_REG, 0); 206762306a36Sopenharmony_ci if (ret) 206862306a36Sopenharmony_ci return ret; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci return 0; 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic int rtl8365mb_detect(struct realtek_priv *priv) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct rtl8365mb *mb = priv->chip_data; 207662306a36Sopenharmony_ci u32 chip_id; 207762306a36Sopenharmony_ci u32 chip_ver; 207862306a36Sopenharmony_ci int ret; 207962306a36Sopenharmony_ci int i; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); 208262306a36Sopenharmony_ci if (ret) { 208362306a36Sopenharmony_ci dev_err(priv->dev, "failed to read chip id and version: %d\n", 208462306a36Sopenharmony_ci ret); 208562306a36Sopenharmony_ci return ret; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) { 208962306a36Sopenharmony_ci const struct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i]; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci if (ci->chip_id == chip_id && ci->chip_ver == chip_ver) { 209262306a36Sopenharmony_ci mb->chip_info = ci; 209362306a36Sopenharmony_ci break; 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci if (!mb->chip_info) { 209862306a36Sopenharmony_ci dev_err(priv->dev, 209962306a36Sopenharmony_ci "unrecognized switch (id=0x%04x, ver=0x%04x)", chip_id, 210062306a36Sopenharmony_ci chip_ver); 210162306a36Sopenharmony_ci return -ENODEV; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci priv->num_ports = RTL8365MB_MAX_NUM_PORTS; 210762306a36Sopenharmony_ci mb->priv = priv; 210862306a36Sopenharmony_ci mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS; 210962306a36Sopenharmony_ci mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; 211062306a36Sopenharmony_ci mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA; 211162306a36Sopenharmony_ci mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; 211262306a36Sopenharmony_ci mb->cpu.format = RTL8365MB_CPU_FORMAT_8BYTES; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci return 0; 211562306a36Sopenharmony_ci} 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_cistatic const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { 211862306a36Sopenharmony_ci .get_tag_protocol = rtl8365mb_get_tag_protocol, 211962306a36Sopenharmony_ci .change_tag_protocol = rtl8365mb_change_tag_protocol, 212062306a36Sopenharmony_ci .setup = rtl8365mb_setup, 212162306a36Sopenharmony_ci .teardown = rtl8365mb_teardown, 212262306a36Sopenharmony_ci .phylink_get_caps = rtl8365mb_phylink_get_caps, 212362306a36Sopenharmony_ci .phylink_mac_config = rtl8365mb_phylink_mac_config, 212462306a36Sopenharmony_ci .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, 212562306a36Sopenharmony_ci .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, 212662306a36Sopenharmony_ci .port_stp_state_set = rtl8365mb_port_stp_state_set, 212762306a36Sopenharmony_ci .get_strings = rtl8365mb_get_strings, 212862306a36Sopenharmony_ci .get_ethtool_stats = rtl8365mb_get_ethtool_stats, 212962306a36Sopenharmony_ci .get_sset_count = rtl8365mb_get_sset_count, 213062306a36Sopenharmony_ci .get_eth_phy_stats = rtl8365mb_get_phy_stats, 213162306a36Sopenharmony_ci .get_eth_mac_stats = rtl8365mb_get_mac_stats, 213262306a36Sopenharmony_ci .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, 213362306a36Sopenharmony_ci .get_stats64 = rtl8365mb_get_stats64, 213462306a36Sopenharmony_ci .port_change_mtu = rtl8365mb_port_change_mtu, 213562306a36Sopenharmony_ci .port_max_mtu = rtl8365mb_port_max_mtu, 213662306a36Sopenharmony_ci}; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_cistatic const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { 213962306a36Sopenharmony_ci .get_tag_protocol = rtl8365mb_get_tag_protocol, 214062306a36Sopenharmony_ci .change_tag_protocol = rtl8365mb_change_tag_protocol, 214162306a36Sopenharmony_ci .setup = rtl8365mb_setup, 214262306a36Sopenharmony_ci .teardown = rtl8365mb_teardown, 214362306a36Sopenharmony_ci .phylink_get_caps = rtl8365mb_phylink_get_caps, 214462306a36Sopenharmony_ci .phylink_mac_config = rtl8365mb_phylink_mac_config, 214562306a36Sopenharmony_ci .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, 214662306a36Sopenharmony_ci .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, 214762306a36Sopenharmony_ci .phy_read = rtl8365mb_dsa_phy_read, 214862306a36Sopenharmony_ci .phy_write = rtl8365mb_dsa_phy_write, 214962306a36Sopenharmony_ci .port_stp_state_set = rtl8365mb_port_stp_state_set, 215062306a36Sopenharmony_ci .get_strings = rtl8365mb_get_strings, 215162306a36Sopenharmony_ci .get_ethtool_stats = rtl8365mb_get_ethtool_stats, 215262306a36Sopenharmony_ci .get_sset_count = rtl8365mb_get_sset_count, 215362306a36Sopenharmony_ci .get_eth_phy_stats = rtl8365mb_get_phy_stats, 215462306a36Sopenharmony_ci .get_eth_mac_stats = rtl8365mb_get_mac_stats, 215562306a36Sopenharmony_ci .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, 215662306a36Sopenharmony_ci .get_stats64 = rtl8365mb_get_stats64, 215762306a36Sopenharmony_ci .port_change_mtu = rtl8365mb_port_change_mtu, 215862306a36Sopenharmony_ci .port_max_mtu = rtl8365mb_port_max_mtu, 215962306a36Sopenharmony_ci}; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_cistatic const struct realtek_ops rtl8365mb_ops = { 216262306a36Sopenharmony_ci .detect = rtl8365mb_detect, 216362306a36Sopenharmony_ci .phy_read = rtl8365mb_phy_read, 216462306a36Sopenharmony_ci .phy_write = rtl8365mb_phy_write, 216562306a36Sopenharmony_ci}; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ciconst struct realtek_variant rtl8365mb_variant = { 216862306a36Sopenharmony_ci .ds_ops_smi = &rtl8365mb_switch_ops_smi, 216962306a36Sopenharmony_ci .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, 217062306a36Sopenharmony_ci .ops = &rtl8365mb_ops, 217162306a36Sopenharmony_ci .clk_delay = 10, 217262306a36Sopenharmony_ci .cmd_read = 0xb9, 217362306a36Sopenharmony_ci .cmd_write = 0xb8, 217462306a36Sopenharmony_ci .chip_data_sz = sizeof(struct rtl8365mb), 217562306a36Sopenharmony_ci}; 217662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8365mb_variant); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ciMODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>"); 217962306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); 218062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2181