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