10e692c27SAlvin Šipraga // SPDX-License-Identifier: GPL-2.0 20e692c27SAlvin Šipraga /* Realtek SMI subdriver for the Realtek RTL8365MB-VC ethernet switch. 30e692c27SAlvin Šipraga * 40e692c27SAlvin Šipraga * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> 50e692c27SAlvin Šipraga * Copyright (C) 2021 Michael Rasmussen <mir@bang-olufsen.dk> 60e692c27SAlvin Šipraga * 70e692c27SAlvin Šipraga * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4 80e692c27SAlvin Šipraga * integrated PHYs for the user facing ports, and an extension interface which 90e692c27SAlvin Šipraga * can be connected to the CPU - or another PHY - via either MII, RMII, or 100e692c27SAlvin Šipraga * RGMII. The switch is configured via the Realtek Simple Management Interface 110e692c27SAlvin Šipraga * (SMI), which uses the MDIO/MDC lines. 120e692c27SAlvin Šipraga * 130e692c27SAlvin Šipraga * Below is a simplified block diagram of the chip and its relevant interfaces. 140e692c27SAlvin Šipraga * 150e692c27SAlvin Šipraga * .-----------------------------------. 160e692c27SAlvin Šipraga * | | 170e692c27SAlvin Šipraga * UTP <---------------> Giga PHY <-> PCS <-> P0 GMAC | 180e692c27SAlvin Šipraga * UTP <---------------> Giga PHY <-> PCS <-> P1 GMAC | 190e692c27SAlvin Šipraga * UTP <---------------> Giga PHY <-> PCS <-> P2 GMAC | 200e692c27SAlvin Šipraga * UTP <---------------> Giga PHY <-> PCS <-> P3 GMAC | 210e692c27SAlvin Šipraga * | | 220e692c27SAlvin Šipraga * CPU/PHY <-MII/RMII/RGMII---> Extension <---> Extension | 230e692c27SAlvin Šipraga * | interface 1 GMAC 1 | 240e692c27SAlvin Šipraga * | | 250e692c27SAlvin Šipraga * SMI driver/ <-MDC/SCL---> Management ~~~~~~~~~~~~~~ | 260e692c27SAlvin Šipraga * EEPROM <-MDIO/SDA--> interface ~REALTEK ~~~~~ | 270e692c27SAlvin Šipraga * | ~RTL8365MB ~~~ | 280e692c27SAlvin Šipraga * | ~GXXXC TAIWAN~ | 290e692c27SAlvin Šipraga * GPIO <--------------> Reset ~~~~~~~~~~~~~~ | 300e692c27SAlvin Šipraga * | | 310e692c27SAlvin Šipraga * Interrupt <----------> Link UP/DOWN events | 320e692c27SAlvin Šipraga * controller | | 330e692c27SAlvin Šipraga * '-----------------------------------' 340e692c27SAlvin Šipraga * 350e692c27SAlvin Šipraga * The driver uses DSA to integrate the 4 user and 1 extension ports into the 360e692c27SAlvin Šipraga * kernel. Netdevices are created for the user ports, as are PHY devices for 370e692c27SAlvin Šipraga * their integrated PHYs. The device tree firmware should also specify the link 380e692c27SAlvin Šipraga * partner of the extension port - either via a fixed-link or other phy-handle. 390e692c27SAlvin Šipraga * See the device tree bindings for more detailed information. Note that the 400e692c27SAlvin Šipraga * driver has only been tested with a fixed-link, but in principle it should not 410e692c27SAlvin Šipraga * matter. 420e692c27SAlvin Šipraga * 430e692c27SAlvin Šipraga * NOTE: Currently, only the RGMII interface is implemented in this driver. 440e692c27SAlvin Šipraga * 450e692c27SAlvin Šipraga * The interrupt line is asserted on link UP/DOWN events. The driver creates a 460e692c27SAlvin Šipraga * custom irqchip to handle this interrupt and demultiplex the events by reading 470e692c27SAlvin Šipraga * the status registers via SMI. Interrupts are then propagated to the relevant 480e692c27SAlvin Šipraga * PHY device. 490e692c27SAlvin Šipraga * 500e692c27SAlvin Šipraga * The EEPROM contains initial register values which the chip will read over I2C 510e692c27SAlvin Šipraga * upon hardware reset. It is also possible to omit the EEPROM. In both cases, 520e692c27SAlvin Šipraga * the driver will manually reprogram some registers using jam tables to reach 530e692c27SAlvin Šipraga * an initial state defined by the vendor driver. 540e692c27SAlvin Šipraga * 550e692c27SAlvin Šipraga * This Linux driver is written based on an OS-agnostic vendor driver from 560e692c27SAlvin Šipraga * Realtek. The reference GPL-licensed sources can be found in the OpenWrt 570e692c27SAlvin Šipraga * source tree under the name rtl8367c. The vendor driver claims to support a 580e692c27SAlvin Šipraga * number of similar switch controllers from Realtek, but the only hardware we 590e692c27SAlvin Šipraga * have is the RTL8365MB-VC. Moreover, there does not seem to be any chip under 600e692c27SAlvin Šipraga * the name RTL8367C. Although one wishes that the 'C' stood for some kind of 610e692c27SAlvin Šipraga * common hardware revision, there exist examples of chips with the suffix -VC 620e692c27SAlvin Šipraga * which are explicitly not supported by the rtl8367c driver and which instead 630e692c27SAlvin Šipraga * require the rtl8367d vendor driver. With all this uncertainty, the driver has 640e692c27SAlvin Šipraga * been modestly named rtl8365mb. Future implementors may wish to rename things 650e692c27SAlvin Šipraga * accordingly. 660e692c27SAlvin Šipraga * 670e692c27SAlvin Šipraga * In the same family of chips, some carry up to 8 user ports and up to 2 680e692c27SAlvin Šipraga * extension ports. Where possible this driver tries to make things generic, but 690e692c27SAlvin Šipraga * more work must be done to support these configurations. According to 700e692c27SAlvin Šipraga * documentation from Realtek, the family should include the following chips: 710e692c27SAlvin Šipraga * 720e692c27SAlvin Šipraga * - RTL8363NB 730e692c27SAlvin Šipraga * - RTL8363NB-VB 740e692c27SAlvin Šipraga * - RTL8363SC 750e692c27SAlvin Šipraga * - RTL8363SC-VB 760e692c27SAlvin Šipraga * - RTL8364NB 770e692c27SAlvin Šipraga * - RTL8364NB-VB 780e692c27SAlvin Šipraga * - RTL8365MB-VC 790e692c27SAlvin Šipraga * - RTL8366SC 800e692c27SAlvin Šipraga * - RTL8367RB-VB 810e692c27SAlvin Šipraga * - RTL8367SB 820e692c27SAlvin Šipraga * - RTL8367S 830e692c27SAlvin Šipraga * - RTL8370MB 840e692c27SAlvin Šipraga * - RTL8310SR 850e692c27SAlvin Šipraga * 860e692c27SAlvin Šipraga * Some of the register logic for these additional chips has been skipped over 870e692c27SAlvin Šipraga * while implementing this driver. It is therefore not possible to assume that 880e692c27SAlvin Šipraga * things will work out-of-the-box for other chips, and a careful review of the 890e692c27SAlvin Šipraga * vendor driver may be needed to expand support. The RTL8365MB-VC seems to be 900e692c27SAlvin Šipraga * one of the simpler chips. 910e692c27SAlvin Šipraga */ 920e692c27SAlvin Šipraga 930e692c27SAlvin Šipraga #include <linux/bitfield.h> 940e692c27SAlvin Šipraga #include <linux/bitops.h> 950e692c27SAlvin Šipraga #include <linux/interrupt.h> 960e692c27SAlvin Šipraga #include <linux/irqdomain.h> 970e692c27SAlvin Šipraga #include <linux/mutex.h> 980e692c27SAlvin Šipraga #include <linux/of_irq.h> 990e692c27SAlvin Šipraga #include <linux/regmap.h> 1000e692c27SAlvin Šipraga #include <linux/if_bridge.h> 1010e692c27SAlvin Šipraga #include <linux/if_vlan.h> 1020e692c27SAlvin Šipraga 1030e692c27SAlvin Šipraga #include "realtek.h" 1040e692c27SAlvin Šipraga #include "realtek-smi.h" 1050e692c27SAlvin Šipraga #include "realtek-mdio.h" 1060e692c27SAlvin Šipraga #include "rtl83xx.h" 107336e3e4aSAlvin Šipraga #include "rtl8365mb_l2.h" 1089da2c867SAlvin Šipraga #include "rtl8365mb_vlan.h" 1090e692c27SAlvin Šipraga 1100e692c27SAlvin Šipraga /* Family-specific data and limits */ 1110e692c27SAlvin Šipraga #define RTL8365MB_PHYADDRMAX 7 1120e692c27SAlvin Šipraga #define RTL8365MB_NUM_PHYREGS 32 1130e692c27SAlvin Šipraga #define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) 1140e692c27SAlvin Šipraga #define RTL8365MB_MAX_NUM_PORTS 11 115336e3e4aSAlvin Šipraga /* Valid for the whole family except RTL8370B, which has 4160 entries. 116336e3e4aSAlvin Šipraga * RTL8370B is mentioned in vendor code but it might not even belong 117336e3e4aSAlvin Šipraga * to the same RTL8367C family. 118336e3e4aSAlvin Šipraga */ 1190e692c27SAlvin Šipraga #define RTL8365MB_LEARN_LIMIT_MAX 2112 120336e3e4aSAlvin Šipraga #define RTL8365MB_MAX_NUM_EXTINTS 3 1210e692c27SAlvin Šipraga 1220e692c27SAlvin Šipraga /* Chip identification registers */ 1230e692c27SAlvin Šipraga #define RTL8365MB_CHIP_ID_REG 0x1300 1240e692c27SAlvin Šipraga 1250e692c27SAlvin Šipraga #define RTL8365MB_CHIP_VER_REG 0x1301 1260e692c27SAlvin Šipraga 1270e692c27SAlvin Šipraga #define RTL8365MB_MAGIC_REG 0x13C2 1280e692c27SAlvin Šipraga #define RTL8365MB_MAGIC_VALUE 0x0249 1290e692c27SAlvin Šipraga 1300e692c27SAlvin Šipraga /* Chip reset register */ 1310e692c27SAlvin Šipraga #define RTL8365MB_CHIP_RESET_REG 0x1322 1320e692c27SAlvin Šipraga #define RTL8365MB_CHIP_RESET_SW_MASK 0x0002 1330e692c27SAlvin Šipraga #define RTL8365MB_CHIP_RESET_HW_MASK 0x0001 1340e692c27SAlvin Šipraga 1350e692c27SAlvin Šipraga /* Interrupt polarity register */ 1360e692c27SAlvin Šipraga #define RTL8365MB_INTR_POLARITY_REG 0x1100 1370e692c27SAlvin Šipraga #define RTL8365MB_INTR_POLARITY_MASK 0x0001 1380e692c27SAlvin Šipraga #define RTL8365MB_INTR_POLARITY_HIGH 0 1390e692c27SAlvin Šipraga #define RTL8365MB_INTR_POLARITY_LOW 1 1400e692c27SAlvin Šipraga 1410e692c27SAlvin Šipraga /* Interrupt control/status register - enable/check specific interrupt types */ 1420e692c27SAlvin Šipraga #define RTL8365MB_INTR_CTRL_REG 0x1101 1430e692c27SAlvin Šipraga #define RTL8365MB_INTR_STATUS_REG 0x1102 1440e692c27SAlvin Šipraga #define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000 1450e692c27SAlvin Šipraga #define RTL8365MB_INTR_SLIENT_START_MASK 0x0800 1460e692c27SAlvin Šipraga #define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200 1470e692c27SAlvin Šipraga #define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100 1480e692c27SAlvin Šipraga #define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080 1490e692c27SAlvin Šipraga #define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040 1500e692c27SAlvin Šipraga #define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020 1510e692c27SAlvin Šipraga #define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010 1520e692c27SAlvin Šipraga #define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008 1530e692c27SAlvin Šipraga #define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004 1540e692c27SAlvin Šipraga #define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002 1550e692c27SAlvin Šipraga #define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001 1560e692c27SAlvin Šipraga #define RTL8365MB_INTR_ALL_MASK \ 1570e692c27SAlvin Šipraga (RTL8365MB_INTR_SLIENT_START_2_MASK | \ 1580e692c27SAlvin Šipraga RTL8365MB_INTR_SLIENT_START_MASK | \ 1590e692c27SAlvin Šipraga RTL8365MB_INTR_ACL_ACTION_MASK | \ 1600e692c27SAlvin Šipraga RTL8365MB_INTR_CABLE_DIAG_FIN_MASK | \ 1610e692c27SAlvin Šipraga RTL8365MB_INTR_INTERRUPT_8051_MASK | \ 1620e692c27SAlvin Šipraga RTL8365MB_INTR_LOOP_DETECTION_MASK | \ 1630e692c27SAlvin Šipraga RTL8365MB_INTR_GREEN_TIMER_MASK | \ 1640e692c27SAlvin Šipraga RTL8365MB_INTR_SPECIAL_CONGEST_MASK | \ 1650e692c27SAlvin Šipraga RTL8365MB_INTR_SPEED_CHANGE_MASK | \ 1660e692c27SAlvin Šipraga RTL8365MB_INTR_LEARN_OVER_MASK | \ 1670e692c27SAlvin Šipraga RTL8365MB_INTR_METER_EXCEEDED_MASK | \ 1680e692c27SAlvin Šipraga RTL8365MB_INTR_LINK_CHANGE_MASK) 1690e692c27SAlvin Šipraga 1700e692c27SAlvin Šipraga /* Per-port interrupt type status registers */ 1710e692c27SAlvin Šipraga #define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106 1720e692c27SAlvin Šipraga #define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF 1730e692c27SAlvin Šipraga 1740e692c27SAlvin Šipraga #define RTL8365MB_PORT_LINKUP_IND_REG 0x1107 1750e692c27SAlvin Šipraga #define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF 1760e692c27SAlvin Šipraga 1770e692c27SAlvin Šipraga /* PHY indirect access registers */ 1780e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00 1790e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002 1800e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 1810e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 1820e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001 1830e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 1840e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01 1850e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02 1860e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0) 1870e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5) 1880e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8) 1890e692c27SAlvin Šipraga #define RTL8365MB_PHY_BASE 0x2000 1900e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03 1910e692c27SAlvin Šipraga #define RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG 0x1F04 1920e692c27SAlvin Šipraga 1930e692c27SAlvin Šipraga /* PHY OCP address prefix register */ 1940e692c27SAlvin Šipraga #define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15 1950e692c27SAlvin Šipraga #define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0 1960e692c27SAlvin Šipraga #define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00 1970e692c27SAlvin Šipraga 1980e692c27SAlvin Šipraga /* The PHY OCP addresses of PHY registers 0~31 start here */ 1990e692c27SAlvin Šipraga #define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 2000e692c27SAlvin Šipraga 2010e692c27SAlvin Šipraga /* External interface port mode values - used in DIGITAL_INTERFACE_SELECT */ 2020e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_DISABLE 0 2030e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_RGMII 1 2040e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 2050e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_MII_PHY 3 2060e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_TMII_MAC 4 2070e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_TMII_PHY 5 2080e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_GMII 6 2090e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_RMII_MAC 7 2100e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_RMII_PHY 8 2110e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_SGMII 9 2120e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_HSGMII 10 2130e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_1000X_100FX 11 2140e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_1000X 12 2150e692c27SAlvin Šipraga #define RTL8365MB_EXT_PORT_MODE_100FX 13 2160e692c27SAlvin Šipraga 2170e692c27SAlvin Šipraga /* External interface mode configuration registers 0~1 */ 2180e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT0,EXT1 */ 2190e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ 2200e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ 2210e692c27SAlvin Šipraga ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ 2220e692c27SAlvin Šipraga (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 2230e692c27SAlvin Šipraga 0x0) 2240e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ 2250e692c27SAlvin Šipraga (0xF << (((_extint) % 2) * 4)) 2260e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ 2270e692c27SAlvin Šipraga (((_extint) % 2) * 4) 2280e692c27SAlvin Šipraga 2290e692c27SAlvin Šipraga /* External interface RGMII TX/RX delay configuration registers 0~2 */ 2300e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ 2310e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ 2320e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ 2330e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_REG(_extint) \ 2340e692c27SAlvin Šipraga ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ 2350e692c27SAlvin Šipraga (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ 2360e692c27SAlvin Šipraga (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ 2370e692c27SAlvin Šipraga 0x0) 2380e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 2390e692c27SAlvin Šipraga #define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 2400e692c27SAlvin Šipraga 2410e692c27SAlvin Šipraga /* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ 2420e692c27SAlvin Šipraga #define RTL8365MB_PORT_SPEED_10M 0 2430e692c27SAlvin Šipraga #define RTL8365MB_PORT_SPEED_100M 1 2440e692c27SAlvin Šipraga #define RTL8365MB_PORT_SPEED_1000M 2 2450e692c27SAlvin Šipraga 2460e692c27SAlvin Šipraga /* External interface force configuration registers 0~2 */ 2470e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ 2480e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ 2490e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ 2500e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ 2510e692c27SAlvin Šipraga ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ 2520e692c27SAlvin Šipraga (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ 2530e692c27SAlvin Šipraga (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ 2540e692c27SAlvin Šipraga 0x0) 2550e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 2560e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 2570e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 2580e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020 2590e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010 2600e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004 2610e692c27SAlvin Šipraga #define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003 2620e692c27SAlvin Šipraga 2630e692c27SAlvin Šipraga /* CPU port mask register - controls which ports are treated as CPU ports */ 2640e692c27SAlvin Šipraga #define RTL8365MB_CPU_PORT_MASK_REG 0x1219 2650e692c27SAlvin Šipraga #define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF 2660e692c27SAlvin Šipraga 2670e692c27SAlvin Šipraga /* CPU control register */ 2680e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_REG 0x121A 2690e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400 2700e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200 2710e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080 2720e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040 2730e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038 2740e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006 2750e692c27SAlvin Šipraga #define RTL8365MB_CPU_CTRL_EN_MASK 0x0001 2760e692c27SAlvin Šipraga 2770e692c27SAlvin Šipraga /* Maximum packet length register */ 2780e692c27SAlvin Šipraga #define RTL8365MB_CFG0_MAX_LEN_REG 0x088C 2790e692c27SAlvin Šipraga #define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF 2800e692c27SAlvin Šipraga #define RTL8365MB_CFG0_MAX_LEN_MAX 0x3FFF 2810e692c27SAlvin Šipraga 2820e692c27SAlvin Šipraga /* Port learning limit registers */ 2830e692c27SAlvin Šipraga #define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20 2840e692c27SAlvin Šipraga #define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \ 2850e692c27SAlvin Šipraga (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport)) 2860e692c27SAlvin Šipraga 2870e692c27SAlvin Šipraga /* Port isolation (forwarding mask) registers */ 2880e692c27SAlvin Šipraga #define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 2890e692c27SAlvin Šipraga #define RTL8365MB_PORT_ISOLATION_REG(_physport) \ 2900e692c27SAlvin Šipraga (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) 2910e692c27SAlvin Šipraga #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF 2920e692c27SAlvin Šipraga 293336e3e4aSAlvin Šipraga /* Extended filter ID registers - used to key forwarding database with IVL */ 294336e3e4aSAlvin Šipraga #define RTL8365MB_EFID_MASK GENMASK(2, 0) 295336e3e4aSAlvin Šipraga #define RTL8365MB_PORT_EFID_REG_BASE 0x0A32 296336e3e4aSAlvin Šipraga #define RTL8365MB_PORT_EFID_REG(_p) \ 297336e3e4aSAlvin Šipraga (RTL8365MB_PORT_EFID_REG_BASE + ((_p) >> 2)) 298336e3e4aSAlvin Šipraga #define RTL8365MB_PORT_EFID_OFFSET(_p) (((_p) & 0x3) << 2) 299336e3e4aSAlvin Šipraga #define RTL8365MB_PORT_EFID_MASK(_p) \ 300336e3e4aSAlvin Šipraga (RTL8365MB_EFID_MASK << RTL8365MB_PORT_EFID_OFFSET(_p)) 301336e3e4aSAlvin Šipraga 3020e692c27SAlvin Šipraga /* MSTP port state registers - indexed by tree instance */ 3030e692c27SAlvin Šipraga #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 3040e692c27SAlvin Šipraga #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ 3050e692c27SAlvin Šipraga (RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3)) 3060e692c27SAlvin Šipraga #define RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(_physport) ((_physport) << 1) 3070e692c27SAlvin Šipraga #define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ 3080e692c27SAlvin Šipraga (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) 3090e692c27SAlvin Šipraga 310*660a9e39SLuiz Angelo Daros de Luca /* Unknown unicast DA flooding port mask */ 311*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890 312*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK 0x07FF 313*660a9e39SLuiz Angelo Daros de Luca 314*660a9e39SLuiz Angelo Daros de Luca /* Unknown multicast DA flooding port mask */ 315*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891 316*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK 0x07FF 317*660a9e39SLuiz Angelo Daros de Luca 318*660a9e39SLuiz Angelo Daros de Luca /* Broadcast flooding port mask */ 319*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892 320*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK 0x07FF 321*660a9e39SLuiz Angelo Daros de Luca 322*660a9e39SLuiz Angelo Daros de Luca #define RTL8365MB_SUPPORTED_BRIDGE_FLAGS \ 323*660a9e39SLuiz Angelo Daros de Luca (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD) 324*660a9e39SLuiz Angelo Daros de Luca 3259da2c867SAlvin Šipraga /* Miscellaneous port configuration register, incl. VLAN egress mode */ 3269da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E 3279da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_REG(_p) \ 3289da2c867SAlvin Šipraga (RTL8365MB_PORT_MISC_CFG_REG_BASE + ((_p) << 5)) 3299da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_SMALL_TAG_IPG_MASK 0x8000 3309da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_TX_ITFSP_MODE_MASK 0x4000 3319da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_FLOWCTRL_INDEP_MASK 0x2000 3329da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_DOT1Q_REMARK_ENABLE_MASK 0x1000 3339da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_INGRESSBW_FLOWCTRL_MASK 0x0800 3349da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_INGRESSBW_IFG_MASK 0x0400 3359da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_RX_SPC_MASK 0x0200 3369da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_CRC_SKIP_MASK 0x0100 3379da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_PKTGEN_TX_FIRST_MASK 0x0080 3389da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_MAC_LOOPBACK_MASK 0x0040 3399da2c867SAlvin Šipraga /* See &rtl8365mb_vlan_egress_mode */ 3409da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK 0x0030 3419da2c867SAlvin Šipraga #define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK 0x000F 3429da2c867SAlvin Šipraga 3439da2c867SAlvin Šipraga /** 3449da2c867SAlvin Šipraga * enum rtl8365mb_vlan_egress_mode - port VLAN egress mode 3459da2c867SAlvin Šipraga * @RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL: follow untag mask in VLAN4k table entry 3469da2c867SAlvin Šipraga * @RTL8365MB_VLAN_EGRESS_MODE_KEEP: the VLAN tag format of egressed packets 3479da2c867SAlvin Šipraga * will remain the same as their ingressed format, but the priority and VID 3489da2c867SAlvin Šipraga * fields may be altered 3499da2c867SAlvin Šipraga * @RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG: always egress with priority tag 3509da2c867SAlvin Šipraga * @RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP: the VLAN tag format of egressed 3519da2c867SAlvin Šipraga * packets will remain the same as their ingressed format, and neither the 3529da2c867SAlvin Šipraga * priority nor VID fields can be altered 3539da2c867SAlvin Šipraga */ 3549da2c867SAlvin Šipraga enum rtl8365mb_vlan_egress_mode { 3559da2c867SAlvin Šipraga RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL = 0, 3569da2c867SAlvin Šipraga RTL8365MB_VLAN_EGRESS_MODE_KEEP = 1, 3579da2c867SAlvin Šipraga RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG = 2, 3589da2c867SAlvin Šipraga RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP = 3, 3599da2c867SAlvin Šipraga }; 3609da2c867SAlvin Šipraga 3619da2c867SAlvin Šipraga /* VLAN control register */ 3629da2c867SAlvin Šipraga #define RTL8365MB_VLAN_CTRL_REG 0x07A8 3639da2c867SAlvin Šipraga #define RTL8365MB_VLAN_CTRL_EN_MASK 0x0001 3649da2c867SAlvin Šipraga 3659da2c867SAlvin Šipraga /* VLAN ingress filter register */ 3669da2c867SAlvin Šipraga #define RTL8365MB_VLAN_INGRESS_REG 0x07A9 3679da2c867SAlvin Šipraga #define RTL8365MB_VLAN_INGRESS_MASK GENMASK(10, 0) 3689da2c867SAlvin Šipraga #define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_OFFSET(_p) (_p) 3699da2c867SAlvin Šipraga #define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(_p) BIT(_p) 3709da2c867SAlvin Šipraga 3719da2c867SAlvin Šipraga /* VLAN "transparent" setting registers */ 3729da2c867SAlvin Šipraga #define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE 0x09D0 3739da2c867SAlvin Šipraga #define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(_p) \ 3749da2c867SAlvin Šipraga (RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE + (_p)) 3759da2c867SAlvin Šipraga 3760e692c27SAlvin Šipraga /* MIB counter value registers */ 3770e692c27SAlvin Šipraga #define RTL8365MB_MIB_COUNTER_BASE 0x1000 3780e692c27SAlvin Šipraga #define RTL8365MB_MIB_COUNTER_REG(_x) (RTL8365MB_MIB_COUNTER_BASE + (_x)) 3790e692c27SAlvin Šipraga 3800e692c27SAlvin Šipraga /* MIB counter address register */ 3810e692c27SAlvin Šipraga #define RTL8365MB_MIB_ADDRESS_REG 0x1004 3820e692c27SAlvin Šipraga #define RTL8365MB_MIB_ADDRESS_PORT_OFFSET 0x007C 3830e692c27SAlvin Šipraga #define RTL8365MB_MIB_ADDRESS(_p, _x) \ 3840e692c27SAlvin Šipraga (((RTL8365MB_MIB_ADDRESS_PORT_OFFSET) * (_p) + (_x)) >> 2) 3850e692c27SAlvin Šipraga 3860e692c27SAlvin Šipraga #define RTL8365MB_MIB_CTRL0_REG 0x1005 3870e692c27SAlvin Šipraga #define RTL8365MB_MIB_CTRL0_RESET_MASK 0x0002 3880e692c27SAlvin Šipraga #define RTL8365MB_MIB_CTRL0_BUSY_MASK 0x0001 3890e692c27SAlvin Šipraga 3900e692c27SAlvin Šipraga /* The DSA callback .get_stats64 runs in atomic context, so we are not allowed 3910e692c27SAlvin Šipraga * to block. On the other hand, accessing MIB counters absolutely requires us to 3920e692c27SAlvin Šipraga * block. The solution is thus to schedule work which polls the MIB counters 3930e692c27SAlvin Šipraga * asynchronously and updates some private data, which the callback can then 3940e692c27SAlvin Šipraga * fetch atomically. Three seconds should be a good enough polling interval. 3950e692c27SAlvin Šipraga */ 3960e692c27SAlvin Šipraga #define RTL8365MB_STATS_INTERVAL_JIFFIES (3 * HZ) 3970e692c27SAlvin Šipraga 3980e692c27SAlvin Šipraga enum rtl8365mb_mib_counter_index { 3990e692c27SAlvin Šipraga RTL8365MB_MIB_ifInOctets, 4000e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsFCSErrors, 4010e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsSymbolErrors, 4020e692c27SAlvin Šipraga RTL8365MB_MIB_dot3InPauseFrames, 4030e692c27SAlvin Šipraga RTL8365MB_MIB_dot3ControlInUnknownOpcodes, 4040e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsFragments, 4050e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsJabbers, 4060e692c27SAlvin Šipraga RTL8365MB_MIB_ifInUcastPkts, 4070e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsDropEvents, 4080e692c27SAlvin Šipraga RTL8365MB_MIB_ifInMulticastPkts, 4090e692c27SAlvin Šipraga RTL8365MB_MIB_ifInBroadcastPkts, 4100e692c27SAlvin Šipraga RTL8365MB_MIB_inMldChecksumError, 4110e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpChecksumError, 4120e692c27SAlvin Šipraga RTL8365MB_MIB_inMldSpecificQuery, 4130e692c27SAlvin Šipraga RTL8365MB_MIB_inMldGeneralQuery, 4140e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpSpecificQuery, 4150e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpGeneralQuery, 4160e692c27SAlvin Šipraga RTL8365MB_MIB_inMldLeaves, 4170e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpLeaves, 4180e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsOctets, 4190e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsUnderSizePkts, 4200e692c27SAlvin Šipraga RTL8365MB_MIB_etherOversizeStats, 4210e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts64Octets, 4220e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts65to127Octets, 4230e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts128to255Octets, 4240e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts256to511Octets, 4250e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts512to1023Octets, 4260e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsPkts1024to1518Octets, 4270e692c27SAlvin Šipraga RTL8365MB_MIB_ifOutOctets, 4280e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsSingleCollisionFrames, 4290e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsMultipleCollisionFrames, 4300e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsDeferredTransmissions, 4310e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsLateCollisions, 4320e692c27SAlvin Šipraga RTL8365MB_MIB_etherStatsCollisions, 4330e692c27SAlvin Šipraga RTL8365MB_MIB_dot3StatsExcessiveCollisions, 4340e692c27SAlvin Šipraga RTL8365MB_MIB_dot3OutPauseFrames, 4350e692c27SAlvin Šipraga RTL8365MB_MIB_ifOutDiscards, 4360e692c27SAlvin Šipraga RTL8365MB_MIB_dot1dTpPortInDiscards, 4370e692c27SAlvin Šipraga RTL8365MB_MIB_ifOutUcastPkts, 4380e692c27SAlvin Šipraga RTL8365MB_MIB_ifOutMulticastPkts, 4390e692c27SAlvin Šipraga RTL8365MB_MIB_ifOutBroadcastPkts, 4400e692c27SAlvin Šipraga RTL8365MB_MIB_outOampduPkts, 4410e692c27SAlvin Šipraga RTL8365MB_MIB_inOampduPkts, 4420e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpJoinsSuccess, 4430e692c27SAlvin Šipraga RTL8365MB_MIB_inIgmpJoinsFail, 4440e692c27SAlvin Šipraga RTL8365MB_MIB_inMldJoinsSuccess, 4450e692c27SAlvin Šipraga RTL8365MB_MIB_inMldJoinsFail, 4460e692c27SAlvin Šipraga RTL8365MB_MIB_inReportSuppressionDrop, 4470e692c27SAlvin Šipraga RTL8365MB_MIB_inLeaveSuppressionDrop, 4480e692c27SAlvin Šipraga RTL8365MB_MIB_outIgmpReports, 4490e692c27SAlvin Šipraga RTL8365MB_MIB_outIgmpLeaves, 4500e692c27SAlvin Šipraga RTL8365MB_MIB_outIgmpGeneralQuery, 4510e692c27SAlvin Šipraga RTL8365MB_MIB_outIgmpSpecificQuery, 4520e692c27SAlvin Šipraga RTL8365MB_MIB_outMldReports, 4530e692c27SAlvin Šipraga RTL8365MB_MIB_outMldLeaves, 4540e692c27SAlvin Šipraga RTL8365MB_MIB_outMldGeneralQuery, 4550e692c27SAlvin Šipraga RTL8365MB_MIB_outMldSpecificQuery, 4560e692c27SAlvin Šipraga RTL8365MB_MIB_inKnownMulticastPkts, 4570e692c27SAlvin Šipraga RTL8365MB_MIB_END, 4580e692c27SAlvin Šipraga }; 4590e692c27SAlvin Šipraga 4600e692c27SAlvin Šipraga struct rtl8365mb_mib_counter { 4610e692c27SAlvin Šipraga u32 offset; 4620e692c27SAlvin Šipraga u32 length; 4630e692c27SAlvin Šipraga const char *name; 4640e692c27SAlvin Šipraga }; 4650e692c27SAlvin Šipraga 4660e692c27SAlvin Šipraga #define RTL8365MB_MAKE_MIB_COUNTER(_offset, _length, _name) \ 4670e692c27SAlvin Šipraga [RTL8365MB_MIB_ ## _name] = { _offset, _length, #_name } 4680e692c27SAlvin Šipraga 4690e692c27SAlvin Šipraga static struct rtl8365mb_mib_counter rtl8365mb_mib_counters[] = { 4700e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(0, 4, ifInOctets), 4710e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(4, 2, dot3StatsFCSErrors), 4720e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(6, 2, dot3StatsSymbolErrors), 4730e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(8, 2, dot3InPauseFrames), 4740e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(10, 2, dot3ControlInUnknownOpcodes), 4750e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(12, 2, etherStatsFragments), 4760e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(14, 2, etherStatsJabbers), 4770e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(16, 2, ifInUcastPkts), 4780e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(18, 2, etherStatsDropEvents), 4790e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(20, 2, ifInMulticastPkts), 4800e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(22, 2, ifInBroadcastPkts), 4810e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(24, 2, inMldChecksumError), 4820e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(26, 2, inIgmpChecksumError), 4830e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(28, 2, inMldSpecificQuery), 4840e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(30, 2, inMldGeneralQuery), 4850e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(32, 2, inIgmpSpecificQuery), 4860e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(34, 2, inIgmpGeneralQuery), 4870e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(36, 2, inMldLeaves), 4880e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(38, 2, inIgmpLeaves), 4890e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(40, 4, etherStatsOctets), 4900e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(44, 2, etherStatsUnderSizePkts), 4910e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(46, 2, etherOversizeStats), 4920e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(48, 2, etherStatsPkts64Octets), 4930e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(50, 2, etherStatsPkts65to127Octets), 4940e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(52, 2, etherStatsPkts128to255Octets), 4950e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(54, 2, etherStatsPkts256to511Octets), 4960e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(56, 2, etherStatsPkts512to1023Octets), 4970e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(58, 2, etherStatsPkts1024to1518Octets), 4980e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(60, 4, ifOutOctets), 4990e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(64, 2, dot3StatsSingleCollisionFrames), 5000e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(66, 2, dot3StatsMultipleCollisionFrames), 5010e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(68, 2, dot3StatsDeferredTransmissions), 5020e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(70, 2, dot3StatsLateCollisions), 5030e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(72, 2, etherStatsCollisions), 5040e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(74, 2, dot3StatsExcessiveCollisions), 5050e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(76, 2, dot3OutPauseFrames), 5060e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(78, 2, ifOutDiscards), 5070e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(80, 2, dot1dTpPortInDiscards), 5080e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(82, 2, ifOutUcastPkts), 5090e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(84, 2, ifOutMulticastPkts), 5100e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(86, 2, ifOutBroadcastPkts), 5110e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(88, 2, outOampduPkts), 5120e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(90, 2, inOampduPkts), 5130e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(92, 4, inIgmpJoinsSuccess), 5140e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(96, 2, inIgmpJoinsFail), 5150e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(98, 2, inMldJoinsSuccess), 5160e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(100, 2, inMldJoinsFail), 5170e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(102, 2, inReportSuppressionDrop), 5180e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(104, 2, inLeaveSuppressionDrop), 5190e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(106, 2, outIgmpReports), 5200e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(108, 2, outIgmpLeaves), 5210e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(110, 2, outIgmpGeneralQuery), 5220e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(112, 2, outIgmpSpecificQuery), 5230e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(114, 2, outMldReports), 5240e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(116, 2, outMldLeaves), 5250e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(118, 2, outMldGeneralQuery), 5260e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(120, 2, outMldSpecificQuery), 5270e692c27SAlvin Šipraga RTL8365MB_MAKE_MIB_COUNTER(122, 2, inKnownMulticastPkts), 5280e692c27SAlvin Šipraga }; 5290e692c27SAlvin Šipraga 5300e692c27SAlvin Šipraga static_assert(ARRAY_SIZE(rtl8365mb_mib_counters) == RTL8365MB_MIB_END); 5310e692c27SAlvin Šipraga 5320e692c27SAlvin Šipraga struct rtl8365mb_jam_tbl_entry { 5330e692c27SAlvin Šipraga u16 reg; 5340e692c27SAlvin Šipraga u16 val; 5350e692c27SAlvin Šipraga }; 5360e692c27SAlvin Šipraga 5370e692c27SAlvin Šipraga /* Lifted from the vendor driver sources */ 5380e692c27SAlvin Šipraga static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_8365mb_vc[] = { 5390e692c27SAlvin Šipraga { 0x13EB, 0x15BB }, { 0x1303, 0x06D6 }, { 0x1304, 0x0700 }, 5400e692c27SAlvin Šipraga { 0x13E2, 0x003F }, { 0x13F9, 0x0090 }, { 0x121E, 0x03CA }, 5410e692c27SAlvin Šipraga { 0x1233, 0x0352 }, { 0x1237, 0x00A0 }, { 0x123A, 0x0030 }, 5420e692c27SAlvin Šipraga { 0x1239, 0x0084 }, { 0x0301, 0x1000 }, { 0x1349, 0x001F }, 5430e692c27SAlvin Šipraga { 0x18E0, 0x4004 }, { 0x122B, 0x241C }, { 0x1305, 0xC000 }, 5440e692c27SAlvin Šipraga { 0x13F0, 0x0000 }, 5450e692c27SAlvin Šipraga }; 5460e692c27SAlvin Šipraga 5470e692c27SAlvin Šipraga static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = { 5480e692c27SAlvin Šipraga { 0x1200, 0x7FCB }, { 0x0884, 0x0003 }, { 0x06EB, 0x0001 }, 5490e692c27SAlvin Šipraga { 0x03Fa, 0x0007 }, { 0x08C8, 0x00C0 }, { 0x0A30, 0x020E }, 5500e692c27SAlvin Šipraga { 0x0800, 0x0000 }, { 0x0802, 0x0000 }, { 0x09DA, 0x0013 }, 5510e692c27SAlvin Šipraga { 0x1D32, 0x0002 }, 5520e692c27SAlvin Šipraga }; 5530e692c27SAlvin Šipraga 5540e692c27SAlvin Šipraga enum rtl8365mb_phy_interface_mode { 5550e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0, 5560e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0), 5570e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_MII = BIT(1), 5580e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_TMII = BIT(2), 5590e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_RMII = BIT(3), 5600e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_RGMII = BIT(4), 5610e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_SGMII = BIT(5), 5620e692c27SAlvin Šipraga RTL8365MB_PHY_INTERFACE_MODE_HSGMII = BIT(6), 5630e692c27SAlvin Šipraga }; 5640e692c27SAlvin Šipraga 5650e692c27SAlvin Šipraga /** 5660e692c27SAlvin Šipraga * struct rtl8365mb_extint - external interface info 5670e692c27SAlvin Šipraga * @port: the port with an external interface 5680e692c27SAlvin Šipraga * @id: the external interface ID, which is either 0, 1, or 2 5690e692c27SAlvin Šipraga * @supported_interfaces: a bitmask of supported PHY interface modes 5700e692c27SAlvin Šipraga * 5710e692c27SAlvin Šipraga * Represents a mapping: port -> { id, supported_interfaces }. To be embedded 5720e692c27SAlvin Šipraga * in &struct rtl8365mb_chip_info for every port with an external interface. 5730e692c27SAlvin Šipraga */ 5740e692c27SAlvin Šipraga struct rtl8365mb_extint { 5750e692c27SAlvin Šipraga int port; 5760e692c27SAlvin Šipraga int id; 5770e692c27SAlvin Šipraga unsigned int supported_interfaces; 5780e692c27SAlvin Šipraga }; 5790e692c27SAlvin Šipraga 5800e692c27SAlvin Šipraga /** 5810e692c27SAlvin Šipraga * struct rtl8365mb_chip_info - static chip-specific info 5820e692c27SAlvin Šipraga * @name: human-readable chip name 5830e692c27SAlvin Šipraga * @chip_id: chip identifier 5840e692c27SAlvin Šipraga * @chip_ver: chip silicon revision 5850e692c27SAlvin Šipraga * @extints: available external interfaces 5860e692c27SAlvin Šipraga * @jam_table: chip-specific initialization jam table 5870e692c27SAlvin Šipraga * @jam_size: size of the chip's jam table 5880e692c27SAlvin Šipraga * 5890e692c27SAlvin Šipraga * These data are specific to a given chip in the family of switches supported 5900e692c27SAlvin Šipraga * by this driver. When adding support for another chip in the family, a new 5910e692c27SAlvin Šipraga * chip info should be added to the rtl8365mb_chip_infos array. 5920e692c27SAlvin Šipraga */ 5930e692c27SAlvin Šipraga struct rtl8365mb_chip_info { 5940e692c27SAlvin Šipraga const char *name; 5950e692c27SAlvin Šipraga u32 chip_id; 5960e692c27SAlvin Šipraga u32 chip_ver; 5970e692c27SAlvin Šipraga const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS]; 5980e692c27SAlvin Šipraga const struct rtl8365mb_jam_tbl_entry *jam_table; 5990e692c27SAlvin Šipraga size_t jam_size; 6000e692c27SAlvin Šipraga }; 6010e692c27SAlvin Šipraga 6020e692c27SAlvin Šipraga /* Chip info for each supported switch in the family */ 6030e692c27SAlvin Šipraga #define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode) 6040e692c27SAlvin Šipraga static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { 6050e692c27SAlvin Šipraga { 6060e692c27SAlvin Šipraga .name = "RTL8365MB-VC", 6070e692c27SAlvin Šipraga .chip_id = 0x6367, 6080e692c27SAlvin Šipraga .chip_ver = 0x0040, 6090e692c27SAlvin Šipraga .extints = { 6100e692c27SAlvin Šipraga { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | 6110e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) }, 6120e692c27SAlvin Šipraga }, 6130e692c27SAlvin Šipraga .jam_table = rtl8365mb_init_jam_8365mb_vc, 6140e692c27SAlvin Šipraga .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 6150e692c27SAlvin Šipraga }, 6160e692c27SAlvin Šipraga { 6170e692c27SAlvin Šipraga .name = "RTL8367S", 6180e692c27SAlvin Šipraga .chip_id = 0x6367, 6190e692c27SAlvin Šipraga .chip_ver = 0x00A0, 6200e692c27SAlvin Šipraga .extints = { 6210e692c27SAlvin Šipraga { 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) }, 6220e692c27SAlvin Šipraga { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | 6230e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) }, 6240e692c27SAlvin Šipraga }, 6250e692c27SAlvin Šipraga .jam_table = rtl8365mb_init_jam_8365mb_vc, 6260e692c27SAlvin Šipraga .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 6270e692c27SAlvin Šipraga }, 6280e692c27SAlvin Šipraga { 6290e692c27SAlvin Šipraga .name = "RTL8367SB", 6300e692c27SAlvin Šipraga .chip_id = 0x6367, 6310e692c27SAlvin Šipraga .chip_ver = 0x0010, 6320e692c27SAlvin Šipraga .extints = { 6330e692c27SAlvin Šipraga { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | 6340e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) | 6350e692c27SAlvin Šipraga PHY_INTF(SGMII) | PHY_INTF(HSGMII) }, 6360e692c27SAlvin Šipraga { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | 6370e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) }, 6380e692c27SAlvin Šipraga }, 6390e692c27SAlvin Šipraga .jam_table = rtl8365mb_init_jam_8365mb_vc, 6400e692c27SAlvin Šipraga .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 6410e692c27SAlvin Šipraga }, 6420e692c27SAlvin Šipraga { 6430e692c27SAlvin Šipraga .name = "RTL8367RB-VB", 6440e692c27SAlvin Šipraga .chip_id = 0x6367, 6450e692c27SAlvin Šipraga .chip_ver = 0x0020, 6460e692c27SAlvin Šipraga .extints = { 6470e692c27SAlvin Šipraga { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | 6480e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) }, 6490e692c27SAlvin Šipraga { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | 6500e692c27SAlvin Šipraga PHY_INTF(RMII) | PHY_INTF(RGMII) }, 6510e692c27SAlvin Šipraga }, 6520e692c27SAlvin Šipraga .jam_table = rtl8365mb_init_jam_8365mb_vc, 6530e692c27SAlvin Šipraga .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), 6540e692c27SAlvin Šipraga }, 6550e692c27SAlvin Šipraga }; 6560e692c27SAlvin Šipraga 6570e692c27SAlvin Šipraga enum rtl8365mb_stp_state { 6580e692c27SAlvin Šipraga RTL8365MB_STP_STATE_DISABLED = 0, 6590e692c27SAlvin Šipraga RTL8365MB_STP_STATE_BLOCKING = 1, 6600e692c27SAlvin Šipraga RTL8365MB_STP_STATE_LEARNING = 2, 6610e692c27SAlvin Šipraga RTL8365MB_STP_STATE_FORWARDING = 3, 6620e692c27SAlvin Šipraga }; 6630e692c27SAlvin Šipraga 6640e692c27SAlvin Šipraga enum rtl8365mb_cpu_insert { 6650e692c27SAlvin Šipraga RTL8365MB_CPU_INSERT_TO_ALL = 0, 6660e692c27SAlvin Šipraga RTL8365MB_CPU_INSERT_TO_TRAPPING = 1, 6670e692c27SAlvin Šipraga RTL8365MB_CPU_INSERT_TO_NONE = 2, 6680e692c27SAlvin Šipraga }; 6690e692c27SAlvin Šipraga 6700e692c27SAlvin Šipraga enum rtl8365mb_cpu_position { 6710e692c27SAlvin Šipraga RTL8365MB_CPU_POS_AFTER_SA = 0, 6720e692c27SAlvin Šipraga RTL8365MB_CPU_POS_BEFORE_CRC = 1, 6730e692c27SAlvin Šipraga }; 6740e692c27SAlvin Šipraga 6750e692c27SAlvin Šipraga enum rtl8365mb_cpu_format { 6760e692c27SAlvin Šipraga RTL8365MB_CPU_FORMAT_8BYTES = 0, 6770e692c27SAlvin Šipraga RTL8365MB_CPU_FORMAT_4BYTES = 1, 6780e692c27SAlvin Šipraga }; 6790e692c27SAlvin Šipraga 6800e692c27SAlvin Šipraga enum rtl8365mb_cpu_rxlen { 6810e692c27SAlvin Šipraga RTL8365MB_CPU_RXLEN_72BYTES = 0, 6820e692c27SAlvin Šipraga RTL8365MB_CPU_RXLEN_64BYTES = 1, 6830e692c27SAlvin Šipraga }; 6840e692c27SAlvin Šipraga 6850e692c27SAlvin Šipraga /** 6860e692c27SAlvin Šipraga * struct rtl8365mb_cpu - CPU port configuration 6870e692c27SAlvin Šipraga * @enable: enable/disable hardware insertion of CPU tag in switch->CPU frames 6880e692c27SAlvin Šipraga * @mask: port mask of ports that parse should parse CPU tags 6890e692c27SAlvin Šipraga * @trap_port: forward trapped frames to this port 6900e692c27SAlvin Šipraga * @insert: CPU tag insertion mode in switch->CPU frames 6910e692c27SAlvin Šipraga * @position: position of CPU tag in frame 6920e692c27SAlvin Šipraga * @rx_length: minimum CPU RX length 6930e692c27SAlvin Šipraga * @format: CPU tag format 6940e692c27SAlvin Šipraga * 6950e692c27SAlvin Šipraga * Represents the CPU tagging and CPU port configuration of the switch. These 6960e692c27SAlvin Šipraga * settings are configurable at runtime. 6970e692c27SAlvin Šipraga */ 6980e692c27SAlvin Šipraga struct rtl8365mb_cpu { 6990e692c27SAlvin Šipraga bool enable; 7000e692c27SAlvin Šipraga u32 mask; 7010e692c27SAlvin Šipraga u32 trap_port; 7020e692c27SAlvin Šipraga enum rtl8365mb_cpu_insert insert; 7030e692c27SAlvin Šipraga enum rtl8365mb_cpu_position position; 7040e692c27SAlvin Šipraga enum rtl8365mb_cpu_rxlen rx_length; 7050e692c27SAlvin Šipraga enum rtl8365mb_cpu_format format; 7060e692c27SAlvin Šipraga }; 7070e692c27SAlvin Šipraga 7080e692c27SAlvin Šipraga /** 7090e692c27SAlvin Šipraga * struct rtl8365mb_port - private per-port data 7100e692c27SAlvin Šipraga * @priv: pointer to parent realtek_priv data 7110e692c27SAlvin Šipraga * @index: DSA port index, same as dsa_port::index 7120e692c27SAlvin Šipraga * @stats: link statistics populated by rtl8365mb_stats_poll, ready for atomic 7130e692c27SAlvin Šipraga * access via rtl8365mb_get_stats64 7140e692c27SAlvin Šipraga * @stats_lock: protect the stats structure during read/update 7150e692c27SAlvin Šipraga * @mib_work: delayed work for polling MIB counters 7160e692c27SAlvin Šipraga */ 7170e692c27SAlvin Šipraga struct rtl8365mb_port { 7180e692c27SAlvin Šipraga struct realtek_priv *priv; 7190e692c27SAlvin Šipraga unsigned int index; 7200e692c27SAlvin Šipraga struct rtnl_link_stats64 stats; 7210e692c27SAlvin Šipraga spinlock_t stats_lock; 7220e692c27SAlvin Šipraga struct delayed_work mib_work; 7230e692c27SAlvin Šipraga }; 7240e692c27SAlvin Šipraga 7250e692c27SAlvin Šipraga /** 7260e692c27SAlvin Šipraga * struct rtl8365mb - driver private data 7270e692c27SAlvin Šipraga * @priv: pointer to parent realtek_priv data 7280e692c27SAlvin Šipraga * @irq: registered IRQ or zero 7290e692c27SAlvin Šipraga * @chip_info: chip-specific info about the attached switch 7300e692c27SAlvin Šipraga * @cpu: CPU tagging and CPU port configuration for this chip 7310e692c27SAlvin Šipraga * @mib_lock: prevent concurrent reads of MIB counters 7320e692c27SAlvin Šipraga * @ports: per-port data 7330e692c27SAlvin Šipraga * 7340e692c27SAlvin Šipraga * Private data for this driver. 7350e692c27SAlvin Šipraga */ 7360e692c27SAlvin Šipraga struct rtl8365mb { 7370e692c27SAlvin Šipraga struct realtek_priv *priv; 7380e692c27SAlvin Šipraga int irq; 7390e692c27SAlvin Šipraga const struct rtl8365mb_chip_info *chip_info; 7400e692c27SAlvin Šipraga struct rtl8365mb_cpu cpu; 7410e692c27SAlvin Šipraga struct mutex mib_lock; 7420e692c27SAlvin Šipraga struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS]; 7430e692c27SAlvin Šipraga }; 7440e692c27SAlvin Šipraga 7450e692c27SAlvin Šipraga static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) 7460e692c27SAlvin Šipraga { 7470e692c27SAlvin Šipraga u32 val; 7480e692c27SAlvin Šipraga 7490e692c27SAlvin Šipraga return regmap_read_poll_timeout(priv->map_nolock, 7500e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_STATUS_REG, 7510e692c27SAlvin Šipraga val, !val, 10, 100); 7520e692c27SAlvin Šipraga } 7530e692c27SAlvin Šipraga 7540e692c27SAlvin Šipraga static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, 7550e692c27SAlvin Šipraga u32 ocp_addr) 7560e692c27SAlvin Šipraga { 7570e692c27SAlvin Šipraga u32 val; 7580e692c27SAlvin Šipraga int ret; 7590e692c27SAlvin Šipraga 7600e692c27SAlvin Šipraga /* Set OCP prefix */ 7610e692c27SAlvin Šipraga val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); 7620e692c27SAlvin Šipraga ret = regmap_update_bits( 7630e692c27SAlvin Šipraga priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG, 7640e692c27SAlvin Šipraga RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, 7650e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); 7660e692c27SAlvin Šipraga if (ret) 7670e692c27SAlvin Šipraga return ret; 7680e692c27SAlvin Šipraga 7690e692c27SAlvin Šipraga /* Set PHY register address */ 7700e692c27SAlvin Šipraga val = RTL8365MB_PHY_BASE; 7710e692c27SAlvin Šipraga val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); 7720e692c27SAlvin Šipraga val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, 7730e692c27SAlvin Šipraga ocp_addr >> 1); 7740e692c27SAlvin Šipraga val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, 7750e692c27SAlvin Šipraga ocp_addr >> 6); 7760e692c27SAlvin Šipraga ret = regmap_write(priv->map_nolock, 7770e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); 7780e692c27SAlvin Šipraga if (ret) 7790e692c27SAlvin Šipraga return ret; 7800e692c27SAlvin Šipraga 7810e692c27SAlvin Šipraga return 0; 7820e692c27SAlvin Šipraga } 7830e692c27SAlvin Šipraga 7840e692c27SAlvin Šipraga static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, 7850e692c27SAlvin Šipraga u32 ocp_addr, u16 *data) 7860e692c27SAlvin Šipraga { 7870e692c27SAlvin Šipraga u32 val; 7880e692c27SAlvin Šipraga int ret; 7890e692c27SAlvin Šipraga 7900e692c27SAlvin Šipraga rtl83xx_lock(priv); 7910e692c27SAlvin Šipraga 7920e692c27SAlvin Šipraga ret = rtl8365mb_phy_poll_busy(priv); 7930e692c27SAlvin Šipraga if (ret) 7940e692c27SAlvin Šipraga goto out; 7950e692c27SAlvin Šipraga 7960e692c27SAlvin Šipraga ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); 7970e692c27SAlvin Šipraga if (ret) 7980e692c27SAlvin Šipraga goto out; 7990e692c27SAlvin Šipraga 8000e692c27SAlvin Šipraga /* Execute read operation */ 8010e692c27SAlvin Šipraga val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, 8020e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | 8030e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, 8040e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); 8050e692c27SAlvin Šipraga ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, 8060e692c27SAlvin Šipraga val); 8070e692c27SAlvin Šipraga if (ret) 8080e692c27SAlvin Šipraga goto out; 8090e692c27SAlvin Šipraga 8100e692c27SAlvin Šipraga ret = rtl8365mb_phy_poll_busy(priv); 8110e692c27SAlvin Šipraga if (ret) 8120e692c27SAlvin Šipraga goto out; 8130e692c27SAlvin Šipraga 8140e692c27SAlvin Šipraga /* Get PHY register data */ 8150e692c27SAlvin Šipraga ret = regmap_read(priv->map_nolock, 8160e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); 8170e692c27SAlvin Šipraga if (ret) 8180e692c27SAlvin Šipraga goto out; 8190e692c27SAlvin Šipraga 8200e692c27SAlvin Šipraga *data = val & 0xFFFF; 8210e692c27SAlvin Šipraga 8220e692c27SAlvin Šipraga out: 8230e692c27SAlvin Šipraga rtl83xx_unlock(priv); 8240e692c27SAlvin Šipraga 8250e692c27SAlvin Šipraga return ret; 8260e692c27SAlvin Šipraga } 8270e692c27SAlvin Šipraga 8280e692c27SAlvin Šipraga static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, 8290e692c27SAlvin Šipraga u32 ocp_addr, u16 data) 8300e692c27SAlvin Šipraga { 8310e692c27SAlvin Šipraga u32 val; 8320e692c27SAlvin Šipraga int ret; 8330e692c27SAlvin Šipraga 8340e692c27SAlvin Šipraga rtl83xx_lock(priv); 8350e692c27SAlvin Šipraga 8360e692c27SAlvin Šipraga ret = rtl8365mb_phy_poll_busy(priv); 8370e692c27SAlvin Šipraga if (ret) 8380e692c27SAlvin Šipraga goto out; 8390e692c27SAlvin Šipraga 8400e692c27SAlvin Šipraga ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); 8410e692c27SAlvin Šipraga if (ret) 8420e692c27SAlvin Šipraga goto out; 8430e692c27SAlvin Šipraga 8440e692c27SAlvin Šipraga /* Set PHY register data */ 8450e692c27SAlvin Šipraga ret = regmap_write(priv->map_nolock, 8460e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); 8470e692c27SAlvin Šipraga if (ret) 8480e692c27SAlvin Šipraga goto out; 8490e692c27SAlvin Šipraga 8500e692c27SAlvin Šipraga /* Execute write operation */ 8510e692c27SAlvin Šipraga val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, 8520e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | 8530e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, 8540e692c27SAlvin Šipraga RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); 8550e692c27SAlvin Šipraga ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, 8560e692c27SAlvin Šipraga val); 8570e692c27SAlvin Šipraga if (ret) 8580e692c27SAlvin Šipraga goto out; 8590e692c27SAlvin Šipraga 8600e692c27SAlvin Šipraga ret = rtl8365mb_phy_poll_busy(priv); 8610e692c27SAlvin Šipraga if (ret) 8620e692c27SAlvin Šipraga goto out; 8630e692c27SAlvin Šipraga 8640e692c27SAlvin Šipraga out: 8650e692c27SAlvin Šipraga rtl83xx_unlock(priv); 8660e692c27SAlvin Šipraga 8670e692c27SAlvin Šipraga return ret; 8680e692c27SAlvin Šipraga } 8690e692c27SAlvin Šipraga 8700e692c27SAlvin Šipraga static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) 8710e692c27SAlvin Šipraga { 8720e692c27SAlvin Šipraga u32 ocp_addr; 8730e692c27SAlvin Šipraga u16 val; 8740e692c27SAlvin Šipraga int ret; 8750e692c27SAlvin Šipraga 8760e692c27SAlvin Šipraga if (phy > RTL8365MB_PHYADDRMAX) 8770e692c27SAlvin Šipraga return -EINVAL; 8780e692c27SAlvin Šipraga 8790e692c27SAlvin Šipraga if (regnum > RTL8365MB_PHYREGMAX) 8800e692c27SAlvin Šipraga return -EINVAL; 8810e692c27SAlvin Šipraga 8820e692c27SAlvin Šipraga ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; 8830e692c27SAlvin Šipraga 8840e692c27SAlvin Šipraga ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); 8850e692c27SAlvin Šipraga if (ret) { 8860e692c27SAlvin Šipraga dev_err(priv->dev, 8870e692c27SAlvin Šipraga "failed to read PHY%d reg %02x @ %04x, ret %pe\n", phy, 8880e692c27SAlvin Šipraga regnum, ocp_addr, ERR_PTR(ret)); 8890e692c27SAlvin Šipraga return ret; 8900e692c27SAlvin Šipraga } 8910e692c27SAlvin Šipraga 8920e692c27SAlvin Šipraga dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", 8930e692c27SAlvin Šipraga phy, regnum, ocp_addr, val); 8940e692c27SAlvin Šipraga 8950e692c27SAlvin Šipraga return val; 8960e692c27SAlvin Šipraga } 8970e692c27SAlvin Šipraga 8980e692c27SAlvin Šipraga static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, 8990e692c27SAlvin Šipraga u16 val) 9000e692c27SAlvin Šipraga { 9010e692c27SAlvin Šipraga u32 ocp_addr; 9020e692c27SAlvin Šipraga int ret; 9030e692c27SAlvin Šipraga 9040e692c27SAlvin Šipraga if (phy > RTL8365MB_PHYADDRMAX) 9050e692c27SAlvin Šipraga return -EINVAL; 9060e692c27SAlvin Šipraga 9070e692c27SAlvin Šipraga if (regnum > RTL8365MB_PHYREGMAX) 9080e692c27SAlvin Šipraga return -EINVAL; 9090e692c27SAlvin Šipraga 9100e692c27SAlvin Šipraga ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; 9110e692c27SAlvin Šipraga 9120e692c27SAlvin Šipraga ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); 9130e692c27SAlvin Šipraga if (ret) { 9140e692c27SAlvin Šipraga dev_err(priv->dev, 9150e692c27SAlvin Šipraga "failed to write PHY%d reg %02x @ %04x, ret %pe\n", phy, 9160e692c27SAlvin Šipraga regnum, ocp_addr, ERR_PTR(ret)); 9170e692c27SAlvin Šipraga return ret; 9180e692c27SAlvin Šipraga } 9190e692c27SAlvin Šipraga 9200e692c27SAlvin Šipraga dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", 9210e692c27SAlvin Šipraga phy, regnum, ocp_addr, val); 9220e692c27SAlvin Šipraga 9230e692c27SAlvin Šipraga return 0; 9240e692c27SAlvin Šipraga } 9250e692c27SAlvin Šipraga 9260e692c27SAlvin Šipraga static const struct rtl8365mb_extint * 9270e692c27SAlvin Šipraga rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) 9280e692c27SAlvin Šipraga { 9290e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 9300e692c27SAlvin Šipraga int i; 9310e692c27SAlvin Šipraga 9320e692c27SAlvin Šipraga for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) { 9330e692c27SAlvin Šipraga const struct rtl8365mb_extint *extint = 9340e692c27SAlvin Šipraga &mb->chip_info->extints[i]; 9350e692c27SAlvin Šipraga 9360e692c27SAlvin Šipraga if (!extint->supported_interfaces) 9370e692c27SAlvin Šipraga continue; 9380e692c27SAlvin Šipraga 9390e692c27SAlvin Šipraga if (extint->port == port) 9400e692c27SAlvin Šipraga return extint; 9410e692c27SAlvin Šipraga } 9420e692c27SAlvin Šipraga 9430e692c27SAlvin Šipraga return NULL; 9440e692c27SAlvin Šipraga } 9450e692c27SAlvin Šipraga 9460e692c27SAlvin Šipraga static enum dsa_tag_protocol 9470e692c27SAlvin Šipraga rtl8365mb_get_tag_protocol(struct dsa_switch *ds, int port, 9480e692c27SAlvin Šipraga enum dsa_tag_protocol mp) 9490e692c27SAlvin Šipraga { 9500e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 9510e692c27SAlvin Šipraga struct rtl8365mb_cpu *cpu; 9520e692c27SAlvin Šipraga struct rtl8365mb *mb; 9530e692c27SAlvin Šipraga 9540e692c27SAlvin Šipraga mb = priv->chip_data; 9550e692c27SAlvin Šipraga cpu = &mb->cpu; 9560e692c27SAlvin Šipraga 9570e692c27SAlvin Šipraga if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) 9580e692c27SAlvin Šipraga return DSA_TAG_PROTO_RTL8_4T; 9590e692c27SAlvin Šipraga 9600e692c27SAlvin Šipraga return DSA_TAG_PROTO_RTL8_4; 9610e692c27SAlvin Šipraga } 9620e692c27SAlvin Šipraga 9630e692c27SAlvin Šipraga static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, 9640e692c27SAlvin Šipraga phy_interface_t interface) 9650e692c27SAlvin Šipraga { 9660e692c27SAlvin Šipraga const struct rtl8365mb_extint *extint = 9670e692c27SAlvin Šipraga rtl8365mb_get_port_extint(priv, port); 9680e692c27SAlvin Šipraga struct dsa_switch *ds = &priv->ds; 9690e692c27SAlvin Šipraga struct device_node *dn; 9700e692c27SAlvin Šipraga struct dsa_port *dp; 9710e692c27SAlvin Šipraga int tx_delay = 0; 9720e692c27SAlvin Šipraga int rx_delay = 0; 9730e692c27SAlvin Šipraga u32 val; 9740e692c27SAlvin Šipraga int ret; 9750e692c27SAlvin Šipraga 9760e692c27SAlvin Šipraga if (!extint) 9770e692c27SAlvin Šipraga return -ENODEV; 9780e692c27SAlvin Šipraga 9790e692c27SAlvin Šipraga dp = dsa_to_port(ds, port); 9800e692c27SAlvin Šipraga dn = dp->dn; 9810e692c27SAlvin Šipraga 9820e692c27SAlvin Šipraga /* Set the RGMII TX/RX delay 9830e692c27SAlvin Šipraga * 9840e692c27SAlvin Šipraga * The Realtek vendor driver indicates the following possible 9850e692c27SAlvin Šipraga * configuration settings: 9860e692c27SAlvin Šipraga * 9870e692c27SAlvin Šipraga * TX delay: 9880e692c27SAlvin Šipraga * 0 = no delay, 1 = 2 ns delay 9890e692c27SAlvin Šipraga * RX delay: 9900e692c27SAlvin Šipraga * 0 = no delay, 7 = maximum delay 9910e692c27SAlvin Šipraga * Each step is approximately 0.3 ns, so the maximum delay is about 9920e692c27SAlvin Šipraga * 2.1 ns. 9930e692c27SAlvin Šipraga * 9940e692c27SAlvin Šipraga * The vendor driver also states that this must be configured *before* 9950e692c27SAlvin Šipraga * forcing the external interface into a particular mode, which is done 9960e692c27SAlvin Šipraga * in the rtl8365mb_phylink_mac_link_{up,down} functions. 9970e692c27SAlvin Šipraga * 9980e692c27SAlvin Šipraga * Only configure an RGMII TX (resp. RX) delay if the 9990e692c27SAlvin Šipraga * tx-internal-delay-ps (resp. rx-internal-delay-ps) OF property is 10000e692c27SAlvin Šipraga * specified. We ignore the detail of the RGMII interface mode 10010e692c27SAlvin Šipraga * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only 10020e692c27SAlvin Šipraga * property. 10030e692c27SAlvin Šipraga */ 10040e692c27SAlvin Šipraga if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) { 10050e692c27SAlvin Šipraga val = val / 1000; /* convert to ns */ 10060e692c27SAlvin Šipraga 10070e692c27SAlvin Šipraga if (val == 0 || val == 2) 10080e692c27SAlvin Šipraga tx_delay = val / 2; 10090e692c27SAlvin Šipraga else 10100e692c27SAlvin Šipraga dev_warn(priv->dev, 10110e692c27SAlvin Šipraga "RGMII TX delay must be 0 or 2 ns\n"); 10120e692c27SAlvin Šipraga } 10130e692c27SAlvin Šipraga 10140e692c27SAlvin Šipraga if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { 10150e692c27SAlvin Šipraga val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */ 10160e692c27SAlvin Šipraga 10170e692c27SAlvin Šipraga if (val <= 7) 10180e692c27SAlvin Šipraga rx_delay = val; 10190e692c27SAlvin Šipraga else 10200e692c27SAlvin Šipraga dev_warn(priv->dev, 10210e692c27SAlvin Šipraga "RGMII RX delay must be 0 to 2.1 ns\n"); 10220e692c27SAlvin Šipraga } 10230e692c27SAlvin Šipraga 10240e692c27SAlvin Šipraga ret = regmap_update_bits( 10250e692c27SAlvin Šipraga priv->map, RTL8365MB_EXT_RGMXF_REG(extint->id), 10260e692c27SAlvin Šipraga RTL8365MB_EXT_RGMXF_TXDELAY_MASK | 10270e692c27SAlvin Šipraga RTL8365MB_EXT_RGMXF_RXDELAY_MASK, 10280e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) | 10290e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay)); 10300e692c27SAlvin Šipraga if (ret) 10310e692c27SAlvin Šipraga return ret; 10320e692c27SAlvin Šipraga 10330e692c27SAlvin Šipraga ret = regmap_update_bits( 10340e692c27SAlvin Šipraga priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id), 10350e692c27SAlvin Šipraga RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id), 10360e692c27SAlvin Šipraga RTL8365MB_EXT_PORT_MODE_RGMII 10370e692c27SAlvin Šipraga << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET( 10380e692c27SAlvin Šipraga extint->id)); 10390e692c27SAlvin Šipraga if (ret) 10400e692c27SAlvin Šipraga return ret; 10410e692c27SAlvin Šipraga 10420e692c27SAlvin Šipraga return 0; 10430e692c27SAlvin Šipraga } 10440e692c27SAlvin Šipraga 10450e692c27SAlvin Šipraga static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, 10460e692c27SAlvin Šipraga bool link, int speed, int duplex, 10470e692c27SAlvin Šipraga bool tx_pause, bool rx_pause) 10480e692c27SAlvin Šipraga { 10490e692c27SAlvin Šipraga const struct rtl8365mb_extint *extint = 10500e692c27SAlvin Šipraga rtl8365mb_get_port_extint(priv, port); 10510e692c27SAlvin Šipraga u32 r_tx_pause; 10520e692c27SAlvin Šipraga u32 r_rx_pause; 10530e692c27SAlvin Šipraga u32 r_duplex; 10540e692c27SAlvin Šipraga u32 r_speed; 10550e692c27SAlvin Šipraga u32 r_link; 10560e692c27SAlvin Šipraga int val; 10570e692c27SAlvin Šipraga int ret; 10580e692c27SAlvin Šipraga 10590e692c27SAlvin Šipraga if (!extint) 10600e692c27SAlvin Šipraga return -ENODEV; 10610e692c27SAlvin Šipraga 10620e692c27SAlvin Šipraga if (link) { 10630e692c27SAlvin Šipraga /* Force the link up with the desired configuration */ 10640e692c27SAlvin Šipraga r_link = 1; 10650e692c27SAlvin Šipraga r_rx_pause = rx_pause ? 1 : 0; 10660e692c27SAlvin Šipraga r_tx_pause = tx_pause ? 1 : 0; 10670e692c27SAlvin Šipraga 10680e692c27SAlvin Šipraga if (speed == SPEED_1000) { 10690e692c27SAlvin Šipraga r_speed = RTL8365MB_PORT_SPEED_1000M; 10700e692c27SAlvin Šipraga } else if (speed == SPEED_100) { 10710e692c27SAlvin Šipraga r_speed = RTL8365MB_PORT_SPEED_100M; 10720e692c27SAlvin Šipraga } else if (speed == SPEED_10) { 10730e692c27SAlvin Šipraga r_speed = RTL8365MB_PORT_SPEED_10M; 10740e692c27SAlvin Šipraga } else { 10750e692c27SAlvin Šipraga dev_err(priv->dev, "unsupported port speed %s\n", 10760e692c27SAlvin Šipraga phy_speed_to_str(speed)); 10770e692c27SAlvin Šipraga return -EINVAL; 10780e692c27SAlvin Šipraga } 10790e692c27SAlvin Šipraga 10800e692c27SAlvin Šipraga if (duplex == DUPLEX_FULL) { 10810e692c27SAlvin Šipraga r_duplex = 1; 10820e692c27SAlvin Šipraga } else if (duplex == DUPLEX_HALF) { 10830e692c27SAlvin Šipraga r_duplex = 0; 10840e692c27SAlvin Šipraga } else { 10850e692c27SAlvin Šipraga dev_err(priv->dev, "unsupported duplex %s\n", 10860e692c27SAlvin Šipraga phy_duplex_to_str(duplex)); 10870e692c27SAlvin Šipraga return -EINVAL; 10880e692c27SAlvin Šipraga } 10890e692c27SAlvin Šipraga } else { 10900e692c27SAlvin Šipraga /* Force the link down and reset any programmed configuration */ 10910e692c27SAlvin Šipraga r_link = 0; 10920e692c27SAlvin Šipraga r_tx_pause = 0; 10930e692c27SAlvin Šipraga r_rx_pause = 0; 10940e692c27SAlvin Šipraga r_speed = 0; 10950e692c27SAlvin Šipraga r_duplex = 0; 10960e692c27SAlvin Šipraga } 10970e692c27SAlvin Šipraga 10980e692c27SAlvin Šipraga val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) | 10990e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK, 11000e692c27SAlvin Šipraga r_tx_pause) | 11010e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK, 11020e692c27SAlvin Šipraga r_rx_pause) | 11030e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK, r_link) | 11040e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK, 11050e692c27SAlvin Šipraga r_duplex) | 11060e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed); 11070e692c27SAlvin Šipraga ret = regmap_write(priv->map, 11080e692c27SAlvin Šipraga RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id), 11090e692c27SAlvin Šipraga val); 11100e692c27SAlvin Šipraga if (ret) 11110e692c27SAlvin Šipraga return ret; 11120e692c27SAlvin Šipraga 11130e692c27SAlvin Šipraga return 0; 11140e692c27SAlvin Šipraga } 11150e692c27SAlvin Šipraga 11160e692c27SAlvin Šipraga static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, 11170e692c27SAlvin Šipraga struct phylink_config *config) 11180e692c27SAlvin Šipraga { 11190e692c27SAlvin Šipraga const struct rtl8365mb_extint *extint = 11200e692c27SAlvin Šipraga rtl8365mb_get_port_extint(ds->priv, port); 11210e692c27SAlvin Šipraga 11220e692c27SAlvin Šipraga config->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | 11230e692c27SAlvin Šipraga MAC_10 | MAC_100 | MAC_1000FD; 11240e692c27SAlvin Šipraga 11250e692c27SAlvin Šipraga if (!extint) { 11260e692c27SAlvin Šipraga __set_bit(PHY_INTERFACE_MODE_INTERNAL, 11270e692c27SAlvin Šipraga config->supported_interfaces); 11280e692c27SAlvin Šipraga 11290e692c27SAlvin Šipraga /* GMII is the default interface mode for phylib, so 11300e692c27SAlvin Šipraga * we have to support it for ports with integrated PHY. 11310e692c27SAlvin Šipraga */ 11320e692c27SAlvin Šipraga __set_bit(PHY_INTERFACE_MODE_GMII, 11330e692c27SAlvin Šipraga config->supported_interfaces); 11340e692c27SAlvin Šipraga return; 11350e692c27SAlvin Šipraga } 11360e692c27SAlvin Šipraga 11370e692c27SAlvin Šipraga /* Populate according to the modes supported by _this driver_, 11380e692c27SAlvin Šipraga * not necessarily the modes supported by the hardware, some of 11390e692c27SAlvin Šipraga * which remain unimplemented. 11400e692c27SAlvin Šipraga */ 11410e692c27SAlvin Šipraga 11420e692c27SAlvin Šipraga if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_RGMII) 11430e692c27SAlvin Šipraga phy_interface_set_rgmii(config->supported_interfaces); 11440e692c27SAlvin Šipraga } 11450e692c27SAlvin Šipraga 11460e692c27SAlvin Šipraga static void rtl8365mb_phylink_mac_config(struct phylink_config *config, 11470e692c27SAlvin Šipraga unsigned int mode, 11480e692c27SAlvin Šipraga const struct phylink_link_state *state) 11490e692c27SAlvin Šipraga { 11500e692c27SAlvin Šipraga struct dsa_port *dp = dsa_phylink_to_port(config); 11510e692c27SAlvin Šipraga struct realtek_priv *priv = dp->ds->priv; 11520e692c27SAlvin Šipraga u8 port = dp->index; 11530e692c27SAlvin Šipraga int ret; 11540e692c27SAlvin Šipraga 11550e692c27SAlvin Šipraga if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) { 11560e692c27SAlvin Šipraga dev_err(priv->dev, 11570e692c27SAlvin Šipraga "port %d supports only conventional PHY or fixed-link\n", 11580e692c27SAlvin Šipraga port); 11590e692c27SAlvin Šipraga return; 11600e692c27SAlvin Šipraga } 11610e692c27SAlvin Šipraga 11620e692c27SAlvin Šipraga if (phy_interface_mode_is_rgmii(state->interface)) { 11630e692c27SAlvin Šipraga ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); 11640e692c27SAlvin Šipraga if (ret) 11650e692c27SAlvin Šipraga dev_err(priv->dev, 11660e692c27SAlvin Šipraga "failed to configure RGMII mode on port %d: %pe\n", 11670e692c27SAlvin Šipraga port, ERR_PTR(ret)); 11680e692c27SAlvin Šipraga return; 11690e692c27SAlvin Šipraga } 11700e692c27SAlvin Šipraga 11710e692c27SAlvin Šipraga /* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also 11720e692c27SAlvin Šipraga * supports 11730e692c27SAlvin Šipraga */ 11740e692c27SAlvin Šipraga } 11750e692c27SAlvin Šipraga 11760e692c27SAlvin Šipraga static void rtl8365mb_phylink_mac_link_down(struct phylink_config *config, 11770e692c27SAlvin Šipraga unsigned int mode, 11780e692c27SAlvin Šipraga phy_interface_t interface) 11790e692c27SAlvin Šipraga { 11800e692c27SAlvin Šipraga struct dsa_port *dp = dsa_phylink_to_port(config); 11810e692c27SAlvin Šipraga struct realtek_priv *priv = dp->ds->priv; 11820e692c27SAlvin Šipraga struct rtl8365mb_port *p; 11830e692c27SAlvin Šipraga struct rtl8365mb *mb; 11840e692c27SAlvin Šipraga u8 port = dp->index; 11850e692c27SAlvin Šipraga int ret; 11860e692c27SAlvin Šipraga 11870e692c27SAlvin Šipraga mb = priv->chip_data; 11880e692c27SAlvin Šipraga p = &mb->ports[port]; 11890e692c27SAlvin Šipraga cancel_delayed_work_sync(&p->mib_work); 11900e692c27SAlvin Šipraga 11910e692c27SAlvin Šipraga if (phy_interface_mode_is_rgmii(interface)) { 11920e692c27SAlvin Šipraga ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0, 11930e692c27SAlvin Šipraga false, false); 11940e692c27SAlvin Šipraga if (ret) 11950e692c27SAlvin Šipraga dev_err(priv->dev, 11960e692c27SAlvin Šipraga "failed to reset forced mode on port %d: %pe\n", 11970e692c27SAlvin Šipraga port, ERR_PTR(ret)); 11980e692c27SAlvin Šipraga 11990e692c27SAlvin Šipraga return; 12000e692c27SAlvin Šipraga } 12010e692c27SAlvin Šipraga } 12020e692c27SAlvin Šipraga 12030e692c27SAlvin Šipraga static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config, 12040e692c27SAlvin Šipraga struct phy_device *phydev, 12050e692c27SAlvin Šipraga unsigned int mode, 12060e692c27SAlvin Šipraga phy_interface_t interface, 12070e692c27SAlvin Šipraga int speed, int duplex, bool tx_pause, 12080e692c27SAlvin Šipraga bool rx_pause) 12090e692c27SAlvin Šipraga { 12100e692c27SAlvin Šipraga struct dsa_port *dp = dsa_phylink_to_port(config); 12110e692c27SAlvin Šipraga struct realtek_priv *priv = dp->ds->priv; 12120e692c27SAlvin Šipraga struct rtl8365mb_port *p; 12130e692c27SAlvin Šipraga struct rtl8365mb *mb; 12140e692c27SAlvin Šipraga u8 port = dp->index; 12150e692c27SAlvin Šipraga int ret; 12160e692c27SAlvin Šipraga 12170e692c27SAlvin Šipraga mb = priv->chip_data; 12180e692c27SAlvin Šipraga p = &mb->ports[port]; 12190e692c27SAlvin Šipraga schedule_delayed_work(&p->mib_work, 0); 12200e692c27SAlvin Šipraga 12210e692c27SAlvin Šipraga if (phy_interface_mode_is_rgmii(interface)) { 12220e692c27SAlvin Šipraga ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed, 12230e692c27SAlvin Šipraga duplex, tx_pause, 12240e692c27SAlvin Šipraga rx_pause); 12250e692c27SAlvin Šipraga if (ret) 12260e692c27SAlvin Šipraga dev_err(priv->dev, 12270e692c27SAlvin Šipraga "failed to force mode on port %d: %pe\n", port, 12280e692c27SAlvin Šipraga ERR_PTR(ret)); 12290e692c27SAlvin Šipraga 12300e692c27SAlvin Šipraga return; 12310e692c27SAlvin Šipraga } 12320e692c27SAlvin Šipraga } 12330e692c27SAlvin Šipraga 12340e692c27SAlvin Šipraga static int rtl8365mb_port_change_mtu(struct dsa_switch *ds, int port, 12350e692c27SAlvin Šipraga int new_mtu) 12360e692c27SAlvin Šipraga { 12370e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 12380e692c27SAlvin Šipraga int frame_size; 12390e692c27SAlvin Šipraga 12400e692c27SAlvin Šipraga /* When a new MTU is set, DSA always sets the CPU port's MTU to the 12410e692c27SAlvin Šipraga * largest MTU of the user ports. Because the switch only has a global 12420e692c27SAlvin Šipraga * RX length register, only allowing CPU port here is enough. 12430e692c27SAlvin Šipraga */ 12440e692c27SAlvin Šipraga if (!dsa_is_cpu_port(ds, port)) 12450e692c27SAlvin Šipraga return 0; 12460e692c27SAlvin Šipraga 12470e692c27SAlvin Šipraga frame_size = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; 12480e692c27SAlvin Šipraga 12490e692c27SAlvin Šipraga dev_dbg(priv->dev, "changing mtu to %d (frame size: %d)\n", 12500e692c27SAlvin Šipraga new_mtu, frame_size); 12510e692c27SAlvin Šipraga 12520e692c27SAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, 12530e692c27SAlvin Šipraga RTL8365MB_CFG0_MAX_LEN_MASK, 12540e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 12550e692c27SAlvin Šipraga frame_size)); 12560e692c27SAlvin Šipraga } 12570e692c27SAlvin Šipraga 12580e692c27SAlvin Šipraga static int rtl8365mb_port_max_mtu(struct dsa_switch *ds, int port) 12590e692c27SAlvin Šipraga { 12600e692c27SAlvin Šipraga return RTL8365MB_CFG0_MAX_LEN_MAX - VLAN_ETH_HLEN - ETH_FCS_LEN; 12610e692c27SAlvin Šipraga } 12620e692c27SAlvin Šipraga 12630e692c27SAlvin Šipraga static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, 12640e692c27SAlvin Šipraga u8 state) 12650e692c27SAlvin Šipraga { 12660e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 12670e692c27SAlvin Šipraga enum rtl8365mb_stp_state val; 12680e692c27SAlvin Šipraga int msti = 0; 12690e692c27SAlvin Šipraga 12700e692c27SAlvin Šipraga switch (state) { 12710e692c27SAlvin Šipraga case BR_STATE_DISABLED: 12720e692c27SAlvin Šipraga val = RTL8365MB_STP_STATE_DISABLED; 12730e692c27SAlvin Šipraga break; 12740e692c27SAlvin Šipraga case BR_STATE_BLOCKING: 12750e692c27SAlvin Šipraga case BR_STATE_LISTENING: 12760e692c27SAlvin Šipraga val = RTL8365MB_STP_STATE_BLOCKING; 12770e692c27SAlvin Šipraga break; 12780e692c27SAlvin Šipraga case BR_STATE_LEARNING: 12790e692c27SAlvin Šipraga val = RTL8365MB_STP_STATE_LEARNING; 12800e692c27SAlvin Šipraga break; 12810e692c27SAlvin Šipraga case BR_STATE_FORWARDING: 12820e692c27SAlvin Šipraga val = RTL8365MB_STP_STATE_FORWARDING; 12830e692c27SAlvin Šipraga break; 12840e692c27SAlvin Šipraga default: 12850e692c27SAlvin Šipraga dev_err(priv->dev, "invalid STP state: %u\n", state); 12860e692c27SAlvin Šipraga return; 12870e692c27SAlvin Šipraga } 12880e692c27SAlvin Šipraga 12890e692c27SAlvin Šipraga regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port), 12900e692c27SAlvin Šipraga RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port), 12910e692c27SAlvin Šipraga val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); 12920e692c27SAlvin Šipraga } 12930e692c27SAlvin Šipraga 12949da2c867SAlvin Šipraga static int rtl8365mb_port_set_transparent(struct realtek_priv *priv, 12959da2c867SAlvin Šipraga int igr_port, int egr_port, 12969da2c867SAlvin Šipraga bool enable) 12979da2c867SAlvin Šipraga { 12989da2c867SAlvin Šipraga dev_dbg(priv->dev, "%s transparent VLAN from %d to %d\n", 12999da2c867SAlvin Šipraga enable ? "Enable" : "Disable", igr_port, egr_port); 13009da2c867SAlvin Šipraga 13019da2c867SAlvin Šipraga /* "Transparent" between the two ports means that packets forwarded by 13029da2c867SAlvin Šipraga * igr_port and egressed on egr_port will not be filtered by the usual 13039da2c867SAlvin Šipraga * VLAN membership settings. 13049da2c867SAlvin Šipraga */ 13059da2c867SAlvin Šipraga return regmap_update_bits(priv->map, 13069da2c867SAlvin Šipraga RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(egr_port), 13079da2c867SAlvin Šipraga BIT(igr_port), enable ? BIT(igr_port) : 0); 13089da2c867SAlvin Šipraga } 13099da2c867SAlvin Šipraga 13109da2c867SAlvin Šipraga static int rtl8365mb_port_set_ingress_filtering(struct realtek_priv *priv, 13119da2c867SAlvin Šipraga int port, bool enable) 13129da2c867SAlvin Šipraga { 13139da2c867SAlvin Šipraga /* Ingress filtering enabled: Discard VLAN-tagged frames if the port is 13149da2c867SAlvin Šipraga * not a member of the VLAN with which the packet is associated. 13159da2c867SAlvin Šipraga * Untagged packets will also be discarded unless the port has a PVID 13169da2c867SAlvin Šipraga * programmed. Priority-tagged frames are treated as untagged frames. 13179da2c867SAlvin Šipraga * 13189da2c867SAlvin Šipraga * Ingress filtering disabled: Accept all tagged and untagged frames. 13199da2c867SAlvin Šipraga */ 13209da2c867SAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_VLAN_INGRESS_REG, 13219da2c867SAlvin Šipraga RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port), 13229da2c867SAlvin Šipraga enable ? 13239da2c867SAlvin Šipraga RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port) : 13249da2c867SAlvin Šipraga 0); 13259da2c867SAlvin Šipraga } 13269da2c867SAlvin Šipraga 13279da2c867SAlvin Šipraga static int 13289da2c867SAlvin Šipraga rtl8365mb_port_set_vlan_egress_mode(struct realtek_priv *priv, int port, 13299da2c867SAlvin Šipraga enum rtl8365mb_vlan_egress_mode mode) 13309da2c867SAlvin Šipraga { 13319da2c867SAlvin Šipraga u32 val; 13329da2c867SAlvin Šipraga 13339da2c867SAlvin Šipraga val = FIELD_PREP(RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, mode); 13349da2c867SAlvin Šipraga return regmap_update_bits(priv->map, 13359da2c867SAlvin Šipraga RTL8365MB_PORT_MISC_CFG_REG(port), 13369da2c867SAlvin Šipraga RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, val); 13379da2c867SAlvin Šipraga } 13389da2c867SAlvin Šipraga 13399da2c867SAlvin Šipraga static int rtl8365mb_port_vlan_filtering(struct dsa_switch *ds, int port, 13409da2c867SAlvin Šipraga bool vlan_filtering, 13419da2c867SAlvin Šipraga struct netlink_ext_ack *extack) 13429da2c867SAlvin Šipraga { 13439da2c867SAlvin Šipraga enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame; 13449da2c867SAlvin Šipraga enum rtl8365mb_vlan_egress_mode mode; 13459da2c867SAlvin Šipraga struct realtek_priv *priv = ds->priv; 13469da2c867SAlvin Šipraga u32 configured_ports = 0; 13479da2c867SAlvin Šipraga struct dsa_port *dp; 13489da2c867SAlvin Šipraga u16 pvid_vid; 13499da2c867SAlvin Šipraga int ret; 13509da2c867SAlvin Šipraga 13519da2c867SAlvin Šipraga dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, 13529da2c867SAlvin Šipraga vlan_filtering ? "enable" : "disable"); 13539da2c867SAlvin Šipraga 13549da2c867SAlvin Šipraga ret = rtl8365mb_vlan_port_get_framefilter(priv, port, 13559da2c867SAlvin Šipraga &prev_accepted_frame); 13569da2c867SAlvin Šipraga if (ret) { 13579da2c867SAlvin Šipraga NL_SET_ERR_MSG_MOD(extack, 13589da2c867SAlvin Šipraga "Failed to get current framefilter"); 13599da2c867SAlvin Šipraga return ret; 13609da2c867SAlvin Šipraga } 13619da2c867SAlvin Šipraga 13629da2c867SAlvin Šipraga /* While filtering, only accepts untagged frames if PVID is enabled */ 13639da2c867SAlvin Šipraga if (vlan_filtering) { 13649da2c867SAlvin Šipraga ret = rtl8365mb_vlan_port_get_pvid(priv, port, &pvid_vid); 13659da2c867SAlvin Šipraga if (ret) 13669da2c867SAlvin Šipraga return ret; 13679da2c867SAlvin Šipraga 13689da2c867SAlvin Šipraga if (pvid_vid) 13699da2c867SAlvin Šipraga accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME; 13709da2c867SAlvin Šipraga else 13719da2c867SAlvin Šipraga accepted_frame = RTL8365MB_FRAME_TYPE_TAGGED_ONLY; 13729da2c867SAlvin Šipraga } else { 13739da2c867SAlvin Šipraga accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME; 13749da2c867SAlvin Šipraga } 13759da2c867SAlvin Šipraga 13769da2c867SAlvin Šipraga /* When vlan filter is enable/disabled in a bridge, this function is 13779da2c867SAlvin Šipraga * called for all member ports. We need to enable/disable ingress 13789da2c867SAlvin Šipraga * VLAN membership check. 13799da2c867SAlvin Šipraga */ 13809da2c867SAlvin Šipraga ret = rtl8365mb_port_set_ingress_filtering(priv, port, vlan_filtering); 13819da2c867SAlvin Šipraga if (ret) 13829da2c867SAlvin Šipraga return ret; 13839da2c867SAlvin Šipraga 13849da2c867SAlvin Šipraga /* However, we also enable/disable egress filtering because the switch 13859da2c867SAlvin Šipraga * still consider the egress interface VLAN membership to forward the 13869da2c867SAlvin Šipraga * traffic. We enable/disable that check disabling/enabling transparent 13879da2c867SAlvin Šipraga * VLAN between the ingress port and all other available ports. 13889da2c867SAlvin Šipraga */ 13899da2c867SAlvin Šipraga dsa_switch_for_each_available_port(dp, ds) { 13909da2c867SAlvin Šipraga /* port isolation will still keep traffic inside the bridge */ 13919da2c867SAlvin Šipraga ret = rtl8365mb_port_set_transparent(priv, port, dp->index, 13929da2c867SAlvin Šipraga !vlan_filtering); 13939da2c867SAlvin Šipraga if (ret) 13949da2c867SAlvin Šipraga goto undo_transparent; 13959da2c867SAlvin Šipraga 13969da2c867SAlvin Šipraga configured_ports |= BIT(dp->index); 13979da2c867SAlvin Šipraga } 13989da2c867SAlvin Šipraga 13999da2c867SAlvin Šipraga if (accepted_frame != prev_accepted_frame) { 14009da2c867SAlvin Šipraga ret = rtl8365mb_vlan_port_set_framefilter(priv, port, 14019da2c867SAlvin Šipraga accepted_frame); 14029da2c867SAlvin Šipraga if (ret) { 14039da2c867SAlvin Šipraga NL_SET_ERR_MSG_MOD(extack, 14049da2c867SAlvin Šipraga "Failed to set port framefilter"); 14059da2c867SAlvin Šipraga goto undo_transparent; 14069da2c867SAlvin Šipraga } 14079da2c867SAlvin Šipraga } 14089da2c867SAlvin Šipraga 14099da2c867SAlvin Šipraga /* When VLAN filtering is disabled, preserve frames exactly as received. 14109da2c867SAlvin Šipraga * Otherwise, the VLAN egress pipeline may still alter tag state 14119da2c867SAlvin Šipraga * according to VLAN membership and untag configuration. 14129da2c867SAlvin Šipraga */ 14139da2c867SAlvin Šipraga if (vlan_filtering) 14149da2c867SAlvin Šipraga mode = RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL; 14159da2c867SAlvin Šipraga else 14169da2c867SAlvin Šipraga mode = RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP; 14179da2c867SAlvin Šipraga 14189da2c867SAlvin Šipraga ret = rtl8365mb_port_set_vlan_egress_mode(priv, port, mode); 14199da2c867SAlvin Šipraga if (ret) 14209da2c867SAlvin Šipraga goto undo_set_framefilter; 14219da2c867SAlvin Šipraga 14229da2c867SAlvin Šipraga return ret; 14239da2c867SAlvin Šipraga 14249da2c867SAlvin Šipraga undo_set_framefilter: 14259da2c867SAlvin Šipraga if (prev_accepted_frame != accepted_frame) 14269da2c867SAlvin Šipraga rtl8365mb_vlan_port_set_framefilter(priv, port, 14279da2c867SAlvin Šipraga prev_accepted_frame); 14289da2c867SAlvin Šipraga undo_transparent: 14299da2c867SAlvin Šipraga /* The DSA core guarantees this callback is only invoked on an actual 14309da2c867SAlvin Šipraga * state transition, ensuring the previous hardware state was the 14319da2c867SAlvin Šipraga * opposite (!vlan_filtering). It is also called during setup but, in 14329da2c867SAlvin Šipraga * that case, any failure here aborts the entire switch initialization. 14339da2c867SAlvin Šipraga * 14349da2c867SAlvin Šipraga * VLAN_INGRESS and VLAN_EGRESS_TRANSPARENT states are directly derived 14359da2c867SAlvin Šipraga * from vlan_filtering. That way, we can simply undo it without 14369da2c867SAlvin Šipraga * checking the current HW state as we do with VLAN_EGRESS_MODE. 14379da2c867SAlvin Šipraga */ 14389da2c867SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 14399da2c867SAlvin Šipraga if (configured_ports & BIT(dp->index)) 14409da2c867SAlvin Šipraga rtl8365mb_port_set_transparent(priv, port, dp->index, 14419da2c867SAlvin Šipraga vlan_filtering); 14429da2c867SAlvin Šipraga } 14439da2c867SAlvin Šipraga 14449da2c867SAlvin Šipraga rtl8365mb_port_set_ingress_filtering(priv, port, !vlan_filtering); 14459da2c867SAlvin Šipraga 14469da2c867SAlvin Šipraga return ret; 14479da2c867SAlvin Šipraga } 14489da2c867SAlvin Šipraga 14499da2c867SAlvin Šipraga static int rtl8365mb_port_vlan_add(struct dsa_switch *ds, int port, 14509da2c867SAlvin Šipraga const struct switchdev_obj_port_vlan *vlan, 14519da2c867SAlvin Šipraga struct netlink_ext_ack *extack) 14529da2c867SAlvin Šipraga { 14539da2c867SAlvin Šipraga bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 14549da2c867SAlvin Šipraga bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); 14559da2c867SAlvin Šipraga u16 pvid_vid; 14569da2c867SAlvin Šipraga struct realtek_priv *priv = ds->priv; 14579da2c867SAlvin Šipraga int ret; 14589da2c867SAlvin Šipraga 14599da2c867SAlvin Šipraga dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n", 14609da2c867SAlvin Šipraga vlan->vid, port, untagged ? "untagged" : "tagged", 14619da2c867SAlvin Šipraga pvid ? "PVID" : "no PVID"); 14629da2c867SAlvin Šipraga 14639da2c867SAlvin Šipraga /* VID == 0 is reserved in this driver */ 14649da2c867SAlvin Šipraga if (vlan->vid == 0) { 14659da2c867SAlvin Šipraga NL_SET_ERR_MSG_MOD(extack, 14669da2c867SAlvin Šipraga "VLAN 0 is reserved by this driver"); 14679da2c867SAlvin Šipraga return -EOPNOTSUPP; 14689da2c867SAlvin Šipraga } 14699da2c867SAlvin Šipraga 14709da2c867SAlvin Šipraga mutex_lock(&priv->vlan_lock); 14719da2c867SAlvin Šipraga 14729da2c867SAlvin Šipraga ret = rtl8365mb_vlan_port_get_pvid(priv, port, &pvid_vid); 14739da2c867SAlvin Šipraga if (ret) 14749da2c867SAlvin Šipraga goto out_unlock; 14759da2c867SAlvin Šipraga 14769da2c867SAlvin Šipraga /* Set PVID if needed */ 14779da2c867SAlvin Šipraga if (pvid) { 14789da2c867SAlvin Šipraga ret = rtl8365mb_vlan_pvid_port_set(ds, port, vlan->vid, 14799da2c867SAlvin Šipraga extack); 14809da2c867SAlvin Šipraga if (ret) 14819da2c867SAlvin Šipraga goto out_unlock; 14829da2c867SAlvin Šipraga } else { 14839da2c867SAlvin Šipraga /* or try to unset it if not */ 14849da2c867SAlvin Šipraga ret = rtl8365mb_vlan_pvid_port_clear(ds, port, vlan->vid); 14859da2c867SAlvin Šipraga if (ret) 14869da2c867SAlvin Šipraga goto out_unlock; 14879da2c867SAlvin Šipraga } 14889da2c867SAlvin Šipraga 14899da2c867SAlvin Šipraga /* add port to vlan4k. It knows nothing about PVID */ 14909da2c867SAlvin Šipraga ret = rtl8365mb_vlan_4k_port_add(ds, port, vlan, extack); 14919da2c867SAlvin Šipraga if (ret) 14929da2c867SAlvin Šipraga goto undo_set_pvid; 14939da2c867SAlvin Šipraga 14949da2c867SAlvin Šipraga ret = 0; 14959da2c867SAlvin Šipraga goto out_unlock; 14969da2c867SAlvin Šipraga 14979da2c867SAlvin Šipraga undo_set_pvid: 14989da2c867SAlvin Šipraga /* undo the pvid definition */ 14999da2c867SAlvin Šipraga if (pvid != (pvid_vid == vlan->vid)) { 15009da2c867SAlvin Šipraga if (pvid_vid) 15019da2c867SAlvin Šipraga (void)rtl8365mb_vlan_pvid_port_set(ds, port, pvid_vid, 15029da2c867SAlvin Šipraga NULL); 15039da2c867SAlvin Šipraga else 15049da2c867SAlvin Šipraga (void)rtl8365mb_vlan_pvid_port_clear(ds, port, 15059da2c867SAlvin Šipraga vlan->vid); 15069da2c867SAlvin Šipraga } 15079da2c867SAlvin Šipraga out_unlock: 15089da2c867SAlvin Šipraga mutex_unlock(&priv->vlan_lock); 15099da2c867SAlvin Šipraga return ret; 15109da2c867SAlvin Šipraga } 15119da2c867SAlvin Šipraga 15129da2c867SAlvin Šipraga static int rtl8365mb_port_vlan_del(struct dsa_switch *ds, int port, 15139da2c867SAlvin Šipraga const struct switchdev_obj_port_vlan *vlan) 15149da2c867SAlvin Šipraga { 15159da2c867SAlvin Šipraga bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 15169da2c867SAlvin Šipraga bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); 15179da2c867SAlvin Šipraga struct realtek_priv *priv = ds->priv; 15189da2c867SAlvin Šipraga int ret; 15199da2c867SAlvin Šipraga 15209da2c867SAlvin Šipraga dev_dbg(priv->dev, "del VLAN %d on port %d, %s, %s\n", 15219da2c867SAlvin Šipraga vlan->vid, port, untagged ? "untagged" : "tagged", 15229da2c867SAlvin Šipraga pvid ? "PVID" : "no PVID"); 15239da2c867SAlvin Šipraga 15249da2c867SAlvin Šipraga /* VID == 0 is reserved in this driver */ 15259da2c867SAlvin Šipraga if (vlan->vid == 0) 15269da2c867SAlvin Šipraga return -EOPNOTSUPP; 15279da2c867SAlvin Šipraga 15289da2c867SAlvin Šipraga mutex_lock(&priv->vlan_lock); 15299da2c867SAlvin Šipraga ret = rtl8365mb_vlan_pvid_port_clear(ds, port, vlan->vid); 15309da2c867SAlvin Šipraga if (ret) 15319da2c867SAlvin Šipraga goto out_unlock; 15329da2c867SAlvin Šipraga 15339da2c867SAlvin Šipraga ret = rtl8365mb_vlan_4k_port_del(ds, port, vlan); 15349da2c867SAlvin Šipraga /* There is little incentive to try to undo the removal of PVID (if it 15359da2c867SAlvin Šipraga * was really in use) as an error here might indicate the ASIC stopped 15369da2c867SAlvin Šipraga * to answer. 15379da2c867SAlvin Šipraga */ 15389da2c867SAlvin Šipraga 15399da2c867SAlvin Šipraga out_unlock: 15409da2c867SAlvin Šipraga mutex_unlock(&priv->vlan_lock); 15419da2c867SAlvin Šipraga return ret; 15429da2c867SAlvin Šipraga } 15439da2c867SAlvin Šipraga 15449da2c867SAlvin Šipraga /* VLAN support is always enabled in the switch. 15459da2c867SAlvin Šipraga * 15469da2c867SAlvin Šipraga * Standalone forwarding relies on transparent VLAN mode combined with per-port 15479da2c867SAlvin Šipraga * isolation masks restricting egress to CPU ports only. 15489da2c867SAlvin Šipraga * 15499da2c867SAlvin Šipraga */ 15509da2c867SAlvin Šipraga static int rtl8365mb_vlan_setup(struct dsa_switch *ds) 15519da2c867SAlvin Šipraga { 15529da2c867SAlvin Šipraga struct realtek_priv *priv = ds->priv; 15539da2c867SAlvin Šipraga struct dsa_port *dp; 15549da2c867SAlvin Šipraga int ret; 15559da2c867SAlvin Šipraga 15569da2c867SAlvin Šipraga dsa_switch_for_each_available_port(dp, ds) { 15579da2c867SAlvin Šipraga /* Disable vlan-filtering for all ports */ 15589da2c867SAlvin Šipraga ret = rtl8365mb_port_vlan_filtering(ds, dp->index, false, NULL); 15599da2c867SAlvin Šipraga if (ret) { 15609da2c867SAlvin Šipraga dev_err(priv->dev, 15619da2c867SAlvin Šipraga "Failed to disable vlan filtering on port %d\n", 15629da2c867SAlvin Šipraga dp->index); 15639da2c867SAlvin Šipraga return ret; 15649da2c867SAlvin Šipraga } 15659da2c867SAlvin Šipraga } 15669da2c867SAlvin Šipraga 15679da2c867SAlvin Šipraga /* VLAN is always enabled. */ 15689da2c867SAlvin Šipraga ret = regmap_update_bits(priv->map, RTL8365MB_VLAN_CTRL_REG, 15699da2c867SAlvin Šipraga RTL8365MB_VLAN_CTRL_EN_MASK, 15709da2c867SAlvin Šipraga FIELD_PREP(RTL8365MB_VLAN_CTRL_EN_MASK, 1)); 15719da2c867SAlvin Šipraga return ret; 15729da2c867SAlvin Šipraga } 15739da2c867SAlvin Šipraga 15740e692c27SAlvin Šipraga static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, 15750e692c27SAlvin Šipraga bool enable) 15760e692c27SAlvin Šipraga { 15770e692c27SAlvin Šipraga /* Enable/disable learning by limiting the number of L2 addresses the 15780e692c27SAlvin Šipraga * port can learn. Realtek documentation states that a limit of zero 15790e692c27SAlvin Šipraga * disables learning. When enabling learning, set it to the chip's 15800e692c27SAlvin Šipraga * maximum. 15810e692c27SAlvin Šipraga */ 15820e692c27SAlvin Šipraga return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), 15830e692c27SAlvin Šipraga enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); 15840e692c27SAlvin Šipraga } 15850e692c27SAlvin Šipraga 1586*660a9e39SLuiz Angelo Daros de Luca static int rtl8365mb_port_set_ucast_flood(struct realtek_priv *priv, int port, 1587*660a9e39SLuiz Angelo Daros de Luca bool enable) 1588*660a9e39SLuiz Angelo Daros de Luca { 1589*660a9e39SLuiz Angelo Daros de Luca /* Frames with unknown unicast DA will be flooded to a programmable 1590*660a9e39SLuiz Angelo Daros de Luca * port mask that by default includes all ports. Add or remove 1591*660a9e39SLuiz Angelo Daros de Luca * the specified port from this port mask accordingly. 1592*660a9e39SLuiz Angelo Daros de Luca */ 1593*660a9e39SLuiz Angelo Daros de Luca return regmap_update_bits(priv->map, 1594*660a9e39SLuiz Angelo Daros de Luca RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG, 1595*660a9e39SLuiz Angelo Daros de Luca BIT(port), enable ? BIT(port) : 0); 1596*660a9e39SLuiz Angelo Daros de Luca } 1597*660a9e39SLuiz Angelo Daros de Luca 1598*660a9e39SLuiz Angelo Daros de Luca static int rtl8365mb_port_set_mcast_flood(struct realtek_priv *priv, int port, 1599*660a9e39SLuiz Angelo Daros de Luca bool enable) 1600*660a9e39SLuiz Angelo Daros de Luca { 1601*660a9e39SLuiz Angelo Daros de Luca return regmap_update_bits(priv->map, 1602*660a9e39SLuiz Angelo Daros de Luca RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG, 1603*660a9e39SLuiz Angelo Daros de Luca BIT(port), enable ? BIT(port) : 0); 1604*660a9e39SLuiz Angelo Daros de Luca } 1605*660a9e39SLuiz Angelo Daros de Luca 1606*660a9e39SLuiz Angelo Daros de Luca static int rtl8365mb_port_set_bcast_flood(struct realtek_priv *priv, int port, 1607*660a9e39SLuiz Angelo Daros de Luca bool enable) 1608*660a9e39SLuiz Angelo Daros de Luca { 1609*660a9e39SLuiz Angelo Daros de Luca return regmap_update_bits(priv->map, 1610*660a9e39SLuiz Angelo Daros de Luca RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG, 1611*660a9e39SLuiz Angelo Daros de Luca BIT(port), enable ? BIT(port) : 0); 1612*660a9e39SLuiz Angelo Daros de Luca } 1613*660a9e39SLuiz Angelo Daros de Luca 1614*660a9e39SLuiz Angelo Daros de Luca static int rtl8365mb_port_pre_bridge_flags(struct dsa_switch *ds, int port, 1615*660a9e39SLuiz Angelo Daros de Luca struct switchdev_brport_flags flags, 1616*660a9e39SLuiz Angelo Daros de Luca struct netlink_ext_ack *extack) 1617*660a9e39SLuiz Angelo Daros de Luca { 1618*660a9e39SLuiz Angelo Daros de Luca struct realtek_priv *priv = ds->priv; 1619*660a9e39SLuiz Angelo Daros de Luca 1620*660a9e39SLuiz Angelo Daros de Luca dev_dbg(priv->dev, "pre_bridge_flags port:%d flags:%lx supported:%lx\n", 1621*660a9e39SLuiz Angelo Daros de Luca port, flags.mask, RTL8365MB_SUPPORTED_BRIDGE_FLAGS); 1622*660a9e39SLuiz Angelo Daros de Luca 1623*660a9e39SLuiz Angelo Daros de Luca if (flags.mask & ~RTL8365MB_SUPPORTED_BRIDGE_FLAGS) 1624*660a9e39SLuiz Angelo Daros de Luca return -EINVAL; 1625*660a9e39SLuiz Angelo Daros de Luca 1626*660a9e39SLuiz Angelo Daros de Luca return 0; 1627*660a9e39SLuiz Angelo Daros de Luca } 1628*660a9e39SLuiz Angelo Daros de Luca 1629183bd68bSAlvin Šipraga static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, 1630183bd68bSAlvin Šipraga u32 efid) 1631183bd68bSAlvin Šipraga { 1632183bd68bSAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_PORT_EFID_REG(port), 1633183bd68bSAlvin Šipraga RTL8365MB_PORT_EFID_MASK(port), 1634183bd68bSAlvin Šipraga efid << RTL8365MB_PORT_EFID_OFFSET(port)); 1635183bd68bSAlvin Šipraga } 1636183bd68bSAlvin Šipraga 1637183bd68bSAlvin Šipraga /* Port isolation manipulation functions. 1638183bd68bSAlvin Šipraga * 1639183bd68bSAlvin Šipraga * The port isolation register controls the forwarding mask of a given 1640183bd68bSAlvin Šipraga * port. The switch will not forward packets ingressed on a given port 1641183bd68bSAlvin Šipraga * to ports which are not enabled in its forwarding mask. 1642183bd68bSAlvin Šipraga * 1643183bd68bSAlvin Šipraga * The port forwarding mask has the highest priority in forwarding 1644183bd68bSAlvin Šipraga * decisions. The only exception to this rule is when the switch 1645183bd68bSAlvin Šipraga * receives a packet on its CPU port with ALLOW=0. In that case the TX 1646183bd68bSAlvin Šipraga * field of the CPU tag will override the forwarding port mask. 1647183bd68bSAlvin Šipraga */ 16480e692c27SAlvin Šipraga static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, 16490e692c27SAlvin Šipraga u32 mask) 16500e692c27SAlvin Šipraga { 1651183bd68bSAlvin Šipraga return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), 1652183bd68bSAlvin Šipraga mask); 1653183bd68bSAlvin Šipraga } 1654183bd68bSAlvin Šipraga 1655183bd68bSAlvin Šipraga static int rtl8365mb_port_add_isolation(struct realtek_priv *priv, int port, 1656183bd68bSAlvin Šipraga u32 mask) 1657183bd68bSAlvin Šipraga { 1658183bd68bSAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), 1659183bd68bSAlvin Šipraga mask, mask); 1660183bd68bSAlvin Šipraga } 1661183bd68bSAlvin Šipraga 1662183bd68bSAlvin Šipraga static int rtl8365mb_port_remove_isolation(struct realtek_priv *priv, int port, 1663183bd68bSAlvin Šipraga u32 mask) 1664183bd68bSAlvin Šipraga { 1665183bd68bSAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), 1666183bd68bSAlvin Šipraga mask, 0); 16670e692c27SAlvin Šipraga } 16680e692c27SAlvin Šipraga 16690e692c27SAlvin Šipraga static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, 16700e692c27SAlvin Šipraga u32 offset, u32 length, u64 *mibvalue) 16710e692c27SAlvin Šipraga { 16720e692c27SAlvin Šipraga u64 tmpvalue = 0; 16730e692c27SAlvin Šipraga u32 val; 16740e692c27SAlvin Šipraga int ret; 16750e692c27SAlvin Šipraga int i; 16760e692c27SAlvin Šipraga 16770e692c27SAlvin Šipraga /* The MIB address is an SRAM address. We request a particular address 16780e692c27SAlvin Šipraga * and then poll the control register before reading the value from some 16790e692c27SAlvin Šipraga * counter registers. 16800e692c27SAlvin Šipraga */ 16810e692c27SAlvin Šipraga ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG, 16820e692c27SAlvin Šipraga RTL8365MB_MIB_ADDRESS(port, offset)); 16830e692c27SAlvin Šipraga if (ret) 16840e692c27SAlvin Šipraga return ret; 16850e692c27SAlvin Šipraga 16860e692c27SAlvin Šipraga /* Poll for completion */ 16870e692c27SAlvin Šipraga ret = regmap_read_poll_timeout(priv->map, RTL8365MB_MIB_CTRL0_REG, val, 16880e692c27SAlvin Šipraga !(val & RTL8365MB_MIB_CTRL0_BUSY_MASK), 16890e692c27SAlvin Šipraga 10, 100); 16900e692c27SAlvin Šipraga if (ret) 16910e692c27SAlvin Šipraga return ret; 16920e692c27SAlvin Šipraga 16930e692c27SAlvin Šipraga /* Presumably this indicates a MIB counter read failure */ 16940e692c27SAlvin Šipraga if (val & RTL8365MB_MIB_CTRL0_RESET_MASK) 16950e692c27SAlvin Šipraga return -EIO; 16960e692c27SAlvin Šipraga 16970e692c27SAlvin Šipraga /* There are four MIB counter registers each holding a 16 bit word of a 16980e692c27SAlvin Šipraga * MIB counter. Depending on the offset, we should read from the upper 16990e692c27SAlvin Šipraga * two or lower two registers. In case the MIB counter is 4 words, we 17000e692c27SAlvin Šipraga * read from all four registers. 17010e692c27SAlvin Šipraga */ 17020e692c27SAlvin Šipraga if (length == 4) 17030e692c27SAlvin Šipraga offset = 3; 17040e692c27SAlvin Šipraga else 17050e692c27SAlvin Šipraga offset = (offset + 1) % 4; 17060e692c27SAlvin Šipraga 17070e692c27SAlvin Šipraga /* Read the MIB counter 16 bits at a time */ 17080e692c27SAlvin Šipraga for (i = 0; i < length; i++) { 17090e692c27SAlvin Šipraga ret = regmap_read(priv->map, 17100e692c27SAlvin Šipraga RTL8365MB_MIB_COUNTER_REG(offset - i), &val); 17110e692c27SAlvin Šipraga if (ret) 17120e692c27SAlvin Šipraga return ret; 17130e692c27SAlvin Šipraga 17140e692c27SAlvin Šipraga tmpvalue = ((tmpvalue) << 16) | (val & 0xFFFF); 17150e692c27SAlvin Šipraga } 17160e692c27SAlvin Šipraga 17170e692c27SAlvin Šipraga /* Only commit the result if no error occurred */ 17180e692c27SAlvin Šipraga *mibvalue = tmpvalue; 17190e692c27SAlvin Šipraga 17200e692c27SAlvin Šipraga return 0; 17210e692c27SAlvin Šipraga } 17220e692c27SAlvin Šipraga 17230e692c27SAlvin Šipraga static void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) 17240e692c27SAlvin Šipraga { 17250e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 17260e692c27SAlvin Šipraga struct rtl8365mb *mb; 17270e692c27SAlvin Šipraga int ret; 17280e692c27SAlvin Šipraga int i; 17290e692c27SAlvin Šipraga 17300e692c27SAlvin Šipraga mb = priv->chip_data; 17310e692c27SAlvin Šipraga 17320e692c27SAlvin Šipraga mutex_lock(&mb->mib_lock); 17330e692c27SAlvin Šipraga for (i = 0; i < RTL8365MB_MIB_END; i++) { 17340e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 17350e692c27SAlvin Šipraga 17360e692c27SAlvin Šipraga ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, 17370e692c27SAlvin Šipraga mib->length, &data[i]); 17380e692c27SAlvin Šipraga if (ret) { 17390e692c27SAlvin Šipraga dev_err(priv->dev, 17400e692c27SAlvin Šipraga "failed to read port %d counters: %pe\n", port, 17410e692c27SAlvin Šipraga ERR_PTR(ret)); 17420e692c27SAlvin Šipraga break; 17430e692c27SAlvin Šipraga } 17440e692c27SAlvin Šipraga } 17450e692c27SAlvin Šipraga mutex_unlock(&mb->mib_lock); 17460e692c27SAlvin Šipraga } 17470e692c27SAlvin Šipraga 17480e692c27SAlvin Šipraga static void rtl8365mb_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data) 17490e692c27SAlvin Šipraga { 17500e692c27SAlvin Šipraga int i; 17510e692c27SAlvin Šipraga 17520e692c27SAlvin Šipraga if (stringset != ETH_SS_STATS) 17530e692c27SAlvin Šipraga return; 17540e692c27SAlvin Šipraga 17550e692c27SAlvin Šipraga for (i = 0; i < RTL8365MB_MIB_END; i++) { 17560e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 17570e692c27SAlvin Šipraga ethtool_puts(&data, mib->name); 17580e692c27SAlvin Šipraga } 17590e692c27SAlvin Šipraga } 17600e692c27SAlvin Šipraga 17610e692c27SAlvin Šipraga static int rtl8365mb_get_sset_count(struct dsa_switch *ds, int port, int sset) 17620e692c27SAlvin Šipraga { 17630e692c27SAlvin Šipraga if (sset != ETH_SS_STATS) 17640e692c27SAlvin Šipraga return -EOPNOTSUPP; 17650e692c27SAlvin Šipraga 17660e692c27SAlvin Šipraga return RTL8365MB_MIB_END; 17670e692c27SAlvin Šipraga } 17680e692c27SAlvin Šipraga 17690e692c27SAlvin Šipraga static void rtl8365mb_get_phy_stats(struct dsa_switch *ds, int port, 17700e692c27SAlvin Šipraga struct ethtool_eth_phy_stats *phy_stats) 17710e692c27SAlvin Šipraga { 17720e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 17730e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *mib; 17740e692c27SAlvin Šipraga struct rtl8365mb *mb; 17750e692c27SAlvin Šipraga 17760e692c27SAlvin Šipraga mb = priv->chip_data; 17770e692c27SAlvin Šipraga mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3StatsSymbolErrors]; 17780e692c27SAlvin Šipraga 17790e692c27SAlvin Šipraga mutex_lock(&mb->mib_lock); 17800e692c27SAlvin Šipraga rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, 17810e692c27SAlvin Šipraga &phy_stats->SymbolErrorDuringCarrier); 17820e692c27SAlvin Šipraga mutex_unlock(&mb->mib_lock); 17830e692c27SAlvin Šipraga } 17840e692c27SAlvin Šipraga 17850e692c27SAlvin Šipraga static void rtl8365mb_get_mac_stats(struct dsa_switch *ds, int port, 17860e692c27SAlvin Šipraga struct ethtool_eth_mac_stats *mac_stats) 17870e692c27SAlvin Šipraga { 17880e692c27SAlvin Šipraga u64 cnt[RTL8365MB_MIB_END] = { 17890e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutOctets] = 1, 17900e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutUcastPkts] = 1, 17910e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutMulticastPkts] = 1, 17920e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutBroadcastPkts] = 1, 17930e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3OutPauseFrames] = 1, 17940e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutDiscards] = 1, 17950e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInOctets] = 1, 17960e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInUcastPkts] = 1, 17970e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInMulticastPkts] = 1, 17980e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInBroadcastPkts] = 1, 17990e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3InPauseFrames] = 1, 18000e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsSingleCollisionFrames] = 1, 18010e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsMultipleCollisionFrames] = 1, 18020e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, 18030e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsDeferredTransmissions] = 1, 18040e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, 18050e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsExcessiveCollisions] = 1, 18060e692c27SAlvin Šipraga 18070e692c27SAlvin Šipraga }; 18080e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 18090e692c27SAlvin Šipraga struct rtl8365mb *mb; 18100e692c27SAlvin Šipraga int ret; 18110e692c27SAlvin Šipraga int i; 18120e692c27SAlvin Šipraga 18130e692c27SAlvin Šipraga mb = priv->chip_data; 18140e692c27SAlvin Šipraga 18150e692c27SAlvin Šipraga mutex_lock(&mb->mib_lock); 18160e692c27SAlvin Šipraga for (i = 0; i < RTL8365MB_MIB_END; i++) { 18170e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i]; 18180e692c27SAlvin Šipraga 18190e692c27SAlvin Šipraga /* Only fetch required MIB counters (marked = 1 above) */ 18200e692c27SAlvin Šipraga if (!cnt[i]) 18210e692c27SAlvin Šipraga continue; 18220e692c27SAlvin Šipraga 18230e692c27SAlvin Šipraga ret = rtl8365mb_mib_counter_read(priv, port, mib->offset, 18240e692c27SAlvin Šipraga mib->length, &cnt[i]); 18250e692c27SAlvin Šipraga if (ret) 18260e692c27SAlvin Šipraga break; 18270e692c27SAlvin Šipraga } 18280e692c27SAlvin Šipraga mutex_unlock(&mb->mib_lock); 18290e692c27SAlvin Šipraga 18300e692c27SAlvin Šipraga /* The RTL8365MB-VC exposes MIB objects, which we have to translate into 18310e692c27SAlvin Šipraga * IEEE 802.3 Managed Objects. This is not always completely faithful, 18320e692c27SAlvin Šipraga * but we try out best. See RFC 3635 for a detailed treatment of the 18330e692c27SAlvin Šipraga * subject. 18340e692c27SAlvin Šipraga */ 18350e692c27SAlvin Šipraga 18360e692c27SAlvin Šipraga mac_stats->FramesTransmittedOK = cnt[RTL8365MB_MIB_ifOutUcastPkts] + 18370e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutMulticastPkts] + 18380e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutBroadcastPkts] + 18390e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3OutPauseFrames] - 18400e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutDiscards]; 18410e692c27SAlvin Šipraga mac_stats->SingleCollisionFrames = 18420e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3StatsSingleCollisionFrames]; 18430e692c27SAlvin Šipraga mac_stats->MultipleCollisionFrames = 18440e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3StatsMultipleCollisionFrames]; 18450e692c27SAlvin Šipraga mac_stats->FramesReceivedOK = cnt[RTL8365MB_MIB_ifInUcastPkts] + 18460e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInMulticastPkts] + 18470e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInBroadcastPkts] + 18480e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3InPauseFrames]; 18490e692c27SAlvin Šipraga mac_stats->FrameCheckSequenceErrors = 18500e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3StatsFCSErrors]; 18510e692c27SAlvin Šipraga mac_stats->OctetsTransmittedOK = cnt[RTL8365MB_MIB_ifOutOctets] - 18520e692c27SAlvin Šipraga 18 * mac_stats->FramesTransmittedOK; 18530e692c27SAlvin Šipraga mac_stats->FramesWithDeferredXmissions = 18540e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3StatsDeferredTransmissions]; 18550e692c27SAlvin Šipraga mac_stats->LateCollisions = cnt[RTL8365MB_MIB_dot3StatsLateCollisions]; 18560e692c27SAlvin Šipraga mac_stats->FramesAbortedDueToXSColls = 18570e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_dot3StatsExcessiveCollisions]; 18580e692c27SAlvin Šipraga mac_stats->OctetsReceivedOK = cnt[RTL8365MB_MIB_ifInOctets] - 18590e692c27SAlvin Šipraga 18 * mac_stats->FramesReceivedOK; 18600e692c27SAlvin Šipraga mac_stats->MulticastFramesXmittedOK = 18610e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutMulticastPkts]; 18620e692c27SAlvin Šipraga mac_stats->BroadcastFramesXmittedOK = 18630e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutBroadcastPkts]; 18640e692c27SAlvin Šipraga mac_stats->MulticastFramesReceivedOK = 18650e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInMulticastPkts]; 18660e692c27SAlvin Šipraga mac_stats->BroadcastFramesReceivedOK = 18670e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInBroadcastPkts]; 18680e692c27SAlvin Šipraga } 18690e692c27SAlvin Šipraga 18700e692c27SAlvin Šipraga static void rtl8365mb_get_ctrl_stats(struct dsa_switch *ds, int port, 18710e692c27SAlvin Šipraga struct ethtool_eth_ctrl_stats *ctrl_stats) 18720e692c27SAlvin Šipraga { 18730e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 18740e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *mib; 18750e692c27SAlvin Šipraga struct rtl8365mb *mb; 18760e692c27SAlvin Šipraga 18770e692c27SAlvin Šipraga mb = priv->chip_data; 18780e692c27SAlvin Šipraga mib = &rtl8365mb_mib_counters[RTL8365MB_MIB_dot3ControlInUnknownOpcodes]; 18790e692c27SAlvin Šipraga 18800e692c27SAlvin Šipraga mutex_lock(&mb->mib_lock); 18810e692c27SAlvin Šipraga rtl8365mb_mib_counter_read(priv, port, mib->offset, mib->length, 18820e692c27SAlvin Šipraga &ctrl_stats->UnsupportedOpcodesReceived); 18830e692c27SAlvin Šipraga mutex_unlock(&mb->mib_lock); 18840e692c27SAlvin Šipraga } 18850e692c27SAlvin Šipraga 18860e692c27SAlvin Šipraga static void rtl8365mb_stats_update(struct realtek_priv *priv, int port) 18870e692c27SAlvin Šipraga { 18880e692c27SAlvin Šipraga u64 cnt[RTL8365MB_MIB_END] = { 18890e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutOctets] = 1, 18900e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutUcastPkts] = 1, 18910e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutMulticastPkts] = 1, 18920e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutBroadcastPkts] = 1, 18930e692c27SAlvin Šipraga [RTL8365MB_MIB_ifOutDiscards] = 1, 18940e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInOctets] = 1, 18950e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInUcastPkts] = 1, 18960e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInMulticastPkts] = 1, 18970e692c27SAlvin Šipraga [RTL8365MB_MIB_ifInBroadcastPkts] = 1, 18980e692c27SAlvin Šipraga [RTL8365MB_MIB_etherStatsDropEvents] = 1, 18990e692c27SAlvin Šipraga [RTL8365MB_MIB_etherStatsCollisions] = 1, 19000e692c27SAlvin Šipraga [RTL8365MB_MIB_etherStatsFragments] = 1, 19010e692c27SAlvin Šipraga [RTL8365MB_MIB_etherStatsJabbers] = 1, 19020e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsFCSErrors] = 1, 19030e692c27SAlvin Šipraga [RTL8365MB_MIB_dot3StatsLateCollisions] = 1, 19040e692c27SAlvin Šipraga }; 19050e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 19060e692c27SAlvin Šipraga struct rtnl_link_stats64 *stats; 19070e692c27SAlvin Šipraga int ret; 19080e692c27SAlvin Šipraga int i; 19090e692c27SAlvin Šipraga 19100e692c27SAlvin Šipraga stats = &mb->ports[port].stats; 19110e692c27SAlvin Šipraga 19120e692c27SAlvin Šipraga mutex_lock(&mb->mib_lock); 19130e692c27SAlvin Šipraga for (i = 0; i < RTL8365MB_MIB_END; i++) { 19140e692c27SAlvin Šipraga struct rtl8365mb_mib_counter *c = &rtl8365mb_mib_counters[i]; 19150e692c27SAlvin Šipraga 19160e692c27SAlvin Šipraga /* Only fetch required MIB counters (marked = 1 above) */ 19170e692c27SAlvin Šipraga if (!cnt[i]) 19180e692c27SAlvin Šipraga continue; 19190e692c27SAlvin Šipraga 19200e692c27SAlvin Šipraga ret = rtl8365mb_mib_counter_read(priv, port, c->offset, 19210e692c27SAlvin Šipraga c->length, &cnt[i]); 19220e692c27SAlvin Šipraga if (ret) 19230e692c27SAlvin Šipraga break; 19240e692c27SAlvin Šipraga } 19250e692c27SAlvin Šipraga mutex_unlock(&mb->mib_lock); 19260e692c27SAlvin Šipraga 19270e692c27SAlvin Šipraga /* Don't update statistics if there was an error reading the counters */ 19280e692c27SAlvin Šipraga if (ret) 19290e692c27SAlvin Šipraga return; 19300e692c27SAlvin Šipraga 19310e692c27SAlvin Šipraga spin_lock(&mb->ports[port].stats_lock); 19320e692c27SAlvin Šipraga 19330e692c27SAlvin Šipraga stats->rx_packets = cnt[RTL8365MB_MIB_ifInUcastPkts] + 19340e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInMulticastPkts] + 19350e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifInBroadcastPkts]; 19360e692c27SAlvin Šipraga 19370e692c27SAlvin Šipraga stats->tx_packets = cnt[RTL8365MB_MIB_ifOutUcastPkts] + 19380e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutMulticastPkts] + 19390e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutBroadcastPkts]; 19400e692c27SAlvin Šipraga 19410e692c27SAlvin Šipraga /* if{In,Out}Octets includes FCS - remove it */ 19420e692c27SAlvin Šipraga stats->rx_bytes = cnt[RTL8365MB_MIB_ifInOctets] - 4 * stats->rx_packets; 19430e692c27SAlvin Šipraga stats->tx_bytes = 19440e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_ifOutOctets] - 4 * stats->tx_packets; 19450e692c27SAlvin Šipraga 19460e692c27SAlvin Šipraga stats->rx_dropped = cnt[RTL8365MB_MIB_etherStatsDropEvents]; 19470e692c27SAlvin Šipraga stats->tx_dropped = cnt[RTL8365MB_MIB_ifOutDiscards]; 19480e692c27SAlvin Šipraga 19490e692c27SAlvin Šipraga stats->multicast = cnt[RTL8365MB_MIB_ifInMulticastPkts]; 19500e692c27SAlvin Šipraga stats->collisions = cnt[RTL8365MB_MIB_etherStatsCollisions]; 19510e692c27SAlvin Šipraga 19520e692c27SAlvin Šipraga stats->rx_length_errors = cnt[RTL8365MB_MIB_etherStatsFragments] + 19530e692c27SAlvin Šipraga cnt[RTL8365MB_MIB_etherStatsJabbers]; 19540e692c27SAlvin Šipraga stats->rx_crc_errors = cnt[RTL8365MB_MIB_dot3StatsFCSErrors]; 19550e692c27SAlvin Šipraga stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors; 19560e692c27SAlvin Šipraga 19570e692c27SAlvin Šipraga stats->tx_aborted_errors = cnt[RTL8365MB_MIB_ifOutDiscards]; 19580e692c27SAlvin Šipraga stats->tx_window_errors = cnt[RTL8365MB_MIB_dot3StatsLateCollisions]; 19590e692c27SAlvin Šipraga stats->tx_errors = stats->tx_aborted_errors + stats->tx_window_errors; 19600e692c27SAlvin Šipraga 19610e692c27SAlvin Šipraga spin_unlock(&mb->ports[port].stats_lock); 19620e692c27SAlvin Šipraga } 19630e692c27SAlvin Šipraga 19640e692c27SAlvin Šipraga static void rtl8365mb_stats_poll(struct work_struct *work) 19650e692c27SAlvin Šipraga { 19660e692c27SAlvin Šipraga struct rtl8365mb_port *p = container_of(to_delayed_work(work), 19670e692c27SAlvin Šipraga struct rtl8365mb_port, 19680e692c27SAlvin Šipraga mib_work); 19690e692c27SAlvin Šipraga struct realtek_priv *priv = p->priv; 19700e692c27SAlvin Šipraga 19710e692c27SAlvin Šipraga rtl8365mb_stats_update(priv, p->index); 19720e692c27SAlvin Šipraga 19730e692c27SAlvin Šipraga schedule_delayed_work(&p->mib_work, RTL8365MB_STATS_INTERVAL_JIFFIES); 19740e692c27SAlvin Šipraga } 19750e692c27SAlvin Šipraga 19760e692c27SAlvin Šipraga static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, 19770e692c27SAlvin Šipraga struct rtnl_link_stats64 *s) 19780e692c27SAlvin Šipraga { 19790e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 19800e692c27SAlvin Šipraga struct rtl8365mb_port *p; 19810e692c27SAlvin Šipraga struct rtl8365mb *mb; 19820e692c27SAlvin Šipraga 19830e692c27SAlvin Šipraga mb = priv->chip_data; 19840e692c27SAlvin Šipraga p = &mb->ports[port]; 19850e692c27SAlvin Šipraga 19860e692c27SAlvin Šipraga spin_lock(&p->stats_lock); 19870e692c27SAlvin Šipraga memcpy(s, &p->stats, sizeof(*s)); 19880e692c27SAlvin Šipraga spin_unlock(&p->stats_lock); 19890e692c27SAlvin Šipraga } 19900e692c27SAlvin Šipraga 19910e692c27SAlvin Šipraga static void rtl8365mb_stats_setup(struct realtek_priv *priv) 19920e692c27SAlvin Šipraga { 19930e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 19940e692c27SAlvin Šipraga struct dsa_switch *ds = &priv->ds; 19950e692c27SAlvin Šipraga struct dsa_port *dp; 19960e692c27SAlvin Šipraga 19970e692c27SAlvin Šipraga /* Per-chip global mutex to protect MIB counter access, since doing 19980e692c27SAlvin Šipraga * so requires accessing a series of registers in a particular order. 19990e692c27SAlvin Šipraga */ 20000e692c27SAlvin Šipraga mutex_init(&mb->mib_lock); 20010e692c27SAlvin Šipraga 20020e692c27SAlvin Šipraga dsa_switch_for_each_available_port(dp, ds) { 20030e692c27SAlvin Šipraga struct rtl8365mb_port *p = &mb->ports[dp->index]; 20040e692c27SAlvin Šipraga 20050e692c27SAlvin Šipraga /* Per-port spinlock to protect the stats64 data */ 20060e692c27SAlvin Šipraga spin_lock_init(&p->stats_lock); 20070e692c27SAlvin Šipraga 20080e692c27SAlvin Šipraga /* This work polls the MIB counters and keeps the stats64 data 20090e692c27SAlvin Šipraga * up-to-date. 20100e692c27SAlvin Šipraga */ 20110e692c27SAlvin Šipraga INIT_DELAYED_WORK(&p->mib_work, rtl8365mb_stats_poll); 20120e692c27SAlvin Šipraga } 20130e692c27SAlvin Šipraga } 20140e692c27SAlvin Šipraga 20150e692c27SAlvin Šipraga static void rtl8365mb_stats_teardown(struct realtek_priv *priv) 20160e692c27SAlvin Šipraga { 20170e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 20180e692c27SAlvin Šipraga struct dsa_switch *ds = &priv->ds; 20190e692c27SAlvin Šipraga struct dsa_port *dp; 20200e692c27SAlvin Šipraga 20210e692c27SAlvin Šipraga dsa_switch_for_each_available_port(dp, ds) { 20220e692c27SAlvin Šipraga struct rtl8365mb_port *p = &mb->ports[dp->index]; 20230e692c27SAlvin Šipraga 20240e692c27SAlvin Šipraga cancel_delayed_work_sync(&p->mib_work); 20250e692c27SAlvin Šipraga } 20260e692c27SAlvin Šipraga } 20270e692c27SAlvin Šipraga 20280e692c27SAlvin Šipraga static int rtl8365mb_get_and_clear_status_reg(struct realtek_priv *priv, u32 reg, 20290e692c27SAlvin Šipraga u32 *val) 20300e692c27SAlvin Šipraga { 20310e692c27SAlvin Šipraga int ret; 20320e692c27SAlvin Šipraga 20330e692c27SAlvin Šipraga ret = regmap_read(priv->map, reg, val); 20340e692c27SAlvin Šipraga if (ret) 20350e692c27SAlvin Šipraga return ret; 20360e692c27SAlvin Šipraga 20370e692c27SAlvin Šipraga return regmap_write(priv->map, reg, *val); 20380e692c27SAlvin Šipraga } 20390e692c27SAlvin Šipraga 20400e692c27SAlvin Šipraga static irqreturn_t rtl8365mb_irq(int irq, void *data) 20410e692c27SAlvin Šipraga { 20420e692c27SAlvin Šipraga struct realtek_priv *priv = data; 20430e692c27SAlvin Šipraga unsigned long line_changes = 0; 20440e692c27SAlvin Šipraga u32 stat; 20450e692c27SAlvin Šipraga int line; 20460e692c27SAlvin Šipraga int ret; 20470e692c27SAlvin Šipraga 20480e692c27SAlvin Šipraga ret = rtl8365mb_get_and_clear_status_reg(priv, RTL8365MB_INTR_STATUS_REG, 20490e692c27SAlvin Šipraga &stat); 20500e692c27SAlvin Šipraga if (ret) 20510e692c27SAlvin Šipraga goto out_error; 20520e692c27SAlvin Šipraga 20530e692c27SAlvin Šipraga if (stat & RTL8365MB_INTR_LINK_CHANGE_MASK) { 20540e692c27SAlvin Šipraga u32 linkdown_ind; 20550e692c27SAlvin Šipraga u32 linkup_ind; 20560e692c27SAlvin Šipraga u32 val; 20570e692c27SAlvin Šipraga 20580e692c27SAlvin Šipraga ret = rtl8365mb_get_and_clear_status_reg( 20590e692c27SAlvin Šipraga priv, RTL8365MB_PORT_LINKUP_IND_REG, &val); 20600e692c27SAlvin Šipraga if (ret) 20610e692c27SAlvin Šipraga goto out_error; 20620e692c27SAlvin Šipraga 20630e692c27SAlvin Šipraga linkup_ind = FIELD_GET(RTL8365MB_PORT_LINKUP_IND_MASK, val); 20640e692c27SAlvin Šipraga 20650e692c27SAlvin Šipraga ret = rtl8365mb_get_and_clear_status_reg( 20660e692c27SAlvin Šipraga priv, RTL8365MB_PORT_LINKDOWN_IND_REG, &val); 20670e692c27SAlvin Šipraga if (ret) 20680e692c27SAlvin Šipraga goto out_error; 20690e692c27SAlvin Šipraga 20700e692c27SAlvin Šipraga linkdown_ind = FIELD_GET(RTL8365MB_PORT_LINKDOWN_IND_MASK, val); 20710e692c27SAlvin Šipraga 20720e692c27SAlvin Šipraga line_changes = linkup_ind | linkdown_ind; 20730e692c27SAlvin Šipraga } 20740e692c27SAlvin Šipraga 20750e692c27SAlvin Šipraga if (!line_changes) 20760e692c27SAlvin Šipraga goto out_none; 20770e692c27SAlvin Šipraga 20780e692c27SAlvin Šipraga for_each_set_bit(line, &line_changes, priv->num_ports) { 20790e692c27SAlvin Šipraga int child_irq = irq_find_mapping(priv->irqdomain, line); 20800e692c27SAlvin Šipraga 20810e692c27SAlvin Šipraga if (!child_irq) 20820e692c27SAlvin Šipraga continue; 20830e692c27SAlvin Šipraga 20840e692c27SAlvin Šipraga handle_nested_irq(child_irq); 20850e692c27SAlvin Šipraga } 20860e692c27SAlvin Šipraga 20870e692c27SAlvin Šipraga return IRQ_HANDLED; 20880e692c27SAlvin Šipraga 20890e692c27SAlvin Šipraga out_error: 20900e692c27SAlvin Šipraga dev_err(priv->dev, "failed to read interrupt status: %pe\n", 20910e692c27SAlvin Šipraga ERR_PTR(ret)); 20920e692c27SAlvin Šipraga 20930e692c27SAlvin Šipraga out_none: 20940e692c27SAlvin Šipraga return IRQ_NONE; 20950e692c27SAlvin Šipraga } 20960e692c27SAlvin Šipraga 20970e692c27SAlvin Šipraga static struct irq_chip rtl8365mb_irq_chip = { 20980e692c27SAlvin Šipraga .name = "rtl8365mb", 20990e692c27SAlvin Šipraga /* The hardware doesn't support masking IRQs on a per-port basis */ 21000e692c27SAlvin Šipraga }; 21010e692c27SAlvin Šipraga 21020e692c27SAlvin Šipraga static int rtl8365mb_irq_map(struct irq_domain *domain, unsigned int irq, 21030e692c27SAlvin Šipraga irq_hw_number_t hwirq) 21040e692c27SAlvin Šipraga { 21050e692c27SAlvin Šipraga struct realtek_priv *priv = domain->host_data; 21060e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 21070e692c27SAlvin Šipraga 21080e692c27SAlvin Šipraga irq_set_chip_data(irq, priv); 21090e692c27SAlvin Šipraga irq_set_chip_and_handler(irq, &rtl8365mb_irq_chip, handle_simple_irq); 21100e692c27SAlvin Šipraga irq_set_nested_thread(irq, 1); 21110e692c27SAlvin Šipraga irq_set_noprobe(irq); 21120e692c27SAlvin Šipraga irq_set_parent(irq, mb->irq); 21130e692c27SAlvin Šipraga 21140e692c27SAlvin Šipraga return 0; 21150e692c27SAlvin Šipraga } 21160e692c27SAlvin Šipraga 21170e692c27SAlvin Šipraga static void rtl8365mb_irq_unmap(struct irq_domain *d, unsigned int irq) 21180e692c27SAlvin Šipraga { 21190e692c27SAlvin Šipraga irq_set_nested_thread(irq, 0); 21200e692c27SAlvin Šipraga irq_set_chip_and_handler(irq, NULL, NULL); 21210e692c27SAlvin Šipraga irq_set_chip_data(irq, NULL); 21220e692c27SAlvin Šipraga } 21230e692c27SAlvin Šipraga 21240e692c27SAlvin Šipraga static const struct irq_domain_ops rtl8365mb_irqdomain_ops = { 21250e692c27SAlvin Šipraga .map = rtl8365mb_irq_map, 21260e692c27SAlvin Šipraga .unmap = rtl8365mb_irq_unmap, 21270e692c27SAlvin Šipraga .xlate = irq_domain_xlate_onecell, 21280e692c27SAlvin Šipraga }; 21290e692c27SAlvin Šipraga 21300e692c27SAlvin Šipraga static int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable) 21310e692c27SAlvin Šipraga { 21320e692c27SAlvin Šipraga return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG, 21330e692c27SAlvin Šipraga RTL8365MB_INTR_LINK_CHANGE_MASK, 21340e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK, 21350e692c27SAlvin Šipraga enable ? 1 : 0)); 21360e692c27SAlvin Šipraga } 21370e692c27SAlvin Šipraga 21380e692c27SAlvin Šipraga static int rtl8365mb_irq_enable(struct realtek_priv *priv) 21390e692c27SAlvin Šipraga { 21400e692c27SAlvin Šipraga return rtl8365mb_set_irq_enable(priv, true); 21410e692c27SAlvin Šipraga } 21420e692c27SAlvin Šipraga 21430e692c27SAlvin Šipraga static int rtl8365mb_irq_disable(struct realtek_priv *priv) 21440e692c27SAlvin Šipraga { 21450e692c27SAlvin Šipraga return rtl8365mb_set_irq_enable(priv, false); 21460e692c27SAlvin Šipraga } 21470e692c27SAlvin Šipraga 21480e692c27SAlvin Šipraga static int rtl8365mb_irq_setup(struct realtek_priv *priv) 21490e692c27SAlvin Šipraga { 21500e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 21510e692c27SAlvin Šipraga struct dsa_switch *ds = &priv->ds; 21520e692c27SAlvin Šipraga struct device_node *intc; 21530e692c27SAlvin Šipraga struct dsa_port *dp; 21540e692c27SAlvin Šipraga u32 irq_trig; 21550e692c27SAlvin Šipraga int virq; 21560e692c27SAlvin Šipraga int irq; 21570e692c27SAlvin Šipraga u32 val; 21580e692c27SAlvin Šipraga int ret; 21590e692c27SAlvin Šipraga 21600e692c27SAlvin Šipraga intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); 21610e692c27SAlvin Šipraga if (!intc) { 21620e692c27SAlvin Šipraga dev_err(priv->dev, "missing child interrupt-controller node\n"); 21630e692c27SAlvin Šipraga return -EINVAL; 21640e692c27SAlvin Šipraga } 21650e692c27SAlvin Šipraga 21660e692c27SAlvin Šipraga /* rtl8365mb IRQs cascade off this one */ 21670e692c27SAlvin Šipraga irq = of_irq_get(intc, 0); 21680e692c27SAlvin Šipraga if (irq <= 0) { 21690e692c27SAlvin Šipraga if (!irq) { 21700e692c27SAlvin Šipraga dev_err(priv->dev, "failed to map IRQ\n"); 21710e692c27SAlvin Šipraga ret = -EINVAL; 21720e692c27SAlvin Šipraga } else { 21730e692c27SAlvin Šipraga ret = dev_err_probe(priv->dev, irq, 21740e692c27SAlvin Šipraga "failed to get parent irq\n"); 21750e692c27SAlvin Šipraga } 21760e692c27SAlvin Šipraga goto out_put_node; 21770e692c27SAlvin Šipraga } 21780e692c27SAlvin Šipraga 21790e692c27SAlvin Šipraga /* Store the irq so that we know to map and free it during teardown */ 21800e692c27SAlvin Šipraga mb->irq = irq; 21810e692c27SAlvin Šipraga 21820e692c27SAlvin Šipraga priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), priv->num_ports, 21830e692c27SAlvin Šipraga &rtl8365mb_irqdomain_ops, priv); 21840e692c27SAlvin Šipraga if (!priv->irqdomain) { 21850e692c27SAlvin Šipraga dev_err(priv->dev, "failed to add irq domain\n"); 21860e692c27SAlvin Šipraga ret = -ENOMEM; 21870e692c27SAlvin Šipraga goto out_put_node; 21880e692c27SAlvin Šipraga } 21890e692c27SAlvin Šipraga 21900e692c27SAlvin Šipraga dsa_switch_for_each_available_port(dp, ds) { 21910e692c27SAlvin Šipraga virq = irq_create_mapping(priv->irqdomain, dp->index); 21920e692c27SAlvin Šipraga if (!virq) { 21930e692c27SAlvin Šipraga dev_err(priv->dev, 21940e692c27SAlvin Šipraga "failed to create irq domain mapping\n"); 21950e692c27SAlvin Šipraga ret = -EINVAL; 21960e692c27SAlvin Šipraga goto out_remove_irqdomain; 21970e692c27SAlvin Šipraga } 21980e692c27SAlvin Šipraga 21990e692c27SAlvin Šipraga irq_set_parent(virq, irq); 22000e692c27SAlvin Šipraga } 22010e692c27SAlvin Šipraga 22020e692c27SAlvin Šipraga /* Configure chip interrupt signal polarity */ 22030e692c27SAlvin Šipraga irq_trig = irq_get_trigger_type(irq); 22040e692c27SAlvin Šipraga switch (irq_trig) { 22050e692c27SAlvin Šipraga case IRQF_TRIGGER_RISING: 22060e692c27SAlvin Šipraga case IRQF_TRIGGER_HIGH: 22070e692c27SAlvin Šipraga val = RTL8365MB_INTR_POLARITY_HIGH; 22080e692c27SAlvin Šipraga break; 22090e692c27SAlvin Šipraga case IRQF_TRIGGER_FALLING: 22100e692c27SAlvin Šipraga case IRQF_TRIGGER_LOW: 22110e692c27SAlvin Šipraga val = RTL8365MB_INTR_POLARITY_LOW; 22120e692c27SAlvin Šipraga break; 22130e692c27SAlvin Šipraga default: 22140e692c27SAlvin Šipraga dev_err(priv->dev, "unsupported irq trigger type %u\n", 22150e692c27SAlvin Šipraga irq_trig); 22160e692c27SAlvin Šipraga ret = -EINVAL; 22170e692c27SAlvin Šipraga goto out_remove_irqdomain; 22180e692c27SAlvin Šipraga } 22190e692c27SAlvin Šipraga 22200e692c27SAlvin Šipraga ret = regmap_update_bits(priv->map, RTL8365MB_INTR_POLARITY_REG, 22210e692c27SAlvin Šipraga RTL8365MB_INTR_POLARITY_MASK, 22220e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val)); 22230e692c27SAlvin Šipraga if (ret) 22240e692c27SAlvin Šipraga goto out_remove_irqdomain; 22250e692c27SAlvin Šipraga 22260e692c27SAlvin Šipraga /* Disable the interrupt in case the chip has it enabled on reset */ 22270e692c27SAlvin Šipraga ret = rtl8365mb_irq_disable(priv); 22280e692c27SAlvin Šipraga if (ret) 22290e692c27SAlvin Šipraga goto out_remove_irqdomain; 22300e692c27SAlvin Šipraga 22310e692c27SAlvin Šipraga /* Clear the interrupt status register */ 22320e692c27SAlvin Šipraga ret = regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG, 22330e692c27SAlvin Šipraga RTL8365MB_INTR_ALL_MASK); 22340e692c27SAlvin Šipraga if (ret) 22350e692c27SAlvin Šipraga goto out_remove_irqdomain; 22360e692c27SAlvin Šipraga 22370e692c27SAlvin Šipraga ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, 22380e692c27SAlvin Šipraga "rtl8365mb", priv); 22390e692c27SAlvin Šipraga if (ret) { 22400e692c27SAlvin Šipraga dev_err(priv->dev, "failed to request irq: %pe\n", 22410e692c27SAlvin Šipraga ERR_PTR(ret)); 22420e692c27SAlvin Šipraga goto out_remove_irqdomain; 22430e692c27SAlvin Šipraga } 22440e692c27SAlvin Šipraga 22450e692c27SAlvin Šipraga ret = rtl8365mb_irq_enable(priv); 22460e692c27SAlvin Šipraga if (ret) 22470e692c27SAlvin Šipraga goto out_free_irq; 22480e692c27SAlvin Šipraga 22490e692c27SAlvin Šipraga of_node_put(intc); 22500e692c27SAlvin Šipraga 22510e692c27SAlvin Šipraga return 0; 22520e692c27SAlvin Šipraga 22530e692c27SAlvin Šipraga out_free_irq: 22540e692c27SAlvin Šipraga free_irq(mb->irq, priv); 22550e692c27SAlvin Šipraga 22560e692c27SAlvin Šipraga out_remove_irqdomain: 22570e692c27SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 22580e692c27SAlvin Šipraga virq = irq_find_mapping(priv->irqdomain, dp->index); 22590e692c27SAlvin Šipraga 22600e692c27SAlvin Šipraga if (virq) 22610e692c27SAlvin Šipraga irq_dispose_mapping(virq); 22620e692c27SAlvin Šipraga } 22630e692c27SAlvin Šipraga 22640e692c27SAlvin Šipraga irq_domain_remove(priv->irqdomain); 22650e692c27SAlvin Šipraga priv->irqdomain = NULL; 22660e692c27SAlvin Šipraga 22670e692c27SAlvin Šipraga out_put_node: 22680e692c27SAlvin Šipraga mb->irq = 0; 22690e692c27SAlvin Šipraga of_node_put(intc); 22700e692c27SAlvin Šipraga 22710e692c27SAlvin Šipraga return ret; 22720e692c27SAlvin Šipraga } 22730e692c27SAlvin Šipraga 22740e692c27SAlvin Šipraga static void rtl8365mb_irq_teardown(struct realtek_priv *priv) 22750e692c27SAlvin Šipraga { 22760e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 22770e692c27SAlvin Šipraga struct dsa_switch *ds = &priv->ds; 22780e692c27SAlvin Šipraga struct dsa_port *dp; 22790e692c27SAlvin Šipraga int virq; 22800e692c27SAlvin Šipraga 22810e692c27SAlvin Šipraga if (mb->irq) { 22820e692c27SAlvin Šipraga free_irq(mb->irq, priv); 22830e692c27SAlvin Šipraga mb->irq = 0; 22840e692c27SAlvin Šipraga } 22850e692c27SAlvin Šipraga 22860e692c27SAlvin Šipraga if (priv->irqdomain) { 22870e692c27SAlvin Šipraga /* Unused ports with a linked PHY still have an active IRQ 22880e692c27SAlvin Šipraga * mapping that must be disposed of during teardown. Loop 22890e692c27SAlvin Šipraga * through all ports. 22900e692c27SAlvin Šipraga */ 22910e692c27SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 22920e692c27SAlvin Šipraga virq = irq_find_mapping(priv->irqdomain, dp->index); 22930e692c27SAlvin Šipraga 22940e692c27SAlvin Šipraga if (virq) 22950e692c27SAlvin Šipraga irq_dispose_mapping(virq); 22960e692c27SAlvin Šipraga } 22970e692c27SAlvin Šipraga 22980e692c27SAlvin Šipraga irq_domain_remove(priv->irqdomain); 22990e692c27SAlvin Šipraga priv->irqdomain = NULL; 23000e692c27SAlvin Šipraga } 23010e692c27SAlvin Šipraga } 23020e692c27SAlvin Šipraga 23030e692c27SAlvin Šipraga static int rtl8365mb_cpu_config(struct realtek_priv *priv) 23040e692c27SAlvin Šipraga { 23050e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 23060e692c27SAlvin Šipraga struct rtl8365mb_cpu *cpu = &mb->cpu; 23070e692c27SAlvin Šipraga u32 val; 23080e692c27SAlvin Šipraga int ret; 23090e692c27SAlvin Šipraga 23100e692c27SAlvin Šipraga ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG, 23110e692c27SAlvin Šipraga RTL8365MB_CPU_PORT_MASK_MASK, 23120e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK, 23130e692c27SAlvin Šipraga cpu->mask)); 23140e692c27SAlvin Šipraga if (ret) 23150e692c27SAlvin Šipraga return ret; 23160e692c27SAlvin Šipraga 23170e692c27SAlvin Šipraga val = FIELD_PREP(RTL8365MB_CPU_CTRL_EN_MASK, cpu->enable ? 1 : 0) | 23180e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_INSERTMODE_MASK, cpu->insert) | 23190e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) | 23200e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) | 23210e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) | 23220e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) | 23230e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK, 23240e692c27SAlvin Šipraga cpu->trap_port >> 3 & 0x1); 23250e692c27SAlvin Šipraga ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val); 23260e692c27SAlvin Šipraga if (ret) 23270e692c27SAlvin Šipraga return ret; 23280e692c27SAlvin Šipraga 23290e692c27SAlvin Šipraga return 0; 23300e692c27SAlvin Šipraga } 23310e692c27SAlvin Šipraga 23320e692c27SAlvin Šipraga static int rtl8365mb_change_tag_protocol(struct dsa_switch *ds, 23330e692c27SAlvin Šipraga enum dsa_tag_protocol proto) 23340e692c27SAlvin Šipraga { 23350e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 23360e692c27SAlvin Šipraga struct rtl8365mb_cpu *cpu; 23370e692c27SAlvin Šipraga struct rtl8365mb *mb; 23380e692c27SAlvin Šipraga 23390e692c27SAlvin Šipraga mb = priv->chip_data; 23400e692c27SAlvin Šipraga cpu = &mb->cpu; 23410e692c27SAlvin Šipraga 23420e692c27SAlvin Šipraga switch (proto) { 23430e692c27SAlvin Šipraga case DSA_TAG_PROTO_RTL8_4: 23440e692c27SAlvin Šipraga cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; 23450e692c27SAlvin Šipraga cpu->position = RTL8365MB_CPU_POS_AFTER_SA; 23460e692c27SAlvin Šipraga break; 23470e692c27SAlvin Šipraga case DSA_TAG_PROTO_RTL8_4T: 23480e692c27SAlvin Šipraga cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; 23490e692c27SAlvin Šipraga cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC; 23500e692c27SAlvin Šipraga break; 23510e692c27SAlvin Šipraga /* The switch also supports a 4-byte format, similar to rtl4a but with 23520e692c27SAlvin Šipraga * the same 0x04 8-bit version and probably 8-bit port source/dest. 23530e692c27SAlvin Šipraga * There is no public doc about it. Not supported yet and it will probably 23540e692c27SAlvin Šipraga * never be. 23550e692c27SAlvin Šipraga */ 23560e692c27SAlvin Šipraga default: 23570e692c27SAlvin Šipraga return -EPROTONOSUPPORT; 23580e692c27SAlvin Šipraga } 23590e692c27SAlvin Šipraga 23600e692c27SAlvin Šipraga return rtl8365mb_cpu_config(priv); 23610e692c27SAlvin Šipraga } 23620e692c27SAlvin Šipraga 23630e692c27SAlvin Šipraga static int rtl8365mb_switch_init(struct realtek_priv *priv) 23640e692c27SAlvin Šipraga { 23650e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 23660e692c27SAlvin Šipraga const struct rtl8365mb_chip_info *ci; 23670e692c27SAlvin Šipraga int ret; 23680e692c27SAlvin Šipraga int i; 23690e692c27SAlvin Šipraga 23700e692c27SAlvin Šipraga ci = mb->chip_info; 23710e692c27SAlvin Šipraga 23720e692c27SAlvin Šipraga /* Do any chip-specific init jam before getting to the common stuff */ 23730e692c27SAlvin Šipraga if (ci->jam_table) { 23740e692c27SAlvin Šipraga for (i = 0; i < ci->jam_size; i++) { 23750e692c27SAlvin Šipraga ret = regmap_write(priv->map, ci->jam_table[i].reg, 23760e692c27SAlvin Šipraga ci->jam_table[i].val); 23770e692c27SAlvin Šipraga if (ret) 23780e692c27SAlvin Šipraga return ret; 23790e692c27SAlvin Šipraga } 23800e692c27SAlvin Šipraga } 23810e692c27SAlvin Šipraga 23820e692c27SAlvin Šipraga /* Common init jam */ 23830e692c27SAlvin Šipraga for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { 23840e692c27SAlvin Šipraga ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, 23850e692c27SAlvin Šipraga rtl8365mb_init_jam_common[i].val); 23860e692c27SAlvin Šipraga if (ret) 23870e692c27SAlvin Šipraga return ret; 23880e692c27SAlvin Šipraga } 23890e692c27SAlvin Šipraga 23900e692c27SAlvin Šipraga return 0; 23910e692c27SAlvin Šipraga } 23920e692c27SAlvin Šipraga 23930e692c27SAlvin Šipraga static int rtl8365mb_reset_chip(struct realtek_priv *priv) 23940e692c27SAlvin Šipraga { 23950e692c27SAlvin Šipraga u32 val; 23960e692c27SAlvin Šipraga 23970e692c27SAlvin Šipraga priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, 23980e692c27SAlvin Šipraga FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); 23990e692c27SAlvin Šipraga 24000e692c27SAlvin Šipraga /* Realtek documentation says the chip needs 1 second to reset. Sleep 24010e692c27SAlvin Šipraga * for 100 ms before accessing any registers to prevent ACK timeouts. 24020e692c27SAlvin Šipraga */ 24030e692c27SAlvin Šipraga msleep(100); 24040e692c27SAlvin Šipraga return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, 24050e692c27SAlvin Šipraga !(val & RTL8365MB_CHIP_RESET_HW_MASK), 24060e692c27SAlvin Šipraga 20000, 1e6); 24070e692c27SAlvin Šipraga } 24080e692c27SAlvin Šipraga 24090e692c27SAlvin Šipraga static int rtl8365mb_setup(struct dsa_switch *ds) 24100e692c27SAlvin Šipraga { 24110e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 24120e692c27SAlvin Šipraga struct rtl8365mb_cpu *cpu; 24130e692c27SAlvin Šipraga u32 downports_mask = 0; 24140e692c27SAlvin Šipraga u32 upports_mask = 0; 24150e692c27SAlvin Šipraga struct rtl8365mb *mb; 24160e692c27SAlvin Šipraga struct dsa_port *dp; 24170e692c27SAlvin Šipraga int ret; 24180e692c27SAlvin Šipraga 24190e692c27SAlvin Šipraga mb = priv->chip_data; 24200e692c27SAlvin Šipraga cpu = &mb->cpu; 24210e692c27SAlvin Šipraga 24220e692c27SAlvin Šipraga ret = rtl8365mb_reset_chip(priv); 24230e692c27SAlvin Šipraga if (ret) { 24240e692c27SAlvin Šipraga dev_err(priv->dev, "failed to reset chip: %pe\n", 24250e692c27SAlvin Šipraga ERR_PTR(ret)); 24260e692c27SAlvin Šipraga goto out_error; 24270e692c27SAlvin Šipraga } 24280e692c27SAlvin Šipraga 24290e692c27SAlvin Šipraga /* Configure switch to vendor-defined initial state */ 24300e692c27SAlvin Šipraga ret = rtl8365mb_switch_init(priv); 24310e692c27SAlvin Šipraga if (ret) { 24320e692c27SAlvin Šipraga dev_err(priv->dev, "failed to initialize switch: %pe\n", 24330e692c27SAlvin Šipraga ERR_PTR(ret)); 24340e692c27SAlvin Šipraga goto out_error; 24350e692c27SAlvin Šipraga } 24360e692c27SAlvin Šipraga 24370e692c27SAlvin Šipraga /* Set up cascading IRQs */ 24380e692c27SAlvin Šipraga ret = rtl8365mb_irq_setup(priv); 24390e692c27SAlvin Šipraga if (ret == -EPROBE_DEFER) 24400e692c27SAlvin Šipraga return ret; 24410e692c27SAlvin Šipraga else if (ret) 24420e692c27SAlvin Šipraga dev_info(priv->dev, "no interrupt support\n"); 24430e692c27SAlvin Šipraga 24440e692c27SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 24450e692c27SAlvin Šipraga /* Cascading (DSA links) is not supported yet. 24460e692c27SAlvin Šipraga * Historically, the driver has always been broken 24470e692c27SAlvin Šipraga * without a dedicated CPU port because CPU tagging 24480e692c27SAlvin Šipraga * would be disabled, rendering the switch entirely 24490e692c27SAlvin Šipraga * non-functional for DSA operations. 24500e692c27SAlvin Šipraga */ 24510e692c27SAlvin Šipraga if (dsa_port_is_dsa(dp)) { 24520e692c27SAlvin Šipraga dev_err(priv->dev, "Cascading (DSA link) not supported\n"); 24530e692c27SAlvin Šipraga ret = -EOPNOTSUPP; 24540e692c27SAlvin Šipraga goto out_teardown_irq; 24550e692c27SAlvin Šipraga } 24560e692c27SAlvin Šipraga } 24570e692c27SAlvin Šipraga 24580e692c27SAlvin Šipraga /* Start with all ports blocked, including unused ports */ 24590e692c27SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 24600e692c27SAlvin Šipraga struct rtl8365mb_port *p = &mb->ports[dp->index]; 24610e692c27SAlvin Šipraga 24620e692c27SAlvin Šipraga /* Set the initial STP state of all ports to DISABLED, otherwise 24630e692c27SAlvin Šipraga * ports will still forward frames to the CPU despite being 24640e692c27SAlvin Šipraga * administratively down by default. 24650e692c27SAlvin Šipraga */ 24660e692c27SAlvin Šipraga rtl8365mb_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED); 24670e692c27SAlvin Šipraga 24680e692c27SAlvin Šipraga /* Start with all port completely isolated */ 24690e692c27SAlvin Šipraga ret = rtl8365mb_port_set_isolation(priv, dp->index, 0); 24700e692c27SAlvin Šipraga if (ret) 24710e692c27SAlvin Šipraga goto out_teardown_irq; 24720e692c27SAlvin Šipraga 2473183bd68bSAlvin Šipraga /* Set the default EFID 0 for standalone mode */ 2474183bd68bSAlvin Šipraga ret = rtl8365mb_port_set_efid(priv, dp->index, 0); 2475183bd68bSAlvin Šipraga if (ret) 2476183bd68bSAlvin Šipraga goto out_teardown_irq; 2477183bd68bSAlvin Šipraga 24780e692c27SAlvin Šipraga /* Disable learning */ 24790e692c27SAlvin Šipraga ret = rtl8365mb_port_set_learning(priv, dp->index, false); 24800e692c27SAlvin Šipraga if (ret) 24810e692c27SAlvin Šipraga goto out_teardown_irq; 24820e692c27SAlvin Šipraga 2483*660a9e39SLuiz Angelo Daros de Luca /* Enable all types of flooding */ 2484*660a9e39SLuiz Angelo Daros de Luca ret = rtl83xx_setup_port_flood_control(priv, dp->index); 2485*660a9e39SLuiz Angelo Daros de Luca if (ret) 2486*660a9e39SLuiz Angelo Daros de Luca goto out_teardown_irq; 2487*660a9e39SLuiz Angelo Daros de Luca 24880e692c27SAlvin Šipraga /* Set up per-port private data */ 24890e692c27SAlvin Šipraga p->priv = priv; 24900e692c27SAlvin Šipraga p->index = dp->index; 24910e692c27SAlvin Šipraga 24920e692c27SAlvin Šipraga /* Collect CPU ports. If we support cascade switches, it should 24930e692c27SAlvin Šipraga * also include the upstream DSA ports. 24940e692c27SAlvin Šipraga */ 24950e692c27SAlvin Šipraga if (!dsa_port_is_cpu(dp)) 24960e692c27SAlvin Šipraga continue; 24970e692c27SAlvin Šipraga 24980e692c27SAlvin Šipraga upports_mask |= BIT(dp->index); 24990e692c27SAlvin Šipraga } 25000e692c27SAlvin Šipraga 25010e692c27SAlvin Šipraga /* Configure user ports */ 25020e692c27SAlvin Šipraga dsa_switch_for_each_port(dp, ds) { 25030e692c27SAlvin Šipraga if (!dsa_port_is_user(dp)) 25040e692c27SAlvin Šipraga continue; 25050e692c27SAlvin Šipraga 25060e692c27SAlvin Šipraga /* Forward only to the CPU */ 25070e692c27SAlvin Šipraga ret = rtl8365mb_port_set_isolation(priv, dp->index, 25080e692c27SAlvin Šipraga upports_mask); 25090e692c27SAlvin Šipraga if (ret) 25100e692c27SAlvin Šipraga goto out_teardown_irq; 25110e692c27SAlvin Šipraga 25120e692c27SAlvin Šipraga /* If we support cascade switches, it should also include the 25130e692c27SAlvin Šipraga * downstream DSA ports. 25140e692c27SAlvin Šipraga */ 25150e692c27SAlvin Šipraga downports_mask |= BIT(dp->index); 25160e692c27SAlvin Šipraga } 25170e692c27SAlvin Šipraga 25180e692c27SAlvin Šipraga /* Configure CPU tagging */ 25190e692c27SAlvin Šipraga /* If we support cascade switches, it should also include the upstream 25200e692c27SAlvin Šipraga * DSA ports. 25210e692c27SAlvin Šipraga */ 25220e692c27SAlvin Šipraga dsa_switch_for_each_cpu_port(dp, ds) { 25230e692c27SAlvin Šipraga /* Use the first CPU port as trap_port */ 25240e692c27SAlvin Šipraga if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) 25250e692c27SAlvin Šipraga cpu->trap_port = dp->index; 25260e692c27SAlvin Šipraga 25270e692c27SAlvin Šipraga /* Forward to all user ports */ 25280e692c27SAlvin Šipraga ret = rtl8365mb_port_set_isolation(priv, dp->index, 25290e692c27SAlvin Šipraga downports_mask); 25300e692c27SAlvin Šipraga if (ret) 25310e692c27SAlvin Šipraga goto out_teardown_irq; 25320e692c27SAlvin Šipraga } 25330e692c27SAlvin Šipraga 25340e692c27SAlvin Šipraga cpu->mask = upports_mask; 25350e692c27SAlvin Šipraga cpu->enable = cpu->mask > 0; 25360e692c27SAlvin Šipraga 25370e692c27SAlvin Šipraga if (!cpu->enable) { 25380e692c27SAlvin Šipraga dev_err(priv->dev, "no CPU port defined\n"); 25390e692c27SAlvin Šipraga ret = -EINVAL; 25400e692c27SAlvin Šipraga goto out_teardown_irq; 25410e692c27SAlvin Šipraga } 25420e692c27SAlvin Šipraga 25430e692c27SAlvin Šipraga ret = rtl8365mb_cpu_config(priv); 25440e692c27SAlvin Šipraga if (ret) 25450e692c27SAlvin Šipraga goto out_teardown_irq; 25460e692c27SAlvin Šipraga 25470e692c27SAlvin Šipraga ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN); 25480e692c27SAlvin Šipraga if (ret) 25490e692c27SAlvin Šipraga goto out_teardown_irq; 25500e692c27SAlvin Šipraga 2551336e3e4aSAlvin Šipraga ds->assisted_learning_on_cpu_port = true; 2552336e3e4aSAlvin Šipraga ds->fdb_isolation = true; 2553336e3e4aSAlvin Šipraga /* The EFID is 3 bits, but EFID 0 is reserved for standalone ports */ 2554336e3e4aSAlvin Šipraga ds->max_num_bridges = FIELD_MAX(RTL8365MB_EFID_MASK); 2555336e3e4aSAlvin Šipraga 25569da2c867SAlvin Šipraga ds->configure_vlan_while_not_filtering = true; 25579da2c867SAlvin Šipraga 25589da2c867SAlvin Šipraga /* Set up VLAN */ 25599da2c867SAlvin Šipraga ret = rtl8365mb_vlan_setup(ds); 25609da2c867SAlvin Šipraga if (ret) 25619da2c867SAlvin Šipraga goto out_teardown_irq; 25629da2c867SAlvin Šipraga 25630e692c27SAlvin Šipraga ret = rtl83xx_setup_user_mdio(ds); 25640e692c27SAlvin Šipraga if (ret) { 25650e692c27SAlvin Šipraga dev_err(priv->dev, "could not set up MDIO bus\n"); 25660e692c27SAlvin Šipraga goto out_teardown_irq; 25670e692c27SAlvin Šipraga } 25680e692c27SAlvin Šipraga 25690e692c27SAlvin Šipraga /* Start statistics counter polling */ 25700e692c27SAlvin Šipraga rtl8365mb_stats_setup(priv); 25710e692c27SAlvin Šipraga 25720e692c27SAlvin Šipraga return 0; 25730e692c27SAlvin Šipraga 25740e692c27SAlvin Šipraga out_teardown_irq: 25750e692c27SAlvin Šipraga rtl8365mb_irq_teardown(priv); 25760e692c27SAlvin Šipraga 25770e692c27SAlvin Šipraga out_error: 25780e692c27SAlvin Šipraga return ret; 25790e692c27SAlvin Šipraga } 25800e692c27SAlvin Šipraga 25810e692c27SAlvin Šipraga static void rtl8365mb_teardown(struct dsa_switch *ds) 25820e692c27SAlvin Šipraga { 25830e692c27SAlvin Šipraga struct realtek_priv *priv = ds->priv; 25840e692c27SAlvin Šipraga 25850e692c27SAlvin Šipraga rtl8365mb_stats_teardown(priv); 25860e692c27SAlvin Šipraga rtl8365mb_irq_teardown(priv); 25870e692c27SAlvin Šipraga } 25880e692c27SAlvin Šipraga 25890e692c27SAlvin Šipraga static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) 25900e692c27SAlvin Šipraga { 25910e692c27SAlvin Šipraga int ret; 25920e692c27SAlvin Šipraga 25930e692c27SAlvin Šipraga /* For some reason we have to write a magic value to an arbitrary 25940e692c27SAlvin Šipraga * register whenever accessing the chip ID/version registers. 25950e692c27SAlvin Šipraga */ 25960e692c27SAlvin Šipraga ret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE); 25970e692c27SAlvin Šipraga if (ret) 25980e692c27SAlvin Šipraga return ret; 25990e692c27SAlvin Šipraga 26000e692c27SAlvin Šipraga ret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id); 26010e692c27SAlvin Šipraga if (ret) 26020e692c27SAlvin Šipraga return ret; 26030e692c27SAlvin Šipraga 26040e692c27SAlvin Šipraga ret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver); 26050e692c27SAlvin Šipraga if (ret) 26060e692c27SAlvin Šipraga return ret; 26070e692c27SAlvin Šipraga 26080e692c27SAlvin Šipraga /* Reset magic register */ 26090e692c27SAlvin Šipraga ret = regmap_write(map, RTL8365MB_MAGIC_REG, 0); 26100e692c27SAlvin Šipraga if (ret) 26110e692c27SAlvin Šipraga return ret; 26120e692c27SAlvin Šipraga 26130e692c27SAlvin Šipraga return 0; 26140e692c27SAlvin Šipraga } 26150e692c27SAlvin Šipraga 26160e692c27SAlvin Šipraga static int rtl8365mb_detect(struct realtek_priv *priv) 26170e692c27SAlvin Šipraga { 26180e692c27SAlvin Šipraga struct rtl8365mb *mb = priv->chip_data; 26190e692c27SAlvin Šipraga u32 chip_id; 26200e692c27SAlvin Šipraga u32 chip_ver; 26210e692c27SAlvin Šipraga int ret; 26220e692c27SAlvin Šipraga int i; 26230e692c27SAlvin Šipraga 26240e692c27SAlvin Šipraga ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); 26250e692c27SAlvin Šipraga if (ret) { 26260e692c27SAlvin Šipraga dev_err(priv->dev, "failed to read chip id and version: %pe\n", 26270e692c27SAlvin Šipraga ERR_PTR(ret)); 26280e692c27SAlvin Šipraga return ret; 26290e692c27SAlvin Šipraga } 26300e692c27SAlvin Šipraga 26310e692c27SAlvin Šipraga for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) { 26320e692c27SAlvin Šipraga const struct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i]; 26330e692c27SAlvin Šipraga 26340e692c27SAlvin Šipraga if (ci->chip_id == chip_id && ci->chip_ver == chip_ver) { 26350e692c27SAlvin Šipraga mb->chip_info = ci; 26360e692c27SAlvin Šipraga break; 26370e692c27SAlvin Šipraga } 26380e692c27SAlvin Šipraga } 26390e692c27SAlvin Šipraga 26400e692c27SAlvin Šipraga if (!mb->chip_info) { 26410e692c27SAlvin Šipraga dev_err(priv->dev, 26420e692c27SAlvin Šipraga "unrecognized switch (id=0x%04x, ver=0x%04x)", chip_id, 26430e692c27SAlvin Šipraga chip_ver); 26440e692c27SAlvin Šipraga return -ENODEV; 26450e692c27SAlvin Šipraga } 26460e692c27SAlvin Šipraga 26470e692c27SAlvin Šipraga dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name); 26480e692c27SAlvin Šipraga 26490e692c27SAlvin Šipraga priv->num_ports = RTL8365MB_MAX_NUM_PORTS; 26500e692c27SAlvin Šipraga mb->priv = priv; 26510e692c27SAlvin Šipraga mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS; 26520e692c27SAlvin Šipraga mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; 26530e692c27SAlvin Šipraga mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA; 26540e692c27SAlvin Šipraga mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; 26550e692c27SAlvin Šipraga mb->cpu.format = RTL8365MB_CPU_FORMAT_8BYTES; 26560e692c27SAlvin Šipraga 26570e692c27SAlvin Šipraga return 0; 26580e692c27SAlvin Šipraga } 26590e692c27SAlvin Šipraga 26600e692c27SAlvin Šipraga static const struct phylink_mac_ops rtl8365mb_phylink_mac_ops = { 26610e692c27SAlvin Šipraga .mac_config = rtl8365mb_phylink_mac_config, 26620e692c27SAlvin Šipraga .mac_link_down = rtl8365mb_phylink_mac_link_down, 26630e692c27SAlvin Šipraga .mac_link_up = rtl8365mb_phylink_mac_link_up, 26640e692c27SAlvin Šipraga }; 26650e692c27SAlvin Šipraga 26660e692c27SAlvin Šipraga static const struct dsa_switch_ops rtl8365mb_switch_ops = { 26670e692c27SAlvin Šipraga .get_tag_protocol = rtl8365mb_get_tag_protocol, 26680e692c27SAlvin Šipraga .change_tag_protocol = rtl8365mb_change_tag_protocol, 26690e692c27SAlvin Šipraga .setup = rtl8365mb_setup, 26700e692c27SAlvin Šipraga .teardown = rtl8365mb_teardown, 26710e692c27SAlvin Šipraga .phylink_get_caps = rtl8365mb_phylink_get_caps, 2672183bd68bSAlvin Šipraga .port_bridge_join = rtl83xx_port_bridge_join, 2673183bd68bSAlvin Šipraga .port_bridge_leave = rtl83xx_port_bridge_leave, 2674*660a9e39SLuiz Angelo Daros de Luca .port_pre_bridge_flags = rtl8365mb_port_pre_bridge_flags, 2675*660a9e39SLuiz Angelo Daros de Luca .port_bridge_flags = rtl83xx_port_bridge_flags, 26760e692c27SAlvin Šipraga .port_stp_state_set = rtl8365mb_port_stp_state_set, 2677336e3e4aSAlvin Šipraga .port_fast_age = rtl83xx_port_fast_age, 2678336e3e4aSAlvin Šipraga .port_fdb_add = rtl83xx_port_fdb_add, 2679336e3e4aSAlvin Šipraga .port_fdb_del = rtl83xx_port_fdb_del, 2680336e3e4aSAlvin Šipraga .port_fdb_dump = rtl83xx_port_fdb_dump, 2681336e3e4aSAlvin Šipraga .port_mdb_add = rtl83xx_port_mdb_add, 2682336e3e4aSAlvin Šipraga .port_mdb_del = rtl83xx_port_mdb_del, 26839da2c867SAlvin Šipraga .port_vlan_add = rtl8365mb_port_vlan_add, 26849da2c867SAlvin Šipraga .port_vlan_del = rtl8365mb_port_vlan_del, 26859da2c867SAlvin Šipraga .port_vlan_filtering = rtl8365mb_port_vlan_filtering, 26860e692c27SAlvin Šipraga .get_strings = rtl8365mb_get_strings, 26870e692c27SAlvin Šipraga .get_ethtool_stats = rtl8365mb_get_ethtool_stats, 26880e692c27SAlvin Šipraga .get_sset_count = rtl8365mb_get_sset_count, 26890e692c27SAlvin Šipraga .get_eth_phy_stats = rtl8365mb_get_phy_stats, 26900e692c27SAlvin Šipraga .get_eth_mac_stats = rtl8365mb_get_mac_stats, 26910e692c27SAlvin Šipraga .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, 26920e692c27SAlvin Šipraga .get_stats64 = rtl8365mb_get_stats64, 26930e692c27SAlvin Šipraga .port_change_mtu = rtl8365mb_port_change_mtu, 26940e692c27SAlvin Šipraga .port_max_mtu = rtl8365mb_port_max_mtu, 26950e692c27SAlvin Šipraga .port_hsr_join = dsa_port_simple_hsr_join, 26960e692c27SAlvin Šipraga .port_hsr_leave = dsa_port_simple_hsr_leave, 26970e692c27SAlvin Šipraga }; 26980e692c27SAlvin Šipraga 26990e692c27SAlvin Šipraga static const struct realtek_ops rtl8365mb_ops = { 27000e692c27SAlvin Šipraga .detect = rtl8365mb_detect, 2701183bd68bSAlvin Šipraga .port_add_isolation = rtl8365mb_port_add_isolation, 2702183bd68bSAlvin Šipraga .port_remove_isolation = rtl8365mb_port_remove_isolation, 2703183bd68bSAlvin Šipraga .port_set_efid = rtl8365mb_port_set_efid, 2704183bd68bSAlvin Šipraga .port_set_learning = rtl8365mb_port_set_learning, 2705*660a9e39SLuiz Angelo Daros de Luca .port_set_ucast_flood = rtl8365mb_port_set_ucast_flood, 2706*660a9e39SLuiz Angelo Daros de Luca .port_set_mcast_flood = rtl8365mb_port_set_mcast_flood, 2707*660a9e39SLuiz Angelo Daros de Luca .port_set_bcast_flood = rtl8365mb_port_set_bcast_flood, 2708336e3e4aSAlvin Šipraga .l2_add_uc = rtl8365mb_l2_add_uc, 2709336e3e4aSAlvin Šipraga .l2_del_uc = rtl8365mb_l2_del_uc, 2710336e3e4aSAlvin Šipraga .l2_get_next_uc = rtl8365mb_l2_get_next_uc, 2711336e3e4aSAlvin Šipraga .l2_add_mc = rtl8365mb_l2_add_mc, 2712336e3e4aSAlvin Šipraga .l2_del_mc = rtl8365mb_l2_del_mc, 2713336e3e4aSAlvin Šipraga .l2_flush = rtl8365mb_l2_flush, 27140e692c27SAlvin Šipraga .phy_read = rtl8365mb_phy_read, 27150e692c27SAlvin Šipraga .phy_write = rtl8365mb_phy_write, 27160e692c27SAlvin Šipraga }; 27170e692c27SAlvin Šipraga 27180e692c27SAlvin Šipraga const struct realtek_variant rtl8365mb_variant = { 27190e692c27SAlvin Šipraga .ds_ops = &rtl8365mb_switch_ops, 27200e692c27SAlvin Šipraga .ops = &rtl8365mb_ops, 27210e692c27SAlvin Šipraga .phylink_mac_ops = &rtl8365mb_phylink_mac_ops, 27220e692c27SAlvin Šipraga .clk_delay = 10, 27230e692c27SAlvin Šipraga .cmd_read = 0xb9, 27240e692c27SAlvin Šipraga .cmd_write = 0xb8, 27250e692c27SAlvin Šipraga .chip_data_sz = sizeof(struct rtl8365mb), 27260e692c27SAlvin Šipraga }; 27270e692c27SAlvin Šipraga 27280e692c27SAlvin Šipraga static const struct of_device_id rtl8365mb_of_match[] = { 27290e692c27SAlvin Šipraga { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, 27300e692c27SAlvin Šipraga { /* sentinel */ }, 27310e692c27SAlvin Šipraga }; 27320e692c27SAlvin Šipraga MODULE_DEVICE_TABLE(of, rtl8365mb_of_match); 27330e692c27SAlvin Šipraga 27340e692c27SAlvin Šipraga static struct platform_driver rtl8365mb_smi_driver = { 27350e692c27SAlvin Šipraga .driver = { 27360e692c27SAlvin Šipraga .name = "rtl8365mb-smi", 27370e692c27SAlvin Šipraga .of_match_table = rtl8365mb_of_match, 27380e692c27SAlvin Šipraga }, 27390e692c27SAlvin Šipraga .probe = realtek_smi_probe, 27400e692c27SAlvin Šipraga .remove = realtek_smi_remove, 27410e692c27SAlvin Šipraga .shutdown = realtek_smi_shutdown, 27420e692c27SAlvin Šipraga }; 27430e692c27SAlvin Šipraga 27440e692c27SAlvin Šipraga static struct mdio_driver rtl8365mb_mdio_driver = { 27450e692c27SAlvin Šipraga .mdiodrv.driver = { 27460e692c27SAlvin Šipraga .name = "rtl8365mb-mdio", 27470e692c27SAlvin Šipraga .of_match_table = rtl8365mb_of_match, 27480e692c27SAlvin Šipraga }, 27490e692c27SAlvin Šipraga .probe = realtek_mdio_probe, 27500e692c27SAlvin Šipraga .remove = realtek_mdio_remove, 27510e692c27SAlvin Šipraga .shutdown = realtek_mdio_shutdown, 27520e692c27SAlvin Šipraga }; 27530e692c27SAlvin Šipraga 27540e692c27SAlvin Šipraga static int rtl8365mb_init(void) 27550e692c27SAlvin Šipraga { 27560e692c27SAlvin Šipraga int ret; 27570e692c27SAlvin Šipraga 27580e692c27SAlvin Šipraga ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver); 27590e692c27SAlvin Šipraga if (ret) 27600e692c27SAlvin Šipraga return ret; 27610e692c27SAlvin Šipraga 27620e692c27SAlvin Šipraga ret = realtek_smi_driver_register(&rtl8365mb_smi_driver); 27630e692c27SAlvin Šipraga if (ret) { 27640e692c27SAlvin Šipraga realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); 27650e692c27SAlvin Šipraga return ret; 27660e692c27SAlvin Šipraga } 27670e692c27SAlvin Šipraga 27680e692c27SAlvin Šipraga return 0; 27690e692c27SAlvin Šipraga } 27700e692c27SAlvin Šipraga module_init(rtl8365mb_init); 27710e692c27SAlvin Šipraga 27720e692c27SAlvin Šipraga static void __exit rtl8365mb_exit(void) 27730e692c27SAlvin Šipraga { 27740e692c27SAlvin Šipraga realtek_smi_driver_unregister(&rtl8365mb_smi_driver); 27750e692c27SAlvin Šipraga realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); 27760e692c27SAlvin Šipraga } 27770e692c27SAlvin Šipraga module_exit(rtl8365mb_exit); 27780e692c27SAlvin Šipraga 27790e692c27SAlvin Šipraga MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>"); 27800e692c27SAlvin Šipraga MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); 27810e692c27SAlvin Šipraga MODULE_LICENSE("GPL"); 27820e692c27SAlvin Šipraga MODULE_IMPORT_NS("REALTEK_DSA"); 2783