162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Realtek SMI subdriver for the Realtek RTL8366RB ethernet switch 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This is a sparsely documented chip, the only viable documentation seems 562306a36Sopenharmony_ci * to be a patched up code drop from the vendor that appear in various 662306a36Sopenharmony_ci * GPL source trees. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 962306a36Sopenharmony_ci * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> 1062306a36Sopenharmony_ci * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> 1162306a36Sopenharmony_ci * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> 1262306a36Sopenharmony_ci * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bitops.h> 1662306a36Sopenharmony_ci#include <linux/etherdevice.h> 1762306a36Sopenharmony_ci#include <linux/if_bridge.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/irqdomain.h> 2062306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 2162306a36Sopenharmony_ci#include <linux/of_irq.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "realtek.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define RTL8366RB_PORT_NUM_CPU 5 2762306a36Sopenharmony_ci#define RTL8366RB_NUM_PORTS 6 2862306a36Sopenharmony_ci#define RTL8366RB_PHY_NO_MAX 4 2962306a36Sopenharmony_ci#define RTL8366RB_PHY_ADDR_MAX 31 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Switch Global Configuration register */ 3262306a36Sopenharmony_ci#define RTL8366RB_SGCR 0x0000 3362306a36Sopenharmony_ci#define RTL8366RB_SGCR_EN_BC_STORM_CTRL BIT(0) 3462306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH(a) ((a) << 4) 3562306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH_MASK RTL8366RB_SGCR_MAX_LENGTH(0x3) 3662306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH_1522 RTL8366RB_SGCR_MAX_LENGTH(0x0) 3762306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH_1536 RTL8366RB_SGCR_MAX_LENGTH(0x1) 3862306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH_1552 RTL8366RB_SGCR_MAX_LENGTH(0x2) 3962306a36Sopenharmony_ci#define RTL8366RB_SGCR_MAX_LENGTH_16000 RTL8366RB_SGCR_MAX_LENGTH(0x3) 4062306a36Sopenharmony_ci#define RTL8366RB_SGCR_EN_VLAN BIT(13) 4162306a36Sopenharmony_ci#define RTL8366RB_SGCR_EN_VLAN_4KTB BIT(14) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Port Enable Control register */ 4462306a36Sopenharmony_ci#define RTL8366RB_PECR 0x0001 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Switch per-port learning disablement register */ 4762306a36Sopenharmony_ci#define RTL8366RB_PORT_LEARNDIS_CTRL 0x0002 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Security control, actually aging register */ 5062306a36Sopenharmony_ci#define RTL8366RB_SECURITY_CTRL 0x0003 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define RTL8366RB_SSCR2 0x0004 5362306a36Sopenharmony_ci#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Port Mode Control registers */ 5662306a36Sopenharmony_ci#define RTL8366RB_PMC0 0x0005 5762306a36Sopenharmony_ci#define RTL8366RB_PMC0_SPI BIT(0) 5862306a36Sopenharmony_ci#define RTL8366RB_PMC0_EN_AUTOLOAD BIT(1) 5962306a36Sopenharmony_ci#define RTL8366RB_PMC0_PROBE BIT(2) 6062306a36Sopenharmony_ci#define RTL8366RB_PMC0_DIS_BISR BIT(3) 6162306a36Sopenharmony_ci#define RTL8366RB_PMC0_ADCTEST BIT(4) 6262306a36Sopenharmony_ci#define RTL8366RB_PMC0_SRAM_DIAG BIT(5) 6362306a36Sopenharmony_ci#define RTL8366RB_PMC0_EN_SCAN BIT(6) 6462306a36Sopenharmony_ci#define RTL8366RB_PMC0_P4_IOMODE_SHIFT 7 6562306a36Sopenharmony_ci#define RTL8366RB_PMC0_P4_IOMODE_MASK GENMASK(9, 7) 6662306a36Sopenharmony_ci#define RTL8366RB_PMC0_P5_IOMODE_SHIFT 10 6762306a36Sopenharmony_ci#define RTL8366RB_PMC0_P5_IOMODE_MASK GENMASK(12, 10) 6862306a36Sopenharmony_ci#define RTL8366RB_PMC0_SDSMODE_SHIFT 13 6962306a36Sopenharmony_ci#define RTL8366RB_PMC0_SDSMODE_MASK GENMASK(15, 13) 7062306a36Sopenharmony_ci#define RTL8366RB_PMC1 0x0006 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Port Mirror Control Register */ 7362306a36Sopenharmony_ci#define RTL8366RB_PMCR 0x0007 7462306a36Sopenharmony_ci#define RTL8366RB_PMCR_SOURCE_PORT(a) (a) 7562306a36Sopenharmony_ci#define RTL8366RB_PMCR_SOURCE_PORT_MASK 0x000f 7662306a36Sopenharmony_ci#define RTL8366RB_PMCR_MONITOR_PORT(a) ((a) << 4) 7762306a36Sopenharmony_ci#define RTL8366RB_PMCR_MONITOR_PORT_MASK 0x00f0 7862306a36Sopenharmony_ci#define RTL8366RB_PMCR_MIRROR_RX BIT(8) 7962306a36Sopenharmony_ci#define RTL8366RB_PMCR_MIRROR_TX BIT(9) 8062306a36Sopenharmony_ci#define RTL8366RB_PMCR_MIRROR_SPC BIT(10) 8162306a36Sopenharmony_ci#define RTL8366RB_PMCR_MIRROR_ISO BIT(11) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* bits 0..7 = port 0, bits 8..15 = port 1 */ 8462306a36Sopenharmony_ci#define RTL8366RB_PAACR0 0x0010 8562306a36Sopenharmony_ci/* bits 0..7 = port 2, bits 8..15 = port 3 */ 8662306a36Sopenharmony_ci#define RTL8366RB_PAACR1 0x0011 8762306a36Sopenharmony_ci/* bits 0..7 = port 4, bits 8..15 = port 5 */ 8862306a36Sopenharmony_ci#define RTL8366RB_PAACR2 0x0012 8962306a36Sopenharmony_ci#define RTL8366RB_PAACR_SPEED_10M 0 9062306a36Sopenharmony_ci#define RTL8366RB_PAACR_SPEED_100M 1 9162306a36Sopenharmony_ci#define RTL8366RB_PAACR_SPEED_1000M 2 9262306a36Sopenharmony_ci#define RTL8366RB_PAACR_FULL_DUPLEX BIT(2) 9362306a36Sopenharmony_ci#define RTL8366RB_PAACR_LINK_UP BIT(4) 9462306a36Sopenharmony_ci#define RTL8366RB_PAACR_TX_PAUSE BIT(5) 9562306a36Sopenharmony_ci#define RTL8366RB_PAACR_RX_PAUSE BIT(6) 9662306a36Sopenharmony_ci#define RTL8366RB_PAACR_AN BIT(7) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define RTL8366RB_PAACR_CPU_PORT (RTL8366RB_PAACR_SPEED_1000M | \ 9962306a36Sopenharmony_ci RTL8366RB_PAACR_FULL_DUPLEX | \ 10062306a36Sopenharmony_ci RTL8366RB_PAACR_LINK_UP | \ 10162306a36Sopenharmony_ci RTL8366RB_PAACR_TX_PAUSE | \ 10262306a36Sopenharmony_ci RTL8366RB_PAACR_RX_PAUSE) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* bits 0..7 = port 0, bits 8..15 = port 1 */ 10562306a36Sopenharmony_ci#define RTL8366RB_PSTAT0 0x0014 10662306a36Sopenharmony_ci/* bits 0..7 = port 2, bits 8..15 = port 3 */ 10762306a36Sopenharmony_ci#define RTL8366RB_PSTAT1 0x0015 10862306a36Sopenharmony_ci/* bits 0..7 = port 4, bits 8..15 = port 5 */ 10962306a36Sopenharmony_ci#define RTL8366RB_PSTAT2 0x0016 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define RTL8366RB_POWER_SAVING_REG 0x0021 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Spanning tree status (STP) control, two bits per port per FID */ 11462306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_BASE 0x0050 /* 0x0050..0x0057 */ 11562306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_DISABLED 0x0 11662306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_BLOCKING 0x1 11762306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_LEARNING 0x2 11862306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_FORWARDING 0x3 11962306a36Sopenharmony_ci#define RTL8366RB_STP_MASK GENMASK(1, 0) 12062306a36Sopenharmony_ci#define RTL8366RB_STP_STATE(port, state) \ 12162306a36Sopenharmony_ci ((state) << ((port) * 2)) 12262306a36Sopenharmony_ci#define RTL8366RB_STP_STATE_MASK(port) \ 12362306a36Sopenharmony_ci RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* CPU port control reg */ 12662306a36Sopenharmony_ci#define RTL8368RB_CPU_CTRL_REG 0x0061 12762306a36Sopenharmony_ci#define RTL8368RB_CPU_PORTS_MSK 0x00FF 12862306a36Sopenharmony_ci/* Disables inserting custom tag length/type 0x8899 */ 12962306a36Sopenharmony_ci#define RTL8368RB_CPU_NO_TAG BIT(15) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define RTL8366RB_SMAR0 0x0070 /* bits 0..15 */ 13262306a36Sopenharmony_ci#define RTL8366RB_SMAR1 0x0071 /* bits 16..31 */ 13362306a36Sopenharmony_ci#define RTL8366RB_SMAR2 0x0072 /* bits 32..47 */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define RTL8366RB_RESET_CTRL_REG 0x0100 13662306a36Sopenharmony_ci#define RTL8366RB_CHIP_CTRL_RESET_HW BIT(0) 13762306a36Sopenharmony_ci#define RTL8366RB_CHIP_CTRL_RESET_SW BIT(1) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define RTL8366RB_CHIP_ID_REG 0x0509 14062306a36Sopenharmony_ci#define RTL8366RB_CHIP_ID_8366 0x5937 14162306a36Sopenharmony_ci#define RTL8366RB_CHIP_VERSION_CTRL_REG 0x050A 14262306a36Sopenharmony_ci#define RTL8366RB_CHIP_VERSION_MASK 0xf 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* PHY registers control */ 14562306a36Sopenharmony_ci#define RTL8366RB_PHY_ACCESS_CTRL_REG 0x8000 14662306a36Sopenharmony_ci#define RTL8366RB_PHY_CTRL_READ BIT(0) 14762306a36Sopenharmony_ci#define RTL8366RB_PHY_CTRL_WRITE 0 14862306a36Sopenharmony_ci#define RTL8366RB_PHY_ACCESS_BUSY_REG 0x8001 14962306a36Sopenharmony_ci#define RTL8366RB_PHY_INT_BUSY BIT(0) 15062306a36Sopenharmony_ci#define RTL8366RB_PHY_EXT_BUSY BIT(4) 15162306a36Sopenharmony_ci#define RTL8366RB_PHY_ACCESS_DATA_REG 0x8002 15262306a36Sopenharmony_ci#define RTL8366RB_PHY_EXT_CTRL_REG 0x8010 15362306a36Sopenharmony_ci#define RTL8366RB_PHY_EXT_WRDATA_REG 0x8011 15462306a36Sopenharmony_ci#define RTL8366RB_PHY_EXT_RDDATA_REG 0x8012 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define RTL8366RB_PHY_REG_MASK 0x1f 15762306a36Sopenharmony_ci#define RTL8366RB_PHY_PAGE_OFFSET 5 15862306a36Sopenharmony_ci#define RTL8366RB_PHY_PAGE_MASK (0xf << 5) 15962306a36Sopenharmony_ci#define RTL8366RB_PHY_NO_OFFSET 9 16062306a36Sopenharmony_ci#define RTL8366RB_PHY_NO_MASK (0x1f << 9) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* VLAN Ingress Control Register 1, one bit per port. 16362306a36Sopenharmony_ci * bit 0 .. 5 will make the switch drop ingress frames without 16462306a36Sopenharmony_ci * VID such as untagged or priority-tagged frames for respective 16562306a36Sopenharmony_ci * port. 16662306a36Sopenharmony_ci * bit 6 .. 11 will make the switch drop ingress frames carrying 16762306a36Sopenharmony_ci * a C-tag with VID != 0 for respective port. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci#define RTL8366RB_VLAN_INGRESS_CTRL1_REG 0x037E 17062306a36Sopenharmony_ci#define RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) (BIT((port)) | BIT((port) + 6)) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* VLAN Ingress Control Register 2, one bit per port. 17362306a36Sopenharmony_ci * bit0 .. bit5 will make the switch drop all ingress frames with 17462306a36Sopenharmony_ci * a VLAN classification that does not include the port is in its 17562306a36Sopenharmony_ci * member set. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci#define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* LED control registers */ 18062306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_REG 0x0430 18162306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 18262306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 18362306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 18462306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 18562306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 18662306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 18762306a36Sopenharmony_ci#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#define RTL8366RB_LED_CTRL_REG 0x0431 19062306a36Sopenharmony_ci#define RTL8366RB_LED_OFF 0x0 19162306a36Sopenharmony_ci#define RTL8366RB_LED_DUP_COL 0x1 19262306a36Sopenharmony_ci#define RTL8366RB_LED_LINK_ACT 0x2 19362306a36Sopenharmony_ci#define RTL8366RB_LED_SPD1000 0x3 19462306a36Sopenharmony_ci#define RTL8366RB_LED_SPD100 0x4 19562306a36Sopenharmony_ci#define RTL8366RB_LED_SPD10 0x5 19662306a36Sopenharmony_ci#define RTL8366RB_LED_SPD1000_ACT 0x6 19762306a36Sopenharmony_ci#define RTL8366RB_LED_SPD100_ACT 0x7 19862306a36Sopenharmony_ci#define RTL8366RB_LED_SPD10_ACT 0x8 19962306a36Sopenharmony_ci#define RTL8366RB_LED_SPD100_10_ACT 0x9 20062306a36Sopenharmony_ci#define RTL8366RB_LED_FIBER 0xa 20162306a36Sopenharmony_ci#define RTL8366RB_LED_AN_FAULT 0xb 20262306a36Sopenharmony_ci#define RTL8366RB_LED_LINK_RX 0xc 20362306a36Sopenharmony_ci#define RTL8366RB_LED_LINK_TX 0xd 20462306a36Sopenharmony_ci#define RTL8366RB_LED_MASTER 0xe 20562306a36Sopenharmony_ci#define RTL8366RB_LED_FORCE 0xf 20662306a36Sopenharmony_ci#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 20762306a36Sopenharmony_ci#define RTL8366RB_LED_1_OFFSET 6 20862306a36Sopenharmony_ci#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 20962306a36Sopenharmony_ci#define RTL8366RB_LED_3_OFFSET 6 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci#define RTL8366RB_MIB_COUNT 33 21262306a36Sopenharmony_ci#define RTL8366RB_GLOBAL_MIB_COUNT 1 21362306a36Sopenharmony_ci#define RTL8366RB_MIB_COUNTER_PORT_OFFSET 0x0050 21462306a36Sopenharmony_ci#define RTL8366RB_MIB_COUNTER_BASE 0x1000 21562306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_REG 0x13F0 21662306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_USER_MASK 0x0FFC 21762306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_BUSY_MASK BIT(0) 21862306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_RESET_MASK BIT(1) 21962306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_PORT_RESET(_p) BIT(2 + (_p)) 22062306a36Sopenharmony_ci#define RTL8366RB_MIB_CTRL_GLOBAL_RESET BIT(11) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci#define RTL8366RB_PORT_VLAN_CTRL_BASE 0x0063 22362306a36Sopenharmony_ci#define RTL8366RB_PORT_VLAN_CTRL_REG(_p) \ 22462306a36Sopenharmony_ci (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4) 22562306a36Sopenharmony_ci#define RTL8366RB_PORT_VLAN_CTRL_MASK 0xf 22662306a36Sopenharmony_ci#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4)) 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#define RTL8366RB_VLAN_TABLE_READ_BASE 0x018C 22962306a36Sopenharmony_ci#define RTL8366RB_VLAN_TABLE_WRITE_BASE 0x0185 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci#define RTL8366RB_TABLE_ACCESS_CTRL_REG 0x0180 23262306a36Sopenharmony_ci#define RTL8366RB_TABLE_VLAN_READ_CTRL 0x0E01 23362306a36Sopenharmony_ci#define RTL8366RB_TABLE_VLAN_WRITE_CTRL 0x0F01 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define RTL8366RB_VLAN_MC_BASE(_x) (0x0020 + (_x) * 3) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define RTL8366RB_PORT_LINK_STATUS_BASE 0x0014 23862306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_SPEED_MASK 0x0003 23962306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_DUPLEX_MASK 0x0004 24062306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_LINK_MASK 0x0010 24162306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK 0x0020 24262306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK 0x0040 24362306a36Sopenharmony_ci#define RTL8366RB_PORT_STATUS_AN_MASK 0x0080 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci#define RTL8366RB_NUM_VLANS 16 24662306a36Sopenharmony_ci#define RTL8366RB_NUM_LEDGROUPS 4 24762306a36Sopenharmony_ci#define RTL8366RB_NUM_VIDS 4096 24862306a36Sopenharmony_ci#define RTL8366RB_PRIORITYMAX 7 24962306a36Sopenharmony_ci#define RTL8366RB_NUM_FIDS 8 25062306a36Sopenharmony_ci#define RTL8366RB_FIDMAX 7 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#define RTL8366RB_PORT_1 BIT(0) /* In userspace port 0 */ 25362306a36Sopenharmony_ci#define RTL8366RB_PORT_2 BIT(1) /* In userspace port 1 */ 25462306a36Sopenharmony_ci#define RTL8366RB_PORT_3 BIT(2) /* In userspace port 2 */ 25562306a36Sopenharmony_ci#define RTL8366RB_PORT_4 BIT(3) /* In userspace port 3 */ 25662306a36Sopenharmony_ci#define RTL8366RB_PORT_5 BIT(4) /* In userspace port 4 */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#define RTL8366RB_PORT_CPU BIT(5) /* CPU port */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci#define RTL8366RB_PORT_ALL (RTL8366RB_PORT_1 | \ 26162306a36Sopenharmony_ci RTL8366RB_PORT_2 | \ 26262306a36Sopenharmony_ci RTL8366RB_PORT_3 | \ 26362306a36Sopenharmony_ci RTL8366RB_PORT_4 | \ 26462306a36Sopenharmony_ci RTL8366RB_PORT_5 | \ 26562306a36Sopenharmony_ci RTL8366RB_PORT_CPU) 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci#define RTL8366RB_PORT_ALL_BUT_CPU (RTL8366RB_PORT_1 | \ 26862306a36Sopenharmony_ci RTL8366RB_PORT_2 | \ 26962306a36Sopenharmony_ci RTL8366RB_PORT_3 | \ 27062306a36Sopenharmony_ci RTL8366RB_PORT_4 | \ 27162306a36Sopenharmony_ci RTL8366RB_PORT_5) 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci#define RTL8366RB_PORT_ALL_EXTERNAL (RTL8366RB_PORT_1 | \ 27462306a36Sopenharmony_ci RTL8366RB_PORT_2 | \ 27562306a36Sopenharmony_ci RTL8366RB_PORT_3 | \ 27662306a36Sopenharmony_ci RTL8366RB_PORT_4) 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci#define RTL8366RB_PORT_ALL_INTERNAL RTL8366RB_PORT_CPU 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* First configuration word per member config, VID and prio */ 28162306a36Sopenharmony_ci#define RTL8366RB_VLAN_VID_MASK 0xfff 28262306a36Sopenharmony_ci#define RTL8366RB_VLAN_PRIORITY_SHIFT 12 28362306a36Sopenharmony_ci#define RTL8366RB_VLAN_PRIORITY_MASK 0x7 28462306a36Sopenharmony_ci/* Second configuration word per member config, member and untagged */ 28562306a36Sopenharmony_ci#define RTL8366RB_VLAN_UNTAG_SHIFT 8 28662306a36Sopenharmony_ci#define RTL8366RB_VLAN_UNTAG_MASK 0xff 28762306a36Sopenharmony_ci#define RTL8366RB_VLAN_MEMBER_MASK 0xff 28862306a36Sopenharmony_ci/* Third config word per member config, STAG currently unused */ 28962306a36Sopenharmony_ci#define RTL8366RB_VLAN_STAG_MBR_MASK 0xff 29062306a36Sopenharmony_ci#define RTL8366RB_VLAN_STAG_MBR_SHIFT 8 29162306a36Sopenharmony_ci#define RTL8366RB_VLAN_STAG_IDX_MASK 0x7 29262306a36Sopenharmony_ci#define RTL8366RB_VLAN_STAG_IDX_SHIFT 5 29362306a36Sopenharmony_ci#define RTL8366RB_VLAN_FID_MASK 0x7 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* Port ingress bandwidth control */ 29662306a36Sopenharmony_ci#define RTL8366RB_IB_BASE 0x0200 29762306a36Sopenharmony_ci#define RTL8366RB_IB_REG(pnum) (RTL8366RB_IB_BASE + (pnum)) 29862306a36Sopenharmony_ci#define RTL8366RB_IB_BDTH_MASK 0x3fff 29962306a36Sopenharmony_ci#define RTL8366RB_IB_PREIFG BIT(14) 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* Port egress bandwidth control */ 30262306a36Sopenharmony_ci#define RTL8366RB_EB_BASE 0x02d1 30362306a36Sopenharmony_ci#define RTL8366RB_EB_REG(pnum) (RTL8366RB_EB_BASE + (pnum)) 30462306a36Sopenharmony_ci#define RTL8366RB_EB_BDTH_MASK 0x3fff 30562306a36Sopenharmony_ci#define RTL8366RB_EB_PREIFG_REG 0x02f8 30662306a36Sopenharmony_ci#define RTL8366RB_EB_PREIFG BIT(9) 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci#define RTL8366RB_BDTH_SW_MAX 1048512 /* 1048576? */ 30962306a36Sopenharmony_ci#define RTL8366RB_BDTH_UNIT 64 31062306a36Sopenharmony_ci#define RTL8366RB_BDTH_REG_DEFAULT 16383 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* QOS */ 31362306a36Sopenharmony_ci#define RTL8366RB_QOS BIT(15) 31462306a36Sopenharmony_ci/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */ 31562306a36Sopenharmony_ci#define RTL8366RB_QOS_DEFAULT_PREIFG 1 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* Interrupt handling */ 31862306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_CONTROL_REG 0x0440 31962306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_POLARITY BIT(0) 32062306a36Sopenharmony_ci#define RTL8366RB_P4_RGMII_LED BIT(2) 32162306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_MASK_REG 0x0441 32262306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_LINK_CHGALL GENMASK(11, 0) 32362306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_ACLEXCEED BIT(8) 32462306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_STORMEXCEED BIT(9) 32562306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_P4_FIBER BIT(12) 32662306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_P4_UTP BIT(13) 32762306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_VALID (RTL8366RB_INTERRUPT_LINK_CHGALL | \ 32862306a36Sopenharmony_ci RTL8366RB_INTERRUPT_ACLEXCEED | \ 32962306a36Sopenharmony_ci RTL8366RB_INTERRUPT_STORMEXCEED | \ 33062306a36Sopenharmony_ci RTL8366RB_INTERRUPT_P4_FIBER | \ 33162306a36Sopenharmony_ci RTL8366RB_INTERRUPT_P4_UTP) 33262306a36Sopenharmony_ci#define RTL8366RB_INTERRUPT_STATUS_REG 0x0442 33362306a36Sopenharmony_ci#define RTL8366RB_NUM_INTERRUPT 14 /* 0..13 */ 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* Port isolation registers */ 33662306a36Sopenharmony_ci#define RTL8366RB_PORT_ISO_BASE 0x0F08 33762306a36Sopenharmony_ci#define RTL8366RB_PORT_ISO(pnum) (RTL8366RB_PORT_ISO_BASE + (pnum)) 33862306a36Sopenharmony_ci#define RTL8366RB_PORT_ISO_EN BIT(0) 33962306a36Sopenharmony_ci#define RTL8366RB_PORT_ISO_PORTS_MASK GENMASK(7, 1) 34062306a36Sopenharmony_ci#define RTL8366RB_PORT_ISO_PORTS(pmask) ((pmask) << 1) 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/* bits 0..5 enable force when cleared */ 34362306a36Sopenharmony_ci#define RTL8366RB_MAC_FORCE_CTRL_REG 0x0F11 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci#define RTL8366RB_OAM_PARSER_REG 0x0F14 34662306a36Sopenharmony_ci#define RTL8366RB_OAM_MULTIPLEXER_REG 0x0F15 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#define RTL8366RB_GREEN_FEATURE_REG 0x0F51 34962306a36Sopenharmony_ci#define RTL8366RB_GREEN_FEATURE_MSK 0x0007 35062306a36Sopenharmony_ci#define RTL8366RB_GREEN_FEATURE_TX BIT(0) 35162306a36Sopenharmony_ci#define RTL8366RB_GREEN_FEATURE_RX BIT(2) 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/** 35462306a36Sopenharmony_ci * struct rtl8366rb - RTL8366RB-specific data 35562306a36Sopenharmony_ci * @max_mtu: per-port max MTU setting 35662306a36Sopenharmony_ci * @pvid_enabled: if PVID is set for respective port 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistruct rtl8366rb { 35962306a36Sopenharmony_ci unsigned int max_mtu[RTL8366RB_NUM_PORTS]; 36062306a36Sopenharmony_ci bool pvid_enabled[RTL8366RB_NUM_PORTS]; 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { 36462306a36Sopenharmony_ci { 0, 0, 4, "IfInOctets" }, 36562306a36Sopenharmony_ci { 0, 4, 4, "EtherStatsOctets" }, 36662306a36Sopenharmony_ci { 0, 8, 2, "EtherStatsUnderSizePkts" }, 36762306a36Sopenharmony_ci { 0, 10, 2, "EtherFragments" }, 36862306a36Sopenharmony_ci { 0, 12, 2, "EtherStatsPkts64Octets" }, 36962306a36Sopenharmony_ci { 0, 14, 2, "EtherStatsPkts65to127Octets" }, 37062306a36Sopenharmony_ci { 0, 16, 2, "EtherStatsPkts128to255Octets" }, 37162306a36Sopenharmony_ci { 0, 18, 2, "EtherStatsPkts256to511Octets" }, 37262306a36Sopenharmony_ci { 0, 20, 2, "EtherStatsPkts512to1023Octets" }, 37362306a36Sopenharmony_ci { 0, 22, 2, "EtherStatsPkts1024to1518Octets" }, 37462306a36Sopenharmony_ci { 0, 24, 2, "EtherOversizeStats" }, 37562306a36Sopenharmony_ci { 0, 26, 2, "EtherStatsJabbers" }, 37662306a36Sopenharmony_ci { 0, 28, 2, "IfInUcastPkts" }, 37762306a36Sopenharmony_ci { 0, 30, 2, "EtherStatsMulticastPkts" }, 37862306a36Sopenharmony_ci { 0, 32, 2, "EtherStatsBroadcastPkts" }, 37962306a36Sopenharmony_ci { 0, 34, 2, "EtherStatsDropEvents" }, 38062306a36Sopenharmony_ci { 0, 36, 2, "Dot3StatsFCSErrors" }, 38162306a36Sopenharmony_ci { 0, 38, 2, "Dot3StatsSymbolErrors" }, 38262306a36Sopenharmony_ci { 0, 40, 2, "Dot3InPauseFrames" }, 38362306a36Sopenharmony_ci { 0, 42, 2, "Dot3ControlInUnknownOpcodes" }, 38462306a36Sopenharmony_ci { 0, 44, 4, "IfOutOctets" }, 38562306a36Sopenharmony_ci { 0, 48, 2, "Dot3StatsSingleCollisionFrames" }, 38662306a36Sopenharmony_ci { 0, 50, 2, "Dot3StatMultipleCollisionFrames" }, 38762306a36Sopenharmony_ci { 0, 52, 2, "Dot3sDeferredTransmissions" }, 38862306a36Sopenharmony_ci { 0, 54, 2, "Dot3StatsLateCollisions" }, 38962306a36Sopenharmony_ci { 0, 56, 2, "EtherStatsCollisions" }, 39062306a36Sopenharmony_ci { 0, 58, 2, "Dot3StatsExcessiveCollisions" }, 39162306a36Sopenharmony_ci { 0, 60, 2, "Dot3OutPauseFrames" }, 39262306a36Sopenharmony_ci { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards" }, 39362306a36Sopenharmony_ci { 0, 64, 2, "Dot1dTpPortInDiscards" }, 39462306a36Sopenharmony_ci { 0, 66, 2, "IfOutUcastPkts" }, 39562306a36Sopenharmony_ci { 0, 68, 2, "IfOutMulticastPkts" }, 39662306a36Sopenharmony_ci { 0, 70, 2, "IfOutBroadcastPkts" }, 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int rtl8366rb_get_mib_counter(struct realtek_priv *priv, 40062306a36Sopenharmony_ci int port, 40162306a36Sopenharmony_ci struct rtl8366_mib_counter *mib, 40262306a36Sopenharmony_ci u64 *mibvalue) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci u32 addr, val; 40562306a36Sopenharmony_ci int ret; 40662306a36Sopenharmony_ci int i; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci addr = RTL8366RB_MIB_COUNTER_BASE + 40962306a36Sopenharmony_ci RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) + 41062306a36Sopenharmony_ci mib->offset; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Writing access counter address first 41362306a36Sopenharmony_ci * then ASIC will prepare 64bits counter wait for being retrived 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci ret = regmap_write(priv->map, addr, 0); /* Write whatever */ 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Read MIB control register */ 42062306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_MIB_CTRL_REG, &val); 42162306a36Sopenharmony_ci if (ret) 42262306a36Sopenharmony_ci return -EIO; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (val & RTL8366RB_MIB_CTRL_BUSY_MASK) 42562306a36Sopenharmony_ci return -EBUSY; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (val & RTL8366RB_MIB_CTRL_RESET_MASK) 42862306a36Sopenharmony_ci return -EIO; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Read each individual MIB 16 bits at the time */ 43162306a36Sopenharmony_ci *mibvalue = 0; 43262306a36Sopenharmony_ci for (i = mib->length; i > 0; i--) { 43362306a36Sopenharmony_ci ret = regmap_read(priv->map, addr + (i - 1), &val); 43462306a36Sopenharmony_ci if (ret) 43562306a36Sopenharmony_ci return ret; 43662306a36Sopenharmony_ci *mibvalue = (*mibvalue << 16) | (val & 0xFFFF); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic u32 rtl8366rb_get_irqmask(struct irq_data *d) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int line = irqd_to_hwirq(d); 44462306a36Sopenharmony_ci u32 val; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* For line interrupts we combine link down in bits 44762306a36Sopenharmony_ci * 6..11 with link up in bits 0..5 into one interrupt. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (line < 12) 45062306a36Sopenharmony_ci val = BIT(line) | BIT(line + 6); 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci val = BIT(line); 45362306a36Sopenharmony_ci return val; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void rtl8366rb_mask_irq(struct irq_data *d) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct realtek_priv *priv = irq_data_get_irq_chip_data(d); 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, 46262306a36Sopenharmony_ci rtl8366rb_get_irqmask(d), 0); 46362306a36Sopenharmony_ci if (ret) 46462306a36Sopenharmony_ci dev_err(priv->dev, "could not mask IRQ\n"); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void rtl8366rb_unmask_irq(struct irq_data *d) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct realtek_priv *priv = irq_data_get_irq_chip_data(d); 47062306a36Sopenharmony_ci int ret; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_MASK_REG, 47362306a36Sopenharmony_ci rtl8366rb_get_irqmask(d), 47462306a36Sopenharmony_ci rtl8366rb_get_irqmask(d)); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci dev_err(priv->dev, "could not unmask IRQ\n"); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic irqreturn_t rtl8366rb_irq(int irq, void *data) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct realtek_priv *priv = data; 48262306a36Sopenharmony_ci u32 stat; 48362306a36Sopenharmony_ci int ret; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* This clears the IRQ status register */ 48662306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, 48762306a36Sopenharmony_ci &stat); 48862306a36Sopenharmony_ci if (ret) { 48962306a36Sopenharmony_ci dev_err(priv->dev, "can't read interrupt status\n"); 49062306a36Sopenharmony_ci return IRQ_NONE; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci stat &= RTL8366RB_INTERRUPT_VALID; 49362306a36Sopenharmony_ci if (!stat) 49462306a36Sopenharmony_ci return IRQ_NONE; 49562306a36Sopenharmony_ci while (stat) { 49662306a36Sopenharmony_ci int line = __ffs(stat); 49762306a36Sopenharmony_ci int child_irq; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci stat &= ~BIT(line); 50062306a36Sopenharmony_ci /* For line interrupts we combine link down in bits 50162306a36Sopenharmony_ci * 6..11 with link up in bits 0..5 into one interrupt. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci if (line < 12 && line > 5) 50462306a36Sopenharmony_ci line -= 5; 50562306a36Sopenharmony_ci child_irq = irq_find_mapping(priv->irqdomain, line); 50662306a36Sopenharmony_ci handle_nested_irq(child_irq); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci return IRQ_HANDLED; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic struct irq_chip rtl8366rb_irq_chip = { 51262306a36Sopenharmony_ci .name = "RTL8366RB", 51362306a36Sopenharmony_ci .irq_mask = rtl8366rb_mask_irq, 51462306a36Sopenharmony_ci .irq_unmask = rtl8366rb_unmask_irq, 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int rtl8366rb_irq_map(struct irq_domain *domain, unsigned int irq, 51862306a36Sopenharmony_ci irq_hw_number_t hwirq) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci irq_set_chip_data(irq, domain->host_data); 52162306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &rtl8366rb_irq_chip, handle_simple_irq); 52262306a36Sopenharmony_ci irq_set_nested_thread(irq, 1); 52362306a36Sopenharmony_ci irq_set_noprobe(irq); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void rtl8366rb_irq_unmap(struct irq_domain *d, unsigned int irq) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci irq_set_nested_thread(irq, 0); 53162306a36Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 53262306a36Sopenharmony_ci irq_set_chip_data(irq, NULL); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic const struct irq_domain_ops rtl8366rb_irqdomain_ops = { 53662306a36Sopenharmony_ci .map = rtl8366rb_irq_map, 53762306a36Sopenharmony_ci .unmap = rtl8366rb_irq_unmap, 53862306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int rtl8366rb_setup_cascaded_irq(struct realtek_priv *priv) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct device_node *intc; 54462306a36Sopenharmony_ci unsigned long irq_trig; 54562306a36Sopenharmony_ci int irq; 54662306a36Sopenharmony_ci int ret; 54762306a36Sopenharmony_ci u32 val; 54862306a36Sopenharmony_ci int i; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); 55162306a36Sopenharmony_ci if (!intc) { 55262306a36Sopenharmony_ci dev_err(priv->dev, "missing child interrupt-controller node\n"); 55362306a36Sopenharmony_ci return -EINVAL; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci /* RB8366RB IRQs cascade off this one */ 55662306a36Sopenharmony_ci irq = of_irq_get(intc, 0); 55762306a36Sopenharmony_ci if (irq <= 0) { 55862306a36Sopenharmony_ci dev_err(priv->dev, "failed to get parent IRQ\n"); 55962306a36Sopenharmony_ci ret = irq ? irq : -EINVAL; 56062306a36Sopenharmony_ci goto out_put_node; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* This clears the IRQ status register */ 56462306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, 56562306a36Sopenharmony_ci &val); 56662306a36Sopenharmony_ci if (ret) { 56762306a36Sopenharmony_ci dev_err(priv->dev, "can't read interrupt status\n"); 56862306a36Sopenharmony_ci goto out_put_node; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Fetch IRQ edge information from the descriptor */ 57262306a36Sopenharmony_ci irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); 57362306a36Sopenharmony_ci switch (irq_trig) { 57462306a36Sopenharmony_ci case IRQF_TRIGGER_RISING: 57562306a36Sopenharmony_ci case IRQF_TRIGGER_HIGH: 57662306a36Sopenharmony_ci dev_info(priv->dev, "active high/rising IRQ\n"); 57762306a36Sopenharmony_ci val = 0; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case IRQF_TRIGGER_FALLING: 58062306a36Sopenharmony_ci case IRQF_TRIGGER_LOW: 58162306a36Sopenharmony_ci dev_info(priv->dev, "active low/falling IRQ\n"); 58262306a36Sopenharmony_ci val = RTL8366RB_INTERRUPT_POLARITY; 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_INTERRUPT_CONTROL_REG, 58662306a36Sopenharmony_ci RTL8366RB_INTERRUPT_POLARITY, 58762306a36Sopenharmony_ci val); 58862306a36Sopenharmony_ci if (ret) { 58962306a36Sopenharmony_ci dev_err(priv->dev, "could not configure IRQ polarity\n"); 59062306a36Sopenharmony_ci goto out_put_node; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = devm_request_threaded_irq(priv->dev, irq, NULL, 59462306a36Sopenharmony_ci rtl8366rb_irq, IRQF_ONESHOT, 59562306a36Sopenharmony_ci "RTL8366RB", priv); 59662306a36Sopenharmony_ci if (ret) { 59762306a36Sopenharmony_ci dev_err(priv->dev, "unable to request irq: %d\n", ret); 59862306a36Sopenharmony_ci goto out_put_node; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci priv->irqdomain = irq_domain_add_linear(intc, 60162306a36Sopenharmony_ci RTL8366RB_NUM_INTERRUPT, 60262306a36Sopenharmony_ci &rtl8366rb_irqdomain_ops, 60362306a36Sopenharmony_ci priv); 60462306a36Sopenharmony_ci if (!priv->irqdomain) { 60562306a36Sopenharmony_ci dev_err(priv->dev, "failed to create IRQ domain\n"); 60662306a36Sopenharmony_ci ret = -EINVAL; 60762306a36Sopenharmony_ci goto out_put_node; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci for (i = 0; i < priv->num_ports; i++) 61062306a36Sopenharmony_ci irq_set_parent(irq_create_mapping(priv->irqdomain, i), irq); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ciout_put_node: 61362306a36Sopenharmony_ci of_node_put(intc); 61462306a36Sopenharmony_ci return ret; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int rtl8366rb_set_addr(struct realtek_priv *priv) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 62062306a36Sopenharmony_ci u16 val; 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci eth_random_addr(addr); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci dev_info(priv->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", 62662306a36Sopenharmony_ci addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 62762306a36Sopenharmony_ci val = addr[0] << 8 | addr[1]; 62862306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_SMAR0, val); 62962306a36Sopenharmony_ci if (ret) 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci val = addr[2] << 8 | addr[3]; 63262306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_SMAR1, val); 63362306a36Sopenharmony_ci if (ret) 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci val = addr[4] << 8 | addr[5]; 63662306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_SMAR2, val); 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* Found in a vendor driver */ 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* Struct for handling the jam tables' entries */ 64662306a36Sopenharmony_cistruct rtl8366rb_jam_tbl_entry { 64762306a36Sopenharmony_ci u16 reg; 64862306a36Sopenharmony_ci u16 val; 64962306a36Sopenharmony_ci}; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* For the "version 0" early silicon, appear in most source releases */ 65262306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_0[] = { 65362306a36Sopenharmony_ci {0x000B, 0x0001}, {0x03A6, 0x0100}, {0x03A7, 0x0001}, {0x02D1, 0x3FFF}, 65462306a36Sopenharmony_ci {0x02D2, 0x3FFF}, {0x02D3, 0x3FFF}, {0x02D4, 0x3FFF}, {0x02D5, 0x3FFF}, 65562306a36Sopenharmony_ci {0x02D6, 0x3FFF}, {0x02D7, 0x3FFF}, {0x02D8, 0x3FFF}, {0x022B, 0x0688}, 65662306a36Sopenharmony_ci {0x022C, 0x0FAC}, {0x03D0, 0x4688}, {0x03D1, 0x01F5}, {0x0000, 0x0830}, 65762306a36Sopenharmony_ci {0x02F9, 0x0200}, {0x02F7, 0x7FFF}, {0x02F8, 0x03FF}, {0x0080, 0x03E8}, 65862306a36Sopenharmony_ci {0x0081, 0x00CE}, {0x0082, 0x00DA}, {0x0083, 0x0230}, {0xBE0F, 0x2000}, 65962306a36Sopenharmony_ci {0x0231, 0x422A}, {0x0232, 0x422A}, {0x0233, 0x422A}, {0x0234, 0x422A}, 66062306a36Sopenharmony_ci {0x0235, 0x422A}, {0x0236, 0x422A}, {0x0237, 0x422A}, {0x0238, 0x422A}, 66162306a36Sopenharmony_ci {0x0239, 0x422A}, {0x023A, 0x422A}, {0x023B, 0x422A}, {0x023C, 0x422A}, 66262306a36Sopenharmony_ci {0x023D, 0x422A}, {0x023E, 0x422A}, {0x023F, 0x422A}, {0x0240, 0x422A}, 66362306a36Sopenharmony_ci {0x0241, 0x422A}, {0x0242, 0x422A}, {0x0243, 0x422A}, {0x0244, 0x422A}, 66462306a36Sopenharmony_ci {0x0245, 0x422A}, {0x0246, 0x422A}, {0x0247, 0x422A}, {0x0248, 0x422A}, 66562306a36Sopenharmony_ci {0x0249, 0x0146}, {0x024A, 0x0146}, {0x024B, 0x0146}, {0xBE03, 0xC961}, 66662306a36Sopenharmony_ci {0x024D, 0x0146}, {0x024E, 0x0146}, {0x024F, 0x0146}, {0x0250, 0x0146}, 66762306a36Sopenharmony_ci {0xBE64, 0x0226}, {0x0252, 0x0146}, {0x0253, 0x0146}, {0x024C, 0x0146}, 66862306a36Sopenharmony_ci {0x0251, 0x0146}, {0x0254, 0x0146}, {0xBE62, 0x3FD0}, {0x0084, 0x0320}, 66962306a36Sopenharmony_ci {0x0255, 0x0146}, {0x0256, 0x0146}, {0x0257, 0x0146}, {0x0258, 0x0146}, 67062306a36Sopenharmony_ci {0x0259, 0x0146}, {0x025A, 0x0146}, {0x025B, 0x0146}, {0x025C, 0x0146}, 67162306a36Sopenharmony_ci {0x025D, 0x0146}, {0x025E, 0x0146}, {0x025F, 0x0146}, {0x0260, 0x0146}, 67262306a36Sopenharmony_ci {0x0261, 0xA23F}, {0x0262, 0x0294}, {0x0263, 0xA23F}, {0x0264, 0x0294}, 67362306a36Sopenharmony_ci {0x0265, 0xA23F}, {0x0266, 0x0294}, {0x0267, 0xA23F}, {0x0268, 0x0294}, 67462306a36Sopenharmony_ci {0x0269, 0xA23F}, {0x026A, 0x0294}, {0x026B, 0xA23F}, {0x026C, 0x0294}, 67562306a36Sopenharmony_ci {0x026D, 0xA23F}, {0x026E, 0x0294}, {0x026F, 0xA23F}, {0x0270, 0x0294}, 67662306a36Sopenharmony_ci {0x02F5, 0x0048}, {0xBE09, 0x0E00}, {0xBE1E, 0x0FA0}, {0xBE14, 0x8448}, 67762306a36Sopenharmony_ci {0xBE15, 0x1007}, {0xBE4A, 0xA284}, {0xC454, 0x3F0B}, {0xC474, 0x3F0B}, 67862306a36Sopenharmony_ci {0xBE48, 0x3672}, {0xBE4B, 0x17A7}, {0xBE4C, 0x0B15}, {0xBE52, 0x0EDD}, 67962306a36Sopenharmony_ci {0xBE49, 0x8C00}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE5D, 0x785C}, 68062306a36Sopenharmony_ci {0xBE61, 0x368A}, {0xBE63, 0x9B84}, {0xC456, 0xCC13}, {0xC476, 0xCC13}, 68162306a36Sopenharmony_ci {0xBE65, 0x307D}, {0xBE6D, 0x0005}, {0xBE6E, 0xE120}, {0xBE2E, 0x7BAF}, 68262306a36Sopenharmony_ci}; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* This v1 init sequence is from Belkin F5D8235 U-Boot release */ 68562306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_1[] = { 68662306a36Sopenharmony_ci {0x0000, 0x0830}, {0x0001, 0x8000}, {0x0400, 0x8130}, {0xBE78, 0x3C3C}, 68762306a36Sopenharmony_ci {0x0431, 0x5432}, {0xBE37, 0x0CE4}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, 68862306a36Sopenharmony_ci {0xC44C, 0x1585}, {0xC44C, 0x1185}, {0xC44C, 0x1585}, {0xC46C, 0x1585}, 68962306a36Sopenharmony_ci {0xC46C, 0x1185}, {0xC46C, 0x1585}, {0xC451, 0x2135}, {0xC471, 0x2135}, 69062306a36Sopenharmony_ci {0xBE10, 0x8140}, {0xBE15, 0x0007}, {0xBE6E, 0xE120}, {0xBE69, 0xD20F}, 69162306a36Sopenharmony_ci {0xBE6B, 0x0320}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF20}, 69262306a36Sopenharmony_ci {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, {0xBE24, 0x0000}, 69362306a36Sopenharmony_ci {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, {0xBE21, 0x0140}, 69462306a36Sopenharmony_ci {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, {0xBE2E, 0x7B7A}, 69562306a36Sopenharmony_ci {0xBE36, 0x0CE4}, {0x02F5, 0x0048}, {0xBE77, 0x2940}, {0x000A, 0x83E0}, 69662306a36Sopenharmony_ci {0xBE79, 0x3C3C}, {0xBE00, 0x1340}, 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* This v2 init sequence is from Belkin F5D8235 U-Boot release */ 70062306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_2[] = { 70162306a36Sopenharmony_ci {0x0450, 0x0000}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, 70262306a36Sopenharmony_ci {0xC44F, 0x6250}, {0xC46F, 0x6250}, {0xC456, 0x0C14}, {0xC476, 0x0C14}, 70362306a36Sopenharmony_ci {0xC44C, 0x1C85}, {0xC44C, 0x1885}, {0xC44C, 0x1C85}, {0xC46C, 0x1C85}, 70462306a36Sopenharmony_ci {0xC46C, 0x1885}, {0xC46C, 0x1C85}, {0xC44C, 0x0885}, {0xC44C, 0x0881}, 70562306a36Sopenharmony_ci {0xC44C, 0x0885}, {0xC46C, 0x0885}, {0xC46C, 0x0881}, {0xC46C, 0x0885}, 70662306a36Sopenharmony_ci {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, 70762306a36Sopenharmony_ci {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6E, 0x0320}, 70862306a36Sopenharmony_ci {0xBE77, 0x2940}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, 70962306a36Sopenharmony_ci {0x8000, 0x0001}, {0xBE15, 0x1007}, {0x8000, 0x0000}, {0xBE15, 0x1007}, 71062306a36Sopenharmony_ci {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, {0xBE10, 0x8140}, 71162306a36Sopenharmony_ci {0xBE00, 0x1340}, {0x0F51, 0x0010}, 71262306a36Sopenharmony_ci}; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/* Appears in a DDWRT code dump */ 71562306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_3[] = { 71662306a36Sopenharmony_ci {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, 71762306a36Sopenharmony_ci {0x0F51, 0x0017}, {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, 71862306a36Sopenharmony_ci {0xC456, 0x0C14}, {0xC476, 0x0C14}, {0xC454, 0x3F8B}, {0xC474, 0x3F8B}, 71962306a36Sopenharmony_ci {0xC450, 0x2071}, {0xC470, 0x2071}, {0xC451, 0x226B}, {0xC471, 0x226B}, 72062306a36Sopenharmony_ci {0xC452, 0xA293}, {0xC472, 0xA293}, {0xC44C, 0x1585}, {0xC44C, 0x1185}, 72162306a36Sopenharmony_ci {0xC44C, 0x1585}, {0xC46C, 0x1585}, {0xC46C, 0x1185}, {0xC46C, 0x1585}, 72262306a36Sopenharmony_ci {0xC44C, 0x0185}, {0xC44C, 0x0181}, {0xC44C, 0x0185}, {0xC46C, 0x0185}, 72362306a36Sopenharmony_ci {0xC46C, 0x0181}, {0xC46C, 0x0185}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, 72462306a36Sopenharmony_ci {0xBE22, 0xDF20}, {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, 72562306a36Sopenharmony_ci {0xBE24, 0x0000}, {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, 72662306a36Sopenharmony_ci {0xBE21, 0x0140}, {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, 72762306a36Sopenharmony_ci {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, 72862306a36Sopenharmony_ci {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6B, 0x0320}, 72962306a36Sopenharmony_ci {0xBE77, 0x2800}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, 73062306a36Sopenharmony_ci {0x8000, 0x0001}, {0xBE10, 0x8140}, {0x8000, 0x0000}, {0xBE10, 0x8140}, 73162306a36Sopenharmony_ci {0xBE15, 0x1007}, {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, 73262306a36Sopenharmony_ci {0xBE10, 0x8140}, {0xBE00, 0x1340}, {0x0450, 0x0000}, {0x0401, 0x0000}, 73362306a36Sopenharmony_ci}; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/* Belkin F5D8235 v1, "belkin,f5d8235-v1" */ 73662306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_f5d8235[] = { 73762306a36Sopenharmony_ci {0x0242, 0x02BF}, {0x0245, 0x02BF}, {0x0248, 0x02BF}, {0x024B, 0x02BF}, 73862306a36Sopenharmony_ci {0x024E, 0x02BF}, {0x0251, 0x02BF}, {0x0254, 0x0A3F}, {0x0256, 0x0A3F}, 73962306a36Sopenharmony_ci {0x0258, 0x0A3F}, {0x025A, 0x0A3F}, {0x025C, 0x0A3F}, {0x025E, 0x0A3F}, 74062306a36Sopenharmony_ci {0x0263, 0x007C}, {0x0100, 0x0004}, {0xBE5B, 0x3500}, {0x800E, 0x200F}, 74162306a36Sopenharmony_ci {0xBE1D, 0x0F00}, {0x8001, 0x5011}, {0x800A, 0xA2F4}, {0x800B, 0x17A3}, 74262306a36Sopenharmony_ci {0xBE4B, 0x17A3}, {0xBE41, 0x5011}, {0xBE17, 0x2100}, {0x8000, 0x8304}, 74362306a36Sopenharmony_ci {0xBE40, 0x8304}, {0xBE4A, 0xA2F4}, {0x800C, 0xA8D5}, {0x8014, 0x5500}, 74462306a36Sopenharmony_ci {0x8015, 0x0004}, {0xBE4C, 0xA8D5}, {0xBE59, 0x0008}, {0xBE09, 0x0E00}, 74562306a36Sopenharmony_ci {0xBE36, 0x1036}, {0xBE37, 0x1036}, {0x800D, 0x00FF}, {0xBE4D, 0x00FF}, 74662306a36Sopenharmony_ci}; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/* DGN3500, "netgear,dgn3500", "netgear,dgn3500b" */ 74962306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_dgn3500[] = { 75062306a36Sopenharmony_ci {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0F51, 0x0017}, 75162306a36Sopenharmony_ci {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, {0x0450, 0x0000}, 75262306a36Sopenharmony_ci {0x0401, 0x0000}, {0x0431, 0x0960}, 75362306a36Sopenharmony_ci}; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* This jam table activates "green ethernet", which means low power mode 75662306a36Sopenharmony_ci * and is claimed to detect the cable length and not use more power than 75762306a36Sopenharmony_ci * necessary, and the ports should enter power saving mode 10 seconds after 75862306a36Sopenharmony_ci * a cable is disconnected. Seems to always be the same. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_cistatic const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = { 76162306a36Sopenharmony_ci {0xBE78, 0x323C}, {0xBE77, 0x5000}, {0xBE2E, 0x7BA7}, 76262306a36Sopenharmony_ci {0xBE59, 0x3459}, {0xBE5A, 0x745A}, {0xBE5B, 0x785C}, 76362306a36Sopenharmony_ci {0xBE5C, 0x785C}, {0xBE6E, 0xE120}, {0xBE79, 0x323C}, 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/* Function that jams the tables in the proper registers */ 76762306a36Sopenharmony_cistatic int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, 76862306a36Sopenharmony_ci int jam_size, struct realtek_priv *priv, 76962306a36Sopenharmony_ci bool write_dbg) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci u32 val; 77262306a36Sopenharmony_ci int ret; 77362306a36Sopenharmony_ci int i; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci for (i = 0; i < jam_size; i++) { 77662306a36Sopenharmony_ci if ((jam_table[i].reg & 0xBE00) == 0xBE00) { 77762306a36Sopenharmony_ci ret = regmap_read(priv->map, 77862306a36Sopenharmony_ci RTL8366RB_PHY_ACCESS_BUSY_REG, 77962306a36Sopenharmony_ci &val); 78062306a36Sopenharmony_ci if (ret) 78162306a36Sopenharmony_ci return ret; 78262306a36Sopenharmony_ci if (!(val & RTL8366RB_PHY_INT_BUSY)) { 78362306a36Sopenharmony_ci ret = regmap_write(priv->map, 78462306a36Sopenharmony_ci RTL8366RB_PHY_ACCESS_CTRL_REG, 78562306a36Sopenharmony_ci RTL8366RB_PHY_CTRL_WRITE); 78662306a36Sopenharmony_ci if (ret) 78762306a36Sopenharmony_ci return ret; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci if (write_dbg) 79162306a36Sopenharmony_ci dev_dbg(priv->dev, "jam %04x into register %04x\n", 79262306a36Sopenharmony_ci jam_table[i].val, 79362306a36Sopenharmony_ci jam_table[i].reg); 79462306a36Sopenharmony_ci ret = regmap_write(priv->map, 79562306a36Sopenharmony_ci jam_table[i].reg, 79662306a36Sopenharmony_ci jam_table[i].val); 79762306a36Sopenharmony_ci if (ret) 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int rtl8366rb_setup(struct dsa_switch *ds) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 80662306a36Sopenharmony_ci const struct rtl8366rb_jam_tbl_entry *jam_table; 80762306a36Sopenharmony_ci struct rtl8366rb *rb; 80862306a36Sopenharmony_ci u32 chip_ver = 0; 80962306a36Sopenharmony_ci u32 chip_id = 0; 81062306a36Sopenharmony_ci int jam_size; 81162306a36Sopenharmony_ci u32 val; 81262306a36Sopenharmony_ci int ret; 81362306a36Sopenharmony_ci int i; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci rb = priv->chip_data; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id); 81862306a36Sopenharmony_ci if (ret) { 81962306a36Sopenharmony_ci dev_err(priv->dev, "unable to read chip id\n"); 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci switch (chip_id) { 82462306a36Sopenharmony_ci case RTL8366RB_CHIP_ID_8366: 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci default: 82762306a36Sopenharmony_ci dev_err(priv->dev, "unknown chip id (%04x)\n", chip_id); 82862306a36Sopenharmony_ci return -ENODEV; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_CHIP_VERSION_CTRL_REG, 83262306a36Sopenharmony_ci &chip_ver); 83362306a36Sopenharmony_ci if (ret) { 83462306a36Sopenharmony_ci dev_err(priv->dev, "unable to read chip version\n"); 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci dev_info(priv->dev, "RTL%04x ver %u chip found\n", 83962306a36Sopenharmony_ci chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Do the init dance using the right jam table */ 84262306a36Sopenharmony_ci switch (chip_ver) { 84362306a36Sopenharmony_ci case 0: 84462306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_ver_0; 84562306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_0); 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci case 1: 84862306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_ver_1; 84962306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_1); 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case 2: 85262306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_ver_2; 85362306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_2); 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci default: 85662306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_ver_3; 85762306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_3); 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Special jam tables for special routers 86262306a36Sopenharmony_ci * TODO: are these necessary? Maintainers, please test 86362306a36Sopenharmony_ci * without them, using just the off-the-shelf tables. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci if (of_machine_is_compatible("belkin,f5d8235-v1")) { 86662306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_f5d8235; 86762306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_f5d8235); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci if (of_machine_is_compatible("netgear,dgn3500") || 87062306a36Sopenharmony_ci of_machine_is_compatible("netgear,dgn3500b")) { 87162306a36Sopenharmony_ci jam_table = rtl8366rb_init_jam_dgn3500; 87262306a36Sopenharmony_ci jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci ret = rtl8366rb_jam_table(jam_table, jam_size, priv, true); 87662306a36Sopenharmony_ci if (ret) 87762306a36Sopenharmony_ci return ret; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* Isolate all user ports so they can only send packets to itself and the CPU port */ 88062306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { 88162306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i), 88262306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) | 88362306a36Sopenharmony_ci RTL8366RB_PORT_ISO_EN); 88462306a36Sopenharmony_ci if (ret) 88562306a36Sopenharmony_ci return ret; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci /* CPU port can send packets to all ports */ 88862306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), 88962306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) | 89062306a36Sopenharmony_ci RTL8366RB_PORT_ISO_EN); 89162306a36Sopenharmony_ci if (ret) 89262306a36Sopenharmony_ci return ret; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Set up the "green ethernet" feature */ 89562306a36Sopenharmony_ci ret = rtl8366rb_jam_table(rtl8366rb_green_jam, 89662306a36Sopenharmony_ci ARRAY_SIZE(rtl8366rb_green_jam), priv, false); 89762306a36Sopenharmony_ci if (ret) 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ret = regmap_write(priv->map, 90162306a36Sopenharmony_ci RTL8366RB_GREEN_FEATURE_REG, 90262306a36Sopenharmony_ci (chip_ver == 1) ? 0x0007 : 0x0003); 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Vendor driver sets 0x240 in registers 0xc and 0xd (undocumented) */ 90762306a36Sopenharmony_ci ret = regmap_write(priv->map, 0x0c, 0x240); 90862306a36Sopenharmony_ci if (ret) 90962306a36Sopenharmony_ci return ret; 91062306a36Sopenharmony_ci ret = regmap_write(priv->map, 0x0d, 0x240); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci return ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Set some random MAC address */ 91562306a36Sopenharmony_ci ret = rtl8366rb_set_addr(priv); 91662306a36Sopenharmony_ci if (ret) 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* Enable CPU port with custom DSA tag 8899. 92062306a36Sopenharmony_ci * 92162306a36Sopenharmony_ci * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers 92262306a36Sopenharmony_ci * the custom tag is turned off. 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8368RB_CPU_CTRL_REG, 92562306a36Sopenharmony_ci 0xFFFF, 92662306a36Sopenharmony_ci BIT(priv->cpu_port)); 92762306a36Sopenharmony_ci if (ret) 92862306a36Sopenharmony_ci return ret; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Make sure we default-enable the fixed CPU port */ 93162306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PECR, 93262306a36Sopenharmony_ci BIT(priv->cpu_port), 93362306a36Sopenharmony_ci 0); 93462306a36Sopenharmony_ci if (ret) 93562306a36Sopenharmony_ci return ret; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* Set maximum packet length to 1536 bytes */ 93862306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_SGCR, 93962306a36Sopenharmony_ci RTL8366RB_SGCR_MAX_LENGTH_MASK, 94062306a36Sopenharmony_ci RTL8366RB_SGCR_MAX_LENGTH_1536); 94162306a36Sopenharmony_ci if (ret) 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_NUM_PORTS; i++) 94462306a36Sopenharmony_ci /* layer 2 size, see rtl8366rb_change_mtu() */ 94562306a36Sopenharmony_ci rb->max_mtu[i] = 1532; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* Disable learning for all ports */ 94862306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, 94962306a36Sopenharmony_ci RTL8366RB_PORT_ALL); 95062306a36Sopenharmony_ci if (ret) 95162306a36Sopenharmony_ci return ret; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Enable auto ageing for all ports */ 95462306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0); 95562306a36Sopenharmony_ci if (ret) 95662306a36Sopenharmony_ci return ret; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* Port 4 setup: this enables Port 4, usually the WAN port, 95962306a36Sopenharmony_ci * common PHY IO mode is apparently mode 0, and this is not what 96062306a36Sopenharmony_ci * the port is initialized to. There is no explanation of the 96162306a36Sopenharmony_ci * IO modes in the Realtek source code, if your WAN port is 96262306a36Sopenharmony_ci * connected to something exotic such as fiber, then this might 96362306a36Sopenharmony_ci * be worth experimenting with. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PMC0, 96662306a36Sopenharmony_ci RTL8366RB_PMC0_P4_IOMODE_MASK, 96762306a36Sopenharmony_ci 0 << RTL8366RB_PMC0_P4_IOMODE_SHIFT); 96862306a36Sopenharmony_ci if (ret) 96962306a36Sopenharmony_ci return ret; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Accept all packets by default, we enable filtering on-demand */ 97262306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, 97362306a36Sopenharmony_ci 0); 97462306a36Sopenharmony_ci if (ret) 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, 97762306a36Sopenharmony_ci 0); 97862306a36Sopenharmony_ci if (ret) 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* Don't drop packets whose DA has not been learned */ 98262306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_SSCR2, 98362306a36Sopenharmony_ci RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0); 98462306a36Sopenharmony_ci if (ret) 98562306a36Sopenharmony_ci return ret; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Set blinking, TODO: make this configurable */ 98862306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, 98962306a36Sopenharmony_ci RTL8366RB_LED_BLINKRATE_MASK, 99062306a36Sopenharmony_ci RTL8366RB_LED_BLINKRATE_56MS); 99162306a36Sopenharmony_ci if (ret) 99262306a36Sopenharmony_ci return ret; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Set up LED activity: 99562306a36Sopenharmony_ci * Each port has 4 LEDs, we configure all ports to the same 99662306a36Sopenharmony_ci * behaviour (no individual config) but we can set up each 99762306a36Sopenharmony_ci * LED separately. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci if (priv->leds_disabled) { 100062306a36Sopenharmony_ci /* Turn everything off */ 100162306a36Sopenharmony_ci regmap_update_bits(priv->map, 100262306a36Sopenharmony_ci RTL8366RB_LED_0_1_CTRL_REG, 100362306a36Sopenharmony_ci 0x0FFF, 0); 100462306a36Sopenharmony_ci regmap_update_bits(priv->map, 100562306a36Sopenharmony_ci RTL8366RB_LED_2_3_CTRL_REG, 100662306a36Sopenharmony_ci 0x0FFF, 0); 100762306a36Sopenharmony_ci regmap_update_bits(priv->map, 100862306a36Sopenharmony_ci RTL8366RB_INTERRUPT_CONTROL_REG, 100962306a36Sopenharmony_ci RTL8366RB_P4_RGMII_LED, 101062306a36Sopenharmony_ci 0); 101162306a36Sopenharmony_ci val = RTL8366RB_LED_OFF; 101262306a36Sopenharmony_ci } else { 101362306a36Sopenharmony_ci /* TODO: make this configurable per LED */ 101462306a36Sopenharmony_ci val = RTL8366RB_LED_FORCE; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 101762306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 101862306a36Sopenharmony_ci RTL8366RB_LED_CTRL_REG, 101962306a36Sopenharmony_ci 0xf << (i * 4), 102062306a36Sopenharmony_ci val << (i * 4)); 102162306a36Sopenharmony_ci if (ret) 102262306a36Sopenharmony_ci return ret; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci ret = rtl8366_reset_vlan(priv); 102662306a36Sopenharmony_ci if (ret) 102762306a36Sopenharmony_ci return ret; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci ret = rtl8366rb_setup_cascaded_irq(priv); 103062306a36Sopenharmony_ci if (ret) 103162306a36Sopenharmony_ci dev_info(priv->dev, "no interrupt support\n"); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (priv->setup_interface) { 103462306a36Sopenharmony_ci ret = priv->setup_interface(ds); 103562306a36Sopenharmony_ci if (ret) { 103662306a36Sopenharmony_ci dev_err(priv->dev, "could not set up MDIO bus\n"); 103762306a36Sopenharmony_ci return -ENODEV; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic enum dsa_tag_protocol rtl8366_get_tag_protocol(struct dsa_switch *ds, 104562306a36Sopenharmony_ci int port, 104662306a36Sopenharmony_ci enum dsa_tag_protocol mp) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci /* This switch uses the 4 byte protocol A Realtek DSA tag */ 104962306a36Sopenharmony_ci return DSA_TAG_PROTO_RTL4_A; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void rtl8366rb_phylink_get_caps(struct dsa_switch *ds, int port, 105362306a36Sopenharmony_ci struct phylink_config *config) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci unsigned long *interfaces = config->supported_interfaces; 105662306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (port == priv->cpu_port) { 105962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_MII, interfaces); 106062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); 106162306a36Sopenharmony_ci /* REVMII only supports 100M FD */ 106262306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_REVMII, interfaces); 106362306a36Sopenharmony_ci /* RGMII only supports 1G FD */ 106462306a36Sopenharmony_ci phy_interface_set_rgmii(interfaces); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci config->mac_capabilities = MAC_1000 | MAC_100 | 106762306a36Sopenharmony_ci MAC_SYM_PAUSE; 106862306a36Sopenharmony_ci } else { 106962306a36Sopenharmony_ci /* RSGMII port, but we don't have that, and we don't 107062306a36Sopenharmony_ci * specify in DT, so phylib uses the default of GMII 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_GMII, interfaces); 107362306a36Sopenharmony_ci config->mac_capabilities = MAC_1000 | MAC_100 | MAC_10 | 107462306a36Sopenharmony_ci MAC_SYM_PAUSE | MAC_ASYM_PAUSE; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic void 107962306a36Sopenharmony_cirtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, 108062306a36Sopenharmony_ci phy_interface_t interface, struct phy_device *phydev, 108162306a36Sopenharmony_ci int speed, int duplex, bool tx_pause, bool rx_pause) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 108462306a36Sopenharmony_ci int ret; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (port != priv->cpu_port) 108762306a36Sopenharmony_ci return; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */ 109262306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG, 109362306a36Sopenharmony_ci BIT(port), BIT(port)); 109462306a36Sopenharmony_ci if (ret) { 109562306a36Sopenharmony_ci dev_err(priv->dev, "failed to force 1Gbit on CPU port\n"); 109662306a36Sopenharmony_ci return; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2, 110062306a36Sopenharmony_ci 0xFF00U, 110162306a36Sopenharmony_ci RTL8366RB_PAACR_CPU_PORT << 8); 110262306a36Sopenharmony_ci if (ret) { 110362306a36Sopenharmony_ci dev_err(priv->dev, "failed to set PAACR on CPU port\n"); 110462306a36Sopenharmony_ci return; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Enable the CPU port */ 110862306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 110962306a36Sopenharmony_ci 0); 111062306a36Sopenharmony_ci if (ret) { 111162306a36Sopenharmony_ci dev_err(priv->dev, "failed to enable the CPU port\n"); 111262306a36Sopenharmony_ci return; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic void 111762306a36Sopenharmony_cirtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, 111862306a36Sopenharmony_ci phy_interface_t interface) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 112162306a36Sopenharmony_ci int ret; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (port != priv->cpu_port) 112462306a36Sopenharmony_ci return; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci dev_dbg(priv->dev, "MAC link down on CPU port (%d)\n", port); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* Disable the CPU port */ 112962306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 113062306a36Sopenharmony_ci BIT(port)); 113162306a36Sopenharmony_ci if (ret) { 113262306a36Sopenharmony_ci dev_err(priv->dev, "failed to disable the CPU port\n"); 113362306a36Sopenharmony_ci return; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic void rb8366rb_set_port_led(struct realtek_priv *priv, 113862306a36Sopenharmony_ci int port, bool enable) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci u16 val = enable ? 0x3f : 0; 114162306a36Sopenharmony_ci int ret; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (priv->leds_disabled) 114462306a36Sopenharmony_ci return; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci switch (port) { 114762306a36Sopenharmony_ci case 0: 114862306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 114962306a36Sopenharmony_ci RTL8366RB_LED_0_1_CTRL_REG, 115062306a36Sopenharmony_ci 0x3F, val); 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci case 1: 115362306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 115462306a36Sopenharmony_ci RTL8366RB_LED_0_1_CTRL_REG, 115562306a36Sopenharmony_ci 0x3F << RTL8366RB_LED_1_OFFSET, 115662306a36Sopenharmony_ci val << RTL8366RB_LED_1_OFFSET); 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci case 2: 115962306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 116062306a36Sopenharmony_ci RTL8366RB_LED_2_3_CTRL_REG, 116162306a36Sopenharmony_ci 0x3F, val); 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci case 3: 116462306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 116562306a36Sopenharmony_ci RTL8366RB_LED_2_3_CTRL_REG, 116662306a36Sopenharmony_ci 0x3F << RTL8366RB_LED_3_OFFSET, 116762306a36Sopenharmony_ci val << RTL8366RB_LED_3_OFFSET); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case 4: 117062306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, 117162306a36Sopenharmony_ci RTL8366RB_INTERRUPT_CONTROL_REG, 117262306a36Sopenharmony_ci RTL8366RB_P4_RGMII_LED, 117362306a36Sopenharmony_ci enable ? RTL8366RB_P4_RGMII_LED : 0); 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci default: 117662306a36Sopenharmony_ci dev_err(priv->dev, "no LED for port %d\n", port); 117762306a36Sopenharmony_ci return; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci if (ret) 118062306a36Sopenharmony_ci dev_err(priv->dev, "error updating LED on port %d\n", port); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int 118462306a36Sopenharmony_cirtl8366rb_port_enable(struct dsa_switch *ds, int port, 118562306a36Sopenharmony_ci struct phy_device *phy) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 118862306a36Sopenharmony_ci int ret; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci dev_dbg(priv->dev, "enable port %d\n", port); 119162306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 119262306a36Sopenharmony_ci 0); 119362306a36Sopenharmony_ci if (ret) 119462306a36Sopenharmony_ci return ret; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci rb8366rb_set_port_led(priv, port, true); 119762306a36Sopenharmony_ci return 0; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void 120162306a36Sopenharmony_cirtl8366rb_port_disable(struct dsa_switch *ds, int port) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 120462306a36Sopenharmony_ci int ret; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci dev_dbg(priv->dev, "disable port %d\n", port); 120762306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), 120862306a36Sopenharmony_ci BIT(port)); 120962306a36Sopenharmony_ci if (ret) 121062306a36Sopenharmony_ci return; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci rb8366rb_set_port_led(priv, port, false); 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic int 121662306a36Sopenharmony_cirtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, 121762306a36Sopenharmony_ci struct dsa_bridge bridge, 121862306a36Sopenharmony_ci bool *tx_fwd_offload, 121962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 122262306a36Sopenharmony_ci unsigned int port_bitmap = 0; 122362306a36Sopenharmony_ci int ret, i; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* Loop over all other ports than the current one */ 122662306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { 122762306a36Sopenharmony_ci /* Current port handled last */ 122862306a36Sopenharmony_ci if (i == port) 122962306a36Sopenharmony_ci continue; 123062306a36Sopenharmony_ci /* Not on this bridge */ 123162306a36Sopenharmony_ci if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 123262306a36Sopenharmony_ci continue; 123362306a36Sopenharmony_ci /* Join this port to each other port on the bridge */ 123462306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), 123562306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(BIT(port)), 123662306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(BIT(port))); 123762306a36Sopenharmony_ci if (ret) 123862306a36Sopenharmony_ci dev_err(priv->dev, "failed to join port %d\n", port); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci port_bitmap |= BIT(i); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* Set the bits for the ports we can access */ 124462306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), 124562306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(port_bitmap), 124662306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(port_bitmap)); 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic void 125062306a36Sopenharmony_cirtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, 125162306a36Sopenharmony_ci struct dsa_bridge bridge) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 125462306a36Sopenharmony_ci unsigned int port_bitmap = 0; 125562306a36Sopenharmony_ci int ret, i; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Loop over all other ports than this one */ 125862306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { 125962306a36Sopenharmony_ci /* Current port handled last */ 126062306a36Sopenharmony_ci if (i == port) 126162306a36Sopenharmony_ci continue; 126262306a36Sopenharmony_ci /* Not on this bridge */ 126362306a36Sopenharmony_ci if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 126462306a36Sopenharmony_ci continue; 126562306a36Sopenharmony_ci /* Remove this port from any other port on the bridge */ 126662306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i), 126762306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0); 126862306a36Sopenharmony_ci if (ret) 126962306a36Sopenharmony_ci dev_err(priv->dev, "failed to leave port %d\n", port); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci port_bitmap |= BIT(i); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /* Clear the bits for the ports we can not access, leave ourselves */ 127562306a36Sopenharmony_ci regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port), 127662306a36Sopenharmony_ci RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0); 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci/** 128062306a36Sopenharmony_ci * rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames 128162306a36Sopenharmony_ci * @priv: SMI state container 128262306a36Sopenharmony_ci * @port: the port to drop untagged and C-tagged frames on 128362306a36Sopenharmony_ci * @drop: whether to drop or pass untagged and C-tagged frames 128462306a36Sopenharmony_ci * 128562306a36Sopenharmony_ci * Return: zero for success, a negative number on error. 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_cistatic int rtl8366rb_drop_untagged(struct realtek_priv *priv, int port, bool drop) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, 129062306a36Sopenharmony_ci RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port), 129162306a36Sopenharmony_ci drop ? RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) : 0); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, 129562306a36Sopenharmony_ci bool vlan_filtering, 129662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 129962306a36Sopenharmony_ci struct rtl8366rb *rb; 130062306a36Sopenharmony_ci int ret; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci rb = priv->chip_data; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, 130562306a36Sopenharmony_ci vlan_filtering ? "enable" : "disable"); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* If the port is not in the member set, the frame will be dropped */ 130862306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, 130962306a36Sopenharmony_ci BIT(port), vlan_filtering ? BIT(port) : 0); 131062306a36Sopenharmony_ci if (ret) 131162306a36Sopenharmony_ci return ret; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* If VLAN filtering is enabled and PVID is also enabled, we must 131462306a36Sopenharmony_ci * not drop any untagged or C-tagged frames. If we turn off VLAN 131562306a36Sopenharmony_ci * filtering on a port, we need to accept any frames. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci if (vlan_filtering) 131862306a36Sopenharmony_ci ret = rtl8366rb_drop_untagged(priv, port, !rb->pvid_enabled[port]); 131962306a36Sopenharmony_ci else 132062306a36Sopenharmony_ci ret = rtl8366rb_drop_untagged(priv, port, false); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci return ret; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic int 132662306a36Sopenharmony_cirtl8366rb_port_pre_bridge_flags(struct dsa_switch *ds, int port, 132762306a36Sopenharmony_ci struct switchdev_brport_flags flags, 132862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci /* We support enabling/disabling learning */ 133162306a36Sopenharmony_ci if (flags.mask & ~(BR_LEARNING)) 133262306a36Sopenharmony_ci return -EINVAL; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci return 0; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic int 133862306a36Sopenharmony_cirtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, 133962306a36Sopenharmony_ci struct switchdev_brport_flags flags, 134062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 134362306a36Sopenharmony_ci int ret; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) { 134662306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, 134762306a36Sopenharmony_ci BIT(port), 134862306a36Sopenharmony_ci (flags.val & BR_LEARNING) ? 0 : BIT(port)); 134962306a36Sopenharmony_ci if (ret) 135062306a36Sopenharmony_ci return ret; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return 0; 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic void 135762306a36Sopenharmony_cirtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 136062306a36Sopenharmony_ci u32 val; 136162306a36Sopenharmony_ci int i; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci switch (state) { 136462306a36Sopenharmony_ci case BR_STATE_DISABLED: 136562306a36Sopenharmony_ci val = RTL8366RB_STP_STATE_DISABLED; 136662306a36Sopenharmony_ci break; 136762306a36Sopenharmony_ci case BR_STATE_BLOCKING: 136862306a36Sopenharmony_ci case BR_STATE_LISTENING: 136962306a36Sopenharmony_ci val = RTL8366RB_STP_STATE_BLOCKING; 137062306a36Sopenharmony_ci break; 137162306a36Sopenharmony_ci case BR_STATE_LEARNING: 137262306a36Sopenharmony_ci val = RTL8366RB_STP_STATE_LEARNING; 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci case BR_STATE_FORWARDING: 137562306a36Sopenharmony_ci val = RTL8366RB_STP_STATE_FORWARDING; 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci default: 137862306a36Sopenharmony_ci dev_err(priv->dev, "unknown bridge state requested\n"); 137962306a36Sopenharmony_ci return; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* Set the same status for the port on all the FIDs */ 138362306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_NUM_FIDS; i++) { 138462306a36Sopenharmony_ci regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i, 138562306a36Sopenharmony_ci RTL8366RB_STP_STATE_MASK(port), 138662306a36Sopenharmony_ci RTL8366RB_STP_STATE(port, val)); 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void 139162306a36Sopenharmony_cirtl8366rb_port_fast_age(struct dsa_switch *ds, int port) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* This will age out any learned L2 entries */ 139662306a36Sopenharmony_ci regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, 139762306a36Sopenharmony_ci BIT(port), BIT(port)); 139862306a36Sopenharmony_ci /* Restore the normal state of things */ 139962306a36Sopenharmony_ci regmap_update_bits(priv->map, RTL8366RB_SECURITY_CTRL, 140062306a36Sopenharmony_ci BIT(port), 0); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 140462306a36Sopenharmony_ci{ 140562306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 140662306a36Sopenharmony_ci struct rtl8366rb *rb; 140762306a36Sopenharmony_ci unsigned int max_mtu; 140862306a36Sopenharmony_ci u32 len; 140962306a36Sopenharmony_ci int i; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* Cache the per-port MTU setting */ 141262306a36Sopenharmony_ci rb = priv->chip_data; 141362306a36Sopenharmony_ci rb->max_mtu[port] = new_mtu; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* Roof out the MTU for the entire switch to the greatest 141662306a36Sopenharmony_ci * common denominator: the biggest set for any one port will 141762306a36Sopenharmony_ci * be the biggest MTU for the switch. 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * The first setting, 1522 bytes, is max IP packet 1500 bytes, 142062306a36Sopenharmony_ci * plus ethernet header, 1518 bytes, plus CPU tag, 4 bytes. 142162306a36Sopenharmony_ci * This function should consider the parameter an SDU, so the 142262306a36Sopenharmony_ci * MTU passed for this setting is 1518 bytes. The same logic 142362306a36Sopenharmony_ci * of subtracting the DSA tag of 4 bytes apply to the other 142462306a36Sopenharmony_ci * settings. 142562306a36Sopenharmony_ci */ 142662306a36Sopenharmony_ci max_mtu = 1518; 142762306a36Sopenharmony_ci for (i = 0; i < RTL8366RB_NUM_PORTS; i++) { 142862306a36Sopenharmony_ci if (rb->max_mtu[i] > max_mtu) 142962306a36Sopenharmony_ci max_mtu = rb->max_mtu[i]; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci if (max_mtu <= 1518) 143262306a36Sopenharmony_ci len = RTL8366RB_SGCR_MAX_LENGTH_1522; 143362306a36Sopenharmony_ci else if (max_mtu > 1518 && max_mtu <= 1532) 143462306a36Sopenharmony_ci len = RTL8366RB_SGCR_MAX_LENGTH_1536; 143562306a36Sopenharmony_ci else if (max_mtu > 1532 && max_mtu <= 1548) 143662306a36Sopenharmony_ci len = RTL8366RB_SGCR_MAX_LENGTH_1552; 143762306a36Sopenharmony_ci else 143862306a36Sopenharmony_ci len = RTL8366RB_SGCR_MAX_LENGTH_16000; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8366RB_SGCR, 144162306a36Sopenharmony_ci RTL8366RB_SGCR_MAX_LENGTH_MASK, 144262306a36Sopenharmony_ci len); 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_cistatic int rtl8366rb_max_mtu(struct dsa_switch *ds, int port) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci /* The max MTU is 16000 bytes, so we subtract the CPU tag 144862306a36Sopenharmony_ci * and the max presented to the system is 15996 bytes. 144962306a36Sopenharmony_ci */ 145062306a36Sopenharmony_ci return 15996; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic int rtl8366rb_get_vlan_4k(struct realtek_priv *priv, u32 vid, 145462306a36Sopenharmony_ci struct rtl8366_vlan_4k *vlan4k) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci u32 data[3]; 145762306a36Sopenharmony_ci int ret; 145862306a36Sopenharmony_ci int i; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k)); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (vid >= RTL8366RB_NUM_VIDS) 146362306a36Sopenharmony_ci return -EINVAL; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* write VID */ 146662306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_VLAN_TABLE_WRITE_BASE, 146762306a36Sopenharmony_ci vid & RTL8366RB_VLAN_VID_MASK); 146862306a36Sopenharmony_ci if (ret) 146962306a36Sopenharmony_ci return ret; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* write table access control word */ 147262306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, 147362306a36Sopenharmony_ci RTL8366RB_TABLE_VLAN_READ_CTRL); 147462306a36Sopenharmony_ci if (ret) 147562306a36Sopenharmony_ci return ret; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 147862306a36Sopenharmony_ci ret = regmap_read(priv->map, 147962306a36Sopenharmony_ci RTL8366RB_VLAN_TABLE_READ_BASE + i, 148062306a36Sopenharmony_ci &data[i]); 148162306a36Sopenharmony_ci if (ret) 148262306a36Sopenharmony_ci return ret; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci vlan4k->vid = vid; 148662306a36Sopenharmony_ci vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) & 148762306a36Sopenharmony_ci RTL8366RB_VLAN_UNTAG_MASK; 148862306a36Sopenharmony_ci vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK; 148962306a36Sopenharmony_ci vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int rtl8366rb_set_vlan_4k(struct realtek_priv *priv, 149562306a36Sopenharmony_ci const struct rtl8366_vlan_4k *vlan4k) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci u32 data[3]; 149862306a36Sopenharmony_ci int ret; 149962306a36Sopenharmony_ci int i; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (vlan4k->vid >= RTL8366RB_NUM_VIDS || 150262306a36Sopenharmony_ci vlan4k->member > RTL8366RB_VLAN_MEMBER_MASK || 150362306a36Sopenharmony_ci vlan4k->untag > RTL8366RB_VLAN_UNTAG_MASK || 150462306a36Sopenharmony_ci vlan4k->fid > RTL8366RB_FIDMAX) 150562306a36Sopenharmony_ci return -EINVAL; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK; 150862306a36Sopenharmony_ci data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) | 150962306a36Sopenharmony_ci ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) << 151062306a36Sopenharmony_ci RTL8366RB_VLAN_UNTAG_SHIFT); 151162306a36Sopenharmony_ci data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 151462306a36Sopenharmony_ci ret = regmap_write(priv->map, 151562306a36Sopenharmony_ci RTL8366RB_VLAN_TABLE_WRITE_BASE + i, 151662306a36Sopenharmony_ci data[i]); 151762306a36Sopenharmony_ci if (ret) 151862306a36Sopenharmony_ci return ret; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* write table access control word */ 152262306a36Sopenharmony_ci ret = regmap_write(priv->map, RTL8366RB_TABLE_ACCESS_CTRL_REG, 152362306a36Sopenharmony_ci RTL8366RB_TABLE_VLAN_WRITE_CTRL); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return ret; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int rtl8366rb_get_vlan_mc(struct realtek_priv *priv, u32 index, 152962306a36Sopenharmony_ci struct rtl8366_vlan_mc *vlanmc) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci u32 data[3]; 153262306a36Sopenharmony_ci int ret; 153362306a36Sopenharmony_ci int i; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc)); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (index >= RTL8366RB_NUM_VLANS) 153862306a36Sopenharmony_ci return -EINVAL; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 154162306a36Sopenharmony_ci ret = regmap_read(priv->map, 154262306a36Sopenharmony_ci RTL8366RB_VLAN_MC_BASE(index) + i, 154362306a36Sopenharmony_ci &data[i]); 154462306a36Sopenharmony_ci if (ret) 154562306a36Sopenharmony_ci return ret; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK; 154962306a36Sopenharmony_ci vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) & 155062306a36Sopenharmony_ci RTL8366RB_VLAN_PRIORITY_MASK; 155162306a36Sopenharmony_ci vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) & 155262306a36Sopenharmony_ci RTL8366RB_VLAN_UNTAG_MASK; 155362306a36Sopenharmony_ci vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK; 155462306a36Sopenharmony_ci vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int rtl8366rb_set_vlan_mc(struct realtek_priv *priv, u32 index, 156062306a36Sopenharmony_ci const struct rtl8366_vlan_mc *vlanmc) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci u32 data[3]; 156362306a36Sopenharmony_ci int ret; 156462306a36Sopenharmony_ci int i; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (index >= RTL8366RB_NUM_VLANS || 156762306a36Sopenharmony_ci vlanmc->vid >= RTL8366RB_NUM_VIDS || 156862306a36Sopenharmony_ci vlanmc->priority > RTL8366RB_PRIORITYMAX || 156962306a36Sopenharmony_ci vlanmc->member > RTL8366RB_VLAN_MEMBER_MASK || 157062306a36Sopenharmony_ci vlanmc->untag > RTL8366RB_VLAN_UNTAG_MASK || 157162306a36Sopenharmony_ci vlanmc->fid > RTL8366RB_FIDMAX) 157262306a36Sopenharmony_ci return -EINVAL; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) | 157562306a36Sopenharmony_ci ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) << 157662306a36Sopenharmony_ci RTL8366RB_VLAN_PRIORITY_SHIFT); 157762306a36Sopenharmony_ci data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) | 157862306a36Sopenharmony_ci ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) << 157962306a36Sopenharmony_ci RTL8366RB_VLAN_UNTAG_SHIFT); 158062306a36Sopenharmony_ci data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 158362306a36Sopenharmony_ci ret = regmap_write(priv->map, 158462306a36Sopenharmony_ci RTL8366RB_VLAN_MC_BASE(index) + i, 158562306a36Sopenharmony_ci data[i]); 158662306a36Sopenharmony_ci if (ret) 158762306a36Sopenharmony_ci return ret; 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci return 0; 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic int rtl8366rb_get_mc_index(struct realtek_priv *priv, int port, int *val) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci u32 data; 159662306a36Sopenharmony_ci int ret; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci if (port >= priv->num_ports) 159962306a36Sopenharmony_ci return -EINVAL; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), 160262306a36Sopenharmony_ci &data); 160362306a36Sopenharmony_ci if (ret) 160462306a36Sopenharmony_ci return ret; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) & 160762306a36Sopenharmony_ci RTL8366RB_PORT_VLAN_CTRL_MASK; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return 0; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct rtl8366rb *rb; 161562306a36Sopenharmony_ci bool pvid_enabled; 161662306a36Sopenharmony_ci int ret; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci rb = priv->chip_data; 161962306a36Sopenharmony_ci pvid_enabled = !!index; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci if (port >= priv->num_ports || index >= RTL8366RB_NUM_VLANS) 162262306a36Sopenharmony_ci return -EINVAL; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci ret = regmap_update_bits(priv->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), 162562306a36Sopenharmony_ci RTL8366RB_PORT_VLAN_CTRL_MASK << 162662306a36Sopenharmony_ci RTL8366RB_PORT_VLAN_CTRL_SHIFT(port), 162762306a36Sopenharmony_ci (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << 162862306a36Sopenharmony_ci RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)); 162962306a36Sopenharmony_ci if (ret) 163062306a36Sopenharmony_ci return ret; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci rb->pvid_enabled[port] = pvid_enabled; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* If VLAN filtering is enabled and PVID is also enabled, we must 163562306a36Sopenharmony_ci * not drop any untagged or C-tagged frames. Make sure to update the 163662306a36Sopenharmony_ci * filtering setting. 163762306a36Sopenharmony_ci */ 163862306a36Sopenharmony_ci if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) 163962306a36Sopenharmony_ci ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci return ret; 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic bool rtl8366rb_is_vlan_valid(struct realtek_priv *priv, unsigned int vlan) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci unsigned int max = RTL8366RB_NUM_VLANS - 1; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (priv->vlan4k_enabled) 164962306a36Sopenharmony_ci max = RTL8366RB_NUM_VIDS - 1; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (vlan > max) 165262306a36Sopenharmony_ci return false; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci return true; 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic int rtl8366rb_enable_vlan(struct realtek_priv *priv, bool enable) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci dev_dbg(priv->dev, "%s VLAN\n", enable ? "enable" : "disable"); 166062306a36Sopenharmony_ci return regmap_update_bits(priv->map, 166162306a36Sopenharmony_ci RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN, 166262306a36Sopenharmony_ci enable ? RTL8366RB_SGCR_EN_VLAN : 0); 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cistatic int rtl8366rb_enable_vlan4k(struct realtek_priv *priv, bool enable) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci dev_dbg(priv->dev, "%s VLAN 4k\n", enable ? "enable" : "disable"); 166862306a36Sopenharmony_ci return regmap_update_bits(priv->map, RTL8366RB_SGCR, 166962306a36Sopenharmony_ci RTL8366RB_SGCR_EN_VLAN_4KTB, 167062306a36Sopenharmony_ci enable ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0); 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci u32 val; 167662306a36Sopenharmony_ci u32 reg; 167762306a36Sopenharmony_ci int ret; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci if (phy > RTL8366RB_PHY_NO_MAX) 168062306a36Sopenharmony_ci return -EINVAL; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci mutex_lock(&priv->map_lock); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, 168562306a36Sopenharmony_ci RTL8366RB_PHY_CTRL_READ); 168662306a36Sopenharmony_ci if (ret) 168762306a36Sopenharmony_ci goto out; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, reg, 0); 169262306a36Sopenharmony_ci if (ret) { 169362306a36Sopenharmony_ci dev_err(priv->dev, 169462306a36Sopenharmony_ci "failed to write PHY%d reg %04x @ %04x, ret %d\n", 169562306a36Sopenharmony_ci phy, regnum, reg, ret); 169662306a36Sopenharmony_ci goto out; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci ret = regmap_read(priv->map_nolock, RTL8366RB_PHY_ACCESS_DATA_REG, 170062306a36Sopenharmony_ci &val); 170162306a36Sopenharmony_ci if (ret) 170262306a36Sopenharmony_ci goto out; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci ret = val; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", 170762306a36Sopenharmony_ci phy, regnum, reg, val); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ciout: 171062306a36Sopenharmony_ci mutex_unlock(&priv->map_lock); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci return ret; 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, 171662306a36Sopenharmony_ci u16 val) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci u32 reg; 171962306a36Sopenharmony_ci int ret; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (phy > RTL8366RB_PHY_NO_MAX) 172262306a36Sopenharmony_ci return -EINVAL; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci mutex_lock(&priv->map_lock); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, 172762306a36Sopenharmony_ci RTL8366RB_PHY_CTRL_WRITE); 172862306a36Sopenharmony_ci if (ret) 172962306a36Sopenharmony_ci goto out; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", 173462306a36Sopenharmony_ci phy, regnum, reg, val); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci ret = regmap_write(priv->map_nolock, reg, val); 173762306a36Sopenharmony_ci if (ret) 173862306a36Sopenharmony_ci goto out; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ciout: 174162306a36Sopenharmony_ci mutex_unlock(&priv->map_lock); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci return ret; 174462306a36Sopenharmony_ci} 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) 174762306a36Sopenharmony_ci{ 174862306a36Sopenharmony_ci return rtl8366rb_phy_read(ds->priv, phy, regnum); 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, 175262306a36Sopenharmony_ci u16 val) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci return rtl8366rb_phy_write(ds->priv, phy, regnum, val); 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cistatic int rtl8366rb_reset_chip(struct realtek_priv *priv) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci int timeout = 10; 176062306a36Sopenharmony_ci u32 val; 176162306a36Sopenharmony_ci int ret; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG, 176462306a36Sopenharmony_ci RTL8366RB_CHIP_CTRL_RESET_HW); 176562306a36Sopenharmony_ci do { 176662306a36Sopenharmony_ci usleep_range(20000, 25000); 176762306a36Sopenharmony_ci ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val); 176862306a36Sopenharmony_ci if (ret) 176962306a36Sopenharmony_ci return ret; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (!(val & RTL8366RB_CHIP_CTRL_RESET_HW)) 177262306a36Sopenharmony_ci break; 177362306a36Sopenharmony_ci } while (--timeout); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (!timeout) { 177662306a36Sopenharmony_ci dev_err(priv->dev, "timeout waiting for the switch to reset\n"); 177762306a36Sopenharmony_ci return -EIO; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci return 0; 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_cistatic int rtl8366rb_detect(struct realtek_priv *priv) 178462306a36Sopenharmony_ci{ 178562306a36Sopenharmony_ci struct device *dev = priv->dev; 178662306a36Sopenharmony_ci int ret; 178762306a36Sopenharmony_ci u32 val; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci /* Detect device */ 179062306a36Sopenharmony_ci ret = regmap_read(priv->map, 0x5c, &val); 179162306a36Sopenharmony_ci if (ret) { 179262306a36Sopenharmony_ci dev_err(dev, "can't get chip ID (%d)\n", ret); 179362306a36Sopenharmony_ci return ret; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci switch (val) { 179762306a36Sopenharmony_ci case 0x6027: 179862306a36Sopenharmony_ci dev_info(dev, "found an RTL8366S switch\n"); 179962306a36Sopenharmony_ci dev_err(dev, "this switch is not yet supported, submit patches!\n"); 180062306a36Sopenharmony_ci return -ENODEV; 180162306a36Sopenharmony_ci case 0x5937: 180262306a36Sopenharmony_ci dev_info(dev, "found an RTL8366RB switch\n"); 180362306a36Sopenharmony_ci priv->cpu_port = RTL8366RB_PORT_NUM_CPU; 180462306a36Sopenharmony_ci priv->num_ports = RTL8366RB_NUM_PORTS; 180562306a36Sopenharmony_ci priv->num_vlan_mc = RTL8366RB_NUM_VLANS; 180662306a36Sopenharmony_ci priv->mib_counters = rtl8366rb_mib_counters; 180762306a36Sopenharmony_ci priv->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters); 180862306a36Sopenharmony_ci break; 180962306a36Sopenharmony_ci default: 181062306a36Sopenharmony_ci dev_info(dev, "found an Unknown Realtek switch (id=0x%04x)\n", 181162306a36Sopenharmony_ci val); 181262306a36Sopenharmony_ci break; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci ret = rtl8366rb_reset_chip(priv); 181662306a36Sopenharmony_ci if (ret) 181762306a36Sopenharmony_ci return ret; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci return 0; 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { 182362306a36Sopenharmony_ci .get_tag_protocol = rtl8366_get_tag_protocol, 182462306a36Sopenharmony_ci .setup = rtl8366rb_setup, 182562306a36Sopenharmony_ci .phylink_get_caps = rtl8366rb_phylink_get_caps, 182662306a36Sopenharmony_ci .phylink_mac_link_up = rtl8366rb_mac_link_up, 182762306a36Sopenharmony_ci .phylink_mac_link_down = rtl8366rb_mac_link_down, 182862306a36Sopenharmony_ci .get_strings = rtl8366_get_strings, 182962306a36Sopenharmony_ci .get_ethtool_stats = rtl8366_get_ethtool_stats, 183062306a36Sopenharmony_ci .get_sset_count = rtl8366_get_sset_count, 183162306a36Sopenharmony_ci .port_bridge_join = rtl8366rb_port_bridge_join, 183262306a36Sopenharmony_ci .port_bridge_leave = rtl8366rb_port_bridge_leave, 183362306a36Sopenharmony_ci .port_vlan_filtering = rtl8366rb_vlan_filtering, 183462306a36Sopenharmony_ci .port_vlan_add = rtl8366_vlan_add, 183562306a36Sopenharmony_ci .port_vlan_del = rtl8366_vlan_del, 183662306a36Sopenharmony_ci .port_enable = rtl8366rb_port_enable, 183762306a36Sopenharmony_ci .port_disable = rtl8366rb_port_disable, 183862306a36Sopenharmony_ci .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, 183962306a36Sopenharmony_ci .port_bridge_flags = rtl8366rb_port_bridge_flags, 184062306a36Sopenharmony_ci .port_stp_state_set = rtl8366rb_port_stp_state_set, 184162306a36Sopenharmony_ci .port_fast_age = rtl8366rb_port_fast_age, 184262306a36Sopenharmony_ci .port_change_mtu = rtl8366rb_change_mtu, 184362306a36Sopenharmony_ci .port_max_mtu = rtl8366rb_max_mtu, 184462306a36Sopenharmony_ci}; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { 184762306a36Sopenharmony_ci .get_tag_protocol = rtl8366_get_tag_protocol, 184862306a36Sopenharmony_ci .setup = rtl8366rb_setup, 184962306a36Sopenharmony_ci .phy_read = rtl8366rb_dsa_phy_read, 185062306a36Sopenharmony_ci .phy_write = rtl8366rb_dsa_phy_write, 185162306a36Sopenharmony_ci .phylink_get_caps = rtl8366rb_phylink_get_caps, 185262306a36Sopenharmony_ci .phylink_mac_link_up = rtl8366rb_mac_link_up, 185362306a36Sopenharmony_ci .phylink_mac_link_down = rtl8366rb_mac_link_down, 185462306a36Sopenharmony_ci .get_strings = rtl8366_get_strings, 185562306a36Sopenharmony_ci .get_ethtool_stats = rtl8366_get_ethtool_stats, 185662306a36Sopenharmony_ci .get_sset_count = rtl8366_get_sset_count, 185762306a36Sopenharmony_ci .port_bridge_join = rtl8366rb_port_bridge_join, 185862306a36Sopenharmony_ci .port_bridge_leave = rtl8366rb_port_bridge_leave, 185962306a36Sopenharmony_ci .port_vlan_filtering = rtl8366rb_vlan_filtering, 186062306a36Sopenharmony_ci .port_vlan_add = rtl8366_vlan_add, 186162306a36Sopenharmony_ci .port_vlan_del = rtl8366_vlan_del, 186262306a36Sopenharmony_ci .port_enable = rtl8366rb_port_enable, 186362306a36Sopenharmony_ci .port_disable = rtl8366rb_port_disable, 186462306a36Sopenharmony_ci .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, 186562306a36Sopenharmony_ci .port_bridge_flags = rtl8366rb_port_bridge_flags, 186662306a36Sopenharmony_ci .port_stp_state_set = rtl8366rb_port_stp_state_set, 186762306a36Sopenharmony_ci .port_fast_age = rtl8366rb_port_fast_age, 186862306a36Sopenharmony_ci .port_change_mtu = rtl8366rb_change_mtu, 186962306a36Sopenharmony_ci .port_max_mtu = rtl8366rb_max_mtu, 187062306a36Sopenharmony_ci}; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic const struct realtek_ops rtl8366rb_ops = { 187362306a36Sopenharmony_ci .detect = rtl8366rb_detect, 187462306a36Sopenharmony_ci .get_vlan_mc = rtl8366rb_get_vlan_mc, 187562306a36Sopenharmony_ci .set_vlan_mc = rtl8366rb_set_vlan_mc, 187662306a36Sopenharmony_ci .get_vlan_4k = rtl8366rb_get_vlan_4k, 187762306a36Sopenharmony_ci .set_vlan_4k = rtl8366rb_set_vlan_4k, 187862306a36Sopenharmony_ci .get_mc_index = rtl8366rb_get_mc_index, 187962306a36Sopenharmony_ci .set_mc_index = rtl8366rb_set_mc_index, 188062306a36Sopenharmony_ci .get_mib_counter = rtl8366rb_get_mib_counter, 188162306a36Sopenharmony_ci .is_vlan_valid = rtl8366rb_is_vlan_valid, 188262306a36Sopenharmony_ci .enable_vlan = rtl8366rb_enable_vlan, 188362306a36Sopenharmony_ci .enable_vlan4k = rtl8366rb_enable_vlan4k, 188462306a36Sopenharmony_ci .phy_read = rtl8366rb_phy_read, 188562306a36Sopenharmony_ci .phy_write = rtl8366rb_phy_write, 188662306a36Sopenharmony_ci}; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ciconst struct realtek_variant rtl8366rb_variant = { 188962306a36Sopenharmony_ci .ds_ops_smi = &rtl8366rb_switch_ops_smi, 189062306a36Sopenharmony_ci .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, 189162306a36Sopenharmony_ci .ops = &rtl8366rb_ops, 189262306a36Sopenharmony_ci .clk_delay = 10, 189362306a36Sopenharmony_ci .cmd_read = 0xa9, 189462306a36Sopenharmony_ci .cmd_write = 0xa8, 189562306a36Sopenharmony_ci .chip_data_sz = sizeof(struct rtl8366rb), 189662306a36Sopenharmony_ci}; 189762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366rb_variant); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 190062306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); 190162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1902