162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/ethernet/micrel/ksx884x.c - Micrel KSZ8841/2 PCI Ethernet driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2009-2010 Micrel, Inc.
662306a36Sopenharmony_ci * 	Tristram Ha <Tristram.Ha@micrel.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/ioport.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/proc_fs.h>
1862306a36Sopenharmony_ci#include <linux/mii.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/ethtool.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci#include <linux/in.h>
2362306a36Sopenharmony_ci#include <linux/ip.h>
2462306a36Sopenharmony_ci#include <linux/if_vlan.h>
2562306a36Sopenharmony_ci#include <linux/crc32.h>
2662306a36Sopenharmony_ci#include <linux/sched.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/micrel_phy.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* DMA Registers */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define KS_DMA_TX_CTRL			0x0000
3462306a36Sopenharmony_ci#define DMA_TX_ENABLE			0x00000001
3562306a36Sopenharmony_ci#define DMA_TX_CRC_ENABLE		0x00000002
3662306a36Sopenharmony_ci#define DMA_TX_PAD_ENABLE		0x00000004
3762306a36Sopenharmony_ci#define DMA_TX_LOOPBACK			0x00000100
3862306a36Sopenharmony_ci#define DMA_TX_FLOW_ENABLE		0x00000200
3962306a36Sopenharmony_ci#define DMA_TX_CSUM_IP			0x00010000
4062306a36Sopenharmony_ci#define DMA_TX_CSUM_TCP			0x00020000
4162306a36Sopenharmony_ci#define DMA_TX_CSUM_UDP			0x00040000
4262306a36Sopenharmony_ci#define DMA_TX_BURST_SIZE		0x3F000000
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define KS_DMA_RX_CTRL			0x0004
4562306a36Sopenharmony_ci#define DMA_RX_ENABLE			0x00000001
4662306a36Sopenharmony_ci#define KS884X_DMA_RX_MULTICAST		0x00000002
4762306a36Sopenharmony_ci#define DMA_RX_PROMISCUOUS		0x00000004
4862306a36Sopenharmony_ci#define DMA_RX_ERROR			0x00000008
4962306a36Sopenharmony_ci#define DMA_RX_UNICAST			0x00000010
5062306a36Sopenharmony_ci#define DMA_RX_ALL_MULTICAST		0x00000020
5162306a36Sopenharmony_ci#define DMA_RX_BROADCAST		0x00000040
5262306a36Sopenharmony_ci#define DMA_RX_FLOW_ENABLE		0x00000200
5362306a36Sopenharmony_ci#define DMA_RX_CSUM_IP			0x00010000
5462306a36Sopenharmony_ci#define DMA_RX_CSUM_TCP			0x00020000
5562306a36Sopenharmony_ci#define DMA_RX_CSUM_UDP			0x00040000
5662306a36Sopenharmony_ci#define DMA_RX_BURST_SIZE		0x3F000000
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define DMA_BURST_SHIFT			24
5962306a36Sopenharmony_ci#define DMA_BURST_DEFAULT		8
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define KS_DMA_TX_START			0x0008
6262306a36Sopenharmony_ci#define KS_DMA_RX_START			0x000C
6362306a36Sopenharmony_ci#define DMA_START			0x00000001
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define KS_DMA_TX_ADDR			0x0010
6662306a36Sopenharmony_ci#define KS_DMA_RX_ADDR			0x0014
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define DMA_ADDR_LIST_MASK		0xFFFFFFFC
6962306a36Sopenharmony_ci#define DMA_ADDR_LIST_SHIFT		2
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* MTR0 */
7262306a36Sopenharmony_ci#define KS884X_MULTICAST_0_OFFSET	0x0020
7362306a36Sopenharmony_ci#define KS884X_MULTICAST_1_OFFSET	0x0021
7462306a36Sopenharmony_ci#define KS884X_MULTICAST_2_OFFSET	0x0022
7562306a36Sopenharmony_ci#define KS884x_MULTICAST_3_OFFSET	0x0023
7662306a36Sopenharmony_ci/* MTR1 */
7762306a36Sopenharmony_ci#define KS884X_MULTICAST_4_OFFSET	0x0024
7862306a36Sopenharmony_ci#define KS884X_MULTICAST_5_OFFSET	0x0025
7962306a36Sopenharmony_ci#define KS884X_MULTICAST_6_OFFSET	0x0026
8062306a36Sopenharmony_ci#define KS884X_MULTICAST_7_OFFSET	0x0027
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Interrupt Registers */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* INTEN */
8562306a36Sopenharmony_ci#define KS884X_INTERRUPTS_ENABLE	0x0028
8662306a36Sopenharmony_ci/* INTST */
8762306a36Sopenharmony_ci#define KS884X_INTERRUPTS_STATUS	0x002C
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define KS884X_INT_RX_STOPPED		0x02000000
9062306a36Sopenharmony_ci#define KS884X_INT_TX_STOPPED		0x04000000
9162306a36Sopenharmony_ci#define KS884X_INT_RX_OVERRUN		0x08000000
9262306a36Sopenharmony_ci#define KS884X_INT_TX_EMPTY		0x10000000
9362306a36Sopenharmony_ci#define KS884X_INT_RX			0x20000000
9462306a36Sopenharmony_ci#define KS884X_INT_TX			0x40000000
9562306a36Sopenharmony_ci#define KS884X_INT_PHY			0x80000000
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define KS884X_INT_RX_MASK		\
9862306a36Sopenharmony_ci	(KS884X_INT_RX | KS884X_INT_RX_OVERRUN)
9962306a36Sopenharmony_ci#define KS884X_INT_TX_MASK		\
10062306a36Sopenharmony_ci	(KS884X_INT_TX | KS884X_INT_TX_EMPTY)
10162306a36Sopenharmony_ci#define KS884X_INT_MASK	(KS884X_INT_RX | KS884X_INT_TX | KS884X_INT_PHY)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* MAC Additional Station Address */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* MAAL0 */
10662306a36Sopenharmony_ci#define KS_ADD_ADDR_0_LO		0x0080
10762306a36Sopenharmony_ci/* MAAH0 */
10862306a36Sopenharmony_ci#define KS_ADD_ADDR_0_HI		0x0084
10962306a36Sopenharmony_ci/* MAAL1 */
11062306a36Sopenharmony_ci#define KS_ADD_ADDR_1_LO		0x0088
11162306a36Sopenharmony_ci/* MAAH1 */
11262306a36Sopenharmony_ci#define KS_ADD_ADDR_1_HI		0x008C
11362306a36Sopenharmony_ci/* MAAL2 */
11462306a36Sopenharmony_ci#define KS_ADD_ADDR_2_LO		0x0090
11562306a36Sopenharmony_ci/* MAAH2 */
11662306a36Sopenharmony_ci#define KS_ADD_ADDR_2_HI		0x0094
11762306a36Sopenharmony_ci/* MAAL3 */
11862306a36Sopenharmony_ci#define KS_ADD_ADDR_3_LO		0x0098
11962306a36Sopenharmony_ci/* MAAH3 */
12062306a36Sopenharmony_ci#define KS_ADD_ADDR_3_HI		0x009C
12162306a36Sopenharmony_ci/* MAAL4 */
12262306a36Sopenharmony_ci#define KS_ADD_ADDR_4_LO		0x00A0
12362306a36Sopenharmony_ci/* MAAH4 */
12462306a36Sopenharmony_ci#define KS_ADD_ADDR_4_HI		0x00A4
12562306a36Sopenharmony_ci/* MAAL5 */
12662306a36Sopenharmony_ci#define KS_ADD_ADDR_5_LO		0x00A8
12762306a36Sopenharmony_ci/* MAAH5 */
12862306a36Sopenharmony_ci#define KS_ADD_ADDR_5_HI		0x00AC
12962306a36Sopenharmony_ci/* MAAL6 */
13062306a36Sopenharmony_ci#define KS_ADD_ADDR_6_LO		0x00B0
13162306a36Sopenharmony_ci/* MAAH6 */
13262306a36Sopenharmony_ci#define KS_ADD_ADDR_6_HI		0x00B4
13362306a36Sopenharmony_ci/* MAAL7 */
13462306a36Sopenharmony_ci#define KS_ADD_ADDR_7_LO		0x00B8
13562306a36Sopenharmony_ci/* MAAH7 */
13662306a36Sopenharmony_ci#define KS_ADD_ADDR_7_HI		0x00BC
13762306a36Sopenharmony_ci/* MAAL8 */
13862306a36Sopenharmony_ci#define KS_ADD_ADDR_8_LO		0x00C0
13962306a36Sopenharmony_ci/* MAAH8 */
14062306a36Sopenharmony_ci#define KS_ADD_ADDR_8_HI		0x00C4
14162306a36Sopenharmony_ci/* MAAL9 */
14262306a36Sopenharmony_ci#define KS_ADD_ADDR_9_LO		0x00C8
14362306a36Sopenharmony_ci/* MAAH9 */
14462306a36Sopenharmony_ci#define KS_ADD_ADDR_9_HI		0x00CC
14562306a36Sopenharmony_ci/* MAAL10 */
14662306a36Sopenharmony_ci#define KS_ADD_ADDR_A_LO		0x00D0
14762306a36Sopenharmony_ci/* MAAH10 */
14862306a36Sopenharmony_ci#define KS_ADD_ADDR_A_HI		0x00D4
14962306a36Sopenharmony_ci/* MAAL11 */
15062306a36Sopenharmony_ci#define KS_ADD_ADDR_B_LO		0x00D8
15162306a36Sopenharmony_ci/* MAAH11 */
15262306a36Sopenharmony_ci#define KS_ADD_ADDR_B_HI		0x00DC
15362306a36Sopenharmony_ci/* MAAL12 */
15462306a36Sopenharmony_ci#define KS_ADD_ADDR_C_LO		0x00E0
15562306a36Sopenharmony_ci/* MAAH12 */
15662306a36Sopenharmony_ci#define KS_ADD_ADDR_C_HI		0x00E4
15762306a36Sopenharmony_ci/* MAAL13 */
15862306a36Sopenharmony_ci#define KS_ADD_ADDR_D_LO		0x00E8
15962306a36Sopenharmony_ci/* MAAH13 */
16062306a36Sopenharmony_ci#define KS_ADD_ADDR_D_HI		0x00EC
16162306a36Sopenharmony_ci/* MAAL14 */
16262306a36Sopenharmony_ci#define KS_ADD_ADDR_E_LO		0x00F0
16362306a36Sopenharmony_ci/* MAAH14 */
16462306a36Sopenharmony_ci#define KS_ADD_ADDR_E_HI		0x00F4
16562306a36Sopenharmony_ci/* MAAL15 */
16662306a36Sopenharmony_ci#define KS_ADD_ADDR_F_LO		0x00F8
16762306a36Sopenharmony_ci/* MAAH15 */
16862306a36Sopenharmony_ci#define KS_ADD_ADDR_F_HI		0x00FC
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci#define ADD_ADDR_HI_MASK		0x0000FFFF
17162306a36Sopenharmony_ci#define ADD_ADDR_ENABLE			0x80000000
17262306a36Sopenharmony_ci#define ADD_ADDR_INCR			8
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Miscellaneous Registers */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/* MARL */
17762306a36Sopenharmony_ci#define KS884X_ADDR_0_OFFSET		0x0200
17862306a36Sopenharmony_ci#define KS884X_ADDR_1_OFFSET		0x0201
17962306a36Sopenharmony_ci/* MARM */
18062306a36Sopenharmony_ci#define KS884X_ADDR_2_OFFSET		0x0202
18162306a36Sopenharmony_ci#define KS884X_ADDR_3_OFFSET		0x0203
18262306a36Sopenharmony_ci/* MARH */
18362306a36Sopenharmony_ci#define KS884X_ADDR_4_OFFSET		0x0204
18462306a36Sopenharmony_ci#define KS884X_ADDR_5_OFFSET		0x0205
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/* OBCR */
18762306a36Sopenharmony_ci#define KS884X_BUS_CTRL_OFFSET		0x0210
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#define BUS_SPEED_125_MHZ		0x0000
19062306a36Sopenharmony_ci#define BUS_SPEED_62_5_MHZ		0x0001
19162306a36Sopenharmony_ci#define BUS_SPEED_41_66_MHZ		0x0002
19262306a36Sopenharmony_ci#define BUS_SPEED_25_MHZ		0x0003
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* EEPCR */
19562306a36Sopenharmony_ci#define KS884X_EEPROM_CTRL_OFFSET	0x0212
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#define EEPROM_CHIP_SELECT		0x0001
19862306a36Sopenharmony_ci#define EEPROM_SERIAL_CLOCK		0x0002
19962306a36Sopenharmony_ci#define EEPROM_DATA_OUT			0x0004
20062306a36Sopenharmony_ci#define EEPROM_DATA_IN			0x0008
20162306a36Sopenharmony_ci#define EEPROM_ACCESS_ENABLE		0x0010
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* MBIR */
20462306a36Sopenharmony_ci#define KS884X_MEM_INFO_OFFSET		0x0214
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#define RX_MEM_TEST_FAILED		0x0008
20762306a36Sopenharmony_ci#define RX_MEM_TEST_FINISHED		0x0010
20862306a36Sopenharmony_ci#define TX_MEM_TEST_FAILED		0x0800
20962306a36Sopenharmony_ci#define TX_MEM_TEST_FINISHED		0x1000
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/* GCR */
21262306a36Sopenharmony_ci#define KS884X_GLOBAL_CTRL_OFFSET	0x0216
21362306a36Sopenharmony_ci#define GLOBAL_SOFTWARE_RESET		0x0001
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci#define KS8841_POWER_MANAGE_OFFSET	0x0218
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/* WFCR */
21862306a36Sopenharmony_ci#define KS8841_WOL_CTRL_OFFSET		0x021A
21962306a36Sopenharmony_ci#define KS8841_WOL_MAGIC_ENABLE		0x0080
22062306a36Sopenharmony_ci#define KS8841_WOL_FRAME3_ENABLE	0x0008
22162306a36Sopenharmony_ci#define KS8841_WOL_FRAME2_ENABLE	0x0004
22262306a36Sopenharmony_ci#define KS8841_WOL_FRAME1_ENABLE	0x0002
22362306a36Sopenharmony_ci#define KS8841_WOL_FRAME0_ENABLE	0x0001
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/* WF0 */
22662306a36Sopenharmony_ci#define KS8841_WOL_FRAME_CRC_OFFSET	0x0220
22762306a36Sopenharmony_ci#define KS8841_WOL_FRAME_BYTE0_OFFSET	0x0224
22862306a36Sopenharmony_ci#define KS8841_WOL_FRAME_BYTE2_OFFSET	0x0228
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/* IACR */
23162306a36Sopenharmony_ci#define KS884X_IACR_P			0x04A0
23262306a36Sopenharmony_ci#define KS884X_IACR_OFFSET		KS884X_IACR_P
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/* IADR1 */
23562306a36Sopenharmony_ci#define KS884X_IADR1_P			0x04A2
23662306a36Sopenharmony_ci#define KS884X_IADR2_P			0x04A4
23762306a36Sopenharmony_ci#define KS884X_IADR3_P			0x04A6
23862306a36Sopenharmony_ci#define KS884X_IADR4_P			0x04A8
23962306a36Sopenharmony_ci#define KS884X_IADR5_P			0x04AA
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci#define KS884X_ACC_CTRL_SEL_OFFSET	KS884X_IACR_P
24262306a36Sopenharmony_ci#define KS884X_ACC_CTRL_INDEX_OFFSET	(KS884X_ACC_CTRL_SEL_OFFSET + 1)
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#define KS884X_ACC_DATA_0_OFFSET	KS884X_IADR4_P
24562306a36Sopenharmony_ci#define KS884X_ACC_DATA_1_OFFSET	(KS884X_ACC_DATA_0_OFFSET + 1)
24662306a36Sopenharmony_ci#define KS884X_ACC_DATA_2_OFFSET	KS884X_IADR5_P
24762306a36Sopenharmony_ci#define KS884X_ACC_DATA_3_OFFSET	(KS884X_ACC_DATA_2_OFFSET + 1)
24862306a36Sopenharmony_ci#define KS884X_ACC_DATA_4_OFFSET	KS884X_IADR2_P
24962306a36Sopenharmony_ci#define KS884X_ACC_DATA_5_OFFSET	(KS884X_ACC_DATA_4_OFFSET + 1)
25062306a36Sopenharmony_ci#define KS884X_ACC_DATA_6_OFFSET	KS884X_IADR3_P
25162306a36Sopenharmony_ci#define KS884X_ACC_DATA_7_OFFSET	(KS884X_ACC_DATA_6_OFFSET + 1)
25262306a36Sopenharmony_ci#define KS884X_ACC_DATA_8_OFFSET	KS884X_IADR1_P
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* P1MBCR */
25562306a36Sopenharmony_ci#define KS884X_P1MBCR_P			0x04D0
25662306a36Sopenharmony_ci#define KS884X_P1MBSR_P			0x04D2
25762306a36Sopenharmony_ci#define KS884X_PHY1ILR_P		0x04D4
25862306a36Sopenharmony_ci#define KS884X_PHY1IHR_P		0x04D6
25962306a36Sopenharmony_ci#define KS884X_P1ANAR_P			0x04D8
26062306a36Sopenharmony_ci#define KS884X_P1ANLPR_P		0x04DA
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/* P2MBCR */
26362306a36Sopenharmony_ci#define KS884X_P2MBCR_P			0x04E0
26462306a36Sopenharmony_ci#define KS884X_P2MBSR_P			0x04E2
26562306a36Sopenharmony_ci#define KS884X_PHY2ILR_P		0x04E4
26662306a36Sopenharmony_ci#define KS884X_PHY2IHR_P		0x04E6
26762306a36Sopenharmony_ci#define KS884X_P2ANAR_P			0x04E8
26862306a36Sopenharmony_ci#define KS884X_P2ANLPR_P		0x04EA
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci#define KS884X_PHY_1_CTRL_OFFSET	KS884X_P1MBCR_P
27162306a36Sopenharmony_ci#define PHY_CTRL_INTERVAL		(KS884X_P2MBCR_P - KS884X_P1MBCR_P)
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#define KS884X_PHY_CTRL_OFFSET		0x00
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#define KS884X_PHY_STATUS_OFFSET	0x02
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci#define KS884X_PHY_ID_1_OFFSET		0x04
27862306a36Sopenharmony_ci#define KS884X_PHY_ID_2_OFFSET		0x06
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci#define KS884X_PHY_AUTO_NEG_OFFSET	0x08
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci#define KS884X_PHY_REMOTE_CAP_OFFSET	0x0A
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci/* P1VCT */
28562306a36Sopenharmony_ci#define KS884X_P1VCT_P			0x04F0
28662306a36Sopenharmony_ci#define KS884X_P1PHYCTRL_P		0x04F2
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/* P2VCT */
28962306a36Sopenharmony_ci#define KS884X_P2VCT_P			0x04F4
29062306a36Sopenharmony_ci#define KS884X_P2PHYCTRL_P		0x04F6
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#define KS884X_PHY_SPECIAL_OFFSET	KS884X_P1VCT_P
29362306a36Sopenharmony_ci#define PHY_SPECIAL_INTERVAL		(KS884X_P2VCT_P - KS884X_P1VCT_P)
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci#define KS884X_PHY_LINK_MD_OFFSET	0x00
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci#define PHY_START_CABLE_DIAG		0x8000
29862306a36Sopenharmony_ci#define PHY_CABLE_DIAG_RESULT		0x6000
29962306a36Sopenharmony_ci#define PHY_CABLE_STAT_NORMAL		0x0000
30062306a36Sopenharmony_ci#define PHY_CABLE_STAT_OPEN		0x2000
30162306a36Sopenharmony_ci#define PHY_CABLE_STAT_SHORT		0x4000
30262306a36Sopenharmony_ci#define PHY_CABLE_STAT_FAILED		0x6000
30362306a36Sopenharmony_ci#define PHY_CABLE_10M_SHORT		0x1000
30462306a36Sopenharmony_ci#define PHY_CABLE_FAULT_COUNTER		0x01FF
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#define KS884X_PHY_PHY_CTRL_OFFSET	0x02
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci#define PHY_STAT_REVERSED_POLARITY	0x0020
30962306a36Sopenharmony_ci#define PHY_STAT_MDIX			0x0010
31062306a36Sopenharmony_ci#define PHY_FORCE_LINK			0x0008
31162306a36Sopenharmony_ci#define PHY_POWER_SAVING_DISABLE	0x0004
31262306a36Sopenharmony_ci#define PHY_REMOTE_LOOPBACK		0x0002
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/* SIDER */
31562306a36Sopenharmony_ci#define KS884X_SIDER_P			0x0400
31662306a36Sopenharmony_ci#define KS884X_CHIP_ID_OFFSET		KS884X_SIDER_P
31762306a36Sopenharmony_ci#define KS884X_FAMILY_ID_OFFSET		(KS884X_CHIP_ID_OFFSET + 1)
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci#define REG_FAMILY_ID			0x88
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci#define REG_CHIP_ID_41			0x8810
32262306a36Sopenharmony_ci#define REG_CHIP_ID_42			0x8800
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#define KS884X_CHIP_ID_MASK_41		0xFF10
32562306a36Sopenharmony_ci#define KS884X_CHIP_ID_MASK		0xFFF0
32662306a36Sopenharmony_ci#define KS884X_CHIP_ID_SHIFT		4
32762306a36Sopenharmony_ci#define KS884X_REVISION_MASK		0x000E
32862306a36Sopenharmony_ci#define KS884X_REVISION_SHIFT		1
32962306a36Sopenharmony_ci#define KS8842_START			0x0001
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci#define CHIP_IP_41_M			0x8810
33262306a36Sopenharmony_ci#define CHIP_IP_42_M			0x8800
33362306a36Sopenharmony_ci#define CHIP_IP_61_M			0x8890
33462306a36Sopenharmony_ci#define CHIP_IP_62_M			0x8880
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci#define CHIP_IP_41_P			0x8850
33762306a36Sopenharmony_ci#define CHIP_IP_42_P			0x8840
33862306a36Sopenharmony_ci#define CHIP_IP_61_P			0x88D0
33962306a36Sopenharmony_ci#define CHIP_IP_62_P			0x88C0
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/* SGCR1 */
34262306a36Sopenharmony_ci#define KS8842_SGCR1_P			0x0402
34362306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_1_OFFSET	KS8842_SGCR1_P
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci#define SWITCH_PASS_ALL			0x8000
34662306a36Sopenharmony_ci#define SWITCH_TX_FLOW_CTRL		0x2000
34762306a36Sopenharmony_ci#define SWITCH_RX_FLOW_CTRL		0x1000
34862306a36Sopenharmony_ci#define SWITCH_CHECK_LENGTH		0x0800
34962306a36Sopenharmony_ci#define SWITCH_AGING_ENABLE		0x0400
35062306a36Sopenharmony_ci#define SWITCH_FAST_AGING		0x0200
35162306a36Sopenharmony_ci#define SWITCH_AGGR_BACKOFF		0x0100
35262306a36Sopenharmony_ci#define SWITCH_PASS_PAUSE		0x0008
35362306a36Sopenharmony_ci#define SWITCH_LINK_AUTO_AGING		0x0001
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/* SGCR2 */
35662306a36Sopenharmony_ci#define KS8842_SGCR2_P			0x0404
35762306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_2_OFFSET	KS8842_SGCR2_P
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci#define SWITCH_VLAN_ENABLE		0x8000
36062306a36Sopenharmony_ci#define SWITCH_IGMP_SNOOP		0x4000
36162306a36Sopenharmony_ci#define IPV6_MLD_SNOOP_ENABLE		0x2000
36262306a36Sopenharmony_ci#define IPV6_MLD_SNOOP_OPTION		0x1000
36362306a36Sopenharmony_ci#define PRIORITY_SCHEME_SELECT		0x0800
36462306a36Sopenharmony_ci#define SWITCH_MIRROR_RX_TX		0x0100
36562306a36Sopenharmony_ci#define UNICAST_VLAN_BOUNDARY		0x0080
36662306a36Sopenharmony_ci#define MULTICAST_STORM_DISABLE		0x0040
36762306a36Sopenharmony_ci#define SWITCH_BACK_PRESSURE		0x0020
36862306a36Sopenharmony_ci#define FAIR_FLOW_CTRL			0x0010
36962306a36Sopenharmony_ci#define NO_EXC_COLLISION_DROP		0x0008
37062306a36Sopenharmony_ci#define SWITCH_HUGE_PACKET		0x0004
37162306a36Sopenharmony_ci#define SWITCH_LEGAL_PACKET		0x0002
37262306a36Sopenharmony_ci#define SWITCH_BUF_RESERVE		0x0001
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/* SGCR3 */
37562306a36Sopenharmony_ci#define KS8842_SGCR3_P			0x0406
37662306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_3_OFFSET	KS8842_SGCR3_P
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci#define BROADCAST_STORM_RATE_LO		0xFF00
37962306a36Sopenharmony_ci#define SWITCH_REPEATER			0x0080
38062306a36Sopenharmony_ci#define SWITCH_HALF_DUPLEX		0x0040
38162306a36Sopenharmony_ci#define SWITCH_FLOW_CTRL		0x0020
38262306a36Sopenharmony_ci#define SWITCH_10_MBIT			0x0010
38362306a36Sopenharmony_ci#define SWITCH_REPLACE_NULL_VID		0x0008
38462306a36Sopenharmony_ci#define BROADCAST_STORM_RATE_HI		0x0007
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci#define BROADCAST_STORM_RATE		0x07FF
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/* SGCR4 */
38962306a36Sopenharmony_ci#define KS8842_SGCR4_P			0x0408
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/* SGCR5 */
39262306a36Sopenharmony_ci#define KS8842_SGCR5_P			0x040A
39362306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_5_OFFSET	KS8842_SGCR5_P
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#define LED_MODE			0x8200
39662306a36Sopenharmony_ci#define LED_SPEED_DUPLEX_ACT		0x0000
39762306a36Sopenharmony_ci#define LED_SPEED_DUPLEX_LINK_ACT	0x8000
39862306a36Sopenharmony_ci#define LED_DUPLEX_10_100		0x0200
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/* SGCR6 */
40162306a36Sopenharmony_ci#define KS8842_SGCR6_P			0x0410
40262306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_6_OFFSET	KS8842_SGCR6_P
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci#define KS8842_PRIORITY_MASK		3
40562306a36Sopenharmony_ci#define KS8842_PRIORITY_SHIFT		2
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/* SGCR7 */
40862306a36Sopenharmony_ci#define KS8842_SGCR7_P			0x0412
40962306a36Sopenharmony_ci#define KS8842_SWITCH_CTRL_7_OFFSET	KS8842_SGCR7_P
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci#define SWITCH_UNK_DEF_PORT_ENABLE	0x0008
41262306a36Sopenharmony_ci#define SWITCH_UNK_DEF_PORT_3		0x0004
41362306a36Sopenharmony_ci#define SWITCH_UNK_DEF_PORT_2		0x0002
41462306a36Sopenharmony_ci#define SWITCH_UNK_DEF_PORT_1		0x0001
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/* MACAR1 */
41762306a36Sopenharmony_ci#define KS8842_MACAR1_P			0x0470
41862306a36Sopenharmony_ci#define KS8842_MACAR2_P			0x0472
41962306a36Sopenharmony_ci#define KS8842_MACAR3_P			0x0474
42062306a36Sopenharmony_ci#define KS8842_MAC_ADDR_1_OFFSET	KS8842_MACAR1_P
42162306a36Sopenharmony_ci#define KS8842_MAC_ADDR_0_OFFSET	(KS8842_MAC_ADDR_1_OFFSET + 1)
42262306a36Sopenharmony_ci#define KS8842_MAC_ADDR_3_OFFSET	KS8842_MACAR2_P
42362306a36Sopenharmony_ci#define KS8842_MAC_ADDR_2_OFFSET	(KS8842_MAC_ADDR_3_OFFSET + 1)
42462306a36Sopenharmony_ci#define KS8842_MAC_ADDR_5_OFFSET	KS8842_MACAR3_P
42562306a36Sopenharmony_ci#define KS8842_MAC_ADDR_4_OFFSET	(KS8842_MAC_ADDR_5_OFFSET + 1)
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/* TOSR1 */
42862306a36Sopenharmony_ci#define KS8842_TOSR1_P			0x0480
42962306a36Sopenharmony_ci#define KS8842_TOSR2_P			0x0482
43062306a36Sopenharmony_ci#define KS8842_TOSR3_P			0x0484
43162306a36Sopenharmony_ci#define KS8842_TOSR4_P			0x0486
43262306a36Sopenharmony_ci#define KS8842_TOSR5_P			0x0488
43362306a36Sopenharmony_ci#define KS8842_TOSR6_P			0x048A
43462306a36Sopenharmony_ci#define KS8842_TOSR7_P			0x0490
43562306a36Sopenharmony_ci#define KS8842_TOSR8_P			0x0492
43662306a36Sopenharmony_ci#define KS8842_TOS_1_OFFSET		KS8842_TOSR1_P
43762306a36Sopenharmony_ci#define KS8842_TOS_2_OFFSET		KS8842_TOSR2_P
43862306a36Sopenharmony_ci#define KS8842_TOS_3_OFFSET		KS8842_TOSR3_P
43962306a36Sopenharmony_ci#define KS8842_TOS_4_OFFSET		KS8842_TOSR4_P
44062306a36Sopenharmony_ci#define KS8842_TOS_5_OFFSET		KS8842_TOSR5_P
44162306a36Sopenharmony_ci#define KS8842_TOS_6_OFFSET		KS8842_TOSR6_P
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci#define KS8842_TOS_7_OFFSET		KS8842_TOSR7_P
44462306a36Sopenharmony_ci#define KS8842_TOS_8_OFFSET		KS8842_TOSR8_P
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/* P1CR1 */
44762306a36Sopenharmony_ci#define KS8842_P1CR1_P			0x0500
44862306a36Sopenharmony_ci#define KS8842_P1CR2_P			0x0502
44962306a36Sopenharmony_ci#define KS8842_P1VIDR_P			0x0504
45062306a36Sopenharmony_ci#define KS8842_P1CR3_P			0x0506
45162306a36Sopenharmony_ci#define KS8842_P1IRCR_P			0x0508
45262306a36Sopenharmony_ci#define KS8842_P1ERCR_P			0x050A
45362306a36Sopenharmony_ci#define KS884X_P1SCSLMD_P		0x0510
45462306a36Sopenharmony_ci#define KS884X_P1CR4_P			0x0512
45562306a36Sopenharmony_ci#define KS884X_P1SR_P			0x0514
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/* P2CR1 */
45862306a36Sopenharmony_ci#define KS8842_P2CR1_P			0x0520
45962306a36Sopenharmony_ci#define KS8842_P2CR2_P			0x0522
46062306a36Sopenharmony_ci#define KS8842_P2VIDR_P			0x0524
46162306a36Sopenharmony_ci#define KS8842_P2CR3_P			0x0526
46262306a36Sopenharmony_ci#define KS8842_P2IRCR_P			0x0528
46362306a36Sopenharmony_ci#define KS8842_P2ERCR_P			0x052A
46462306a36Sopenharmony_ci#define KS884X_P2SCSLMD_P		0x0530
46562306a36Sopenharmony_ci#define KS884X_P2CR4_P			0x0532
46662306a36Sopenharmony_ci#define KS884X_P2SR_P			0x0534
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/* P3CR1 */
46962306a36Sopenharmony_ci#define KS8842_P3CR1_P			0x0540
47062306a36Sopenharmony_ci#define KS8842_P3CR2_P			0x0542
47162306a36Sopenharmony_ci#define KS8842_P3VIDR_P			0x0544
47262306a36Sopenharmony_ci#define KS8842_P3CR3_P			0x0546
47362306a36Sopenharmony_ci#define KS8842_P3IRCR_P			0x0548
47462306a36Sopenharmony_ci#define KS8842_P3ERCR_P			0x054A
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci#define KS8842_PORT_1_CTRL_1		KS8842_P1CR1_P
47762306a36Sopenharmony_ci#define KS8842_PORT_2_CTRL_1		KS8842_P2CR1_P
47862306a36Sopenharmony_ci#define KS8842_PORT_3_CTRL_1		KS8842_P3CR1_P
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci#define PORT_CTRL_ADDR(port, addr)		\
48162306a36Sopenharmony_ci	(addr = KS8842_PORT_1_CTRL_1 + (port) *	\
48262306a36Sopenharmony_ci		(KS8842_PORT_2_CTRL_1 - KS8842_PORT_1_CTRL_1))
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci#define KS8842_PORT_CTRL_1_OFFSET	0x00
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci#define PORT_BROADCAST_STORM		0x0080
48762306a36Sopenharmony_ci#define PORT_DIFFSERV_ENABLE		0x0040
48862306a36Sopenharmony_ci#define PORT_802_1P_ENABLE		0x0020
48962306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_MASK	0x0018
49062306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_BASE	0x0003
49162306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_SHIFT	3
49262306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_0		0x0000
49362306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_1		0x0008
49462306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_2		0x0010
49562306a36Sopenharmony_ci#define PORT_BASED_PRIORITY_3		0x0018
49662306a36Sopenharmony_ci#define PORT_INSERT_TAG			0x0004
49762306a36Sopenharmony_ci#define PORT_REMOVE_TAG			0x0002
49862306a36Sopenharmony_ci#define PORT_PRIO_QUEUE_ENABLE		0x0001
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#define KS8842_PORT_CTRL_2_OFFSET	0x02
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci#define PORT_INGRESS_VLAN_FILTER	0x4000
50362306a36Sopenharmony_ci#define PORT_DISCARD_NON_VID		0x2000
50462306a36Sopenharmony_ci#define PORT_FORCE_FLOW_CTRL		0x1000
50562306a36Sopenharmony_ci#define PORT_BACK_PRESSURE		0x0800
50662306a36Sopenharmony_ci#define PORT_TX_ENABLE			0x0400
50762306a36Sopenharmony_ci#define PORT_RX_ENABLE			0x0200
50862306a36Sopenharmony_ci#define PORT_LEARN_DISABLE		0x0100
50962306a36Sopenharmony_ci#define PORT_MIRROR_SNIFFER		0x0080
51062306a36Sopenharmony_ci#define PORT_MIRROR_RX			0x0040
51162306a36Sopenharmony_ci#define PORT_MIRROR_TX			0x0020
51262306a36Sopenharmony_ci#define PORT_USER_PRIORITY_CEILING	0x0008
51362306a36Sopenharmony_ci#define PORT_VLAN_MEMBERSHIP		0x0007
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci#define KS8842_PORT_CTRL_VID_OFFSET	0x04
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#define PORT_DEFAULT_VID		0x0001
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci#define KS8842_PORT_CTRL_3_OFFSET	0x06
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci#define PORT_INGRESS_LIMIT_MODE		0x000C
52262306a36Sopenharmony_ci#define PORT_INGRESS_ALL		0x0000
52362306a36Sopenharmony_ci#define PORT_INGRESS_UNICAST		0x0004
52462306a36Sopenharmony_ci#define PORT_INGRESS_MULTICAST		0x0008
52562306a36Sopenharmony_ci#define PORT_INGRESS_BROADCAST		0x000C
52662306a36Sopenharmony_ci#define PORT_COUNT_IFG			0x0002
52762306a36Sopenharmony_ci#define PORT_COUNT_PREAMBLE		0x0001
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci#define KS8842_PORT_IN_RATE_OFFSET	0x08
53062306a36Sopenharmony_ci#define KS8842_PORT_OUT_RATE_OFFSET	0x0A
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci#define PORT_PRIORITY_RATE		0x0F
53362306a36Sopenharmony_ci#define PORT_PRIORITY_RATE_SHIFT	4
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci#define KS884X_PORT_LINK_MD		0x10
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci#define PORT_CABLE_10M_SHORT		0x8000
53862306a36Sopenharmony_ci#define PORT_CABLE_DIAG_RESULT		0x6000
53962306a36Sopenharmony_ci#define PORT_CABLE_STAT_NORMAL		0x0000
54062306a36Sopenharmony_ci#define PORT_CABLE_STAT_OPEN		0x2000
54162306a36Sopenharmony_ci#define PORT_CABLE_STAT_SHORT		0x4000
54262306a36Sopenharmony_ci#define PORT_CABLE_STAT_FAILED		0x6000
54362306a36Sopenharmony_ci#define PORT_START_CABLE_DIAG		0x1000
54462306a36Sopenharmony_ci#define PORT_FORCE_LINK			0x0800
54562306a36Sopenharmony_ci#define PORT_POWER_SAVING_DISABLE	0x0400
54662306a36Sopenharmony_ci#define PORT_PHY_REMOTE_LOOPBACK	0x0200
54762306a36Sopenharmony_ci#define PORT_CABLE_FAULT_COUNTER	0x01FF
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci#define KS884X_PORT_CTRL_4_OFFSET	0x12
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci#define PORT_LED_OFF			0x8000
55262306a36Sopenharmony_ci#define PORT_TX_DISABLE			0x4000
55362306a36Sopenharmony_ci#define PORT_AUTO_NEG_RESTART		0x2000
55462306a36Sopenharmony_ci#define PORT_REMOTE_FAULT_DISABLE	0x1000
55562306a36Sopenharmony_ci#define PORT_POWER_DOWN			0x0800
55662306a36Sopenharmony_ci#define PORT_AUTO_MDIX_DISABLE		0x0400
55762306a36Sopenharmony_ci#define PORT_FORCE_MDIX			0x0200
55862306a36Sopenharmony_ci#define PORT_LOOPBACK			0x0100
55962306a36Sopenharmony_ci#define PORT_AUTO_NEG_ENABLE		0x0080
56062306a36Sopenharmony_ci#define PORT_FORCE_100_MBIT		0x0040
56162306a36Sopenharmony_ci#define PORT_FORCE_FULL_DUPLEX		0x0020
56262306a36Sopenharmony_ci#define PORT_AUTO_NEG_SYM_PAUSE		0x0010
56362306a36Sopenharmony_ci#define PORT_AUTO_NEG_100BTX_FD		0x0008
56462306a36Sopenharmony_ci#define PORT_AUTO_NEG_100BTX		0x0004
56562306a36Sopenharmony_ci#define PORT_AUTO_NEG_10BT_FD		0x0002
56662306a36Sopenharmony_ci#define PORT_AUTO_NEG_10BT		0x0001
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci#define KS884X_PORT_STATUS_OFFSET	0x14
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci#define PORT_HP_MDIX			0x8000
57162306a36Sopenharmony_ci#define PORT_REVERSED_POLARITY		0x2000
57262306a36Sopenharmony_ci#define PORT_RX_FLOW_CTRL		0x0800
57362306a36Sopenharmony_ci#define PORT_TX_FLOW_CTRL		0x1000
57462306a36Sopenharmony_ci#define PORT_STATUS_SPEED_100MBIT	0x0400
57562306a36Sopenharmony_ci#define PORT_STATUS_FULL_DUPLEX		0x0200
57662306a36Sopenharmony_ci#define PORT_REMOTE_FAULT		0x0100
57762306a36Sopenharmony_ci#define PORT_MDIX_STATUS		0x0080
57862306a36Sopenharmony_ci#define PORT_AUTO_NEG_COMPLETE		0x0040
57962306a36Sopenharmony_ci#define PORT_STATUS_LINK_GOOD		0x0020
58062306a36Sopenharmony_ci#define PORT_REMOTE_SYM_PAUSE		0x0010
58162306a36Sopenharmony_ci#define PORT_REMOTE_100BTX_FD		0x0008
58262306a36Sopenharmony_ci#define PORT_REMOTE_100BTX		0x0004
58362306a36Sopenharmony_ci#define PORT_REMOTE_10BT_FD		0x0002
58462306a36Sopenharmony_ci#define PORT_REMOTE_10BT		0x0001
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/*
58762306a36Sopenharmony_ci#define STATIC_MAC_TABLE_ADDR		00-0000FFFF-FFFFFFFF
58862306a36Sopenharmony_ci#define STATIC_MAC_TABLE_FWD_PORTS	00-00070000-00000000
58962306a36Sopenharmony_ci#define STATIC_MAC_TABLE_VALID		00-00080000-00000000
59062306a36Sopenharmony_ci#define STATIC_MAC_TABLE_OVERRIDE	00-00100000-00000000
59162306a36Sopenharmony_ci#define STATIC_MAC_TABLE_USE_FID	00-00200000-00000000
59262306a36Sopenharmony_ci#define STATIC_MAC_TABLE_FID		00-03C00000-00000000
59362306a36Sopenharmony_ci*/
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci#define STATIC_MAC_TABLE_ADDR		0x0000FFFF
59662306a36Sopenharmony_ci#define STATIC_MAC_TABLE_FWD_PORTS	0x00070000
59762306a36Sopenharmony_ci#define STATIC_MAC_TABLE_VALID		0x00080000
59862306a36Sopenharmony_ci#define STATIC_MAC_TABLE_OVERRIDE	0x00100000
59962306a36Sopenharmony_ci#define STATIC_MAC_TABLE_USE_FID	0x00200000
60062306a36Sopenharmony_ci#define STATIC_MAC_TABLE_FID		0x03C00000
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci#define STATIC_MAC_FWD_PORTS_SHIFT	16
60362306a36Sopenharmony_ci#define STATIC_MAC_FID_SHIFT		22
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/*
60662306a36Sopenharmony_ci#define VLAN_TABLE_VID			00-00000000-00000FFF
60762306a36Sopenharmony_ci#define VLAN_TABLE_FID			00-00000000-0000F000
60862306a36Sopenharmony_ci#define VLAN_TABLE_MEMBERSHIP		00-00000000-00070000
60962306a36Sopenharmony_ci#define VLAN_TABLE_VALID		00-00000000-00080000
61062306a36Sopenharmony_ci*/
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci#define VLAN_TABLE_VID			0x00000FFF
61362306a36Sopenharmony_ci#define VLAN_TABLE_FID			0x0000F000
61462306a36Sopenharmony_ci#define VLAN_TABLE_MEMBERSHIP		0x00070000
61562306a36Sopenharmony_ci#define VLAN_TABLE_VALID		0x00080000
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci#define VLAN_TABLE_FID_SHIFT		12
61862306a36Sopenharmony_ci#define VLAN_TABLE_MEMBERSHIP_SHIFT	16
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/*
62162306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_ADDR		00-0000FFFF-FFFFFFFF
62262306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_FID		00-000F0000-00000000
62362306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_SRC_PORT	00-00300000-00000000
62462306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_TIMESTAMP	00-00C00000-00000000
62562306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_ENTRIES	03-FF000000-00000000
62662306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_MAC_EMPTY	04-00000000-00000000
62762306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_RESERVED	78-00000000-00000000
62862306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_NOT_READY	80-00000000-00000000
62962306a36Sopenharmony_ci*/
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_ADDR		0x0000FFFF
63262306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_FID		0x000F0000
63362306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_SRC_PORT	0x00300000
63462306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_TIMESTAMP	0x00C00000
63562306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_ENTRIES	0xFF000000
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_ENTRIES_H	0x03
63862306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_MAC_EMPTY	0x04
63962306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_RESERVED	0x78
64062306a36Sopenharmony_ci#define DYNAMIC_MAC_TABLE_NOT_READY	0x80
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci#define DYNAMIC_MAC_FID_SHIFT		16
64362306a36Sopenharmony_ci#define DYNAMIC_MAC_SRC_PORT_SHIFT	20
64462306a36Sopenharmony_ci#define DYNAMIC_MAC_TIMESTAMP_SHIFT	22
64562306a36Sopenharmony_ci#define DYNAMIC_MAC_ENTRIES_SHIFT	24
64662306a36Sopenharmony_ci#define DYNAMIC_MAC_ENTRIES_H_SHIFT	8
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci/*
64962306a36Sopenharmony_ci#define MIB_COUNTER_VALUE		00-00000000-3FFFFFFF
65062306a36Sopenharmony_ci#define MIB_COUNTER_VALID		00-00000000-40000000
65162306a36Sopenharmony_ci#define MIB_COUNTER_OVERFLOW		00-00000000-80000000
65262306a36Sopenharmony_ci*/
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci#define MIB_COUNTER_VALUE		0x3FFFFFFF
65562306a36Sopenharmony_ci#define MIB_COUNTER_VALID		0x40000000
65662306a36Sopenharmony_ci#define MIB_COUNTER_OVERFLOW		0x80000000
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci#define MIB_PACKET_DROPPED		0x0000FFFF
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_TX_0	0x100
66162306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_TX_1	0x101
66262306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_TX	0x102
66362306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_RX_0	0x103
66462306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_RX_1	0x104
66562306a36Sopenharmony_ci#define KS_MIB_PACKET_DROPPED_RX	0x105
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/* Change default LED mode. */
66862306a36Sopenharmony_ci#define SET_DEFAULT_LED			LED_SPEED_DUPLEX_ACT
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci#define MAC_ADDR_ORDER(i)		(ETH_ALEN - 1 - (i))
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci#define MAX_ETHERNET_BODY_SIZE		1500
67362306a36Sopenharmony_ci#define ETHERNET_HEADER_SIZE		(14 + VLAN_HLEN)
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci#define MAX_ETHERNET_PACKET_SIZE	\
67662306a36Sopenharmony_ci	(MAX_ETHERNET_BODY_SIZE + ETHERNET_HEADER_SIZE)
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci#define REGULAR_RX_BUF_SIZE		(MAX_ETHERNET_PACKET_SIZE + 4)
67962306a36Sopenharmony_ci#define MAX_RX_BUF_SIZE			(1912 + 4)
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci#define ADDITIONAL_ENTRIES		16
68262306a36Sopenharmony_ci#define MAX_MULTICAST_LIST		32
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci#define HW_MULTICAST_SIZE		8
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci#define HW_TO_DEV_PORT(port)		(port - 1)
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cienum {
68962306a36Sopenharmony_ci	media_connected,
69062306a36Sopenharmony_ci	media_disconnected
69162306a36Sopenharmony_ci};
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cienum {
69462306a36Sopenharmony_ci	OID_COUNTER_UNKOWN,
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	OID_COUNTER_FIRST,
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* total transmit errors */
69962306a36Sopenharmony_ci	OID_COUNTER_XMIT_ERROR,
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* total receive errors */
70262306a36Sopenharmony_ci	OID_COUNTER_RCV_ERROR,
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	OID_COUNTER_LAST
70562306a36Sopenharmony_ci};
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/*
70862306a36Sopenharmony_ci * Hardware descriptor definitions
70962306a36Sopenharmony_ci */
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci#define DESC_ALIGNMENT			16
71262306a36Sopenharmony_ci#define BUFFER_ALIGNMENT		8
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci#define NUM_OF_RX_DESC			64
71562306a36Sopenharmony_ci#define NUM_OF_TX_DESC			64
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci#define KS_DESC_RX_FRAME_LEN		0x000007FF
71862306a36Sopenharmony_ci#define KS_DESC_RX_FRAME_TYPE		0x00008000
71962306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_CRC		0x00010000
72062306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_RUNT		0x00020000
72162306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_TOO_LONG	0x00040000
72262306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_PHY		0x00080000
72362306a36Sopenharmony_ci#define KS884X_DESC_RX_PORT_MASK	0x00300000
72462306a36Sopenharmony_ci#define KS_DESC_RX_MULTICAST		0x01000000
72562306a36Sopenharmony_ci#define KS_DESC_RX_ERROR		0x02000000
72662306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_CSUM_UDP	0x04000000
72762306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_CSUM_TCP	0x08000000
72862306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_CSUM_IP	0x10000000
72962306a36Sopenharmony_ci#define KS_DESC_RX_LAST			0x20000000
73062306a36Sopenharmony_ci#define KS_DESC_RX_FIRST		0x40000000
73162306a36Sopenharmony_ci#define KS_DESC_RX_ERROR_COND		\
73262306a36Sopenharmony_ci	(KS_DESC_RX_ERROR_CRC |		\
73362306a36Sopenharmony_ci	KS_DESC_RX_ERROR_RUNT |		\
73462306a36Sopenharmony_ci	KS_DESC_RX_ERROR_PHY |		\
73562306a36Sopenharmony_ci	KS_DESC_RX_ERROR_TOO_LONG)
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci#define KS_DESC_HW_OWNED		0x80000000
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci#define KS_DESC_BUF_SIZE		0x000007FF
74062306a36Sopenharmony_ci#define KS884X_DESC_TX_PORT_MASK	0x00300000
74162306a36Sopenharmony_ci#define KS_DESC_END_OF_RING		0x02000000
74262306a36Sopenharmony_ci#define KS_DESC_TX_CSUM_GEN_UDP		0x04000000
74362306a36Sopenharmony_ci#define KS_DESC_TX_CSUM_GEN_TCP		0x08000000
74462306a36Sopenharmony_ci#define KS_DESC_TX_CSUM_GEN_IP		0x10000000
74562306a36Sopenharmony_ci#define KS_DESC_TX_LAST			0x20000000
74662306a36Sopenharmony_ci#define KS_DESC_TX_FIRST		0x40000000
74762306a36Sopenharmony_ci#define KS_DESC_TX_INTERRUPT		0x80000000
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci#define KS_DESC_PORT_SHIFT		20
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci#define KS_DESC_RX_MASK			(KS_DESC_BUF_SIZE)
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci#define KS_DESC_TX_MASK			\
75462306a36Sopenharmony_ci	(KS_DESC_TX_INTERRUPT |		\
75562306a36Sopenharmony_ci	KS_DESC_TX_FIRST |		\
75662306a36Sopenharmony_ci	KS_DESC_TX_LAST |		\
75762306a36Sopenharmony_ci	KS_DESC_TX_CSUM_GEN_IP |	\
75862306a36Sopenharmony_ci	KS_DESC_TX_CSUM_GEN_TCP |	\
75962306a36Sopenharmony_ci	KS_DESC_TX_CSUM_GEN_UDP |	\
76062306a36Sopenharmony_ci	KS_DESC_BUF_SIZE)
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistruct ksz_desc_rx_stat {
76362306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
76462306a36Sopenharmony_ci	u32 hw_owned:1;
76562306a36Sopenharmony_ci	u32 first_desc:1;
76662306a36Sopenharmony_ci	u32 last_desc:1;
76762306a36Sopenharmony_ci	u32 csum_err_ip:1;
76862306a36Sopenharmony_ci	u32 csum_err_tcp:1;
76962306a36Sopenharmony_ci	u32 csum_err_udp:1;
77062306a36Sopenharmony_ci	u32 error:1;
77162306a36Sopenharmony_ci	u32 multicast:1;
77262306a36Sopenharmony_ci	u32 src_port:4;
77362306a36Sopenharmony_ci	u32 err_phy:1;
77462306a36Sopenharmony_ci	u32 err_too_long:1;
77562306a36Sopenharmony_ci	u32 err_runt:1;
77662306a36Sopenharmony_ci	u32 err_crc:1;
77762306a36Sopenharmony_ci	u32 frame_type:1;
77862306a36Sopenharmony_ci	u32 reserved1:4;
77962306a36Sopenharmony_ci	u32 frame_len:11;
78062306a36Sopenharmony_ci#else
78162306a36Sopenharmony_ci	u32 frame_len:11;
78262306a36Sopenharmony_ci	u32 reserved1:4;
78362306a36Sopenharmony_ci	u32 frame_type:1;
78462306a36Sopenharmony_ci	u32 err_crc:1;
78562306a36Sopenharmony_ci	u32 err_runt:1;
78662306a36Sopenharmony_ci	u32 err_too_long:1;
78762306a36Sopenharmony_ci	u32 err_phy:1;
78862306a36Sopenharmony_ci	u32 src_port:4;
78962306a36Sopenharmony_ci	u32 multicast:1;
79062306a36Sopenharmony_ci	u32 error:1;
79162306a36Sopenharmony_ci	u32 csum_err_udp:1;
79262306a36Sopenharmony_ci	u32 csum_err_tcp:1;
79362306a36Sopenharmony_ci	u32 csum_err_ip:1;
79462306a36Sopenharmony_ci	u32 last_desc:1;
79562306a36Sopenharmony_ci	u32 first_desc:1;
79662306a36Sopenharmony_ci	u32 hw_owned:1;
79762306a36Sopenharmony_ci#endif
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistruct ksz_desc_tx_stat {
80162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
80262306a36Sopenharmony_ci	u32 hw_owned:1;
80362306a36Sopenharmony_ci	u32 reserved1:31;
80462306a36Sopenharmony_ci#else
80562306a36Sopenharmony_ci	u32 reserved1:31;
80662306a36Sopenharmony_ci	u32 hw_owned:1;
80762306a36Sopenharmony_ci#endif
80862306a36Sopenharmony_ci};
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistruct ksz_desc_rx_buf {
81162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
81262306a36Sopenharmony_ci	u32 reserved4:6;
81362306a36Sopenharmony_ci	u32 end_of_ring:1;
81462306a36Sopenharmony_ci	u32 reserved3:14;
81562306a36Sopenharmony_ci	u32 buf_size:11;
81662306a36Sopenharmony_ci#else
81762306a36Sopenharmony_ci	u32 buf_size:11;
81862306a36Sopenharmony_ci	u32 reserved3:14;
81962306a36Sopenharmony_ci	u32 end_of_ring:1;
82062306a36Sopenharmony_ci	u32 reserved4:6;
82162306a36Sopenharmony_ci#endif
82262306a36Sopenharmony_ci};
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistruct ksz_desc_tx_buf {
82562306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
82662306a36Sopenharmony_ci	u32 intr:1;
82762306a36Sopenharmony_ci	u32 first_seg:1;
82862306a36Sopenharmony_ci	u32 last_seg:1;
82962306a36Sopenharmony_ci	u32 csum_gen_ip:1;
83062306a36Sopenharmony_ci	u32 csum_gen_tcp:1;
83162306a36Sopenharmony_ci	u32 csum_gen_udp:1;
83262306a36Sopenharmony_ci	u32 end_of_ring:1;
83362306a36Sopenharmony_ci	u32 reserved4:1;
83462306a36Sopenharmony_ci	u32 dest_port:4;
83562306a36Sopenharmony_ci	u32 reserved3:9;
83662306a36Sopenharmony_ci	u32 buf_size:11;
83762306a36Sopenharmony_ci#else
83862306a36Sopenharmony_ci	u32 buf_size:11;
83962306a36Sopenharmony_ci	u32 reserved3:9;
84062306a36Sopenharmony_ci	u32 dest_port:4;
84162306a36Sopenharmony_ci	u32 reserved4:1;
84262306a36Sopenharmony_ci	u32 end_of_ring:1;
84362306a36Sopenharmony_ci	u32 csum_gen_udp:1;
84462306a36Sopenharmony_ci	u32 csum_gen_tcp:1;
84562306a36Sopenharmony_ci	u32 csum_gen_ip:1;
84662306a36Sopenharmony_ci	u32 last_seg:1;
84762306a36Sopenharmony_ci	u32 first_seg:1;
84862306a36Sopenharmony_ci	u32 intr:1;
84962306a36Sopenharmony_ci#endif
85062306a36Sopenharmony_ci};
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ciunion desc_stat {
85362306a36Sopenharmony_ci	struct ksz_desc_rx_stat rx;
85462306a36Sopenharmony_ci	struct ksz_desc_tx_stat tx;
85562306a36Sopenharmony_ci	u32 data;
85662306a36Sopenharmony_ci};
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ciunion desc_buf {
85962306a36Sopenharmony_ci	struct ksz_desc_rx_buf rx;
86062306a36Sopenharmony_ci	struct ksz_desc_tx_buf tx;
86162306a36Sopenharmony_ci	u32 data;
86262306a36Sopenharmony_ci};
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * struct ksz_hw_desc - Hardware descriptor data structure
86662306a36Sopenharmony_ci * @ctrl:	Descriptor control value.
86762306a36Sopenharmony_ci * @buf:	Descriptor buffer value.
86862306a36Sopenharmony_ci * @addr:	Physical address of memory buffer.
86962306a36Sopenharmony_ci * @next:	Pointer to next hardware descriptor.
87062306a36Sopenharmony_ci */
87162306a36Sopenharmony_cistruct ksz_hw_desc {
87262306a36Sopenharmony_ci	union desc_stat ctrl;
87362306a36Sopenharmony_ci	union desc_buf buf;
87462306a36Sopenharmony_ci	u32 addr;
87562306a36Sopenharmony_ci	u32 next;
87662306a36Sopenharmony_ci};
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/**
87962306a36Sopenharmony_ci * struct ksz_sw_desc - Software descriptor data structure
88062306a36Sopenharmony_ci * @ctrl:	Descriptor control value.
88162306a36Sopenharmony_ci * @buf:	Descriptor buffer value.
88262306a36Sopenharmony_ci * @buf_size:	Current buffers size value in hardware descriptor.
88362306a36Sopenharmony_ci */
88462306a36Sopenharmony_cistruct ksz_sw_desc {
88562306a36Sopenharmony_ci	union desc_stat ctrl;
88662306a36Sopenharmony_ci	union desc_buf buf;
88762306a36Sopenharmony_ci	u32 buf_size;
88862306a36Sopenharmony_ci};
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci/**
89162306a36Sopenharmony_ci * struct ksz_dma_buf - OS dependent DMA buffer data structure
89262306a36Sopenharmony_ci * @skb:	Associated socket buffer.
89362306a36Sopenharmony_ci * @dma:	Associated physical DMA address.
89462306a36Sopenharmony_ci * @len:	Actual len used.
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_cistruct ksz_dma_buf {
89762306a36Sopenharmony_ci	struct sk_buff *skb;
89862306a36Sopenharmony_ci	dma_addr_t dma;
89962306a36Sopenharmony_ci	int len;
90062306a36Sopenharmony_ci};
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci/**
90362306a36Sopenharmony_ci * struct ksz_desc - Descriptor structure
90462306a36Sopenharmony_ci * @phw:	Hardware descriptor pointer to uncached physical memory.
90562306a36Sopenharmony_ci * @sw:		Cached memory to hold hardware descriptor values for
90662306a36Sopenharmony_ci * 		manipulation.
90762306a36Sopenharmony_ci * @dma_buf:	Operating system dependent data structure to hold physical
90862306a36Sopenharmony_ci * 		memory buffer allocation information.
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_cistruct ksz_desc {
91162306a36Sopenharmony_ci	struct ksz_hw_desc *phw;
91262306a36Sopenharmony_ci	struct ksz_sw_desc sw;
91362306a36Sopenharmony_ci	struct ksz_dma_buf dma_buf;
91462306a36Sopenharmony_ci};
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci#define DMA_BUFFER(desc)  ((struct ksz_dma_buf *)(&(desc)->dma_buf))
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * struct ksz_desc_info - Descriptor information data structure
92062306a36Sopenharmony_ci * @ring:	First descriptor in the ring.
92162306a36Sopenharmony_ci * @cur:	Current descriptor being manipulated.
92262306a36Sopenharmony_ci * @ring_virt:	First hardware descriptor in the ring.
92362306a36Sopenharmony_ci * @ring_phys:	The physical address of the first descriptor of the ring.
92462306a36Sopenharmony_ci * @size:	Size of hardware descriptor.
92562306a36Sopenharmony_ci * @alloc:	Number of descriptors allocated.
92662306a36Sopenharmony_ci * @avail:	Number of descriptors available for use.
92762306a36Sopenharmony_ci * @last:	Index for last descriptor released to hardware.
92862306a36Sopenharmony_ci * @next:	Index for next descriptor available for use.
92962306a36Sopenharmony_ci * @mask:	Mask for index wrapping.
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistruct ksz_desc_info {
93262306a36Sopenharmony_ci	struct ksz_desc *ring;
93362306a36Sopenharmony_ci	struct ksz_desc *cur;
93462306a36Sopenharmony_ci	struct ksz_hw_desc *ring_virt;
93562306a36Sopenharmony_ci	u32 ring_phys;
93662306a36Sopenharmony_ci	int size;
93762306a36Sopenharmony_ci	int alloc;
93862306a36Sopenharmony_ci	int avail;
93962306a36Sopenharmony_ci	int last;
94062306a36Sopenharmony_ci	int next;
94162306a36Sopenharmony_ci	int mask;
94262306a36Sopenharmony_ci};
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/*
94562306a36Sopenharmony_ci * KSZ8842 switch definitions
94662306a36Sopenharmony_ci */
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cienum {
94962306a36Sopenharmony_ci	TABLE_STATIC_MAC = 0,
95062306a36Sopenharmony_ci	TABLE_VLAN,
95162306a36Sopenharmony_ci	TABLE_DYNAMIC_MAC,
95262306a36Sopenharmony_ci	TABLE_MIB
95362306a36Sopenharmony_ci};
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci#define LEARNED_MAC_TABLE_ENTRIES	1024
95662306a36Sopenharmony_ci#define STATIC_MAC_TABLE_ENTRIES	8
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/**
95962306a36Sopenharmony_ci * struct ksz_mac_table - Static MAC table data structure
96062306a36Sopenharmony_ci * @mac_addr:	MAC address to filter.
96162306a36Sopenharmony_ci * @vid:	VID value.
96262306a36Sopenharmony_ci * @fid:	FID value.
96362306a36Sopenharmony_ci * @ports:	Port membership.
96462306a36Sopenharmony_ci * @override:	Override setting.
96562306a36Sopenharmony_ci * @use_fid:	FID use setting.
96662306a36Sopenharmony_ci * @valid:	Valid setting indicating the entry is being used.
96762306a36Sopenharmony_ci */
96862306a36Sopenharmony_cistruct ksz_mac_table {
96962306a36Sopenharmony_ci	u8 mac_addr[ETH_ALEN];
97062306a36Sopenharmony_ci	u16 vid;
97162306a36Sopenharmony_ci	u8 fid;
97262306a36Sopenharmony_ci	u8 ports;
97362306a36Sopenharmony_ci	u8 override:1;
97462306a36Sopenharmony_ci	u8 use_fid:1;
97562306a36Sopenharmony_ci	u8 valid:1;
97662306a36Sopenharmony_ci};
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci#define VLAN_TABLE_ENTRIES		16
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci * struct ksz_vlan_table - VLAN table data structure
98262306a36Sopenharmony_ci * @vid:	VID value.
98362306a36Sopenharmony_ci * @fid:	FID value.
98462306a36Sopenharmony_ci * @member:	Port membership.
98562306a36Sopenharmony_ci */
98662306a36Sopenharmony_cistruct ksz_vlan_table {
98762306a36Sopenharmony_ci	u16 vid;
98862306a36Sopenharmony_ci	u8 fid;
98962306a36Sopenharmony_ci	u8 member;
99062306a36Sopenharmony_ci};
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci#define DIFFSERV_ENTRIES		64
99362306a36Sopenharmony_ci#define PRIO_802_1P_ENTRIES		8
99462306a36Sopenharmony_ci#define PRIO_QUEUES			4
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci#define SWITCH_PORT_NUM			2
99762306a36Sopenharmony_ci#define TOTAL_PORT_NUM			(SWITCH_PORT_NUM + 1)
99862306a36Sopenharmony_ci#define HOST_MASK			(1 << SWITCH_PORT_NUM)
99962306a36Sopenharmony_ci#define PORT_MASK			7
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci#define MAIN_PORT			0
100262306a36Sopenharmony_ci#define OTHER_PORT			1
100362306a36Sopenharmony_ci#define HOST_PORT			SWITCH_PORT_NUM
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci#define PORT_COUNTER_NUM		0x20
100662306a36Sopenharmony_ci#define TOTAL_PORT_COUNTER_NUM		(PORT_COUNTER_NUM + 2)
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci#define MIB_COUNTER_RX_LO_PRIORITY	0x00
100962306a36Sopenharmony_ci#define MIB_COUNTER_RX_HI_PRIORITY	0x01
101062306a36Sopenharmony_ci#define MIB_COUNTER_RX_UNDERSIZE	0x02
101162306a36Sopenharmony_ci#define MIB_COUNTER_RX_FRAGMENT		0x03
101262306a36Sopenharmony_ci#define MIB_COUNTER_RX_OVERSIZE		0x04
101362306a36Sopenharmony_ci#define MIB_COUNTER_RX_JABBER		0x05
101462306a36Sopenharmony_ci#define MIB_COUNTER_RX_SYMBOL_ERR	0x06
101562306a36Sopenharmony_ci#define MIB_COUNTER_RX_CRC_ERR		0x07
101662306a36Sopenharmony_ci#define MIB_COUNTER_RX_ALIGNMENT_ERR	0x08
101762306a36Sopenharmony_ci#define MIB_COUNTER_RX_CTRL_8808	0x09
101862306a36Sopenharmony_ci#define MIB_COUNTER_RX_PAUSE		0x0A
101962306a36Sopenharmony_ci#define MIB_COUNTER_RX_BROADCAST	0x0B
102062306a36Sopenharmony_ci#define MIB_COUNTER_RX_MULTICAST	0x0C
102162306a36Sopenharmony_ci#define MIB_COUNTER_RX_UNICAST		0x0D
102262306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_64		0x0E
102362306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_65_127	0x0F
102462306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_128_255	0x10
102562306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_256_511	0x11
102662306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_512_1023	0x12
102762306a36Sopenharmony_ci#define MIB_COUNTER_RX_OCTET_1024_1522	0x13
102862306a36Sopenharmony_ci#define MIB_COUNTER_TX_LO_PRIORITY	0x14
102962306a36Sopenharmony_ci#define MIB_COUNTER_TX_HI_PRIORITY	0x15
103062306a36Sopenharmony_ci#define MIB_COUNTER_TX_LATE_COLLISION	0x16
103162306a36Sopenharmony_ci#define MIB_COUNTER_TX_PAUSE		0x17
103262306a36Sopenharmony_ci#define MIB_COUNTER_TX_BROADCAST	0x18
103362306a36Sopenharmony_ci#define MIB_COUNTER_TX_MULTICAST	0x19
103462306a36Sopenharmony_ci#define MIB_COUNTER_TX_UNICAST		0x1A
103562306a36Sopenharmony_ci#define MIB_COUNTER_TX_DEFERRED		0x1B
103662306a36Sopenharmony_ci#define MIB_COUNTER_TX_TOTAL_COLLISION	0x1C
103762306a36Sopenharmony_ci#define MIB_COUNTER_TX_EXCESS_COLLISION	0x1D
103862306a36Sopenharmony_ci#define MIB_COUNTER_TX_SINGLE_COLLISION	0x1E
103962306a36Sopenharmony_ci#define MIB_COUNTER_TX_MULTI_COLLISION	0x1F
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci#define MIB_COUNTER_RX_DROPPED_PACKET	0x20
104262306a36Sopenharmony_ci#define MIB_COUNTER_TX_DROPPED_PACKET	0x21
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci/**
104562306a36Sopenharmony_ci * struct ksz_port_mib - Port MIB data structure
104662306a36Sopenharmony_ci * @cnt_ptr:	Current pointer to MIB counter index.
104762306a36Sopenharmony_ci * @link_down:	Indication the link has just gone down.
104862306a36Sopenharmony_ci * @state:	Connection status of the port.
104962306a36Sopenharmony_ci * @mib_start:	The starting counter index.  Some ports do not start at 0.
105062306a36Sopenharmony_ci * @counter:	64-bit MIB counter value.
105162306a36Sopenharmony_ci * @dropped:	Temporary buffer to remember last read packet dropped values.
105262306a36Sopenharmony_ci *
105362306a36Sopenharmony_ci * MIB counters needs to be read periodically so that counters do not get
105462306a36Sopenharmony_ci * overflowed and give incorrect values.  A right balance is needed to
105562306a36Sopenharmony_ci * satisfy this condition and not waste too much CPU time.
105662306a36Sopenharmony_ci *
105762306a36Sopenharmony_ci * It is pointless to read MIB counters when the port is disconnected.  The
105862306a36Sopenharmony_ci * @state provides the connection status so that MIB counters are read only
105962306a36Sopenharmony_ci * when the port is connected.  The @link_down indicates the port is just
106062306a36Sopenharmony_ci * disconnected so that all MIB counters are read one last time to update the
106162306a36Sopenharmony_ci * information.
106262306a36Sopenharmony_ci */
106362306a36Sopenharmony_cistruct ksz_port_mib {
106462306a36Sopenharmony_ci	u8 cnt_ptr;
106562306a36Sopenharmony_ci	u8 link_down;
106662306a36Sopenharmony_ci	u8 state;
106762306a36Sopenharmony_ci	u8 mib_start;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	u64 counter[TOTAL_PORT_COUNTER_NUM];
107062306a36Sopenharmony_ci	u32 dropped[2];
107162306a36Sopenharmony_ci};
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/**
107462306a36Sopenharmony_ci * struct ksz_port_cfg - Port configuration data structure
107562306a36Sopenharmony_ci * @vid:	VID value.
107662306a36Sopenharmony_ci * @member:	Port membership.
107762306a36Sopenharmony_ci * @port_prio:	Port priority.
107862306a36Sopenharmony_ci * @rx_rate:	Receive priority rate.
107962306a36Sopenharmony_ci * @tx_rate:	Transmit priority rate.
108062306a36Sopenharmony_ci * @stp_state:	Current Spanning Tree Protocol state.
108162306a36Sopenharmony_ci */
108262306a36Sopenharmony_cistruct ksz_port_cfg {
108362306a36Sopenharmony_ci	u16 vid;
108462306a36Sopenharmony_ci	u8 member;
108562306a36Sopenharmony_ci	u8 port_prio;
108662306a36Sopenharmony_ci	u32 rx_rate[PRIO_QUEUES];
108762306a36Sopenharmony_ci	u32 tx_rate[PRIO_QUEUES];
108862306a36Sopenharmony_ci	int stp_state;
108962306a36Sopenharmony_ci};
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci/**
109262306a36Sopenharmony_ci * struct ksz_switch - KSZ8842 switch data structure
109362306a36Sopenharmony_ci * @mac_table:	MAC table entries information.
109462306a36Sopenharmony_ci * @vlan_table:	VLAN table entries information.
109562306a36Sopenharmony_ci * @port_cfg:	Port configuration information.
109662306a36Sopenharmony_ci * @diffserv:	DiffServ priority settings.  Possible values from 6-bit of ToS
109762306a36Sopenharmony_ci * 		(bit7 ~ bit2) field.
109862306a36Sopenharmony_ci * @p_802_1p:	802.1P priority settings.  Possible values from 3-bit of 802.1p
109962306a36Sopenharmony_ci * 		Tag priority field.
110062306a36Sopenharmony_ci * @br_addr:	Bridge address.  Used for STP.
110162306a36Sopenharmony_ci * @other_addr:	Other MAC address.  Used for multiple network device mode.
110262306a36Sopenharmony_ci * @broad_per:	Broadcast storm percentage.
110362306a36Sopenharmony_ci * @member:	Current port membership.  Used for STP.
110462306a36Sopenharmony_ci */
110562306a36Sopenharmony_cistruct ksz_switch {
110662306a36Sopenharmony_ci	struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES];
110762306a36Sopenharmony_ci	struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES];
110862306a36Sopenharmony_ci	struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	u8 diffserv[DIFFSERV_ENTRIES];
111162306a36Sopenharmony_ci	u8 p_802_1p[PRIO_802_1P_ENTRIES];
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	u8 br_addr[ETH_ALEN];
111462306a36Sopenharmony_ci	u8 other_addr[ETH_ALEN];
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	u8 broad_per;
111762306a36Sopenharmony_ci	u8 member;
111862306a36Sopenharmony_ci};
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci#define TX_RATE_UNIT			10000
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/**
112362306a36Sopenharmony_ci * struct ksz_port_info - Port information data structure
112462306a36Sopenharmony_ci * @state:	Connection status of the port.
112562306a36Sopenharmony_ci * @tx_rate:	Transmit rate divided by 10000 to get Mbit.
112662306a36Sopenharmony_ci * @duplex:	Duplex mode.
112762306a36Sopenharmony_ci * @advertised:	Advertised auto-negotiation setting.  Used to determine link.
112862306a36Sopenharmony_ci * @partner:	Auto-negotiation partner setting.  Used to determine link.
112962306a36Sopenharmony_ci * @port_id:	Port index to access actual hardware register.
113062306a36Sopenharmony_ci * @pdev:	Pointer to OS dependent network device.
113162306a36Sopenharmony_ci */
113262306a36Sopenharmony_cistruct ksz_port_info {
113362306a36Sopenharmony_ci	uint state;
113462306a36Sopenharmony_ci	uint tx_rate;
113562306a36Sopenharmony_ci	u8 duplex;
113662306a36Sopenharmony_ci	u8 advertised;
113762306a36Sopenharmony_ci	u8 partner;
113862306a36Sopenharmony_ci	u8 port_id;
113962306a36Sopenharmony_ci	void *pdev;
114062306a36Sopenharmony_ci};
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci#define MAX_TX_HELD_SIZE		52000
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci/* Hardware features and bug fixes. */
114562306a36Sopenharmony_ci#define LINK_INT_WORKING		(1 << 0)
114662306a36Sopenharmony_ci#define SMALL_PACKET_TX_BUG		(1 << 1)
114762306a36Sopenharmony_ci#define HALF_DUPLEX_SIGNAL_BUG		(1 << 2)
114862306a36Sopenharmony_ci#define RX_HUGE_FRAME			(1 << 4)
114962306a36Sopenharmony_ci#define STP_SUPPORT			(1 << 8)
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci/* Software overrides. */
115262306a36Sopenharmony_ci#define PAUSE_FLOW_CTRL			(1 << 0)
115362306a36Sopenharmony_ci#define FAST_AGING			(1 << 1)
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/**
115662306a36Sopenharmony_ci * struct ksz_hw - KSZ884X hardware data structure
115762306a36Sopenharmony_ci * @io:			Virtual address assigned.
115862306a36Sopenharmony_ci * @ksz_switch:		Pointer to KSZ8842 switch.
115962306a36Sopenharmony_ci * @port_info:		Port information.
116062306a36Sopenharmony_ci * @port_mib:		Port MIB information.
116162306a36Sopenharmony_ci * @dev_count:		Number of network devices this hardware supports.
116262306a36Sopenharmony_ci * @dst_ports:		Destination ports in switch for transmission.
116362306a36Sopenharmony_ci * @id:			Hardware ID.  Used for display only.
116462306a36Sopenharmony_ci * @mib_cnt:		Number of MIB counters this hardware has.
116562306a36Sopenharmony_ci * @mib_port_cnt:	Number of ports with MIB counters.
116662306a36Sopenharmony_ci * @tx_cfg:		Cached transmit control settings.
116762306a36Sopenharmony_ci * @rx_cfg:		Cached receive control settings.
116862306a36Sopenharmony_ci * @intr_mask:		Current interrupt mask.
116962306a36Sopenharmony_ci * @intr_set:		Current interrup set.
117062306a36Sopenharmony_ci * @intr_blocked:	Interrupt blocked.
117162306a36Sopenharmony_ci * @rx_desc_info:	Receive descriptor information.
117262306a36Sopenharmony_ci * @tx_desc_info:	Transmit descriptor information.
117362306a36Sopenharmony_ci * @tx_int_cnt:		Transmit interrupt count.  Used for TX optimization.
117462306a36Sopenharmony_ci * @tx_int_mask:	Transmit interrupt mask.  Used for TX optimization.
117562306a36Sopenharmony_ci * @tx_size:		Transmit data size.  Used for TX optimization.
117662306a36Sopenharmony_ci * 			The maximum is defined by MAX_TX_HELD_SIZE.
117762306a36Sopenharmony_ci * @perm_addr:		Permanent MAC address.
117862306a36Sopenharmony_ci * @override_addr:	Overridden MAC address.
117962306a36Sopenharmony_ci * @address:		Additional MAC address entries.
118062306a36Sopenharmony_ci * @addr_list_size:	Additional MAC address list size.
118162306a36Sopenharmony_ci * @mac_override:	Indication of MAC address overridden.
118262306a36Sopenharmony_ci * @promiscuous:	Counter to keep track of promiscuous mode set.
118362306a36Sopenharmony_ci * @all_multi:		Counter to keep track of all multicast mode set.
118462306a36Sopenharmony_ci * @multi_list:		Multicast address entries.
118562306a36Sopenharmony_ci * @multi_bits:		Cached multicast hash table settings.
118662306a36Sopenharmony_ci * @multi_list_size:	Multicast address list size.
118762306a36Sopenharmony_ci * @enabled:		Indication of hardware enabled.
118862306a36Sopenharmony_ci * @rx_stop:		Indication of receive process stop.
118962306a36Sopenharmony_ci * @reserved2:		none
119062306a36Sopenharmony_ci * @features:		Hardware features to enable.
119162306a36Sopenharmony_ci * @overrides:		Hardware features to override.
119262306a36Sopenharmony_ci * @parent:		Pointer to parent, network device private structure.
119362306a36Sopenharmony_ci */
119462306a36Sopenharmony_cistruct ksz_hw {
119562306a36Sopenharmony_ci	void __iomem *io;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	struct ksz_switch *ksz_switch;
119862306a36Sopenharmony_ci	struct ksz_port_info port_info[SWITCH_PORT_NUM];
119962306a36Sopenharmony_ci	struct ksz_port_mib port_mib[TOTAL_PORT_NUM];
120062306a36Sopenharmony_ci	int dev_count;
120162306a36Sopenharmony_ci	int dst_ports;
120262306a36Sopenharmony_ci	int id;
120362306a36Sopenharmony_ci	int mib_cnt;
120462306a36Sopenharmony_ci	int mib_port_cnt;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	u32 tx_cfg;
120762306a36Sopenharmony_ci	u32 rx_cfg;
120862306a36Sopenharmony_ci	u32 intr_mask;
120962306a36Sopenharmony_ci	u32 intr_set;
121062306a36Sopenharmony_ci	uint intr_blocked;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	struct ksz_desc_info rx_desc_info;
121362306a36Sopenharmony_ci	struct ksz_desc_info tx_desc_info;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	int tx_int_cnt;
121662306a36Sopenharmony_ci	int tx_int_mask;
121762306a36Sopenharmony_ci	int tx_size;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	u8 perm_addr[ETH_ALEN];
122062306a36Sopenharmony_ci	u8 override_addr[ETH_ALEN];
122162306a36Sopenharmony_ci	u8 address[ADDITIONAL_ENTRIES][ETH_ALEN];
122262306a36Sopenharmony_ci	u8 addr_list_size;
122362306a36Sopenharmony_ci	u8 mac_override;
122462306a36Sopenharmony_ci	u8 promiscuous;
122562306a36Sopenharmony_ci	u8 all_multi;
122662306a36Sopenharmony_ci	u8 multi_list[MAX_MULTICAST_LIST][ETH_ALEN];
122762306a36Sopenharmony_ci	u8 multi_bits[HW_MULTICAST_SIZE];
122862306a36Sopenharmony_ci	u8 multi_list_size;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	u8 enabled;
123162306a36Sopenharmony_ci	u8 rx_stop;
123262306a36Sopenharmony_ci	u8 reserved2[1];
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	uint features;
123562306a36Sopenharmony_ci	uint overrides;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	void *parent;
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cienum {
124162306a36Sopenharmony_ci	PHY_NO_FLOW_CTRL,
124262306a36Sopenharmony_ci	PHY_FLOW_CTRL,
124362306a36Sopenharmony_ci	PHY_TX_ONLY,
124462306a36Sopenharmony_ci	PHY_RX_ONLY
124562306a36Sopenharmony_ci};
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci/**
124862306a36Sopenharmony_ci * struct ksz_port - Virtual port data structure
124962306a36Sopenharmony_ci * @duplex:		Duplex mode setting.  1 for half duplex, 2 for full
125062306a36Sopenharmony_ci * 			duplex, and 0 for auto, which normally results in full
125162306a36Sopenharmony_ci * 			duplex.
125262306a36Sopenharmony_ci * @speed:		Speed setting.  10 for 10 Mbit, 100 for 100 Mbit, and
125362306a36Sopenharmony_ci * 			0 for auto, which normally results in 100 Mbit.
125462306a36Sopenharmony_ci * @force_link:		Force link setting.  0 for auto-negotiation, and 1 for
125562306a36Sopenharmony_ci * 			force.
125662306a36Sopenharmony_ci * @flow_ctrl:		Flow control setting.  PHY_NO_FLOW_CTRL for no flow
125762306a36Sopenharmony_ci * 			control, and PHY_FLOW_CTRL for flow control.
125862306a36Sopenharmony_ci * 			PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100
125962306a36Sopenharmony_ci * 			Mbit PHY.
126062306a36Sopenharmony_ci * @first_port:		Index of first port this port supports.
126162306a36Sopenharmony_ci * @mib_port_cnt:	Number of ports with MIB counters.
126262306a36Sopenharmony_ci * @port_cnt:		Number of ports this port supports.
126362306a36Sopenharmony_ci * @counter:		Port statistics counter.
126462306a36Sopenharmony_ci * @hw:			Pointer to hardware structure.
126562306a36Sopenharmony_ci * @linked:		Pointer to port information linked to this port.
126662306a36Sopenharmony_ci */
126762306a36Sopenharmony_cistruct ksz_port {
126862306a36Sopenharmony_ci	u8 duplex;
126962306a36Sopenharmony_ci	u8 speed;
127062306a36Sopenharmony_ci	u8 force_link;
127162306a36Sopenharmony_ci	u8 flow_ctrl;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	int first_port;
127462306a36Sopenharmony_ci	int mib_port_cnt;
127562306a36Sopenharmony_ci	int port_cnt;
127662306a36Sopenharmony_ci	u64 counter[OID_COUNTER_LAST];
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	struct ksz_hw *hw;
127962306a36Sopenharmony_ci	struct ksz_port_info *linked;
128062306a36Sopenharmony_ci};
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci/**
128362306a36Sopenharmony_ci * struct ksz_timer_info - Timer information data structure
128462306a36Sopenharmony_ci * @timer:	Kernel timer.
128562306a36Sopenharmony_ci * @cnt:	Running timer counter.
128662306a36Sopenharmony_ci * @max:	Number of times to run timer; -1 for infinity.
128762306a36Sopenharmony_ci * @period:	Timer period in jiffies.
128862306a36Sopenharmony_ci */
128962306a36Sopenharmony_cistruct ksz_timer_info {
129062306a36Sopenharmony_ci	struct timer_list timer;
129162306a36Sopenharmony_ci	int cnt;
129262306a36Sopenharmony_ci	int max;
129362306a36Sopenharmony_ci	int period;
129462306a36Sopenharmony_ci};
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci/**
129762306a36Sopenharmony_ci * struct ksz_shared_mem - OS dependent shared memory data structure
129862306a36Sopenharmony_ci * @dma_addr:	Physical DMA address allocated.
129962306a36Sopenharmony_ci * @alloc_size:	Allocation size.
130062306a36Sopenharmony_ci * @phys:	Actual physical address used.
130162306a36Sopenharmony_ci * @alloc_virt:	Virtual address allocated.
130262306a36Sopenharmony_ci * @virt:	Actual virtual address used.
130362306a36Sopenharmony_ci */
130462306a36Sopenharmony_cistruct ksz_shared_mem {
130562306a36Sopenharmony_ci	dma_addr_t dma_addr;
130662306a36Sopenharmony_ci	uint alloc_size;
130762306a36Sopenharmony_ci	uint phys;
130862306a36Sopenharmony_ci	u8 *alloc_virt;
130962306a36Sopenharmony_ci	u8 *virt;
131062306a36Sopenharmony_ci};
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci/**
131362306a36Sopenharmony_ci * struct ksz_counter_info - OS dependent counter information data structure
131462306a36Sopenharmony_ci * @counter:	Wait queue to wakeup after counters are read.
131562306a36Sopenharmony_ci * @time:	Next time in jiffies to read counter.
131662306a36Sopenharmony_ci * @read:	Indication of counters read in full or not.
131762306a36Sopenharmony_ci */
131862306a36Sopenharmony_cistruct ksz_counter_info {
131962306a36Sopenharmony_ci	wait_queue_head_t counter;
132062306a36Sopenharmony_ci	unsigned long time;
132162306a36Sopenharmony_ci	int read;
132262306a36Sopenharmony_ci};
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci/**
132562306a36Sopenharmony_ci * struct dev_info - Network device information data structure
132662306a36Sopenharmony_ci * @dev:		Pointer to network device.
132762306a36Sopenharmony_ci * @pdev:		Pointer to PCI device.
132862306a36Sopenharmony_ci * @hw:			Hardware structure.
132962306a36Sopenharmony_ci * @desc_pool:		Physical memory used for descriptor pool.
133062306a36Sopenharmony_ci * @hwlock:		Spinlock to prevent hardware from accessing.
133162306a36Sopenharmony_ci * @lock:		Mutex lock to prevent device from accessing.
133262306a36Sopenharmony_ci * @dev_rcv:		Receive process function used.
133362306a36Sopenharmony_ci * @last_skb:		Socket buffer allocated for descriptor rx fragments.
133462306a36Sopenharmony_ci * @skb_index:		Buffer index for receiving fragments.
133562306a36Sopenharmony_ci * @skb_len:		Buffer length for receiving fragments.
133662306a36Sopenharmony_ci * @mib_read:		Workqueue to read MIB counters.
133762306a36Sopenharmony_ci * @mib_timer_info:	Timer to read MIB counters.
133862306a36Sopenharmony_ci * @counter:		Used for MIB reading.
133962306a36Sopenharmony_ci * @mtu:		Current MTU used.  The default is REGULAR_RX_BUF_SIZE;
134062306a36Sopenharmony_ci * 			the maximum is MAX_RX_BUF_SIZE.
134162306a36Sopenharmony_ci * @opened:		Counter to keep track of device open.
134262306a36Sopenharmony_ci * @rx_tasklet:		Receive processing tasklet.
134362306a36Sopenharmony_ci * @tx_tasklet:		Transmit processing tasklet.
134462306a36Sopenharmony_ci * @wol_enable:		Wake-on-LAN enable set by ethtool.
134562306a36Sopenharmony_ci * @wol_support:	Wake-on-LAN support used by ethtool.
134662306a36Sopenharmony_ci * @pme_wait:		Used for KSZ8841 power management.
134762306a36Sopenharmony_ci */
134862306a36Sopenharmony_cistruct dev_info {
134962306a36Sopenharmony_ci	struct net_device *dev;
135062306a36Sopenharmony_ci	struct pci_dev *pdev;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	struct ksz_hw hw;
135362306a36Sopenharmony_ci	struct ksz_shared_mem desc_pool;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	spinlock_t hwlock;
135662306a36Sopenharmony_ci	struct mutex lock;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	int (*dev_rcv)(struct dev_info *);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	struct sk_buff *last_skb;
136162306a36Sopenharmony_ci	int skb_index;
136262306a36Sopenharmony_ci	int skb_len;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	struct work_struct mib_read;
136562306a36Sopenharmony_ci	struct ksz_timer_info mib_timer_info;
136662306a36Sopenharmony_ci	struct ksz_counter_info counter[TOTAL_PORT_NUM];
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	int mtu;
136962306a36Sopenharmony_ci	int opened;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	struct tasklet_struct rx_tasklet;
137262306a36Sopenharmony_ci	struct tasklet_struct tx_tasklet;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	int wol_enable;
137562306a36Sopenharmony_ci	int wol_support;
137662306a36Sopenharmony_ci	unsigned long pme_wait;
137762306a36Sopenharmony_ci};
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci/**
138062306a36Sopenharmony_ci * struct dev_priv - Network device private data structure
138162306a36Sopenharmony_ci * @adapter:		Adapter device information.
138262306a36Sopenharmony_ci * @port:		Port information.
138362306a36Sopenharmony_ci * @monitor_timer_info:	Timer to monitor ports.
138462306a36Sopenharmony_ci * @proc_sem:		Semaphore for proc accessing.
138562306a36Sopenharmony_ci * @id:			Device ID.
138662306a36Sopenharmony_ci * @mii_if:		MII interface information.
138762306a36Sopenharmony_ci * @advertising:	Temporary variable to store advertised settings.
138862306a36Sopenharmony_ci * @msg_enable:		The message flags controlling driver output.
138962306a36Sopenharmony_ci * @media_state:	The connection status of the device.
139062306a36Sopenharmony_ci * @multicast:		The all multicast state of the device.
139162306a36Sopenharmony_ci * @promiscuous:	The promiscuous state of the device.
139262306a36Sopenharmony_ci */
139362306a36Sopenharmony_cistruct dev_priv {
139462306a36Sopenharmony_ci	struct dev_info *adapter;
139562306a36Sopenharmony_ci	struct ksz_port port;
139662306a36Sopenharmony_ci	struct ksz_timer_info monitor_timer_info;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	struct semaphore proc_sem;
139962306a36Sopenharmony_ci	int id;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	struct mii_if_info mii_if;
140262306a36Sopenharmony_ci	u32 advertising;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	u32 msg_enable;
140562306a36Sopenharmony_ci	int media_state;
140662306a36Sopenharmony_ci	int multicast;
140762306a36Sopenharmony_ci	int promiscuous;
140862306a36Sopenharmony_ci};
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci#define DRV_NAME		"KSZ884X PCI"
141162306a36Sopenharmony_ci#define DEVICE_NAME		"KSZ884x PCI"
141262306a36Sopenharmony_ci#define DRV_VERSION		"1.0.0"
141362306a36Sopenharmony_ci#define DRV_RELDATE		"Feb 8, 2010"
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic char version[] =
141662306a36Sopenharmony_ci	"Micrel " DEVICE_NAME " " DRV_VERSION " (" DRV_RELDATE ")";
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic u8 DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x88, 0x42, 0x01 };
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci/*
142162306a36Sopenharmony_ci * Interrupt processing primary routines
142262306a36Sopenharmony_ci */
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic inline void hw_ack_intr(struct ksz_hw *hw, uint interrupt)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	writel(interrupt, hw->io + KS884X_INTERRUPTS_STATUS);
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic inline void hw_dis_intr(struct ksz_hw *hw)
143062306a36Sopenharmony_ci{
143162306a36Sopenharmony_ci	hw->intr_blocked = hw->intr_mask;
143262306a36Sopenharmony_ci	writel(0, hw->io + KS884X_INTERRUPTS_ENABLE);
143362306a36Sopenharmony_ci	hw->intr_set = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic inline void hw_set_intr(struct ksz_hw *hw, uint interrupt)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	hw->intr_set = interrupt;
143962306a36Sopenharmony_ci	writel(interrupt, hw->io + KS884X_INTERRUPTS_ENABLE);
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic inline void hw_ena_intr(struct ksz_hw *hw)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	hw->intr_blocked = 0;
144562306a36Sopenharmony_ci	hw_set_intr(hw, hw->intr_mask);
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic inline void hw_dis_intr_bit(struct ksz_hw *hw, uint bit)
144962306a36Sopenharmony_ci{
145062306a36Sopenharmony_ci	hw->intr_mask &= ~(bit);
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic inline void hw_turn_off_intr(struct ksz_hw *hw, uint interrupt)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	u32 read_intr;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
145862306a36Sopenharmony_ci	hw->intr_set = read_intr & ~interrupt;
145962306a36Sopenharmony_ci	writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
146062306a36Sopenharmony_ci	hw_dis_intr_bit(hw, interrupt);
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/**
146462306a36Sopenharmony_ci * hw_turn_on_intr - turn on specified interrupts
146562306a36Sopenharmony_ci * @hw: 	The hardware instance.
146662306a36Sopenharmony_ci * @bit:	The interrupt bits to be on.
146762306a36Sopenharmony_ci *
146862306a36Sopenharmony_ci * This routine turns on the specified interrupts in the interrupt mask so that
146962306a36Sopenharmony_ci * those interrupts will be enabled.
147062306a36Sopenharmony_ci */
147162306a36Sopenharmony_cistatic void hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
147262306a36Sopenharmony_ci{
147362306a36Sopenharmony_ci	hw->intr_mask |= bit;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (!hw->intr_blocked)
147662306a36Sopenharmony_ci		hw_set_intr(hw, hw->intr_mask);
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic inline void hw_read_intr(struct ksz_hw *hw, uint *status)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	*status = readl(hw->io + KS884X_INTERRUPTS_STATUS);
148262306a36Sopenharmony_ci	*status = *status & hw->intr_set;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic inline void hw_restore_intr(struct ksz_hw *hw, uint interrupt)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	if (interrupt)
148862306a36Sopenharmony_ci		hw_ena_intr(hw);
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci/**
149262306a36Sopenharmony_ci * hw_block_intr - block hardware interrupts
149362306a36Sopenharmony_ci * @hw: The hardware instance.
149462306a36Sopenharmony_ci *
149562306a36Sopenharmony_ci * This function blocks all interrupts of the hardware and returns the current
149662306a36Sopenharmony_ci * interrupt enable mask so that interrupts can be restored later.
149762306a36Sopenharmony_ci *
149862306a36Sopenharmony_ci * Return the current interrupt enable mask.
149962306a36Sopenharmony_ci */
150062306a36Sopenharmony_cistatic uint hw_block_intr(struct ksz_hw *hw)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	uint interrupt = 0;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (!hw->intr_blocked) {
150562306a36Sopenharmony_ci		hw_dis_intr(hw);
150662306a36Sopenharmony_ci		interrupt = hw->intr_blocked;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci	return interrupt;
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci/*
151262306a36Sopenharmony_ci * Hardware descriptor routines
151362306a36Sopenharmony_ci */
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic inline void reset_desc(struct ksz_desc *desc, union desc_stat status)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	status.rx.hw_owned = 0;
151862306a36Sopenharmony_ci	desc->phw->ctrl.data = cpu_to_le32(status.data);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cistatic inline void release_desc(struct ksz_desc *desc)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	desc->sw.ctrl.tx.hw_owned = 1;
152462306a36Sopenharmony_ci	if (desc->sw.buf_size != desc->sw.buf.data) {
152562306a36Sopenharmony_ci		desc->sw.buf_size = desc->sw.buf.data;
152662306a36Sopenharmony_ci		desc->phw->buf.data = cpu_to_le32(desc->sw.buf.data);
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci	desc->phw->ctrl.data = cpu_to_le32(desc->sw.ctrl.data);
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic void get_rx_pkt(struct ksz_desc_info *info, struct ksz_desc **desc)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	*desc = &info->ring[info->last];
153462306a36Sopenharmony_ci	info->last++;
153562306a36Sopenharmony_ci	info->last &= info->mask;
153662306a36Sopenharmony_ci	info->avail--;
153762306a36Sopenharmony_ci	(*desc)->sw.buf.data &= ~KS_DESC_RX_MASK;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cistatic inline void set_rx_buf(struct ksz_desc *desc, u32 addr)
154162306a36Sopenharmony_ci{
154262306a36Sopenharmony_ci	desc->phw->addr = cpu_to_le32(addr);
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic inline void set_rx_len(struct ksz_desc *desc, u32 len)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	desc->sw.buf.rx.buf_size = len;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_cistatic inline void get_tx_pkt(struct ksz_desc_info *info,
155162306a36Sopenharmony_ci	struct ksz_desc **desc)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	*desc = &info->ring[info->next];
155462306a36Sopenharmony_ci	info->next++;
155562306a36Sopenharmony_ci	info->next &= info->mask;
155662306a36Sopenharmony_ci	info->avail--;
155762306a36Sopenharmony_ci	(*desc)->sw.buf.data &= ~KS_DESC_TX_MASK;
155862306a36Sopenharmony_ci}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_cistatic inline void set_tx_buf(struct ksz_desc *desc, u32 addr)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	desc->phw->addr = cpu_to_le32(addr);
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic inline void set_tx_len(struct ksz_desc *desc, u32 len)
156662306a36Sopenharmony_ci{
156762306a36Sopenharmony_ci	desc->sw.buf.tx.buf_size = len;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci/* Switch functions */
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci#define TABLE_READ			0x10
157362306a36Sopenharmony_ci#define TABLE_SEL_SHIFT			2
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci#define HW_DELAY(hw, reg)			\
157662306a36Sopenharmony_ci	do {					\
157762306a36Sopenharmony_ci		readw(hw->io + reg);		\
157862306a36Sopenharmony_ci	} while (0)
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci/**
158162306a36Sopenharmony_ci * sw_r_table - read 4 bytes of data from switch table
158262306a36Sopenharmony_ci * @hw:		The hardware instance.
158362306a36Sopenharmony_ci * @table:	The table selector.
158462306a36Sopenharmony_ci * @addr:	The address of the table entry.
158562306a36Sopenharmony_ci * @data:	Buffer to store the read data.
158662306a36Sopenharmony_ci *
158762306a36Sopenharmony_ci * This routine reads 4 bytes of data from the table of the switch.
158862306a36Sopenharmony_ci * Hardware interrupts are disabled to minimize corruption of read data.
158962306a36Sopenharmony_ci */
159062306a36Sopenharmony_cistatic void sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	u16 ctrl_addr;
159362306a36Sopenharmony_ci	uint interrupt;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	ctrl_addr = (((table << TABLE_SEL_SHIFT) | TABLE_READ) << 8) | addr;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	interrupt = hw_block_intr(hw);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
160062306a36Sopenharmony_ci	HW_DELAY(hw, KS884X_IACR_OFFSET);
160162306a36Sopenharmony_ci	*data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	hw_restore_intr(hw, interrupt);
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci/**
160762306a36Sopenharmony_ci * sw_w_table_64 - write 8 bytes of data to the switch table
160862306a36Sopenharmony_ci * @hw:		The hardware instance.
160962306a36Sopenharmony_ci * @table:	The table selector.
161062306a36Sopenharmony_ci * @addr:	The address of the table entry.
161162306a36Sopenharmony_ci * @data_hi:	The high part of data to be written (bit63 ~ bit32).
161262306a36Sopenharmony_ci * @data_lo:	The low part of data to be written (bit31 ~ bit0).
161362306a36Sopenharmony_ci *
161462306a36Sopenharmony_ci * This routine writes 8 bytes of data to the table of the switch.
161562306a36Sopenharmony_ci * Hardware interrupts are disabled to minimize corruption of written data.
161662306a36Sopenharmony_ci */
161762306a36Sopenharmony_cistatic void sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
161862306a36Sopenharmony_ci	u32 data_lo)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	u16 ctrl_addr;
162162306a36Sopenharmony_ci	uint interrupt;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	ctrl_addr = ((table << TABLE_SEL_SHIFT) << 8) | addr;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	interrupt = hw_block_intr(hw);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	writel(data_hi, hw->io + KS884X_ACC_DATA_4_OFFSET);
162862306a36Sopenharmony_ci	writel(data_lo, hw->io + KS884X_ACC_DATA_0_OFFSET);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
163162306a36Sopenharmony_ci	HW_DELAY(hw, KS884X_IACR_OFFSET);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	hw_restore_intr(hw, interrupt);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci/**
163762306a36Sopenharmony_ci * sw_w_sta_mac_table - write to the static MAC table
163862306a36Sopenharmony_ci * @hw: 	The hardware instance.
163962306a36Sopenharmony_ci * @addr:	The address of the table entry.
164062306a36Sopenharmony_ci * @mac_addr:	The MAC address.
164162306a36Sopenharmony_ci * @ports:	The port members.
164262306a36Sopenharmony_ci * @override:	The flag to override the port receive/transmit settings.
164362306a36Sopenharmony_ci * @valid:	The flag to indicate entry is valid.
164462306a36Sopenharmony_ci * @use_fid:	The flag to indicate the FID is valid.
164562306a36Sopenharmony_ci * @fid:	The FID value.
164662306a36Sopenharmony_ci *
164762306a36Sopenharmony_ci * This routine writes an entry of the static MAC table of the switch.  It
164862306a36Sopenharmony_ci * calls sw_w_table_64() to write the data.
164962306a36Sopenharmony_ci */
165062306a36Sopenharmony_cistatic void sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
165162306a36Sopenharmony_ci	u8 ports, int override, int valid, int use_fid, u8 fid)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	u32 data_hi;
165462306a36Sopenharmony_ci	u32 data_lo;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	data_lo = ((u32) mac_addr[2] << 24) |
165762306a36Sopenharmony_ci		((u32) mac_addr[3] << 16) |
165862306a36Sopenharmony_ci		((u32) mac_addr[4] << 8) | mac_addr[5];
165962306a36Sopenharmony_ci	data_hi = ((u32) mac_addr[0] << 8) | mac_addr[1];
166062306a36Sopenharmony_ci	data_hi |= (u32) ports << STATIC_MAC_FWD_PORTS_SHIFT;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	if (override)
166362306a36Sopenharmony_ci		data_hi |= STATIC_MAC_TABLE_OVERRIDE;
166462306a36Sopenharmony_ci	if (use_fid) {
166562306a36Sopenharmony_ci		data_hi |= STATIC_MAC_TABLE_USE_FID;
166662306a36Sopenharmony_ci		data_hi |= (u32) fid << STATIC_MAC_FID_SHIFT;
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci	if (valid)
166962306a36Sopenharmony_ci		data_hi |= STATIC_MAC_TABLE_VALID;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	sw_w_table_64(hw, TABLE_STATIC_MAC, addr, data_hi, data_lo);
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci/**
167562306a36Sopenharmony_ci * sw_r_vlan_table - read from the VLAN table
167662306a36Sopenharmony_ci * @hw: 	The hardware instance.
167762306a36Sopenharmony_ci * @addr:	The address of the table entry.
167862306a36Sopenharmony_ci * @vid:	Buffer to store the VID.
167962306a36Sopenharmony_ci * @fid:	Buffer to store the VID.
168062306a36Sopenharmony_ci * @member:	Buffer to store the port membership.
168162306a36Sopenharmony_ci *
168262306a36Sopenharmony_ci * This function reads an entry of the VLAN table of the switch.  It calls
168362306a36Sopenharmony_ci * sw_r_table() to get the data.
168462306a36Sopenharmony_ci *
168562306a36Sopenharmony_ci * Return 0 if the entry is valid; otherwise -1.
168662306a36Sopenharmony_ci */
168762306a36Sopenharmony_cistatic int sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
168862306a36Sopenharmony_ci	u8 *member)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	u32 data;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	sw_r_table(hw, TABLE_VLAN, addr, &data);
169362306a36Sopenharmony_ci	if (data & VLAN_TABLE_VALID) {
169462306a36Sopenharmony_ci		*vid = (u16)(data & VLAN_TABLE_VID);
169562306a36Sopenharmony_ci		*fid = (u8)((data & VLAN_TABLE_FID) >> VLAN_TABLE_FID_SHIFT);
169662306a36Sopenharmony_ci		*member = (u8)((data & VLAN_TABLE_MEMBERSHIP) >>
169762306a36Sopenharmony_ci			VLAN_TABLE_MEMBERSHIP_SHIFT);
169862306a36Sopenharmony_ci		return 0;
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci	return -1;
170162306a36Sopenharmony_ci}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci/**
170462306a36Sopenharmony_ci * port_r_mib_cnt - read MIB counter
170562306a36Sopenharmony_ci * @hw: 	The hardware instance.
170662306a36Sopenharmony_ci * @port:	The port index.
170762306a36Sopenharmony_ci * @addr:	The address of the counter.
170862306a36Sopenharmony_ci * @cnt:	Buffer to store the counter.
170962306a36Sopenharmony_ci *
171062306a36Sopenharmony_ci * This routine reads a MIB counter of the port.
171162306a36Sopenharmony_ci * Hardware interrupts are disabled to minimize corruption of read data.
171262306a36Sopenharmony_ci */
171362306a36Sopenharmony_cistatic void port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	u32 data;
171662306a36Sopenharmony_ci	u16 ctrl_addr;
171762306a36Sopenharmony_ci	uint interrupt;
171862306a36Sopenharmony_ci	int timeout;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	ctrl_addr = addr + PORT_COUNTER_NUM * port;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	interrupt = hw_block_intr(hw);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ) << 8);
172562306a36Sopenharmony_ci	writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
172662306a36Sopenharmony_ci	HW_DELAY(hw, KS884X_IACR_OFFSET);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	for (timeout = 100; timeout > 0; timeout--) {
172962306a36Sopenharmony_ci		data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci		if (data & MIB_COUNTER_VALID) {
173262306a36Sopenharmony_ci			if (data & MIB_COUNTER_OVERFLOW)
173362306a36Sopenharmony_ci				*cnt += MIB_COUNTER_VALUE + 1;
173462306a36Sopenharmony_ci			*cnt += data & MIB_COUNTER_VALUE;
173562306a36Sopenharmony_ci			break;
173662306a36Sopenharmony_ci		}
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	hw_restore_intr(hw, interrupt);
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci/**
174362306a36Sopenharmony_ci * port_r_mib_pkt - read dropped packet counts
174462306a36Sopenharmony_ci * @hw: 	The hardware instance.
174562306a36Sopenharmony_ci * @port:	The port index.
174662306a36Sopenharmony_ci * @last:	last one
174762306a36Sopenharmony_ci * @cnt:	Buffer to store the receive and transmit dropped packet counts.
174862306a36Sopenharmony_ci *
174962306a36Sopenharmony_ci * This routine reads the dropped packet counts of the port.
175062306a36Sopenharmony_ci * Hardware interrupts are disabled to minimize corruption of read data.
175162306a36Sopenharmony_ci */
175262306a36Sopenharmony_cistatic void port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	u32 cur;
175562306a36Sopenharmony_ci	u32 data;
175662306a36Sopenharmony_ci	u16 ctrl_addr;
175762306a36Sopenharmony_ci	uint interrupt;
175862306a36Sopenharmony_ci	int index;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	index = KS_MIB_PACKET_DROPPED_RX_0 + port;
176162306a36Sopenharmony_ci	do {
176262306a36Sopenharmony_ci		interrupt = hw_block_intr(hw);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci		ctrl_addr = (u16) index;
176562306a36Sopenharmony_ci		ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ)
176662306a36Sopenharmony_ci			<< 8);
176762306a36Sopenharmony_ci		writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
176862306a36Sopenharmony_ci		HW_DELAY(hw, KS884X_IACR_OFFSET);
176962306a36Sopenharmony_ci		data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci		hw_restore_intr(hw, interrupt);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci		data &= MIB_PACKET_DROPPED;
177462306a36Sopenharmony_ci		cur = *last;
177562306a36Sopenharmony_ci		if (data != cur) {
177662306a36Sopenharmony_ci			*last = data;
177762306a36Sopenharmony_ci			if (data < cur)
177862306a36Sopenharmony_ci				data += MIB_PACKET_DROPPED + 1;
177962306a36Sopenharmony_ci			data -= cur;
178062306a36Sopenharmony_ci			*cnt += data;
178162306a36Sopenharmony_ci		}
178262306a36Sopenharmony_ci		++last;
178362306a36Sopenharmony_ci		++cnt;
178462306a36Sopenharmony_ci		index -= KS_MIB_PACKET_DROPPED_TX -
178562306a36Sopenharmony_ci			KS_MIB_PACKET_DROPPED_TX_0 + 1;
178662306a36Sopenharmony_ci	} while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci/**
179062306a36Sopenharmony_ci * port_r_cnt - read MIB counters periodically
179162306a36Sopenharmony_ci * @hw: 	The hardware instance.
179262306a36Sopenharmony_ci * @port:	The port index.
179362306a36Sopenharmony_ci *
179462306a36Sopenharmony_ci * This routine is used to read the counters of the port periodically to avoid
179562306a36Sopenharmony_ci * counter overflow.  The hardware should be acquired first before calling this
179662306a36Sopenharmony_ci * routine.
179762306a36Sopenharmony_ci *
179862306a36Sopenharmony_ci * Return non-zero when not all counters not read.
179962306a36Sopenharmony_ci */
180062306a36Sopenharmony_cistatic int port_r_cnt(struct ksz_hw *hw, int port)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	struct ksz_port_mib *mib = &hw->port_mib[port];
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (mib->mib_start < PORT_COUNTER_NUM)
180562306a36Sopenharmony_ci		while (mib->cnt_ptr < PORT_COUNTER_NUM) {
180662306a36Sopenharmony_ci			port_r_mib_cnt(hw, port, mib->cnt_ptr,
180762306a36Sopenharmony_ci				&mib->counter[mib->cnt_ptr]);
180862306a36Sopenharmony_ci			++mib->cnt_ptr;
180962306a36Sopenharmony_ci		}
181062306a36Sopenharmony_ci	if (hw->mib_cnt > PORT_COUNTER_NUM)
181162306a36Sopenharmony_ci		port_r_mib_pkt(hw, port, mib->dropped,
181262306a36Sopenharmony_ci			&mib->counter[PORT_COUNTER_NUM]);
181362306a36Sopenharmony_ci	mib->cnt_ptr = 0;
181462306a36Sopenharmony_ci	return 0;
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci/**
181862306a36Sopenharmony_ci * port_init_cnt - initialize MIB counter values
181962306a36Sopenharmony_ci * @hw: 	The hardware instance.
182062306a36Sopenharmony_ci * @port:	The port index.
182162306a36Sopenharmony_ci *
182262306a36Sopenharmony_ci * This routine is used to initialize all counters to zero if the hardware
182362306a36Sopenharmony_ci * cannot do it after reset.
182462306a36Sopenharmony_ci */
182562306a36Sopenharmony_cistatic void port_init_cnt(struct ksz_hw *hw, int port)
182662306a36Sopenharmony_ci{
182762306a36Sopenharmony_ci	struct ksz_port_mib *mib = &hw->port_mib[port];
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	mib->cnt_ptr = 0;
183062306a36Sopenharmony_ci	if (mib->mib_start < PORT_COUNTER_NUM)
183162306a36Sopenharmony_ci		do {
183262306a36Sopenharmony_ci			port_r_mib_cnt(hw, port, mib->cnt_ptr,
183362306a36Sopenharmony_ci				&mib->counter[mib->cnt_ptr]);
183462306a36Sopenharmony_ci			++mib->cnt_ptr;
183562306a36Sopenharmony_ci		} while (mib->cnt_ptr < PORT_COUNTER_NUM);
183662306a36Sopenharmony_ci	if (hw->mib_cnt > PORT_COUNTER_NUM)
183762306a36Sopenharmony_ci		port_r_mib_pkt(hw, port, mib->dropped,
183862306a36Sopenharmony_ci			&mib->counter[PORT_COUNTER_NUM]);
183962306a36Sopenharmony_ci	memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
184062306a36Sopenharmony_ci	mib->cnt_ptr = 0;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci/*
184462306a36Sopenharmony_ci * Port functions
184562306a36Sopenharmony_ci */
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci/**
184862306a36Sopenharmony_ci * port_cfg - set port register bits
184962306a36Sopenharmony_ci * @hw: 	The hardware instance.
185062306a36Sopenharmony_ci * @port:	The port index.
185162306a36Sopenharmony_ci * @offset:	The offset of the port register.
185262306a36Sopenharmony_ci * @bits:	The data bits to set.
185362306a36Sopenharmony_ci * @set:	The flag indicating whether the bits are to be set or not.
185462306a36Sopenharmony_ci *
185562306a36Sopenharmony_ci * This routine sets or resets the specified bits of the port register.
185662306a36Sopenharmony_ci */
185762306a36Sopenharmony_cistatic void port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits,
185862306a36Sopenharmony_ci	int set)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	u32 addr;
186162306a36Sopenharmony_ci	u16 data;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
186462306a36Sopenharmony_ci	addr += offset;
186562306a36Sopenharmony_ci	data = readw(hw->io + addr);
186662306a36Sopenharmony_ci	if (set)
186762306a36Sopenharmony_ci		data |= bits;
186862306a36Sopenharmony_ci	else
186962306a36Sopenharmony_ci		data &= ~bits;
187062306a36Sopenharmony_ci	writew(data, hw->io + addr);
187162306a36Sopenharmony_ci}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci/**
187462306a36Sopenharmony_ci * port_r8 - read byte from port register
187562306a36Sopenharmony_ci * @hw: 	The hardware instance.
187662306a36Sopenharmony_ci * @port:	The port index.
187762306a36Sopenharmony_ci * @offset:	The offset of the port register.
187862306a36Sopenharmony_ci * @data:	Buffer to store the data.
187962306a36Sopenharmony_ci *
188062306a36Sopenharmony_ci * This routine reads a byte from the port register.
188162306a36Sopenharmony_ci */
188262306a36Sopenharmony_cistatic void port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
188362306a36Sopenharmony_ci{
188462306a36Sopenharmony_ci	u32 addr;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
188762306a36Sopenharmony_ci	addr += offset;
188862306a36Sopenharmony_ci	*data = readb(hw->io + addr);
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci/**
189262306a36Sopenharmony_ci * port_r16 - read word from port register.
189362306a36Sopenharmony_ci * @hw: 	The hardware instance.
189462306a36Sopenharmony_ci * @port:	The port index.
189562306a36Sopenharmony_ci * @offset:	The offset of the port register.
189662306a36Sopenharmony_ci * @data:	Buffer to store the data.
189762306a36Sopenharmony_ci *
189862306a36Sopenharmony_ci * This routine reads a word from the port register.
189962306a36Sopenharmony_ci */
190062306a36Sopenharmony_cistatic void port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
190162306a36Sopenharmony_ci{
190262306a36Sopenharmony_ci	u32 addr;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
190562306a36Sopenharmony_ci	addr += offset;
190662306a36Sopenharmony_ci	*data = readw(hw->io + addr);
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci/**
191062306a36Sopenharmony_ci * port_w16 - write word to port register.
191162306a36Sopenharmony_ci * @hw: 	The hardware instance.
191262306a36Sopenharmony_ci * @port:	The port index.
191362306a36Sopenharmony_ci * @offset:	The offset of the port register.
191462306a36Sopenharmony_ci * @data:	Data to write.
191562306a36Sopenharmony_ci *
191662306a36Sopenharmony_ci * This routine writes a word to the port register.
191762306a36Sopenharmony_ci */
191862306a36Sopenharmony_cistatic void port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
191962306a36Sopenharmony_ci{
192062306a36Sopenharmony_ci	u32 addr;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
192362306a36Sopenharmony_ci	addr += offset;
192462306a36Sopenharmony_ci	writew(data, hw->io + addr);
192562306a36Sopenharmony_ci}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci/**
192862306a36Sopenharmony_ci * sw_chk - check switch register bits
192962306a36Sopenharmony_ci * @hw: 	The hardware instance.
193062306a36Sopenharmony_ci * @addr:	The address of the switch register.
193162306a36Sopenharmony_ci * @bits:	The data bits to check.
193262306a36Sopenharmony_ci *
193362306a36Sopenharmony_ci * This function checks whether the specified bits of the switch register are
193462306a36Sopenharmony_ci * set or not.
193562306a36Sopenharmony_ci *
193662306a36Sopenharmony_ci * Return 0 if the bits are not set.
193762306a36Sopenharmony_ci */
193862306a36Sopenharmony_cistatic int sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
193962306a36Sopenharmony_ci{
194062306a36Sopenharmony_ci	u16 data;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	data = readw(hw->io + addr);
194362306a36Sopenharmony_ci	return (data & bits) == bits;
194462306a36Sopenharmony_ci}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci/**
194762306a36Sopenharmony_ci * sw_cfg - set switch register bits
194862306a36Sopenharmony_ci * @hw: 	The hardware instance.
194962306a36Sopenharmony_ci * @addr:	The address of the switch register.
195062306a36Sopenharmony_ci * @bits:	The data bits to set.
195162306a36Sopenharmony_ci * @set:	The flag indicating whether the bits are to be set or not.
195262306a36Sopenharmony_ci *
195362306a36Sopenharmony_ci * This function sets or resets the specified bits of the switch register.
195462306a36Sopenharmony_ci */
195562306a36Sopenharmony_cistatic void sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	u16 data;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	data = readw(hw->io + addr);
196062306a36Sopenharmony_ci	if (set)
196162306a36Sopenharmony_ci		data |= bits;
196262306a36Sopenharmony_ci	else
196362306a36Sopenharmony_ci		data &= ~bits;
196462306a36Sopenharmony_ci	writew(data, hw->io + addr);
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/* Bandwidth */
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_cistatic inline void port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
197062306a36Sopenharmony_ci{
197162306a36Sopenharmony_ci	port_cfg(hw, p,
197262306a36Sopenharmony_ci		KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
197362306a36Sopenharmony_ci}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci/* Driver set switch broadcast storm protection at 10% rate. */
197662306a36Sopenharmony_ci#define BROADCAST_STORM_PROTECTION_RATE	10
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci/* 148,800 frames * 67 ms / 100 */
197962306a36Sopenharmony_ci#define BROADCAST_STORM_VALUE		9969
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci/**
198262306a36Sopenharmony_ci * sw_cfg_broad_storm - configure broadcast storm threshold
198362306a36Sopenharmony_ci * @hw: 	The hardware instance.
198462306a36Sopenharmony_ci * @percent:	Broadcast storm threshold in percent of transmit rate.
198562306a36Sopenharmony_ci *
198662306a36Sopenharmony_ci * This routine configures the broadcast storm threshold of the switch.
198762306a36Sopenharmony_ci */
198862306a36Sopenharmony_cistatic void sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
198962306a36Sopenharmony_ci{
199062306a36Sopenharmony_ci	u16 data;
199162306a36Sopenharmony_ci	u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	if (value > BROADCAST_STORM_RATE)
199462306a36Sopenharmony_ci		value = BROADCAST_STORM_RATE;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
199762306a36Sopenharmony_ci	data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
199862306a36Sopenharmony_ci	data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
199962306a36Sopenharmony_ci	writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
200062306a36Sopenharmony_ci}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci/**
200362306a36Sopenharmony_ci * sw_get_broad_storm - get broadcast storm threshold
200462306a36Sopenharmony_ci * @hw: 	The hardware instance.
200562306a36Sopenharmony_ci * @percent:	Buffer to store the broadcast storm threshold percentage.
200662306a36Sopenharmony_ci *
200762306a36Sopenharmony_ci * This routine retrieves the broadcast storm threshold of the switch.
200862306a36Sopenharmony_ci */
200962306a36Sopenharmony_cistatic void sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	int num;
201262306a36Sopenharmony_ci	u16 data;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
201562306a36Sopenharmony_ci	num = (data & BROADCAST_STORM_RATE_HI);
201662306a36Sopenharmony_ci	num <<= 8;
201762306a36Sopenharmony_ci	num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
201862306a36Sopenharmony_ci	num = DIV_ROUND_CLOSEST(num * 100, BROADCAST_STORM_VALUE);
201962306a36Sopenharmony_ci	*percent = (u8) num;
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci/**
202362306a36Sopenharmony_ci * sw_dis_broad_storm - disable broadstorm
202462306a36Sopenharmony_ci * @hw: 	The hardware instance.
202562306a36Sopenharmony_ci * @port:	The port index.
202662306a36Sopenharmony_ci *
202762306a36Sopenharmony_ci * This routine disables the broadcast storm limit function of the switch.
202862306a36Sopenharmony_ci */
202962306a36Sopenharmony_cistatic void sw_dis_broad_storm(struct ksz_hw *hw, int port)
203062306a36Sopenharmony_ci{
203162306a36Sopenharmony_ci	port_cfg_broad_storm(hw, port, 0);
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci/**
203562306a36Sopenharmony_ci * sw_ena_broad_storm - enable broadcast storm
203662306a36Sopenharmony_ci * @hw: 	The hardware instance.
203762306a36Sopenharmony_ci * @port:	The port index.
203862306a36Sopenharmony_ci *
203962306a36Sopenharmony_ci * This routine enables the broadcast storm limit function of the switch.
204062306a36Sopenharmony_ci */
204162306a36Sopenharmony_cistatic void sw_ena_broad_storm(struct ksz_hw *hw, int port)
204262306a36Sopenharmony_ci{
204362306a36Sopenharmony_ci	sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
204462306a36Sopenharmony_ci	port_cfg_broad_storm(hw, port, 1);
204562306a36Sopenharmony_ci}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci/**
204862306a36Sopenharmony_ci * sw_init_broad_storm - initialize broadcast storm
204962306a36Sopenharmony_ci * @hw: 	The hardware instance.
205062306a36Sopenharmony_ci *
205162306a36Sopenharmony_ci * This routine initializes the broadcast storm limit function of the switch.
205262306a36Sopenharmony_ci */
205362306a36Sopenharmony_cistatic void sw_init_broad_storm(struct ksz_hw *hw)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	int port;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	hw->ksz_switch->broad_per = 1;
205862306a36Sopenharmony_ci	sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
205962306a36Sopenharmony_ci	for (port = 0; port < TOTAL_PORT_NUM; port++)
206062306a36Sopenharmony_ci		sw_dis_broad_storm(hw, port);
206162306a36Sopenharmony_ci	sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
206262306a36Sopenharmony_ci}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci/**
206562306a36Sopenharmony_ci * hw_cfg_broad_storm - configure broadcast storm
206662306a36Sopenharmony_ci * @hw: 	The hardware instance.
206762306a36Sopenharmony_ci * @percent:	Broadcast storm threshold in percent of transmit rate.
206862306a36Sopenharmony_ci *
206962306a36Sopenharmony_ci * This routine configures the broadcast storm threshold of the switch.
207062306a36Sopenharmony_ci * It is called by user functions.  The hardware should be acquired first.
207162306a36Sopenharmony_ci */
207262306a36Sopenharmony_cistatic void hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	if (percent > 100)
207562306a36Sopenharmony_ci		percent = 100;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	sw_cfg_broad_storm(hw, percent);
207862306a36Sopenharmony_ci	sw_get_broad_storm(hw, &percent);
207962306a36Sopenharmony_ci	hw->ksz_switch->broad_per = percent;
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci/**
208362306a36Sopenharmony_ci * sw_dis_prio_rate - disable switch priority rate
208462306a36Sopenharmony_ci * @hw: 	The hardware instance.
208562306a36Sopenharmony_ci * @port:	The port index.
208662306a36Sopenharmony_ci *
208762306a36Sopenharmony_ci * This routine disables the priority rate function of the switch.
208862306a36Sopenharmony_ci */
208962306a36Sopenharmony_cistatic void sw_dis_prio_rate(struct ksz_hw *hw, int port)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	u32 addr;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
209462306a36Sopenharmony_ci	addr += KS8842_PORT_IN_RATE_OFFSET;
209562306a36Sopenharmony_ci	writel(0, hw->io + addr);
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci/**
209962306a36Sopenharmony_ci * sw_init_prio_rate - initialize switch prioirty rate
210062306a36Sopenharmony_ci * @hw: 	The hardware instance.
210162306a36Sopenharmony_ci *
210262306a36Sopenharmony_ci * This routine initializes the priority rate function of the switch.
210362306a36Sopenharmony_ci */
210462306a36Sopenharmony_cistatic void sw_init_prio_rate(struct ksz_hw *hw)
210562306a36Sopenharmony_ci{
210662306a36Sopenharmony_ci	int port;
210762306a36Sopenharmony_ci	int prio;
210862306a36Sopenharmony_ci	struct ksz_switch *sw = hw->ksz_switch;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	for (port = 0; port < TOTAL_PORT_NUM; port++) {
211162306a36Sopenharmony_ci		for (prio = 0; prio < PRIO_QUEUES; prio++) {
211262306a36Sopenharmony_ci			sw->port_cfg[port].rx_rate[prio] =
211362306a36Sopenharmony_ci			sw->port_cfg[port].tx_rate[prio] = 0;
211462306a36Sopenharmony_ci		}
211562306a36Sopenharmony_ci		sw_dis_prio_rate(hw, port);
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci/* Communication */
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_cistatic inline void port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	port_cfg(hw, p,
212462306a36Sopenharmony_ci		KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
212562306a36Sopenharmony_ci}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci/* Mirroring */
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_cistatic inline void port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
213062306a36Sopenharmony_ci{
213162306a36Sopenharmony_ci	port_cfg(hw, p,
213262306a36Sopenharmony_ci		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
213362306a36Sopenharmony_ci}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_cistatic inline void port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
213662306a36Sopenharmony_ci{
213762306a36Sopenharmony_ci	port_cfg(hw, p,
213862306a36Sopenharmony_ci		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
213962306a36Sopenharmony_ci}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_cistatic inline void port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	port_cfg(hw, p,
214462306a36Sopenharmony_ci		KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
214562306a36Sopenharmony_ci}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_cistatic inline void sw_cfg_mirror_rx_tx(struct ksz_hw *hw, int set)
214862306a36Sopenharmony_ci{
214962306a36Sopenharmony_ci	sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, SWITCH_MIRROR_RX_TX, set);
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_cistatic void sw_init_mirror(struct ksz_hw *hw)
215362306a36Sopenharmony_ci{
215462306a36Sopenharmony_ci	int port;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	for (port = 0; port < TOTAL_PORT_NUM; port++) {
215762306a36Sopenharmony_ci		port_cfg_mirror_sniffer(hw, port, 0);
215862306a36Sopenharmony_ci		port_cfg_mirror_rx(hw, port, 0);
215962306a36Sopenharmony_ci		port_cfg_mirror_tx(hw, port, 0);
216062306a36Sopenharmony_ci	}
216162306a36Sopenharmony_ci	sw_cfg_mirror_rx_tx(hw, 0);
216262306a36Sopenharmony_ci}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci/* Priority */
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_cistatic inline void port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
216762306a36Sopenharmony_ci{
216862306a36Sopenharmony_ci	port_cfg(hw, p,
216962306a36Sopenharmony_ci		KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
217062306a36Sopenharmony_ci}
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_cistatic inline void port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	port_cfg(hw, p,
217562306a36Sopenharmony_ci		KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
217662306a36Sopenharmony_ci}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_cistatic inline void port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
217962306a36Sopenharmony_ci{
218062306a36Sopenharmony_ci	port_cfg(hw, p,
218162306a36Sopenharmony_ci		KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_cistatic inline void port_cfg_prio(struct ksz_hw *hw, int p, int set)
218562306a36Sopenharmony_ci{
218662306a36Sopenharmony_ci	port_cfg(hw, p,
218762306a36Sopenharmony_ci		KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
218862306a36Sopenharmony_ci}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci/**
219162306a36Sopenharmony_ci * sw_dis_diffserv - disable switch DiffServ priority
219262306a36Sopenharmony_ci * @hw: 	The hardware instance.
219362306a36Sopenharmony_ci * @port:	The port index.
219462306a36Sopenharmony_ci *
219562306a36Sopenharmony_ci * This routine disables the DiffServ priority function of the switch.
219662306a36Sopenharmony_ci */
219762306a36Sopenharmony_cistatic void sw_dis_diffserv(struct ksz_hw *hw, int port)
219862306a36Sopenharmony_ci{
219962306a36Sopenharmony_ci	port_cfg_diffserv(hw, port, 0);
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci/**
220362306a36Sopenharmony_ci * sw_dis_802_1p - disable switch 802.1p priority
220462306a36Sopenharmony_ci * @hw: 	The hardware instance.
220562306a36Sopenharmony_ci * @port:	The port index.
220662306a36Sopenharmony_ci *
220762306a36Sopenharmony_ci * This routine disables the 802.1p priority function of the switch.
220862306a36Sopenharmony_ci */
220962306a36Sopenharmony_cistatic void sw_dis_802_1p(struct ksz_hw *hw, int port)
221062306a36Sopenharmony_ci{
221162306a36Sopenharmony_ci	port_cfg_802_1p(hw, port, 0);
221262306a36Sopenharmony_ci}
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci/**
221562306a36Sopenharmony_ci * sw_cfg_replace_null_vid -
221662306a36Sopenharmony_ci * @hw: 	The hardware instance.
221762306a36Sopenharmony_ci * @set:	The flag to disable or enable.
221862306a36Sopenharmony_ci *
221962306a36Sopenharmony_ci */
222062306a36Sopenharmony_cistatic void sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
222162306a36Sopenharmony_ci{
222262306a36Sopenharmony_ci	sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
222362306a36Sopenharmony_ci}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci/**
222662306a36Sopenharmony_ci * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping
222762306a36Sopenharmony_ci * @hw: 	The hardware instance.
222862306a36Sopenharmony_ci * @port:	The port index.
222962306a36Sopenharmony_ci * @set:	The flag to disable or enable.
223062306a36Sopenharmony_ci *
223162306a36Sopenharmony_ci * This routine enables the 802.1p priority re-mapping function of the switch.
223262306a36Sopenharmony_ci * That allows 802.1p priority field to be replaced with the port's default
223362306a36Sopenharmony_ci * tag's priority value if the ingress packet's 802.1p priority has a higher
223462306a36Sopenharmony_ci * priority than port's default tag's priority.
223562306a36Sopenharmony_ci */
223662306a36Sopenharmony_cistatic void sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	port_cfg_replace_vid(hw, port, set);
223962306a36Sopenharmony_ci}
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci/**
224262306a36Sopenharmony_ci * sw_cfg_port_based - configure switch port based priority
224362306a36Sopenharmony_ci * @hw: 	The hardware instance.
224462306a36Sopenharmony_ci * @port:	The port index.
224562306a36Sopenharmony_ci * @prio:	The priority to set.
224662306a36Sopenharmony_ci *
224762306a36Sopenharmony_ci * This routine configures the port based priority of the switch.
224862306a36Sopenharmony_ci */
224962306a36Sopenharmony_cistatic void sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
225062306a36Sopenharmony_ci{
225162306a36Sopenharmony_ci	u16 data;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	if (prio > PORT_BASED_PRIORITY_BASE)
225462306a36Sopenharmony_ci		prio = PORT_BASED_PRIORITY_BASE;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	hw->ksz_switch->port_cfg[port].port_prio = prio;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
225962306a36Sopenharmony_ci	data &= ~PORT_BASED_PRIORITY_MASK;
226062306a36Sopenharmony_ci	data |= prio << PORT_BASED_PRIORITY_SHIFT;
226162306a36Sopenharmony_ci	port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
226262306a36Sopenharmony_ci}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci/**
226562306a36Sopenharmony_ci * sw_dis_multi_queue - disable transmit multiple queues
226662306a36Sopenharmony_ci * @hw: 	The hardware instance.
226762306a36Sopenharmony_ci * @port:	The port index.
226862306a36Sopenharmony_ci *
226962306a36Sopenharmony_ci * This routine disables the transmit multiple queues selection of the switch
227062306a36Sopenharmony_ci * port.  Only single transmit queue on the port.
227162306a36Sopenharmony_ci */
227262306a36Sopenharmony_cistatic void sw_dis_multi_queue(struct ksz_hw *hw, int port)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	port_cfg_prio(hw, port, 0);
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci/**
227862306a36Sopenharmony_ci * sw_init_prio - initialize switch priority
227962306a36Sopenharmony_ci * @hw: 	The hardware instance.
228062306a36Sopenharmony_ci *
228162306a36Sopenharmony_ci * This routine initializes the switch QoS priority functions.
228262306a36Sopenharmony_ci */
228362306a36Sopenharmony_cistatic void sw_init_prio(struct ksz_hw *hw)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	int port;
228662306a36Sopenharmony_ci	int tos;
228762306a36Sopenharmony_ci	struct ksz_switch *sw = hw->ksz_switch;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	/*
229062306a36Sopenharmony_ci	 * Init all the 802.1p tag priority value to be assigned to different
229162306a36Sopenharmony_ci	 * priority queue.
229262306a36Sopenharmony_ci	 */
229362306a36Sopenharmony_ci	sw->p_802_1p[0] = 0;
229462306a36Sopenharmony_ci	sw->p_802_1p[1] = 0;
229562306a36Sopenharmony_ci	sw->p_802_1p[2] = 1;
229662306a36Sopenharmony_ci	sw->p_802_1p[3] = 1;
229762306a36Sopenharmony_ci	sw->p_802_1p[4] = 2;
229862306a36Sopenharmony_ci	sw->p_802_1p[5] = 2;
229962306a36Sopenharmony_ci	sw->p_802_1p[6] = 3;
230062306a36Sopenharmony_ci	sw->p_802_1p[7] = 3;
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	/*
230362306a36Sopenharmony_ci	 * Init all the DiffServ priority value to be assigned to priority
230462306a36Sopenharmony_ci	 * queue 0.
230562306a36Sopenharmony_ci	 */
230662306a36Sopenharmony_ci	for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
230762306a36Sopenharmony_ci		sw->diffserv[tos] = 0;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	/* All QoS functions disabled. */
231062306a36Sopenharmony_ci	for (port = 0; port < TOTAL_PORT_NUM; port++) {
231162306a36Sopenharmony_ci		sw_dis_multi_queue(hw, port);
231262306a36Sopenharmony_ci		sw_dis_diffserv(hw, port);
231362306a36Sopenharmony_ci		sw_dis_802_1p(hw, port);
231462306a36Sopenharmony_ci		sw_cfg_replace_vid(hw, port, 0);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci		sw->port_cfg[port].port_prio = 0;
231762306a36Sopenharmony_ci		sw_cfg_port_based(hw, port, sw->port_cfg[port].port_prio);
231862306a36Sopenharmony_ci	}
231962306a36Sopenharmony_ci	sw_cfg_replace_null_vid(hw, 0);
232062306a36Sopenharmony_ci}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci/**
232362306a36Sopenharmony_ci * port_get_def_vid - get port default VID.
232462306a36Sopenharmony_ci * @hw: 	The hardware instance.
232562306a36Sopenharmony_ci * @port:	The port index.
232662306a36Sopenharmony_ci * @vid:	Buffer to store the VID.
232762306a36Sopenharmony_ci *
232862306a36Sopenharmony_ci * This routine retrieves the default VID of the port.
232962306a36Sopenharmony_ci */
233062306a36Sopenharmony_cistatic void port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
233162306a36Sopenharmony_ci{
233262306a36Sopenharmony_ci	u32 addr;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
233562306a36Sopenharmony_ci	addr += KS8842_PORT_CTRL_VID_OFFSET;
233662306a36Sopenharmony_ci	*vid = readw(hw->io + addr);
233762306a36Sopenharmony_ci}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci/**
234062306a36Sopenharmony_ci * sw_init_vlan - initialize switch VLAN
234162306a36Sopenharmony_ci * @hw: 	The hardware instance.
234262306a36Sopenharmony_ci *
234362306a36Sopenharmony_ci * This routine initializes the VLAN function of the switch.
234462306a36Sopenharmony_ci */
234562306a36Sopenharmony_cistatic void sw_init_vlan(struct ksz_hw *hw)
234662306a36Sopenharmony_ci{
234762306a36Sopenharmony_ci	int port;
234862306a36Sopenharmony_ci	int entry;
234962306a36Sopenharmony_ci	struct ksz_switch *sw = hw->ksz_switch;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	/* Read 16 VLAN entries from device's VLAN table. */
235262306a36Sopenharmony_ci	for (entry = 0; entry < VLAN_TABLE_ENTRIES; entry++) {
235362306a36Sopenharmony_ci		sw_r_vlan_table(hw, entry,
235462306a36Sopenharmony_ci			&sw->vlan_table[entry].vid,
235562306a36Sopenharmony_ci			&sw->vlan_table[entry].fid,
235662306a36Sopenharmony_ci			&sw->vlan_table[entry].member);
235762306a36Sopenharmony_ci	}
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	for (port = 0; port < TOTAL_PORT_NUM; port++) {
236062306a36Sopenharmony_ci		port_get_def_vid(hw, port, &sw->port_cfg[port].vid);
236162306a36Sopenharmony_ci		sw->port_cfg[port].member = PORT_MASK;
236262306a36Sopenharmony_ci	}
236362306a36Sopenharmony_ci}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/**
236662306a36Sopenharmony_ci * sw_cfg_port_base_vlan - configure port-based VLAN membership
236762306a36Sopenharmony_ci * @hw: 	The hardware instance.
236862306a36Sopenharmony_ci * @port:	The port index.
236962306a36Sopenharmony_ci * @member:	The port-based VLAN membership.
237062306a36Sopenharmony_ci *
237162306a36Sopenharmony_ci * This routine configures the port-based VLAN membership of the port.
237262306a36Sopenharmony_ci */
237362306a36Sopenharmony_cistatic void sw_cfg_port_base_vlan(struct ksz_hw *hw, int port, u8 member)
237462306a36Sopenharmony_ci{
237562306a36Sopenharmony_ci	u32 addr;
237662306a36Sopenharmony_ci	u8 data;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	PORT_CTRL_ADDR(port, addr);
237962306a36Sopenharmony_ci	addr += KS8842_PORT_CTRL_2_OFFSET;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	data = readb(hw->io + addr);
238262306a36Sopenharmony_ci	data &= ~PORT_VLAN_MEMBERSHIP;
238362306a36Sopenharmony_ci	data |= (member & PORT_MASK);
238462306a36Sopenharmony_ci	writeb(data, hw->io + addr);
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	hw->ksz_switch->port_cfg[port].member = member;
238762306a36Sopenharmony_ci}
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci/**
239062306a36Sopenharmony_ci * sw_set_addr - configure switch MAC address
239162306a36Sopenharmony_ci * @hw: 	The hardware instance.
239262306a36Sopenharmony_ci * @mac_addr:	The MAC address.
239362306a36Sopenharmony_ci *
239462306a36Sopenharmony_ci * This function configures the MAC address of the switch.
239562306a36Sopenharmony_ci */
239662306a36Sopenharmony_cistatic void sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
239762306a36Sopenharmony_ci{
239862306a36Sopenharmony_ci	int i;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	for (i = 0; i < 6; i += 2) {
240162306a36Sopenharmony_ci		writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
240262306a36Sopenharmony_ci		writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci}
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci/**
240762306a36Sopenharmony_ci * sw_set_global_ctrl - set switch global control
240862306a36Sopenharmony_ci * @hw: 	The hardware instance.
240962306a36Sopenharmony_ci *
241062306a36Sopenharmony_ci * This routine sets the global control of the switch function.
241162306a36Sopenharmony_ci */
241262306a36Sopenharmony_cistatic void sw_set_global_ctrl(struct ksz_hw *hw)
241362306a36Sopenharmony_ci{
241462306a36Sopenharmony_ci	u16 data;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	/* Enable switch MII flow control. */
241762306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
241862306a36Sopenharmony_ci	data |= SWITCH_FLOW_CTRL;
241962306a36Sopenharmony_ci	writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	/* Enable aggressive back off algorithm in half duplex mode. */
242462306a36Sopenharmony_ci	data |= SWITCH_AGGR_BACKOFF;
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	/* Enable automatic fast aging when link changed detected. */
242762306a36Sopenharmony_ci	data |= SWITCH_AGING_ENABLE;
242862306a36Sopenharmony_ci	data |= SWITCH_LINK_AUTO_AGING;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	if (hw->overrides & FAST_AGING)
243162306a36Sopenharmony_ci		data |= SWITCH_FAST_AGING;
243262306a36Sopenharmony_ci	else
243362306a36Sopenharmony_ci		data &= ~SWITCH_FAST_AGING;
243462306a36Sopenharmony_ci	writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	/* Enable no excessive collision drop. */
243962306a36Sopenharmony_ci	data |= NO_EXC_COLLISION_DROP;
244062306a36Sopenharmony_ci	writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
244162306a36Sopenharmony_ci}
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_cienum {
244462306a36Sopenharmony_ci	STP_STATE_DISABLED = 0,
244562306a36Sopenharmony_ci	STP_STATE_LISTENING,
244662306a36Sopenharmony_ci	STP_STATE_LEARNING,
244762306a36Sopenharmony_ci	STP_STATE_FORWARDING,
244862306a36Sopenharmony_ci	STP_STATE_BLOCKED,
244962306a36Sopenharmony_ci	STP_STATE_SIMPLE
245062306a36Sopenharmony_ci};
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci/**
245362306a36Sopenharmony_ci * port_set_stp_state - configure port spanning tree state
245462306a36Sopenharmony_ci * @hw: 	The hardware instance.
245562306a36Sopenharmony_ci * @port:	The port index.
245662306a36Sopenharmony_ci * @state:	The spanning tree state.
245762306a36Sopenharmony_ci *
245862306a36Sopenharmony_ci * This routine configures the spanning tree state of the port.
245962306a36Sopenharmony_ci */
246062306a36Sopenharmony_cistatic void port_set_stp_state(struct ksz_hw *hw, int port, int state)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	u16 data;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data);
246562306a36Sopenharmony_ci	switch (state) {
246662306a36Sopenharmony_ci	case STP_STATE_DISABLED:
246762306a36Sopenharmony_ci		data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
246862306a36Sopenharmony_ci		data |= PORT_LEARN_DISABLE;
246962306a36Sopenharmony_ci		break;
247062306a36Sopenharmony_ci	case STP_STATE_LISTENING:
247162306a36Sopenharmony_ci/*
247262306a36Sopenharmony_ci * No need to turn on transmit because of port direct mode.
247362306a36Sopenharmony_ci * Turning on receive is required if static MAC table is not setup.
247462306a36Sopenharmony_ci */
247562306a36Sopenharmony_ci		data &= ~PORT_TX_ENABLE;
247662306a36Sopenharmony_ci		data |= PORT_RX_ENABLE;
247762306a36Sopenharmony_ci		data |= PORT_LEARN_DISABLE;
247862306a36Sopenharmony_ci		break;
247962306a36Sopenharmony_ci	case STP_STATE_LEARNING:
248062306a36Sopenharmony_ci		data &= ~PORT_TX_ENABLE;
248162306a36Sopenharmony_ci		data |= PORT_RX_ENABLE;
248262306a36Sopenharmony_ci		data &= ~PORT_LEARN_DISABLE;
248362306a36Sopenharmony_ci		break;
248462306a36Sopenharmony_ci	case STP_STATE_FORWARDING:
248562306a36Sopenharmony_ci		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
248662306a36Sopenharmony_ci		data &= ~PORT_LEARN_DISABLE;
248762306a36Sopenharmony_ci		break;
248862306a36Sopenharmony_ci	case STP_STATE_BLOCKED:
248962306a36Sopenharmony_ci/*
249062306a36Sopenharmony_ci * Need to setup static MAC table with override to keep receiving BPDU
249162306a36Sopenharmony_ci * messages.  See sw_init_stp routine.
249262306a36Sopenharmony_ci */
249362306a36Sopenharmony_ci		data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
249462306a36Sopenharmony_ci		data |= PORT_LEARN_DISABLE;
249562306a36Sopenharmony_ci		break;
249662306a36Sopenharmony_ci	case STP_STATE_SIMPLE:
249762306a36Sopenharmony_ci		data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
249862306a36Sopenharmony_ci		data |= PORT_LEARN_DISABLE;
249962306a36Sopenharmony_ci		break;
250062306a36Sopenharmony_ci	}
250162306a36Sopenharmony_ci	port_w16(hw, port, KS8842_PORT_CTRL_2_OFFSET, data);
250262306a36Sopenharmony_ci	hw->ksz_switch->port_cfg[port].stp_state = state;
250362306a36Sopenharmony_ci}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci#define STP_ENTRY			0
250662306a36Sopenharmony_ci#define BROADCAST_ENTRY			1
250762306a36Sopenharmony_ci#define BRIDGE_ADDR_ENTRY		2
250862306a36Sopenharmony_ci#define IPV6_ADDR_ENTRY			3
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci/**
251162306a36Sopenharmony_ci * sw_clr_sta_mac_table - clear static MAC table
251262306a36Sopenharmony_ci * @hw: 	The hardware instance.
251362306a36Sopenharmony_ci *
251462306a36Sopenharmony_ci * This routine clears the static MAC table.
251562306a36Sopenharmony_ci */
251662306a36Sopenharmony_cistatic void sw_clr_sta_mac_table(struct ksz_hw *hw)
251762306a36Sopenharmony_ci{
251862306a36Sopenharmony_ci	struct ksz_mac_table *entry;
251962306a36Sopenharmony_ci	int i;
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	for (i = 0; i < STATIC_MAC_TABLE_ENTRIES; i++) {
252262306a36Sopenharmony_ci		entry = &hw->ksz_switch->mac_table[i];
252362306a36Sopenharmony_ci		sw_w_sta_mac_table(hw, i,
252462306a36Sopenharmony_ci			entry->mac_addr, entry->ports,
252562306a36Sopenharmony_ci			entry->override, 0,
252662306a36Sopenharmony_ci			entry->use_fid, entry->fid);
252762306a36Sopenharmony_ci	}
252862306a36Sopenharmony_ci}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci/**
253162306a36Sopenharmony_ci * sw_init_stp - initialize switch spanning tree support
253262306a36Sopenharmony_ci * @hw: 	The hardware instance.
253362306a36Sopenharmony_ci *
253462306a36Sopenharmony_ci * This routine initializes the spanning tree support of the switch.
253562306a36Sopenharmony_ci */
253662306a36Sopenharmony_cistatic void sw_init_stp(struct ksz_hw *hw)
253762306a36Sopenharmony_ci{
253862306a36Sopenharmony_ci	struct ksz_mac_table *entry;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	entry = &hw->ksz_switch->mac_table[STP_ENTRY];
254162306a36Sopenharmony_ci	entry->mac_addr[0] = 0x01;
254262306a36Sopenharmony_ci	entry->mac_addr[1] = 0x80;
254362306a36Sopenharmony_ci	entry->mac_addr[2] = 0xC2;
254462306a36Sopenharmony_ci	entry->mac_addr[3] = 0x00;
254562306a36Sopenharmony_ci	entry->mac_addr[4] = 0x00;
254662306a36Sopenharmony_ci	entry->mac_addr[5] = 0x00;
254762306a36Sopenharmony_ci	entry->ports = HOST_MASK;
254862306a36Sopenharmony_ci	entry->override = 1;
254962306a36Sopenharmony_ci	entry->valid = 1;
255062306a36Sopenharmony_ci	sw_w_sta_mac_table(hw, STP_ENTRY,
255162306a36Sopenharmony_ci		entry->mac_addr, entry->ports,
255262306a36Sopenharmony_ci		entry->override, entry->valid,
255362306a36Sopenharmony_ci		entry->use_fid, entry->fid);
255462306a36Sopenharmony_ci}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci/**
255762306a36Sopenharmony_ci * sw_block_addr - block certain packets from the host port
255862306a36Sopenharmony_ci * @hw: 	The hardware instance.
255962306a36Sopenharmony_ci *
256062306a36Sopenharmony_ci * This routine blocks certain packets from reaching to the host port.
256162306a36Sopenharmony_ci */
256262306a36Sopenharmony_cistatic void sw_block_addr(struct ksz_hw *hw)
256362306a36Sopenharmony_ci{
256462306a36Sopenharmony_ci	struct ksz_mac_table *entry;
256562306a36Sopenharmony_ci	int i;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	for (i = BROADCAST_ENTRY; i <= IPV6_ADDR_ENTRY; i++) {
256862306a36Sopenharmony_ci		entry = &hw->ksz_switch->mac_table[i];
256962306a36Sopenharmony_ci		entry->valid = 0;
257062306a36Sopenharmony_ci		sw_w_sta_mac_table(hw, i,
257162306a36Sopenharmony_ci			entry->mac_addr, entry->ports,
257262306a36Sopenharmony_ci			entry->override, entry->valid,
257362306a36Sopenharmony_ci			entry->use_fid, entry->fid);
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci}
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_cistatic inline void hw_r_phy_ctrl(struct ksz_hw *hw, int phy, u16 *data)
257862306a36Sopenharmony_ci{
257962306a36Sopenharmony_ci	*data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
258062306a36Sopenharmony_ci}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_cistatic inline void hw_w_phy_ctrl(struct ksz_hw *hw, int phy, u16 data)
258362306a36Sopenharmony_ci{
258462306a36Sopenharmony_ci	writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
258562306a36Sopenharmony_ci}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci/**
258862306a36Sopenharmony_ci * hw_r_phy - read data from PHY register
258962306a36Sopenharmony_ci * @hw: 	The hardware instance.
259062306a36Sopenharmony_ci * @port:	Port to read.
259162306a36Sopenharmony_ci * @reg:	PHY register to read.
259262306a36Sopenharmony_ci * @val:	Buffer to store the read data.
259362306a36Sopenharmony_ci *
259462306a36Sopenharmony_ci * This routine reads data from the PHY register.
259562306a36Sopenharmony_ci */
259662306a36Sopenharmony_cistatic void hw_r_phy(struct ksz_hw *hw, int port, u16 reg, u16 *val)
259762306a36Sopenharmony_ci{
259862306a36Sopenharmony_ci	int phy;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
260162306a36Sopenharmony_ci	*val = readw(hw->io + phy);
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci/**
260562306a36Sopenharmony_ci * hw_w_phy - write data to PHY register
260662306a36Sopenharmony_ci * @hw: 	The hardware instance.
260762306a36Sopenharmony_ci * @port:	Port to write.
260862306a36Sopenharmony_ci * @reg:	PHY register to write.
260962306a36Sopenharmony_ci * @val:	Word data to write.
261062306a36Sopenharmony_ci *
261162306a36Sopenharmony_ci * This routine writes data to the PHY register.
261262306a36Sopenharmony_ci */
261362306a36Sopenharmony_cistatic void hw_w_phy(struct ksz_hw *hw, int port, u16 reg, u16 val)
261462306a36Sopenharmony_ci{
261562306a36Sopenharmony_ci	int phy;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
261862306a36Sopenharmony_ci	writew(val, hw->io + phy);
261962306a36Sopenharmony_ci}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci/*
262262306a36Sopenharmony_ci * EEPROM access functions
262362306a36Sopenharmony_ci */
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci#define AT93C_CODE			0
262662306a36Sopenharmony_ci#define AT93C_WR_OFF			0x00
262762306a36Sopenharmony_ci#define AT93C_WR_ALL			0x10
262862306a36Sopenharmony_ci#define AT93C_ER_ALL			0x20
262962306a36Sopenharmony_ci#define AT93C_WR_ON			0x30
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci#define AT93C_WRITE			1
263262306a36Sopenharmony_ci#define AT93C_READ			2
263362306a36Sopenharmony_ci#define AT93C_ERASE			3
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_ci#define EEPROM_DELAY			4
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic inline void drop_gpio(struct ksz_hw *hw, u8 gpio)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	u16 data;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
264262306a36Sopenharmony_ci	data &= ~gpio;
264362306a36Sopenharmony_ci	writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
264462306a36Sopenharmony_ci}
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_cistatic inline void raise_gpio(struct ksz_hw *hw, u8 gpio)
264762306a36Sopenharmony_ci{
264862306a36Sopenharmony_ci	u16 data;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
265162306a36Sopenharmony_ci	data |= gpio;
265262306a36Sopenharmony_ci	writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_cistatic inline u8 state_gpio(struct ksz_hw *hw, u8 gpio)
265662306a36Sopenharmony_ci{
265762306a36Sopenharmony_ci	u16 data;
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci	data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
266062306a36Sopenharmony_ci	return (u8)(data & gpio);
266162306a36Sopenharmony_ci}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_cistatic void eeprom_clk(struct ksz_hw *hw)
266462306a36Sopenharmony_ci{
266562306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_SERIAL_CLOCK);
266662306a36Sopenharmony_ci	udelay(EEPROM_DELAY);
266762306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_SERIAL_CLOCK);
266862306a36Sopenharmony_ci	udelay(EEPROM_DELAY);
266962306a36Sopenharmony_ci}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_cistatic u16 spi_r(struct ksz_hw *hw)
267262306a36Sopenharmony_ci{
267362306a36Sopenharmony_ci	int i;
267462306a36Sopenharmony_ci	u16 temp = 0;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	for (i = 15; i >= 0; i--) {
267762306a36Sopenharmony_ci		raise_gpio(hw, EEPROM_SERIAL_CLOCK);
267862306a36Sopenharmony_ci		udelay(EEPROM_DELAY);
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci		temp |= (state_gpio(hw, EEPROM_DATA_IN)) ? 1 << i : 0;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci		drop_gpio(hw, EEPROM_SERIAL_CLOCK);
268362306a36Sopenharmony_ci		udelay(EEPROM_DELAY);
268462306a36Sopenharmony_ci	}
268562306a36Sopenharmony_ci	return temp;
268662306a36Sopenharmony_ci}
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_cistatic void spi_w(struct ksz_hw *hw, u16 data)
268962306a36Sopenharmony_ci{
269062306a36Sopenharmony_ci	int i;
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	for (i = 15; i >= 0; i--) {
269362306a36Sopenharmony_ci		(data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
269462306a36Sopenharmony_ci			drop_gpio(hw, EEPROM_DATA_OUT);
269562306a36Sopenharmony_ci		eeprom_clk(hw);
269662306a36Sopenharmony_ci	}
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_cistatic void spi_reg(struct ksz_hw *hw, u8 data, u8 reg)
270062306a36Sopenharmony_ci{
270162306a36Sopenharmony_ci	int i;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	/* Initial start bit */
270462306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_DATA_OUT);
270562306a36Sopenharmony_ci	eeprom_clk(hw);
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	/* AT93C operation */
270862306a36Sopenharmony_ci	for (i = 1; i >= 0; i--) {
270962306a36Sopenharmony_ci		(data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
271062306a36Sopenharmony_ci			drop_gpio(hw, EEPROM_DATA_OUT);
271162306a36Sopenharmony_ci		eeprom_clk(hw);
271262306a36Sopenharmony_ci	}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	/* Address location */
271562306a36Sopenharmony_ci	for (i = 5; i >= 0; i--) {
271662306a36Sopenharmony_ci		(reg & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
271762306a36Sopenharmony_ci			drop_gpio(hw, EEPROM_DATA_OUT);
271862306a36Sopenharmony_ci		eeprom_clk(hw);
271962306a36Sopenharmony_ci	}
272062306a36Sopenharmony_ci}
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci#define EEPROM_DATA_RESERVED		0
272362306a36Sopenharmony_ci#define EEPROM_DATA_MAC_ADDR_0		1
272462306a36Sopenharmony_ci#define EEPROM_DATA_MAC_ADDR_1		2
272562306a36Sopenharmony_ci#define EEPROM_DATA_MAC_ADDR_2		3
272662306a36Sopenharmony_ci#define EEPROM_DATA_SUBSYS_ID		4
272762306a36Sopenharmony_ci#define EEPROM_DATA_SUBSYS_VEN_ID	5
272862306a36Sopenharmony_ci#define EEPROM_DATA_PM_CAP		6
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci/* User defined EEPROM data */
273162306a36Sopenharmony_ci#define EEPROM_DATA_OTHER_MAC_ADDR	9
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci/**
273462306a36Sopenharmony_ci * eeprom_read - read from AT93C46 EEPROM
273562306a36Sopenharmony_ci * @hw: 	The hardware instance.
273662306a36Sopenharmony_ci * @reg:	The register offset.
273762306a36Sopenharmony_ci *
273862306a36Sopenharmony_ci * This function reads a word from the AT93C46 EEPROM.
273962306a36Sopenharmony_ci *
274062306a36Sopenharmony_ci * Return the data value.
274162306a36Sopenharmony_ci */
274262306a36Sopenharmony_cistatic u16 eeprom_read(struct ksz_hw *hw, u8 reg)
274362306a36Sopenharmony_ci{
274462306a36Sopenharmony_ci	u16 data;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	spi_reg(hw, AT93C_READ, reg);
274962306a36Sopenharmony_ci	data = spi_r(hw);
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	return data;
275462306a36Sopenharmony_ci}
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci/**
275762306a36Sopenharmony_ci * eeprom_write - write to AT93C46 EEPROM
275862306a36Sopenharmony_ci * @hw: 	The hardware instance.
275962306a36Sopenharmony_ci * @reg:	The register offset.
276062306a36Sopenharmony_ci * @data:	The data value.
276162306a36Sopenharmony_ci *
276262306a36Sopenharmony_ci * This procedure writes a word to the AT93C46 EEPROM.
276362306a36Sopenharmony_ci */
276462306a36Sopenharmony_cistatic void eeprom_write(struct ksz_hw *hw, u8 reg, u16 data)
276562306a36Sopenharmony_ci{
276662306a36Sopenharmony_ci	int timeout;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	/* Enable write. */
277162306a36Sopenharmony_ci	spi_reg(hw, AT93C_CODE, AT93C_WR_ON);
277262306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_CHIP_SELECT);
277362306a36Sopenharmony_ci	udelay(1);
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	/* Erase the register. */
277662306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_CHIP_SELECT);
277762306a36Sopenharmony_ci	spi_reg(hw, AT93C_ERASE, reg);
277862306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_CHIP_SELECT);
277962306a36Sopenharmony_ci	udelay(1);
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	/* Check operation complete. */
278262306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_CHIP_SELECT);
278362306a36Sopenharmony_ci	timeout = 8;
278462306a36Sopenharmony_ci	mdelay(2);
278562306a36Sopenharmony_ci	do {
278662306a36Sopenharmony_ci		mdelay(1);
278762306a36Sopenharmony_ci	} while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
278862306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_CHIP_SELECT);
278962306a36Sopenharmony_ci	udelay(1);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	/* Write the register. */
279262306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_CHIP_SELECT);
279362306a36Sopenharmony_ci	spi_reg(hw, AT93C_WRITE, reg);
279462306a36Sopenharmony_ci	spi_w(hw, data);
279562306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_CHIP_SELECT);
279662306a36Sopenharmony_ci	udelay(1);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	/* Check operation complete. */
279962306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_CHIP_SELECT);
280062306a36Sopenharmony_ci	timeout = 8;
280162306a36Sopenharmony_ci	mdelay(2);
280262306a36Sopenharmony_ci	do {
280362306a36Sopenharmony_ci		mdelay(1);
280462306a36Sopenharmony_ci	} while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
280562306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_CHIP_SELECT);
280662306a36Sopenharmony_ci	udelay(1);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	/* Disable write. */
280962306a36Sopenharmony_ci	raise_gpio(hw, EEPROM_CHIP_SELECT);
281062306a36Sopenharmony_ci	spi_reg(hw, AT93C_CODE, AT93C_WR_OFF);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
281362306a36Sopenharmony_ci}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci/*
281662306a36Sopenharmony_ci * Link detection routines
281762306a36Sopenharmony_ci */
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_cistatic u16 advertised_flow_ctrl(struct ksz_port *port, u16 ctrl)
282062306a36Sopenharmony_ci{
282162306a36Sopenharmony_ci	ctrl &= ~PORT_AUTO_NEG_SYM_PAUSE;
282262306a36Sopenharmony_ci	switch (port->flow_ctrl) {
282362306a36Sopenharmony_ci	case PHY_FLOW_CTRL:
282462306a36Sopenharmony_ci		ctrl |= PORT_AUTO_NEG_SYM_PAUSE;
282562306a36Sopenharmony_ci		break;
282662306a36Sopenharmony_ci	/* Not supported. */
282762306a36Sopenharmony_ci	case PHY_TX_ONLY:
282862306a36Sopenharmony_ci	case PHY_RX_ONLY:
282962306a36Sopenharmony_ci	default:
283062306a36Sopenharmony_ci		break;
283162306a36Sopenharmony_ci	}
283262306a36Sopenharmony_ci	return ctrl;
283362306a36Sopenharmony_ci}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_cistatic void set_flow_ctrl(struct ksz_hw *hw, int rx, int tx)
283662306a36Sopenharmony_ci{
283762306a36Sopenharmony_ci	u32 rx_cfg;
283862306a36Sopenharmony_ci	u32 tx_cfg;
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	rx_cfg = hw->rx_cfg;
284162306a36Sopenharmony_ci	tx_cfg = hw->tx_cfg;
284262306a36Sopenharmony_ci	if (rx)
284362306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_FLOW_ENABLE;
284462306a36Sopenharmony_ci	else
284562306a36Sopenharmony_ci		hw->rx_cfg &= ~DMA_RX_FLOW_ENABLE;
284662306a36Sopenharmony_ci	if (tx)
284762306a36Sopenharmony_ci		hw->tx_cfg |= DMA_TX_FLOW_ENABLE;
284862306a36Sopenharmony_ci	else
284962306a36Sopenharmony_ci		hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
285062306a36Sopenharmony_ci	if (hw->enabled) {
285162306a36Sopenharmony_ci		if (rx_cfg != hw->rx_cfg)
285262306a36Sopenharmony_ci			writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
285362306a36Sopenharmony_ci		if (tx_cfg != hw->tx_cfg)
285462306a36Sopenharmony_ci			writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci}
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_cistatic void determine_flow_ctrl(struct ksz_hw *hw, struct ksz_port *port,
285962306a36Sopenharmony_ci	u16 local, u16 remote)
286062306a36Sopenharmony_ci{
286162306a36Sopenharmony_ci	int rx;
286262306a36Sopenharmony_ci	int tx;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	if (hw->overrides & PAUSE_FLOW_CTRL)
286562306a36Sopenharmony_ci		return;
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci	rx = tx = 0;
286862306a36Sopenharmony_ci	if (port->force_link)
286962306a36Sopenharmony_ci		rx = tx = 1;
287062306a36Sopenharmony_ci	if (remote & LPA_PAUSE_CAP) {
287162306a36Sopenharmony_ci		if (local & ADVERTISE_PAUSE_CAP) {
287262306a36Sopenharmony_ci			rx = tx = 1;
287362306a36Sopenharmony_ci		} else if ((remote & LPA_PAUSE_ASYM) &&
287462306a36Sopenharmony_ci			   (local &
287562306a36Sopenharmony_ci			    (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) ==
287662306a36Sopenharmony_ci			   ADVERTISE_PAUSE_ASYM) {
287762306a36Sopenharmony_ci			tx = 1;
287862306a36Sopenharmony_ci		}
287962306a36Sopenharmony_ci	} else if (remote & LPA_PAUSE_ASYM) {
288062306a36Sopenharmony_ci		if ((local & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM))
288162306a36Sopenharmony_ci		    == (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM))
288262306a36Sopenharmony_ci			rx = 1;
288362306a36Sopenharmony_ci	}
288462306a36Sopenharmony_ci	if (!hw->ksz_switch)
288562306a36Sopenharmony_ci		set_flow_ctrl(hw, rx, tx);
288662306a36Sopenharmony_ci}
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_cistatic inline void port_cfg_change(struct ksz_hw *hw, struct ksz_port *port,
288962306a36Sopenharmony_ci	struct ksz_port_info *info, u16 link_status)
289062306a36Sopenharmony_ci{
289162306a36Sopenharmony_ci	if ((hw->features & HALF_DUPLEX_SIGNAL_BUG) &&
289262306a36Sopenharmony_ci			!(hw->overrides & PAUSE_FLOW_CTRL)) {
289362306a36Sopenharmony_ci		u32 cfg = hw->tx_cfg;
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci		/* Disable flow control in the half duplex mode. */
289662306a36Sopenharmony_ci		if (1 == info->duplex)
289762306a36Sopenharmony_ci			hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
289862306a36Sopenharmony_ci		if (hw->enabled && cfg != hw->tx_cfg)
289962306a36Sopenharmony_ci			writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
290062306a36Sopenharmony_ci	}
290162306a36Sopenharmony_ci}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci/**
290462306a36Sopenharmony_ci * port_get_link_speed - get current link status
290562306a36Sopenharmony_ci * @port: 	The port instance.
290662306a36Sopenharmony_ci *
290762306a36Sopenharmony_ci * This routine reads PHY registers to determine the current link status of the
290862306a36Sopenharmony_ci * switch ports.
290962306a36Sopenharmony_ci */
291062306a36Sopenharmony_cistatic void port_get_link_speed(struct ksz_port *port)
291162306a36Sopenharmony_ci{
291262306a36Sopenharmony_ci	uint interrupt;
291362306a36Sopenharmony_ci	struct ksz_port_info *info;
291462306a36Sopenharmony_ci	struct ksz_port_info *linked = NULL;
291562306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
291662306a36Sopenharmony_ci	u16 data;
291762306a36Sopenharmony_ci	u16 status;
291862306a36Sopenharmony_ci	u8 local;
291962306a36Sopenharmony_ci	u8 remote;
292062306a36Sopenharmony_ci	int i;
292162306a36Sopenharmony_ci	int p;
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	interrupt = hw_block_intr(hw);
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
292662306a36Sopenharmony_ci		info = &hw->port_info[p];
292762306a36Sopenharmony_ci		port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
292862306a36Sopenharmony_ci		port_r16(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci		/*
293162306a36Sopenharmony_ci		 * Link status is changing all the time even when there is no
293262306a36Sopenharmony_ci		 * cable connection!
293362306a36Sopenharmony_ci		 */
293462306a36Sopenharmony_ci		remote = status & (PORT_AUTO_NEG_COMPLETE |
293562306a36Sopenharmony_ci			PORT_STATUS_LINK_GOOD);
293662306a36Sopenharmony_ci		local = (u8) data;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci		/* No change to status. */
293962306a36Sopenharmony_ci		if (local == info->advertised && remote == info->partner)
294062306a36Sopenharmony_ci			continue;
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci		info->advertised = local;
294362306a36Sopenharmony_ci		info->partner = remote;
294462306a36Sopenharmony_ci		if (status & PORT_STATUS_LINK_GOOD) {
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci			/* Remember the first linked port. */
294762306a36Sopenharmony_ci			if (!linked)
294862306a36Sopenharmony_ci				linked = info;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci			info->tx_rate = 10 * TX_RATE_UNIT;
295162306a36Sopenharmony_ci			if (status & PORT_STATUS_SPEED_100MBIT)
295262306a36Sopenharmony_ci				info->tx_rate = 100 * TX_RATE_UNIT;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci			info->duplex = 1;
295562306a36Sopenharmony_ci			if (status & PORT_STATUS_FULL_DUPLEX)
295662306a36Sopenharmony_ci				info->duplex = 2;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci			if (media_connected != info->state) {
295962306a36Sopenharmony_ci				hw_r_phy(hw, p, KS884X_PHY_AUTO_NEG_OFFSET,
296062306a36Sopenharmony_ci					&data);
296162306a36Sopenharmony_ci				hw_r_phy(hw, p, KS884X_PHY_REMOTE_CAP_OFFSET,
296262306a36Sopenharmony_ci					&status);
296362306a36Sopenharmony_ci				determine_flow_ctrl(hw, port, data, status);
296462306a36Sopenharmony_ci				if (hw->ksz_switch) {
296562306a36Sopenharmony_ci					port_cfg_back_pressure(hw, p,
296662306a36Sopenharmony_ci						(1 == info->duplex));
296762306a36Sopenharmony_ci				}
296862306a36Sopenharmony_ci				port_cfg_change(hw, port, info, status);
296962306a36Sopenharmony_ci			}
297062306a36Sopenharmony_ci			info->state = media_connected;
297162306a36Sopenharmony_ci		} else {
297262306a36Sopenharmony_ci			/* Indicate the link just goes down. */
297362306a36Sopenharmony_ci			if (media_disconnected != info->state)
297462306a36Sopenharmony_ci				hw->port_mib[p].link_down = 1;
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci			info->state = media_disconnected;
297762306a36Sopenharmony_ci		}
297862306a36Sopenharmony_ci		hw->port_mib[p].state = (u8) info->state;
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	if (linked && media_disconnected == port->linked->state)
298262306a36Sopenharmony_ci		port->linked = linked;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	hw_restore_intr(hw, interrupt);
298562306a36Sopenharmony_ci}
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci#define PHY_RESET_TIMEOUT		10
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci/**
299062306a36Sopenharmony_ci * port_set_link_speed - set port speed
299162306a36Sopenharmony_ci * @port: 	The port instance.
299262306a36Sopenharmony_ci *
299362306a36Sopenharmony_ci * This routine sets the link speed of the switch ports.
299462306a36Sopenharmony_ci */
299562306a36Sopenharmony_cistatic void port_set_link_speed(struct ksz_port *port)
299662306a36Sopenharmony_ci{
299762306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
299862306a36Sopenharmony_ci	u16 data;
299962306a36Sopenharmony_ci	u16 cfg;
300062306a36Sopenharmony_ci	u8 status;
300162306a36Sopenharmony_ci	int i;
300262306a36Sopenharmony_ci	int p;
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
300562306a36Sopenharmony_ci		port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
300662306a36Sopenharmony_ci		port_r8(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci		cfg = 0;
300962306a36Sopenharmony_ci		if (status & PORT_STATUS_LINK_GOOD)
301062306a36Sopenharmony_ci			cfg = data;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci		data |= PORT_AUTO_NEG_ENABLE;
301362306a36Sopenharmony_ci		data = advertised_flow_ctrl(port, data);
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci		data |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |
301662306a36Sopenharmony_ci			PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci		/* Check if manual configuration is specified by the user. */
301962306a36Sopenharmony_ci		if (port->speed || port->duplex) {
302062306a36Sopenharmony_ci			if (10 == port->speed)
302162306a36Sopenharmony_ci				data &= ~(PORT_AUTO_NEG_100BTX_FD |
302262306a36Sopenharmony_ci					PORT_AUTO_NEG_100BTX);
302362306a36Sopenharmony_ci			else if (100 == port->speed)
302462306a36Sopenharmony_ci				data &= ~(PORT_AUTO_NEG_10BT_FD |
302562306a36Sopenharmony_ci					PORT_AUTO_NEG_10BT);
302662306a36Sopenharmony_ci			if (1 == port->duplex)
302762306a36Sopenharmony_ci				data &= ~(PORT_AUTO_NEG_100BTX_FD |
302862306a36Sopenharmony_ci					PORT_AUTO_NEG_10BT_FD);
302962306a36Sopenharmony_ci			else if (2 == port->duplex)
303062306a36Sopenharmony_ci				data &= ~(PORT_AUTO_NEG_100BTX |
303162306a36Sopenharmony_ci					PORT_AUTO_NEG_10BT);
303262306a36Sopenharmony_ci		}
303362306a36Sopenharmony_ci		if (data != cfg) {
303462306a36Sopenharmony_ci			data |= PORT_AUTO_NEG_RESTART;
303562306a36Sopenharmony_ci			port_w16(hw, p, KS884X_PORT_CTRL_4_OFFSET, data);
303662306a36Sopenharmony_ci		}
303762306a36Sopenharmony_ci	}
303862306a36Sopenharmony_ci}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci/**
304162306a36Sopenharmony_ci * port_force_link_speed - force port speed
304262306a36Sopenharmony_ci * @port: 	The port instance.
304362306a36Sopenharmony_ci *
304462306a36Sopenharmony_ci * This routine forces the link speed of the switch ports.
304562306a36Sopenharmony_ci */
304662306a36Sopenharmony_cistatic void port_force_link_speed(struct ksz_port *port)
304762306a36Sopenharmony_ci{
304862306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
304962306a36Sopenharmony_ci	u16 data;
305062306a36Sopenharmony_ci	int i;
305162306a36Sopenharmony_ci	int phy;
305262306a36Sopenharmony_ci	int p;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
305562306a36Sopenharmony_ci		phy = KS884X_PHY_1_CTRL_OFFSET + p * PHY_CTRL_INTERVAL;
305662306a36Sopenharmony_ci		hw_r_phy_ctrl(hw, phy, &data);
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci		data &= ~BMCR_ANENABLE;
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci		if (10 == port->speed)
306162306a36Sopenharmony_ci			data &= ~BMCR_SPEED100;
306262306a36Sopenharmony_ci		else if (100 == port->speed)
306362306a36Sopenharmony_ci			data |= BMCR_SPEED100;
306462306a36Sopenharmony_ci		if (1 == port->duplex)
306562306a36Sopenharmony_ci			data &= ~BMCR_FULLDPLX;
306662306a36Sopenharmony_ci		else if (2 == port->duplex)
306762306a36Sopenharmony_ci			data |= BMCR_FULLDPLX;
306862306a36Sopenharmony_ci		hw_w_phy_ctrl(hw, phy, data);
306962306a36Sopenharmony_ci	}
307062306a36Sopenharmony_ci}
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_cistatic void port_set_power_saving(struct ksz_port *port, int enable)
307362306a36Sopenharmony_ci{
307462306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
307562306a36Sopenharmony_ci	int i;
307662306a36Sopenharmony_ci	int p;
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++)
307962306a36Sopenharmony_ci		port_cfg(hw, p,
308062306a36Sopenharmony_ci			KS884X_PORT_CTRL_4_OFFSET, PORT_POWER_DOWN, enable);
308162306a36Sopenharmony_ci}
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci/*
308462306a36Sopenharmony_ci * KSZ8841 power management functions
308562306a36Sopenharmony_ci */
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci/**
308862306a36Sopenharmony_ci * hw_chk_wol_pme_status - check PMEN pin
308962306a36Sopenharmony_ci * @hw: 	The hardware instance.
309062306a36Sopenharmony_ci *
309162306a36Sopenharmony_ci * This function is used to check PMEN pin is asserted.
309262306a36Sopenharmony_ci *
309362306a36Sopenharmony_ci * Return 1 if PMEN pin is asserted; otherwise, 0.
309462306a36Sopenharmony_ci */
309562306a36Sopenharmony_cistatic int hw_chk_wol_pme_status(struct ksz_hw *hw)
309662306a36Sopenharmony_ci{
309762306a36Sopenharmony_ci	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
309862306a36Sopenharmony_ci	struct pci_dev *pdev = hw_priv->pdev;
309962306a36Sopenharmony_ci	u16 data;
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	if (!pdev->pm_cap)
310262306a36Sopenharmony_ci		return 0;
310362306a36Sopenharmony_ci	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
310462306a36Sopenharmony_ci	return (data & PCI_PM_CTRL_PME_STATUS) == PCI_PM_CTRL_PME_STATUS;
310562306a36Sopenharmony_ci}
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci/**
310862306a36Sopenharmony_ci * hw_clr_wol_pme_status - clear PMEN pin
310962306a36Sopenharmony_ci * @hw: 	The hardware instance.
311062306a36Sopenharmony_ci *
311162306a36Sopenharmony_ci * This routine is used to clear PME_Status to deassert PMEN pin.
311262306a36Sopenharmony_ci */
311362306a36Sopenharmony_cistatic void hw_clr_wol_pme_status(struct ksz_hw *hw)
311462306a36Sopenharmony_ci{
311562306a36Sopenharmony_ci	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
311662306a36Sopenharmony_ci	struct pci_dev *pdev = hw_priv->pdev;
311762306a36Sopenharmony_ci	u16 data;
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	if (!pdev->pm_cap)
312062306a36Sopenharmony_ci		return;
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	/* Clear PME_Status to deassert PMEN pin. */
312362306a36Sopenharmony_ci	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
312462306a36Sopenharmony_ci	data |= PCI_PM_CTRL_PME_STATUS;
312562306a36Sopenharmony_ci	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
312662306a36Sopenharmony_ci}
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci/**
312962306a36Sopenharmony_ci * hw_cfg_wol_pme - enable or disable Wake-on-LAN
313062306a36Sopenharmony_ci * @hw: 	The hardware instance.
313162306a36Sopenharmony_ci * @set:	The flag indicating whether to enable or disable.
313262306a36Sopenharmony_ci *
313362306a36Sopenharmony_ci * This routine is used to enable or disable Wake-on-LAN.
313462306a36Sopenharmony_ci */
313562306a36Sopenharmony_cistatic void hw_cfg_wol_pme(struct ksz_hw *hw, int set)
313662306a36Sopenharmony_ci{
313762306a36Sopenharmony_ci	struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
313862306a36Sopenharmony_ci	struct pci_dev *pdev = hw_priv->pdev;
313962306a36Sopenharmony_ci	u16 data;
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci	if (!pdev->pm_cap)
314262306a36Sopenharmony_ci		return;
314362306a36Sopenharmony_ci	pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
314462306a36Sopenharmony_ci	data &= ~PCI_PM_CTRL_STATE_MASK;
314562306a36Sopenharmony_ci	if (set)
314662306a36Sopenharmony_ci		data |= PCI_PM_CTRL_PME_ENABLE | PCI_D3hot;
314762306a36Sopenharmony_ci	else
314862306a36Sopenharmony_ci		data &= ~PCI_PM_CTRL_PME_ENABLE;
314962306a36Sopenharmony_ci	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
315062306a36Sopenharmony_ci}
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci/**
315362306a36Sopenharmony_ci * hw_cfg_wol - configure Wake-on-LAN features
315462306a36Sopenharmony_ci * @hw: 	The hardware instance.
315562306a36Sopenharmony_ci * @frame:	The pattern frame bit.
315662306a36Sopenharmony_ci * @set:	The flag indicating whether to enable or disable.
315762306a36Sopenharmony_ci *
315862306a36Sopenharmony_ci * This routine is used to enable or disable certain Wake-on-LAN features.
315962306a36Sopenharmony_ci */
316062306a36Sopenharmony_cistatic void hw_cfg_wol(struct ksz_hw *hw, u16 frame, int set)
316162306a36Sopenharmony_ci{
316262306a36Sopenharmony_ci	u16 data;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	data = readw(hw->io + KS8841_WOL_CTRL_OFFSET);
316562306a36Sopenharmony_ci	if (set)
316662306a36Sopenharmony_ci		data |= frame;
316762306a36Sopenharmony_ci	else
316862306a36Sopenharmony_ci		data &= ~frame;
316962306a36Sopenharmony_ci	writew(data, hw->io + KS8841_WOL_CTRL_OFFSET);
317062306a36Sopenharmony_ci}
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci/**
317362306a36Sopenharmony_ci * hw_set_wol_frame - program Wake-on-LAN pattern
317462306a36Sopenharmony_ci * @hw: 	The hardware instance.
317562306a36Sopenharmony_ci * @i:		The frame index.
317662306a36Sopenharmony_ci * @mask_size:	The size of the mask.
317762306a36Sopenharmony_ci * @mask:	Mask to ignore certain bytes in the pattern.
317862306a36Sopenharmony_ci * @frame_size:	The size of the frame.
317962306a36Sopenharmony_ci * @pattern:	The frame data.
318062306a36Sopenharmony_ci *
318162306a36Sopenharmony_ci * This routine is used to program Wake-on-LAN pattern.
318262306a36Sopenharmony_ci */
318362306a36Sopenharmony_cistatic void hw_set_wol_frame(struct ksz_hw *hw, int i, uint mask_size,
318462306a36Sopenharmony_ci	const u8 *mask, uint frame_size, const u8 *pattern)
318562306a36Sopenharmony_ci{
318662306a36Sopenharmony_ci	int bits;
318762306a36Sopenharmony_ci	int from;
318862306a36Sopenharmony_ci	int len;
318962306a36Sopenharmony_ci	int to;
319062306a36Sopenharmony_ci	u32 crc;
319162306a36Sopenharmony_ci	u8 data[64];
319262306a36Sopenharmony_ci	u8 val = 0;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	if (frame_size > mask_size * 8)
319562306a36Sopenharmony_ci		frame_size = mask_size * 8;
319662306a36Sopenharmony_ci	if (frame_size > 64)
319762306a36Sopenharmony_ci		frame_size = 64;
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	i *= 0x10;
320062306a36Sopenharmony_ci	writel(0, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i);
320162306a36Sopenharmony_ci	writel(0, hw->io + KS8841_WOL_FRAME_BYTE2_OFFSET + i);
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	bits = len = from = to = 0;
320462306a36Sopenharmony_ci	do {
320562306a36Sopenharmony_ci		if (bits) {
320662306a36Sopenharmony_ci			if ((val & 1))
320762306a36Sopenharmony_ci				data[to++] = pattern[from];
320862306a36Sopenharmony_ci			val >>= 1;
320962306a36Sopenharmony_ci			++from;
321062306a36Sopenharmony_ci			--bits;
321162306a36Sopenharmony_ci		} else {
321262306a36Sopenharmony_ci			val = mask[len];
321362306a36Sopenharmony_ci			writeb(val, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i
321462306a36Sopenharmony_ci				+ len);
321562306a36Sopenharmony_ci			++len;
321662306a36Sopenharmony_ci			if (val)
321762306a36Sopenharmony_ci				bits = 8;
321862306a36Sopenharmony_ci			else
321962306a36Sopenharmony_ci				from += 8;
322062306a36Sopenharmony_ci		}
322162306a36Sopenharmony_ci	} while (from < (int) frame_size);
322262306a36Sopenharmony_ci	if (val) {
322362306a36Sopenharmony_ci		bits = mask[len - 1];
322462306a36Sopenharmony_ci		val <<= (from % 8);
322562306a36Sopenharmony_ci		bits &= ~val;
322662306a36Sopenharmony_ci		writeb(bits, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i + len -
322762306a36Sopenharmony_ci			1);
322862306a36Sopenharmony_ci	}
322962306a36Sopenharmony_ci	crc = ether_crc(to, data);
323062306a36Sopenharmony_ci	writel(crc, hw->io + KS8841_WOL_FRAME_CRC_OFFSET + i);
323162306a36Sopenharmony_ci}
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci/**
323462306a36Sopenharmony_ci * hw_add_wol_arp - add ARP pattern
323562306a36Sopenharmony_ci * @hw: 	The hardware instance.
323662306a36Sopenharmony_ci * @ip_addr:	The IPv4 address assigned to the device.
323762306a36Sopenharmony_ci *
323862306a36Sopenharmony_ci * This routine is used to add ARP pattern for waking up the host.
323962306a36Sopenharmony_ci */
324062306a36Sopenharmony_cistatic void hw_add_wol_arp(struct ksz_hw *hw, const u8 *ip_addr)
324162306a36Sopenharmony_ci{
324262306a36Sopenharmony_ci	static const u8 mask[6] = { 0x3F, 0xF0, 0x3F, 0x00, 0xC0, 0x03 };
324362306a36Sopenharmony_ci	u8 pattern[42] = {
324462306a36Sopenharmony_ci		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
324562306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324662306a36Sopenharmony_ci		0x08, 0x06,
324762306a36Sopenharmony_ci		0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
324862306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324962306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00,
325062306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325162306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00 };
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	memcpy(&pattern[38], ip_addr, 4);
325462306a36Sopenharmony_ci	hw_set_wol_frame(hw, 3, 6, mask, 42, pattern);
325562306a36Sopenharmony_ci}
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci/**
325862306a36Sopenharmony_ci * hw_add_wol_bcast - add broadcast pattern
325962306a36Sopenharmony_ci * @hw: 	The hardware instance.
326062306a36Sopenharmony_ci *
326162306a36Sopenharmony_ci * This routine is used to add broadcast pattern for waking up the host.
326262306a36Sopenharmony_ci */
326362306a36Sopenharmony_cistatic void hw_add_wol_bcast(struct ksz_hw *hw)
326462306a36Sopenharmony_ci{
326562306a36Sopenharmony_ci	static const u8 mask[] = { 0x3F };
326662306a36Sopenharmony_ci	static const u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci	hw_set_wol_frame(hw, 2, 1, mask, ETH_ALEN, pattern);
326962306a36Sopenharmony_ci}
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci/**
327262306a36Sopenharmony_ci * hw_add_wol_mcast - add multicast pattern
327362306a36Sopenharmony_ci * @hw: 	The hardware instance.
327462306a36Sopenharmony_ci *
327562306a36Sopenharmony_ci * This routine is used to add multicast pattern for waking up the host.
327662306a36Sopenharmony_ci *
327762306a36Sopenharmony_ci * It is assumed the multicast packet is the ICMPv6 neighbor solicitation used
327862306a36Sopenharmony_ci * by IPv6 ping command.  Note that multicast packets are filtred through the
327962306a36Sopenharmony_ci * multicast hash table, so not all multicast packets can wake up the host.
328062306a36Sopenharmony_ci */
328162306a36Sopenharmony_cistatic void hw_add_wol_mcast(struct ksz_hw *hw)
328262306a36Sopenharmony_ci{
328362306a36Sopenharmony_ci	static const u8 mask[] = { 0x3F };
328462306a36Sopenharmony_ci	u8 pattern[] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	memcpy(&pattern[3], &hw->override_addr[3], 3);
328762306a36Sopenharmony_ci	hw_set_wol_frame(hw, 1, 1, mask, 6, pattern);
328862306a36Sopenharmony_ci}
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci/**
329162306a36Sopenharmony_ci * hw_add_wol_ucast - add unicast pattern
329262306a36Sopenharmony_ci * @hw: 	The hardware instance.
329362306a36Sopenharmony_ci *
329462306a36Sopenharmony_ci * This routine is used to add unicast pattern to wakeup the host.
329562306a36Sopenharmony_ci *
329662306a36Sopenharmony_ci * It is assumed the unicast packet is directed to the device, as the hardware
329762306a36Sopenharmony_ci * can only receive them in normal case.
329862306a36Sopenharmony_ci */
329962306a36Sopenharmony_cistatic void hw_add_wol_ucast(struct ksz_hw *hw)
330062306a36Sopenharmony_ci{
330162306a36Sopenharmony_ci	static const u8 mask[] = { 0x3F };
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	hw_set_wol_frame(hw, 0, 1, mask, ETH_ALEN, hw->override_addr);
330462306a36Sopenharmony_ci}
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci/**
330762306a36Sopenharmony_ci * hw_enable_wol - enable Wake-on-LAN
330862306a36Sopenharmony_ci * @hw: 	The hardware instance.
330962306a36Sopenharmony_ci * @wol_enable:	The Wake-on-LAN settings.
331062306a36Sopenharmony_ci * @net_addr:	The IPv4 address assigned to the device.
331162306a36Sopenharmony_ci *
331262306a36Sopenharmony_ci * This routine is used to enable Wake-on-LAN depending on driver settings.
331362306a36Sopenharmony_ci */
331462306a36Sopenharmony_cistatic void hw_enable_wol(struct ksz_hw *hw, u32 wol_enable, const u8 *net_addr)
331562306a36Sopenharmony_ci{
331662306a36Sopenharmony_ci	hw_cfg_wol(hw, KS8841_WOL_MAGIC_ENABLE, (wol_enable & WAKE_MAGIC));
331762306a36Sopenharmony_ci	hw_cfg_wol(hw, KS8841_WOL_FRAME0_ENABLE, (wol_enable & WAKE_UCAST));
331862306a36Sopenharmony_ci	hw_add_wol_ucast(hw);
331962306a36Sopenharmony_ci	hw_cfg_wol(hw, KS8841_WOL_FRAME1_ENABLE, (wol_enable & WAKE_MCAST));
332062306a36Sopenharmony_ci	hw_add_wol_mcast(hw);
332162306a36Sopenharmony_ci	hw_cfg_wol(hw, KS8841_WOL_FRAME2_ENABLE, (wol_enable & WAKE_BCAST));
332262306a36Sopenharmony_ci	hw_cfg_wol(hw, KS8841_WOL_FRAME3_ENABLE, (wol_enable & WAKE_ARP));
332362306a36Sopenharmony_ci	hw_add_wol_arp(hw, net_addr);
332462306a36Sopenharmony_ci}
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci/**
332762306a36Sopenharmony_ci * hw_init - check driver is correct for the hardware
332862306a36Sopenharmony_ci * @hw: 	The hardware instance.
332962306a36Sopenharmony_ci *
333062306a36Sopenharmony_ci * This function checks the hardware is correct for this driver and sets the
333162306a36Sopenharmony_ci * hardware up for proper initialization.
333262306a36Sopenharmony_ci *
333362306a36Sopenharmony_ci * Return number of ports or 0 if not right.
333462306a36Sopenharmony_ci */
333562306a36Sopenharmony_cistatic int hw_init(struct ksz_hw *hw)
333662306a36Sopenharmony_ci{
333762306a36Sopenharmony_ci	int rc = 0;
333862306a36Sopenharmony_ci	u16 data;
333962306a36Sopenharmony_ci	u16 revision;
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	/* Set bus speed to 125MHz. */
334262306a36Sopenharmony_ci	writew(BUS_SPEED_125_MHZ, hw->io + KS884X_BUS_CTRL_OFFSET);
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci	/* Check KSZ884x chip ID. */
334562306a36Sopenharmony_ci	data = readw(hw->io + KS884X_CHIP_ID_OFFSET);
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	revision = (data & KS884X_REVISION_MASK) >> KS884X_REVISION_SHIFT;
334862306a36Sopenharmony_ci	data &= KS884X_CHIP_ID_MASK_41;
334962306a36Sopenharmony_ci	if (REG_CHIP_ID_41 == data)
335062306a36Sopenharmony_ci		rc = 1;
335162306a36Sopenharmony_ci	else if (REG_CHIP_ID_42 == data)
335262306a36Sopenharmony_ci		rc = 2;
335362306a36Sopenharmony_ci	else
335462306a36Sopenharmony_ci		return 0;
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ci	/* Setup hardware features or bug workarounds. */
335762306a36Sopenharmony_ci	if (revision <= 1) {
335862306a36Sopenharmony_ci		hw->features |= SMALL_PACKET_TX_BUG;
335962306a36Sopenharmony_ci		if (1 == rc)
336062306a36Sopenharmony_ci			hw->features |= HALF_DUPLEX_SIGNAL_BUG;
336162306a36Sopenharmony_ci	}
336262306a36Sopenharmony_ci	return rc;
336362306a36Sopenharmony_ci}
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ci/**
336662306a36Sopenharmony_ci * hw_reset - reset the hardware
336762306a36Sopenharmony_ci * @hw: 	The hardware instance.
336862306a36Sopenharmony_ci *
336962306a36Sopenharmony_ci * This routine resets the hardware.
337062306a36Sopenharmony_ci */
337162306a36Sopenharmony_cistatic void hw_reset(struct ksz_hw *hw)
337262306a36Sopenharmony_ci{
337362306a36Sopenharmony_ci	writew(GLOBAL_SOFTWARE_RESET, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	/* Wait for device to reset. */
337662306a36Sopenharmony_ci	mdelay(10);
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	/* Write 0 to clear device reset. */
337962306a36Sopenharmony_ci	writew(0, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
338062306a36Sopenharmony_ci}
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci/**
338362306a36Sopenharmony_ci * hw_setup - setup the hardware
338462306a36Sopenharmony_ci * @hw: 	The hardware instance.
338562306a36Sopenharmony_ci *
338662306a36Sopenharmony_ci * This routine setup the hardware for proper operation.
338762306a36Sopenharmony_ci */
338862306a36Sopenharmony_cistatic void hw_setup(struct ksz_hw *hw)
338962306a36Sopenharmony_ci{
339062306a36Sopenharmony_ci#if SET_DEFAULT_LED
339162306a36Sopenharmony_ci	u16 data;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	/* Change default LED mode. */
339462306a36Sopenharmony_ci	data = readw(hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
339562306a36Sopenharmony_ci	data &= ~LED_MODE;
339662306a36Sopenharmony_ci	data |= SET_DEFAULT_LED;
339762306a36Sopenharmony_ci	writew(data, hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
339862306a36Sopenharmony_ci#endif
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	/* Setup transmit control. */
340162306a36Sopenharmony_ci	hw->tx_cfg = (DMA_TX_PAD_ENABLE | DMA_TX_CRC_ENABLE |
340262306a36Sopenharmony_ci		(DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_TX_ENABLE);
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	/* Setup receive control. */
340562306a36Sopenharmony_ci	hw->rx_cfg = (DMA_RX_BROADCAST | DMA_RX_UNICAST |
340662306a36Sopenharmony_ci		(DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_RX_ENABLE);
340762306a36Sopenharmony_ci	hw->rx_cfg |= KS884X_DMA_RX_MULTICAST;
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci	/* Hardware cannot handle UDP packet in IP fragments. */
341062306a36Sopenharmony_ci	hw->rx_cfg |= (DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci	if (hw->all_multi)
341362306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
341462306a36Sopenharmony_ci	if (hw->promiscuous)
341562306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_PROMISCUOUS;
341662306a36Sopenharmony_ci}
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci/**
341962306a36Sopenharmony_ci * hw_setup_intr - setup interrupt mask
342062306a36Sopenharmony_ci * @hw: 	The hardware instance.
342162306a36Sopenharmony_ci *
342262306a36Sopenharmony_ci * This routine setup the interrupt mask for proper operation.
342362306a36Sopenharmony_ci */
342462306a36Sopenharmony_cistatic void hw_setup_intr(struct ksz_hw *hw)
342562306a36Sopenharmony_ci{
342662306a36Sopenharmony_ci	hw->intr_mask = KS884X_INT_MASK | KS884X_INT_RX_OVERRUN;
342762306a36Sopenharmony_ci}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_cistatic void ksz_check_desc_num(struct ksz_desc_info *info)
343062306a36Sopenharmony_ci{
343162306a36Sopenharmony_ci#define MIN_DESC_SHIFT  2
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	int alloc = info->alloc;
343462306a36Sopenharmony_ci	int shift;
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci	shift = 0;
343762306a36Sopenharmony_ci	while (!(alloc & 1)) {
343862306a36Sopenharmony_ci		shift++;
343962306a36Sopenharmony_ci		alloc >>= 1;
344062306a36Sopenharmony_ci	}
344162306a36Sopenharmony_ci	if (alloc != 1 || shift < MIN_DESC_SHIFT) {
344262306a36Sopenharmony_ci		pr_alert("Hardware descriptor numbers not right!\n");
344362306a36Sopenharmony_ci		while (alloc) {
344462306a36Sopenharmony_ci			shift++;
344562306a36Sopenharmony_ci			alloc >>= 1;
344662306a36Sopenharmony_ci		}
344762306a36Sopenharmony_ci		if (shift < MIN_DESC_SHIFT)
344862306a36Sopenharmony_ci			shift = MIN_DESC_SHIFT;
344962306a36Sopenharmony_ci		alloc = 1 << shift;
345062306a36Sopenharmony_ci		info->alloc = alloc;
345162306a36Sopenharmony_ci	}
345262306a36Sopenharmony_ci	info->mask = info->alloc - 1;
345362306a36Sopenharmony_ci}
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_cistatic void hw_init_desc(struct ksz_desc_info *desc_info, int transmit)
345662306a36Sopenharmony_ci{
345762306a36Sopenharmony_ci	int i;
345862306a36Sopenharmony_ci	u32 phys = desc_info->ring_phys;
345962306a36Sopenharmony_ci	struct ksz_hw_desc *desc = desc_info->ring_virt;
346062306a36Sopenharmony_ci	struct ksz_desc *cur = desc_info->ring;
346162306a36Sopenharmony_ci	struct ksz_desc *previous = NULL;
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_ci	for (i = 0; i < desc_info->alloc; i++) {
346462306a36Sopenharmony_ci		cur->phw = desc++;
346562306a36Sopenharmony_ci		phys += desc_info->size;
346662306a36Sopenharmony_ci		previous = cur++;
346762306a36Sopenharmony_ci		previous->phw->next = cpu_to_le32(phys);
346862306a36Sopenharmony_ci	}
346962306a36Sopenharmony_ci	previous->phw->next = cpu_to_le32(desc_info->ring_phys);
347062306a36Sopenharmony_ci	previous->sw.buf.rx.end_of_ring = 1;
347162306a36Sopenharmony_ci	previous->phw->buf.data = cpu_to_le32(previous->sw.buf.data);
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	desc_info->avail = desc_info->alloc;
347462306a36Sopenharmony_ci	desc_info->last = desc_info->next = 0;
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	desc_info->cur = desc_info->ring;
347762306a36Sopenharmony_ci}
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci/**
348062306a36Sopenharmony_ci * hw_set_desc_base - set descriptor base addresses
348162306a36Sopenharmony_ci * @hw: 	The hardware instance.
348262306a36Sopenharmony_ci * @tx_addr:	The transmit descriptor base.
348362306a36Sopenharmony_ci * @rx_addr:	The receive descriptor base.
348462306a36Sopenharmony_ci *
348562306a36Sopenharmony_ci * This routine programs the descriptor base addresses after reset.
348662306a36Sopenharmony_ci */
348762306a36Sopenharmony_cistatic void hw_set_desc_base(struct ksz_hw *hw, u32 tx_addr, u32 rx_addr)
348862306a36Sopenharmony_ci{
348962306a36Sopenharmony_ci	/* Set base address of Tx/Rx descriptors. */
349062306a36Sopenharmony_ci	writel(tx_addr, hw->io + KS_DMA_TX_ADDR);
349162306a36Sopenharmony_ci	writel(rx_addr, hw->io + KS_DMA_RX_ADDR);
349262306a36Sopenharmony_ci}
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_cistatic void hw_reset_pkts(struct ksz_desc_info *info)
349562306a36Sopenharmony_ci{
349662306a36Sopenharmony_ci	info->cur = info->ring;
349762306a36Sopenharmony_ci	info->avail = info->alloc;
349862306a36Sopenharmony_ci	info->last = info->next = 0;
349962306a36Sopenharmony_ci}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_cistatic inline void hw_resume_rx(struct ksz_hw *hw)
350262306a36Sopenharmony_ci{
350362306a36Sopenharmony_ci	writel(DMA_START, hw->io + KS_DMA_RX_START);
350462306a36Sopenharmony_ci}
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci/**
350762306a36Sopenharmony_ci * hw_start_rx - start receiving
350862306a36Sopenharmony_ci * @hw: 	The hardware instance.
350962306a36Sopenharmony_ci *
351062306a36Sopenharmony_ci * This routine starts the receive function of the hardware.
351162306a36Sopenharmony_ci */
351262306a36Sopenharmony_cistatic void hw_start_rx(struct ksz_hw *hw)
351362306a36Sopenharmony_ci{
351462306a36Sopenharmony_ci	writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	/* Notify when the receive stops. */
351762306a36Sopenharmony_ci	hw->intr_mask |= KS884X_INT_RX_STOPPED;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	writel(DMA_START, hw->io + KS_DMA_RX_START);
352062306a36Sopenharmony_ci	hw_ack_intr(hw, KS884X_INT_RX_STOPPED);
352162306a36Sopenharmony_ci	hw->rx_stop++;
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	/* Variable overflows. */
352462306a36Sopenharmony_ci	if (0 == hw->rx_stop)
352562306a36Sopenharmony_ci		hw->rx_stop = 2;
352662306a36Sopenharmony_ci}
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci/**
352962306a36Sopenharmony_ci * hw_stop_rx - stop receiving
353062306a36Sopenharmony_ci * @hw: 	The hardware instance.
353162306a36Sopenharmony_ci *
353262306a36Sopenharmony_ci * This routine stops the receive function of the hardware.
353362306a36Sopenharmony_ci */
353462306a36Sopenharmony_cistatic void hw_stop_rx(struct ksz_hw *hw)
353562306a36Sopenharmony_ci{
353662306a36Sopenharmony_ci	hw->rx_stop = 0;
353762306a36Sopenharmony_ci	hw_turn_off_intr(hw, KS884X_INT_RX_STOPPED);
353862306a36Sopenharmony_ci	writel((hw->rx_cfg & ~DMA_RX_ENABLE), hw->io + KS_DMA_RX_CTRL);
353962306a36Sopenharmony_ci}
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci/**
354262306a36Sopenharmony_ci * hw_start_tx - start transmitting
354362306a36Sopenharmony_ci * @hw: 	The hardware instance.
354462306a36Sopenharmony_ci *
354562306a36Sopenharmony_ci * This routine starts the transmit function of the hardware.
354662306a36Sopenharmony_ci */
354762306a36Sopenharmony_cistatic void hw_start_tx(struct ksz_hw *hw)
354862306a36Sopenharmony_ci{
354962306a36Sopenharmony_ci	writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
355062306a36Sopenharmony_ci}
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci/**
355362306a36Sopenharmony_ci * hw_stop_tx - stop transmitting
355462306a36Sopenharmony_ci * @hw: 	The hardware instance.
355562306a36Sopenharmony_ci *
355662306a36Sopenharmony_ci * This routine stops the transmit function of the hardware.
355762306a36Sopenharmony_ci */
355862306a36Sopenharmony_cistatic void hw_stop_tx(struct ksz_hw *hw)
355962306a36Sopenharmony_ci{
356062306a36Sopenharmony_ci	writel((hw->tx_cfg & ~DMA_TX_ENABLE), hw->io + KS_DMA_TX_CTRL);
356162306a36Sopenharmony_ci}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci/**
356462306a36Sopenharmony_ci * hw_disable - disable hardware
356562306a36Sopenharmony_ci * @hw: 	The hardware instance.
356662306a36Sopenharmony_ci *
356762306a36Sopenharmony_ci * This routine disables the hardware.
356862306a36Sopenharmony_ci */
356962306a36Sopenharmony_cistatic void hw_disable(struct ksz_hw *hw)
357062306a36Sopenharmony_ci{
357162306a36Sopenharmony_ci	hw_stop_rx(hw);
357262306a36Sopenharmony_ci	hw_stop_tx(hw);
357362306a36Sopenharmony_ci	hw->enabled = 0;
357462306a36Sopenharmony_ci}
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci/**
357762306a36Sopenharmony_ci * hw_enable - enable hardware
357862306a36Sopenharmony_ci * @hw: 	The hardware instance.
357962306a36Sopenharmony_ci *
358062306a36Sopenharmony_ci * This routine enables the hardware.
358162306a36Sopenharmony_ci */
358262306a36Sopenharmony_cistatic void hw_enable(struct ksz_hw *hw)
358362306a36Sopenharmony_ci{
358462306a36Sopenharmony_ci	hw_start_tx(hw);
358562306a36Sopenharmony_ci	hw_start_rx(hw);
358662306a36Sopenharmony_ci	hw->enabled = 1;
358762306a36Sopenharmony_ci}
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci/**
359062306a36Sopenharmony_ci * hw_alloc_pkt - allocate enough descriptors for transmission
359162306a36Sopenharmony_ci * @hw: 	The hardware instance.
359262306a36Sopenharmony_ci * @length:	The length of the packet.
359362306a36Sopenharmony_ci * @physical:	Number of descriptors required.
359462306a36Sopenharmony_ci *
359562306a36Sopenharmony_ci * This function allocates descriptors for transmission.
359662306a36Sopenharmony_ci *
359762306a36Sopenharmony_ci * Return 0 if not successful; 1 for buffer copy; or number of descriptors.
359862306a36Sopenharmony_ci */
359962306a36Sopenharmony_cistatic int hw_alloc_pkt(struct ksz_hw *hw, int length, int physical)
360062306a36Sopenharmony_ci{
360162306a36Sopenharmony_ci	/* Always leave one descriptor free. */
360262306a36Sopenharmony_ci	if (hw->tx_desc_info.avail <= 1)
360362306a36Sopenharmony_ci		return 0;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	/* Allocate a descriptor for transmission and mark it current. */
360662306a36Sopenharmony_ci	get_tx_pkt(&hw->tx_desc_info, &hw->tx_desc_info.cur);
360762306a36Sopenharmony_ci	hw->tx_desc_info.cur->sw.buf.tx.first_seg = 1;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci	/* Keep track of number of transmit descriptors used so far. */
361062306a36Sopenharmony_ci	++hw->tx_int_cnt;
361162306a36Sopenharmony_ci	hw->tx_size += length;
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci	/* Cannot hold on too much data. */
361462306a36Sopenharmony_ci	if (hw->tx_size >= MAX_TX_HELD_SIZE)
361562306a36Sopenharmony_ci		hw->tx_int_cnt = hw->tx_int_mask + 1;
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci	if (physical > hw->tx_desc_info.avail)
361862306a36Sopenharmony_ci		return 1;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	return hw->tx_desc_info.avail;
362162306a36Sopenharmony_ci}
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci/**
362462306a36Sopenharmony_ci * hw_send_pkt - mark packet for transmission
362562306a36Sopenharmony_ci * @hw: 	The hardware instance.
362662306a36Sopenharmony_ci *
362762306a36Sopenharmony_ci * This routine marks the packet for transmission in PCI version.
362862306a36Sopenharmony_ci */
362962306a36Sopenharmony_cistatic void hw_send_pkt(struct ksz_hw *hw)
363062306a36Sopenharmony_ci{
363162306a36Sopenharmony_ci	struct ksz_desc *cur = hw->tx_desc_info.cur;
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	cur->sw.buf.tx.last_seg = 1;
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	/* Interrupt only after specified number of descriptors used. */
363662306a36Sopenharmony_ci	if (hw->tx_int_cnt > hw->tx_int_mask) {
363762306a36Sopenharmony_ci		cur->sw.buf.tx.intr = 1;
363862306a36Sopenharmony_ci		hw->tx_int_cnt = 0;
363962306a36Sopenharmony_ci		hw->tx_size = 0;
364062306a36Sopenharmony_ci	}
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	/* KSZ8842 supports port directed transmission. */
364362306a36Sopenharmony_ci	cur->sw.buf.tx.dest_port = hw->dst_ports;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	release_desc(cur);
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci	writel(0, hw->io + KS_DMA_TX_START);
364862306a36Sopenharmony_ci}
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_cistatic int empty_addr(u8 *addr)
365162306a36Sopenharmony_ci{
365262306a36Sopenharmony_ci	u32 *addr1 = (u32 *) addr;
365362306a36Sopenharmony_ci	u16 *addr2 = (u16 *) &addr[4];
365462306a36Sopenharmony_ci
365562306a36Sopenharmony_ci	return 0 == *addr1 && 0 == *addr2;
365662306a36Sopenharmony_ci}
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci/**
365962306a36Sopenharmony_ci * hw_set_addr - set MAC address
366062306a36Sopenharmony_ci * @hw: 	The hardware instance.
366162306a36Sopenharmony_ci *
366262306a36Sopenharmony_ci * This routine programs the MAC address of the hardware when the address is
366362306a36Sopenharmony_ci * overridden.
366462306a36Sopenharmony_ci */
366562306a36Sopenharmony_cistatic void hw_set_addr(struct ksz_hw *hw)
366662306a36Sopenharmony_ci{
366762306a36Sopenharmony_ci	int i;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
367062306a36Sopenharmony_ci		writeb(hw->override_addr[MAC_ADDR_ORDER(i)],
367162306a36Sopenharmony_ci			hw->io + KS884X_ADDR_0_OFFSET + i);
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci	sw_set_addr(hw, hw->override_addr);
367462306a36Sopenharmony_ci}
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_ci/**
367762306a36Sopenharmony_ci * hw_read_addr - read MAC address
367862306a36Sopenharmony_ci * @hw: 	The hardware instance.
367962306a36Sopenharmony_ci *
368062306a36Sopenharmony_ci * This routine retrieves the MAC address of the hardware.
368162306a36Sopenharmony_ci */
368262306a36Sopenharmony_cistatic void hw_read_addr(struct ksz_hw *hw)
368362306a36Sopenharmony_ci{
368462306a36Sopenharmony_ci	int i;
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
368762306a36Sopenharmony_ci		hw->perm_addr[MAC_ADDR_ORDER(i)] = readb(hw->io +
368862306a36Sopenharmony_ci			KS884X_ADDR_0_OFFSET + i);
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	if (!hw->mac_override) {
369162306a36Sopenharmony_ci		memcpy(hw->override_addr, hw->perm_addr, ETH_ALEN);
369262306a36Sopenharmony_ci		if (empty_addr(hw->override_addr)) {
369362306a36Sopenharmony_ci			memcpy(hw->perm_addr, DEFAULT_MAC_ADDRESS, ETH_ALEN);
369462306a36Sopenharmony_ci			memcpy(hw->override_addr, DEFAULT_MAC_ADDRESS,
369562306a36Sopenharmony_ci			       ETH_ALEN);
369662306a36Sopenharmony_ci			hw->override_addr[5] += hw->id;
369762306a36Sopenharmony_ci			hw_set_addr(hw);
369862306a36Sopenharmony_ci		}
369962306a36Sopenharmony_ci	}
370062306a36Sopenharmony_ci}
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_cistatic void hw_ena_add_addr(struct ksz_hw *hw, int index, u8 *mac_addr)
370362306a36Sopenharmony_ci{
370462306a36Sopenharmony_ci	int i;
370562306a36Sopenharmony_ci	u32 mac_addr_lo;
370662306a36Sopenharmony_ci	u32 mac_addr_hi;
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci	mac_addr_hi = 0;
370962306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
371062306a36Sopenharmony_ci		mac_addr_hi <<= 8;
371162306a36Sopenharmony_ci		mac_addr_hi |= mac_addr[i];
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci	mac_addr_hi |= ADD_ADDR_ENABLE;
371462306a36Sopenharmony_ci	mac_addr_lo = 0;
371562306a36Sopenharmony_ci	for (i = 2; i < 6; i++) {
371662306a36Sopenharmony_ci		mac_addr_lo <<= 8;
371762306a36Sopenharmony_ci		mac_addr_lo |= mac_addr[i];
371862306a36Sopenharmony_ci	}
371962306a36Sopenharmony_ci	index *= ADD_ADDR_INCR;
372062306a36Sopenharmony_ci
372162306a36Sopenharmony_ci	writel(mac_addr_lo, hw->io + index + KS_ADD_ADDR_0_LO);
372262306a36Sopenharmony_ci	writel(mac_addr_hi, hw->io + index + KS_ADD_ADDR_0_HI);
372362306a36Sopenharmony_ci}
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_cistatic void hw_set_add_addr(struct ksz_hw *hw)
372662306a36Sopenharmony_ci{
372762306a36Sopenharmony_ci	int i;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	for (i = 0; i < ADDITIONAL_ENTRIES; i++) {
373062306a36Sopenharmony_ci		if (empty_addr(hw->address[i]))
373162306a36Sopenharmony_ci			writel(0, hw->io + ADD_ADDR_INCR * i +
373262306a36Sopenharmony_ci				KS_ADD_ADDR_0_HI);
373362306a36Sopenharmony_ci		else
373462306a36Sopenharmony_ci			hw_ena_add_addr(hw, i, hw->address[i]);
373562306a36Sopenharmony_ci	}
373662306a36Sopenharmony_ci}
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_cistatic int hw_add_addr(struct ksz_hw *hw, const u8 *mac_addr)
373962306a36Sopenharmony_ci{
374062306a36Sopenharmony_ci	int i;
374162306a36Sopenharmony_ci	int j = ADDITIONAL_ENTRIES;
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	if (ether_addr_equal(hw->override_addr, mac_addr))
374462306a36Sopenharmony_ci		return 0;
374562306a36Sopenharmony_ci	for (i = 0; i < hw->addr_list_size; i++) {
374662306a36Sopenharmony_ci		if (ether_addr_equal(hw->address[i], mac_addr))
374762306a36Sopenharmony_ci			return 0;
374862306a36Sopenharmony_ci		if (ADDITIONAL_ENTRIES == j && empty_addr(hw->address[i]))
374962306a36Sopenharmony_ci			j = i;
375062306a36Sopenharmony_ci	}
375162306a36Sopenharmony_ci	if (j < ADDITIONAL_ENTRIES) {
375262306a36Sopenharmony_ci		memcpy(hw->address[j], mac_addr, ETH_ALEN);
375362306a36Sopenharmony_ci		hw_ena_add_addr(hw, j, hw->address[j]);
375462306a36Sopenharmony_ci		return 0;
375562306a36Sopenharmony_ci	}
375662306a36Sopenharmony_ci	return -1;
375762306a36Sopenharmony_ci}
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_cistatic int hw_del_addr(struct ksz_hw *hw, const u8 *mac_addr)
376062306a36Sopenharmony_ci{
376162306a36Sopenharmony_ci	int i;
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_ci	for (i = 0; i < hw->addr_list_size; i++) {
376462306a36Sopenharmony_ci		if (ether_addr_equal(hw->address[i], mac_addr)) {
376562306a36Sopenharmony_ci			eth_zero_addr(hw->address[i]);
376662306a36Sopenharmony_ci			writel(0, hw->io + ADD_ADDR_INCR * i +
376762306a36Sopenharmony_ci				KS_ADD_ADDR_0_HI);
376862306a36Sopenharmony_ci			return 0;
376962306a36Sopenharmony_ci		}
377062306a36Sopenharmony_ci	}
377162306a36Sopenharmony_ci	return -1;
377262306a36Sopenharmony_ci}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci/**
377562306a36Sopenharmony_ci * hw_clr_multicast - clear multicast addresses
377662306a36Sopenharmony_ci * @hw: 	The hardware instance.
377762306a36Sopenharmony_ci *
377862306a36Sopenharmony_ci * This routine removes all multicast addresses set in the hardware.
377962306a36Sopenharmony_ci */
378062306a36Sopenharmony_cistatic void hw_clr_multicast(struct ksz_hw *hw)
378162306a36Sopenharmony_ci{
378262306a36Sopenharmony_ci	int i;
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	for (i = 0; i < HW_MULTICAST_SIZE; i++) {
378562306a36Sopenharmony_ci		hw->multi_bits[i] = 0;
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci		writeb(0, hw->io + KS884X_MULTICAST_0_OFFSET + i);
378862306a36Sopenharmony_ci	}
378962306a36Sopenharmony_ci}
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci/**
379262306a36Sopenharmony_ci * hw_set_grp_addr - set multicast addresses
379362306a36Sopenharmony_ci * @hw: 	The hardware instance.
379462306a36Sopenharmony_ci *
379562306a36Sopenharmony_ci * This routine programs multicast addresses for the hardware to accept those
379662306a36Sopenharmony_ci * addresses.
379762306a36Sopenharmony_ci */
379862306a36Sopenharmony_cistatic void hw_set_grp_addr(struct ksz_hw *hw)
379962306a36Sopenharmony_ci{
380062306a36Sopenharmony_ci	int i;
380162306a36Sopenharmony_ci	int index;
380262306a36Sopenharmony_ci	int position;
380362306a36Sopenharmony_ci	int value;
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	memset(hw->multi_bits, 0, sizeof(u8) * HW_MULTICAST_SIZE);
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	for (i = 0; i < hw->multi_list_size; i++) {
380862306a36Sopenharmony_ci		position = (ether_crc(6, hw->multi_list[i]) >> 26) & 0x3f;
380962306a36Sopenharmony_ci		index = position >> 3;
381062306a36Sopenharmony_ci		value = 1 << (position & 7);
381162306a36Sopenharmony_ci		hw->multi_bits[index] |= (u8) value;
381262306a36Sopenharmony_ci	}
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	for (i = 0; i < HW_MULTICAST_SIZE; i++)
381562306a36Sopenharmony_ci		writeb(hw->multi_bits[i], hw->io + KS884X_MULTICAST_0_OFFSET +
381662306a36Sopenharmony_ci			i);
381762306a36Sopenharmony_ci}
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci/**
382062306a36Sopenharmony_ci * hw_set_multicast - enable or disable all multicast receiving
382162306a36Sopenharmony_ci * @hw: 	The hardware instance.
382262306a36Sopenharmony_ci * @multicast:	To turn on or off the all multicast feature.
382362306a36Sopenharmony_ci *
382462306a36Sopenharmony_ci * This routine enables/disables the hardware to accept all multicast packets.
382562306a36Sopenharmony_ci */
382662306a36Sopenharmony_cistatic void hw_set_multicast(struct ksz_hw *hw, u8 multicast)
382762306a36Sopenharmony_ci{
382862306a36Sopenharmony_ci	/* Stop receiving for reconfiguration. */
382962306a36Sopenharmony_ci	hw_stop_rx(hw);
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	if (multicast)
383262306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
383362306a36Sopenharmony_ci	else
383462306a36Sopenharmony_ci		hw->rx_cfg &= ~DMA_RX_ALL_MULTICAST;
383562306a36Sopenharmony_ci
383662306a36Sopenharmony_ci	if (hw->enabled)
383762306a36Sopenharmony_ci		hw_start_rx(hw);
383862306a36Sopenharmony_ci}
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_ci/**
384162306a36Sopenharmony_ci * hw_set_promiscuous - enable or disable promiscuous receiving
384262306a36Sopenharmony_ci * @hw: 	The hardware instance.
384362306a36Sopenharmony_ci * @prom:	To turn on or off the promiscuous feature.
384462306a36Sopenharmony_ci *
384562306a36Sopenharmony_ci * This routine enables/disables the hardware to accept all packets.
384662306a36Sopenharmony_ci */
384762306a36Sopenharmony_cistatic void hw_set_promiscuous(struct ksz_hw *hw, u8 prom)
384862306a36Sopenharmony_ci{
384962306a36Sopenharmony_ci	/* Stop receiving for reconfiguration. */
385062306a36Sopenharmony_ci	hw_stop_rx(hw);
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_ci	if (prom)
385362306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_PROMISCUOUS;
385462306a36Sopenharmony_ci	else
385562306a36Sopenharmony_ci		hw->rx_cfg &= ~DMA_RX_PROMISCUOUS;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	if (hw->enabled)
385862306a36Sopenharmony_ci		hw_start_rx(hw);
385962306a36Sopenharmony_ci}
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci/**
386262306a36Sopenharmony_ci * sw_enable - enable the switch
386362306a36Sopenharmony_ci * @hw: 	The hardware instance.
386462306a36Sopenharmony_ci * @enable:	The flag to enable or disable the switch
386562306a36Sopenharmony_ci *
386662306a36Sopenharmony_ci * This routine is used to enable/disable the switch in KSZ8842.
386762306a36Sopenharmony_ci */
386862306a36Sopenharmony_cistatic void sw_enable(struct ksz_hw *hw, int enable)
386962306a36Sopenharmony_ci{
387062306a36Sopenharmony_ci	int port;
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	for (port = 0; port < SWITCH_PORT_NUM; port++) {
387362306a36Sopenharmony_ci		if (hw->dev_count > 1) {
387462306a36Sopenharmony_ci			/* Set port-base vlan membership with host port. */
387562306a36Sopenharmony_ci			sw_cfg_port_base_vlan(hw, port,
387662306a36Sopenharmony_ci				HOST_MASK | (1 << port));
387762306a36Sopenharmony_ci			port_set_stp_state(hw, port, STP_STATE_DISABLED);
387862306a36Sopenharmony_ci		} else {
387962306a36Sopenharmony_ci			sw_cfg_port_base_vlan(hw, port, PORT_MASK);
388062306a36Sopenharmony_ci			port_set_stp_state(hw, port, STP_STATE_FORWARDING);
388162306a36Sopenharmony_ci		}
388262306a36Sopenharmony_ci	}
388362306a36Sopenharmony_ci	if (hw->dev_count > 1)
388462306a36Sopenharmony_ci		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
388562306a36Sopenharmony_ci	else
388662306a36Sopenharmony_ci		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_FORWARDING);
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci	if (enable)
388962306a36Sopenharmony_ci		enable = KS8842_START;
389062306a36Sopenharmony_ci	writew(enable, hw->io + KS884X_CHIP_ID_OFFSET);
389162306a36Sopenharmony_ci}
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci/**
389462306a36Sopenharmony_ci * sw_setup - setup the switch
389562306a36Sopenharmony_ci * @hw: 	The hardware instance.
389662306a36Sopenharmony_ci *
389762306a36Sopenharmony_ci * This routine setup the hardware switch engine for default operation.
389862306a36Sopenharmony_ci */
389962306a36Sopenharmony_cistatic void sw_setup(struct ksz_hw *hw)
390062306a36Sopenharmony_ci{
390162306a36Sopenharmony_ci	int port;
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_ci	sw_set_global_ctrl(hw);
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	/* Enable switch broadcast storm protection at 10% percent rate. */
390662306a36Sopenharmony_ci	sw_init_broad_storm(hw);
390762306a36Sopenharmony_ci	hw_cfg_broad_storm(hw, BROADCAST_STORM_PROTECTION_RATE);
390862306a36Sopenharmony_ci	for (port = 0; port < SWITCH_PORT_NUM; port++)
390962306a36Sopenharmony_ci		sw_ena_broad_storm(hw, port);
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	sw_init_prio(hw);
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci	sw_init_mirror(hw);
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	sw_init_prio_rate(hw);
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci	sw_init_vlan(hw);
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	if (hw->features & STP_SUPPORT)
392062306a36Sopenharmony_ci		sw_init_stp(hw);
392162306a36Sopenharmony_ci	if (!sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
392262306a36Sopenharmony_ci			SWITCH_TX_FLOW_CTRL | SWITCH_RX_FLOW_CTRL))
392362306a36Sopenharmony_ci		hw->overrides |= PAUSE_FLOW_CTRL;
392462306a36Sopenharmony_ci	sw_enable(hw, 1);
392562306a36Sopenharmony_ci}
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci/**
392862306a36Sopenharmony_ci * ksz_start_timer - start kernel timer
392962306a36Sopenharmony_ci * @info:	Kernel timer information.
393062306a36Sopenharmony_ci * @time:	The time tick.
393162306a36Sopenharmony_ci *
393262306a36Sopenharmony_ci * This routine starts the kernel timer after the specified time tick.
393362306a36Sopenharmony_ci */
393462306a36Sopenharmony_cistatic void ksz_start_timer(struct ksz_timer_info *info, int time)
393562306a36Sopenharmony_ci{
393662306a36Sopenharmony_ci	info->cnt = 0;
393762306a36Sopenharmony_ci	info->timer.expires = jiffies + time;
393862306a36Sopenharmony_ci	add_timer(&info->timer);
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	/* infinity */
394162306a36Sopenharmony_ci	info->max = -1;
394262306a36Sopenharmony_ci}
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci/**
394562306a36Sopenharmony_ci * ksz_stop_timer - stop kernel timer
394662306a36Sopenharmony_ci * @info:	Kernel timer information.
394762306a36Sopenharmony_ci *
394862306a36Sopenharmony_ci * This routine stops the kernel timer.
394962306a36Sopenharmony_ci */
395062306a36Sopenharmony_cistatic void ksz_stop_timer(struct ksz_timer_info *info)
395162306a36Sopenharmony_ci{
395262306a36Sopenharmony_ci	if (info->max) {
395362306a36Sopenharmony_ci		info->max = 0;
395462306a36Sopenharmony_ci		del_timer_sync(&info->timer);
395562306a36Sopenharmony_ci	}
395662306a36Sopenharmony_ci}
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_cistatic void ksz_init_timer(struct ksz_timer_info *info, int period,
395962306a36Sopenharmony_ci	void (*function)(struct timer_list *))
396062306a36Sopenharmony_ci{
396162306a36Sopenharmony_ci	info->max = 0;
396262306a36Sopenharmony_ci	info->period = period;
396362306a36Sopenharmony_ci	timer_setup(&info->timer, function, 0);
396462306a36Sopenharmony_ci}
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_cistatic void ksz_update_timer(struct ksz_timer_info *info)
396762306a36Sopenharmony_ci{
396862306a36Sopenharmony_ci	++info->cnt;
396962306a36Sopenharmony_ci	if (info->max > 0) {
397062306a36Sopenharmony_ci		if (info->cnt < info->max) {
397162306a36Sopenharmony_ci			info->timer.expires = jiffies + info->period;
397262306a36Sopenharmony_ci			add_timer(&info->timer);
397362306a36Sopenharmony_ci		} else
397462306a36Sopenharmony_ci			info->max = 0;
397562306a36Sopenharmony_ci	} else if (info->max < 0) {
397662306a36Sopenharmony_ci		info->timer.expires = jiffies + info->period;
397762306a36Sopenharmony_ci		add_timer(&info->timer);
397862306a36Sopenharmony_ci	}
397962306a36Sopenharmony_ci}
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci/**
398262306a36Sopenharmony_ci * ksz_alloc_soft_desc - allocate software descriptors
398362306a36Sopenharmony_ci * @desc_info:	Descriptor information structure.
398462306a36Sopenharmony_ci * @transmit:	Indication that descriptors are for transmit.
398562306a36Sopenharmony_ci *
398662306a36Sopenharmony_ci * This local function allocates software descriptors for manipulation in
398762306a36Sopenharmony_ci * memory.
398862306a36Sopenharmony_ci *
398962306a36Sopenharmony_ci * Return 0 if successful.
399062306a36Sopenharmony_ci */
399162306a36Sopenharmony_cistatic int ksz_alloc_soft_desc(struct ksz_desc_info *desc_info, int transmit)
399262306a36Sopenharmony_ci{
399362306a36Sopenharmony_ci	desc_info->ring = kcalloc(desc_info->alloc, sizeof(struct ksz_desc),
399462306a36Sopenharmony_ci				  GFP_KERNEL);
399562306a36Sopenharmony_ci	if (!desc_info->ring)
399662306a36Sopenharmony_ci		return 1;
399762306a36Sopenharmony_ci	hw_init_desc(desc_info, transmit);
399862306a36Sopenharmony_ci	return 0;
399962306a36Sopenharmony_ci}
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci/**
400262306a36Sopenharmony_ci * ksz_alloc_desc - allocate hardware descriptors
400362306a36Sopenharmony_ci * @adapter:	Adapter information structure.
400462306a36Sopenharmony_ci *
400562306a36Sopenharmony_ci * This local function allocates hardware descriptors for receiving and
400662306a36Sopenharmony_ci * transmitting.
400762306a36Sopenharmony_ci *
400862306a36Sopenharmony_ci * Return 0 if successful.
400962306a36Sopenharmony_ci */
401062306a36Sopenharmony_cistatic int ksz_alloc_desc(struct dev_info *adapter)
401162306a36Sopenharmony_ci{
401262306a36Sopenharmony_ci	struct ksz_hw *hw = &adapter->hw;
401362306a36Sopenharmony_ci	int offset;
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci	/* Allocate memory for RX & TX descriptors. */
401662306a36Sopenharmony_ci	adapter->desc_pool.alloc_size =
401762306a36Sopenharmony_ci		hw->rx_desc_info.size * hw->rx_desc_info.alloc +
401862306a36Sopenharmony_ci		hw->tx_desc_info.size * hw->tx_desc_info.alloc +
401962306a36Sopenharmony_ci		DESC_ALIGNMENT;
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci	adapter->desc_pool.alloc_virt =
402262306a36Sopenharmony_ci		dma_alloc_coherent(&adapter->pdev->dev,
402362306a36Sopenharmony_ci				   adapter->desc_pool.alloc_size,
402462306a36Sopenharmony_ci				   &adapter->desc_pool.dma_addr, GFP_KERNEL);
402562306a36Sopenharmony_ci	if (adapter->desc_pool.alloc_virt == NULL) {
402662306a36Sopenharmony_ci		adapter->desc_pool.alloc_size = 0;
402762306a36Sopenharmony_ci		return 1;
402862306a36Sopenharmony_ci	}
402962306a36Sopenharmony_ci
403062306a36Sopenharmony_ci	/* Align to the next cache line boundary. */
403162306a36Sopenharmony_ci	offset = (((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT) ?
403262306a36Sopenharmony_ci		(DESC_ALIGNMENT -
403362306a36Sopenharmony_ci		((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT)) : 0);
403462306a36Sopenharmony_ci	adapter->desc_pool.virt = adapter->desc_pool.alloc_virt + offset;
403562306a36Sopenharmony_ci	adapter->desc_pool.phys = adapter->desc_pool.dma_addr + offset;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	/* Allocate receive/transmit descriptors. */
403862306a36Sopenharmony_ci	hw->rx_desc_info.ring_virt = (struct ksz_hw_desc *)
403962306a36Sopenharmony_ci		adapter->desc_pool.virt;
404062306a36Sopenharmony_ci	hw->rx_desc_info.ring_phys = adapter->desc_pool.phys;
404162306a36Sopenharmony_ci	offset = hw->rx_desc_info.alloc * hw->rx_desc_info.size;
404262306a36Sopenharmony_ci	hw->tx_desc_info.ring_virt = (struct ksz_hw_desc *)
404362306a36Sopenharmony_ci		(adapter->desc_pool.virt + offset);
404462306a36Sopenharmony_ci	hw->tx_desc_info.ring_phys = adapter->desc_pool.phys + offset;
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	if (ksz_alloc_soft_desc(&hw->rx_desc_info, 0))
404762306a36Sopenharmony_ci		return 1;
404862306a36Sopenharmony_ci	if (ksz_alloc_soft_desc(&hw->tx_desc_info, 1))
404962306a36Sopenharmony_ci		return 1;
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	return 0;
405262306a36Sopenharmony_ci}
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci/**
405562306a36Sopenharmony_ci * free_dma_buf - release DMA buffer resources
405662306a36Sopenharmony_ci * @adapter:	Adapter information structure.
405762306a36Sopenharmony_ci * @dma_buf:	pointer to buf
405862306a36Sopenharmony_ci * @direction:	to or from device
405962306a36Sopenharmony_ci *
406062306a36Sopenharmony_ci * This routine is just a helper function to release the DMA buffer resources.
406162306a36Sopenharmony_ci */
406262306a36Sopenharmony_cistatic void free_dma_buf(struct dev_info *adapter, struct ksz_dma_buf *dma_buf,
406362306a36Sopenharmony_ci	int direction)
406462306a36Sopenharmony_ci{
406562306a36Sopenharmony_ci	dma_unmap_single(&adapter->pdev->dev, dma_buf->dma, dma_buf->len,
406662306a36Sopenharmony_ci			 direction);
406762306a36Sopenharmony_ci	dev_kfree_skb(dma_buf->skb);
406862306a36Sopenharmony_ci	dma_buf->skb = NULL;
406962306a36Sopenharmony_ci	dma_buf->dma = 0;
407062306a36Sopenharmony_ci}
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci/**
407362306a36Sopenharmony_ci * ksz_init_rx_buffers - initialize receive descriptors
407462306a36Sopenharmony_ci * @adapter:	Adapter information structure.
407562306a36Sopenharmony_ci *
407662306a36Sopenharmony_ci * This routine initializes DMA buffers for receiving.
407762306a36Sopenharmony_ci */
407862306a36Sopenharmony_cistatic void ksz_init_rx_buffers(struct dev_info *adapter)
407962306a36Sopenharmony_ci{
408062306a36Sopenharmony_ci	int i;
408162306a36Sopenharmony_ci	struct ksz_desc *desc;
408262306a36Sopenharmony_ci	struct ksz_dma_buf *dma_buf;
408362306a36Sopenharmony_ci	struct ksz_hw *hw = &adapter->hw;
408462306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->rx_desc_info;
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci	for (i = 0; i < hw->rx_desc_info.alloc; i++) {
408762306a36Sopenharmony_ci		get_rx_pkt(info, &desc);
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci		dma_buf = DMA_BUFFER(desc);
409062306a36Sopenharmony_ci		if (dma_buf->skb && dma_buf->len != adapter->mtu)
409162306a36Sopenharmony_ci			free_dma_buf(adapter, dma_buf, DMA_FROM_DEVICE);
409262306a36Sopenharmony_ci		dma_buf->len = adapter->mtu;
409362306a36Sopenharmony_ci		if (!dma_buf->skb)
409462306a36Sopenharmony_ci			dma_buf->skb = alloc_skb(dma_buf->len, GFP_ATOMIC);
409562306a36Sopenharmony_ci		if (dma_buf->skb && !dma_buf->dma)
409662306a36Sopenharmony_ci			dma_buf->dma = dma_map_single(&adapter->pdev->dev,
409762306a36Sopenharmony_ci						skb_tail_pointer(dma_buf->skb),
409862306a36Sopenharmony_ci						dma_buf->len,
409962306a36Sopenharmony_ci						DMA_FROM_DEVICE);
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci		/* Set descriptor. */
410262306a36Sopenharmony_ci		set_rx_buf(desc, dma_buf->dma);
410362306a36Sopenharmony_ci		set_rx_len(desc, dma_buf->len);
410462306a36Sopenharmony_ci		release_desc(desc);
410562306a36Sopenharmony_ci	}
410662306a36Sopenharmony_ci}
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci/**
410962306a36Sopenharmony_ci * ksz_alloc_mem - allocate memory for hardware descriptors
411062306a36Sopenharmony_ci * @adapter:	Adapter information structure.
411162306a36Sopenharmony_ci *
411262306a36Sopenharmony_ci * This function allocates memory for use by hardware descriptors for receiving
411362306a36Sopenharmony_ci * and transmitting.
411462306a36Sopenharmony_ci *
411562306a36Sopenharmony_ci * Return 0 if successful.
411662306a36Sopenharmony_ci */
411762306a36Sopenharmony_cistatic int ksz_alloc_mem(struct dev_info *adapter)
411862306a36Sopenharmony_ci{
411962306a36Sopenharmony_ci	struct ksz_hw *hw = &adapter->hw;
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	/* Determine the number of receive and transmit descriptors. */
412262306a36Sopenharmony_ci	hw->rx_desc_info.alloc = NUM_OF_RX_DESC;
412362306a36Sopenharmony_ci	hw->tx_desc_info.alloc = NUM_OF_TX_DESC;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	/* Determine how many descriptors to skip transmit interrupt. */
412662306a36Sopenharmony_ci	hw->tx_int_cnt = 0;
412762306a36Sopenharmony_ci	hw->tx_int_mask = NUM_OF_TX_DESC / 4;
412862306a36Sopenharmony_ci	if (hw->tx_int_mask > 8)
412962306a36Sopenharmony_ci		hw->tx_int_mask = 8;
413062306a36Sopenharmony_ci	while (hw->tx_int_mask) {
413162306a36Sopenharmony_ci		hw->tx_int_cnt++;
413262306a36Sopenharmony_ci		hw->tx_int_mask >>= 1;
413362306a36Sopenharmony_ci	}
413462306a36Sopenharmony_ci	if (hw->tx_int_cnt) {
413562306a36Sopenharmony_ci		hw->tx_int_mask = (1 << (hw->tx_int_cnt - 1)) - 1;
413662306a36Sopenharmony_ci		hw->tx_int_cnt = 0;
413762306a36Sopenharmony_ci	}
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	/* Determine the descriptor size. */
414062306a36Sopenharmony_ci	hw->rx_desc_info.size =
414162306a36Sopenharmony_ci		(((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
414262306a36Sopenharmony_ci		DESC_ALIGNMENT) * DESC_ALIGNMENT);
414362306a36Sopenharmony_ci	hw->tx_desc_info.size =
414462306a36Sopenharmony_ci		(((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
414562306a36Sopenharmony_ci		DESC_ALIGNMENT) * DESC_ALIGNMENT);
414662306a36Sopenharmony_ci	if (hw->rx_desc_info.size != sizeof(struct ksz_hw_desc))
414762306a36Sopenharmony_ci		pr_alert("Hardware descriptor size not right!\n");
414862306a36Sopenharmony_ci	ksz_check_desc_num(&hw->rx_desc_info);
414962306a36Sopenharmony_ci	ksz_check_desc_num(&hw->tx_desc_info);
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	/* Allocate descriptors. */
415262306a36Sopenharmony_ci	if (ksz_alloc_desc(adapter))
415362306a36Sopenharmony_ci		return 1;
415462306a36Sopenharmony_ci
415562306a36Sopenharmony_ci	return 0;
415662306a36Sopenharmony_ci}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci/**
415962306a36Sopenharmony_ci * ksz_free_desc - free software and hardware descriptors
416062306a36Sopenharmony_ci * @adapter:	Adapter information structure.
416162306a36Sopenharmony_ci *
416262306a36Sopenharmony_ci * This local routine frees the software and hardware descriptors allocated by
416362306a36Sopenharmony_ci * ksz_alloc_desc().
416462306a36Sopenharmony_ci */
416562306a36Sopenharmony_cistatic void ksz_free_desc(struct dev_info *adapter)
416662306a36Sopenharmony_ci{
416762306a36Sopenharmony_ci	struct ksz_hw *hw = &adapter->hw;
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	/* Reset descriptor. */
417062306a36Sopenharmony_ci	hw->rx_desc_info.ring_virt = NULL;
417162306a36Sopenharmony_ci	hw->tx_desc_info.ring_virt = NULL;
417262306a36Sopenharmony_ci	hw->rx_desc_info.ring_phys = 0;
417362306a36Sopenharmony_ci	hw->tx_desc_info.ring_phys = 0;
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	/* Free memory. */
417662306a36Sopenharmony_ci	if (adapter->desc_pool.alloc_virt)
417762306a36Sopenharmony_ci		dma_free_coherent(&adapter->pdev->dev,
417862306a36Sopenharmony_ci				  adapter->desc_pool.alloc_size,
417962306a36Sopenharmony_ci				  adapter->desc_pool.alloc_virt,
418062306a36Sopenharmony_ci				  adapter->desc_pool.dma_addr);
418162306a36Sopenharmony_ci
418262306a36Sopenharmony_ci	/* Reset resource pool. */
418362306a36Sopenharmony_ci	adapter->desc_pool.alloc_size = 0;
418462306a36Sopenharmony_ci	adapter->desc_pool.alloc_virt = NULL;
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	kfree(hw->rx_desc_info.ring);
418762306a36Sopenharmony_ci	hw->rx_desc_info.ring = NULL;
418862306a36Sopenharmony_ci	kfree(hw->tx_desc_info.ring);
418962306a36Sopenharmony_ci	hw->tx_desc_info.ring = NULL;
419062306a36Sopenharmony_ci}
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci/**
419362306a36Sopenharmony_ci * ksz_free_buffers - free buffers used in the descriptors
419462306a36Sopenharmony_ci * @adapter:	Adapter information structure.
419562306a36Sopenharmony_ci * @desc_info:	Descriptor information structure.
419662306a36Sopenharmony_ci * @direction:	to or from device
419762306a36Sopenharmony_ci *
419862306a36Sopenharmony_ci * This local routine frees buffers used in the DMA buffers.
419962306a36Sopenharmony_ci */
420062306a36Sopenharmony_cistatic void ksz_free_buffers(struct dev_info *adapter,
420162306a36Sopenharmony_ci	struct ksz_desc_info *desc_info, int direction)
420262306a36Sopenharmony_ci{
420362306a36Sopenharmony_ci	int i;
420462306a36Sopenharmony_ci	struct ksz_dma_buf *dma_buf;
420562306a36Sopenharmony_ci	struct ksz_desc *desc = desc_info->ring;
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	for (i = 0; i < desc_info->alloc; i++) {
420862306a36Sopenharmony_ci		dma_buf = DMA_BUFFER(desc);
420962306a36Sopenharmony_ci		if (dma_buf->skb)
421062306a36Sopenharmony_ci			free_dma_buf(adapter, dma_buf, direction);
421162306a36Sopenharmony_ci		desc++;
421262306a36Sopenharmony_ci	}
421362306a36Sopenharmony_ci}
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci/**
421662306a36Sopenharmony_ci * ksz_free_mem - free all resources used by descriptors
421762306a36Sopenharmony_ci * @adapter:	Adapter information structure.
421862306a36Sopenharmony_ci *
421962306a36Sopenharmony_ci * This local routine frees all the resources allocated by ksz_alloc_mem().
422062306a36Sopenharmony_ci */
422162306a36Sopenharmony_cistatic void ksz_free_mem(struct dev_info *adapter)
422262306a36Sopenharmony_ci{
422362306a36Sopenharmony_ci	/* Free transmit buffers. */
422462306a36Sopenharmony_ci	ksz_free_buffers(adapter, &adapter->hw.tx_desc_info, DMA_TO_DEVICE);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	/* Free receive buffers. */
422762306a36Sopenharmony_ci	ksz_free_buffers(adapter, &adapter->hw.rx_desc_info, DMA_FROM_DEVICE);
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci	/* Free descriptors. */
423062306a36Sopenharmony_ci	ksz_free_desc(adapter);
423162306a36Sopenharmony_ci}
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_cistatic void get_mib_counters(struct ksz_hw *hw, int first, int cnt,
423462306a36Sopenharmony_ci	u64 *counter)
423562306a36Sopenharmony_ci{
423662306a36Sopenharmony_ci	int i;
423762306a36Sopenharmony_ci	int mib;
423862306a36Sopenharmony_ci	int port;
423962306a36Sopenharmony_ci	struct ksz_port_mib *port_mib;
424062306a36Sopenharmony_ci
424162306a36Sopenharmony_ci	memset(counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
424262306a36Sopenharmony_ci	for (i = 0, port = first; i < cnt; i++, port++) {
424362306a36Sopenharmony_ci		port_mib = &hw->port_mib[port];
424462306a36Sopenharmony_ci		for (mib = port_mib->mib_start; mib < hw->mib_cnt; mib++)
424562306a36Sopenharmony_ci			counter[mib] += port_mib->counter[mib];
424662306a36Sopenharmony_ci	}
424762306a36Sopenharmony_ci}
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_ci/**
425062306a36Sopenharmony_ci * send_packet - send packet
425162306a36Sopenharmony_ci * @skb:	Socket buffer.
425262306a36Sopenharmony_ci * @dev:	Network device.
425362306a36Sopenharmony_ci *
425462306a36Sopenharmony_ci * This routine is used to send a packet out to the network.
425562306a36Sopenharmony_ci */
425662306a36Sopenharmony_cistatic void send_packet(struct sk_buff *skb, struct net_device *dev)
425762306a36Sopenharmony_ci{
425862306a36Sopenharmony_ci	struct ksz_desc *desc;
425962306a36Sopenharmony_ci	struct ksz_desc *first;
426062306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
426162306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
426262306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
426362306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->tx_desc_info;
426462306a36Sopenharmony_ci	struct ksz_dma_buf *dma_buf;
426562306a36Sopenharmony_ci	int len;
426662306a36Sopenharmony_ci	int last_frag = skb_shinfo(skb)->nr_frags;
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci	/*
426962306a36Sopenharmony_ci	 * KSZ8842 with multiple device interfaces needs to be told which port
427062306a36Sopenharmony_ci	 * to send.
427162306a36Sopenharmony_ci	 */
427262306a36Sopenharmony_ci	if (hw->dev_count > 1)
427362306a36Sopenharmony_ci		hw->dst_ports = 1 << priv->port.first_port;
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci	/* Hardware will pad the length to 60. */
427662306a36Sopenharmony_ci	len = skb->len;
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci	/* Remember the very first descriptor. */
427962306a36Sopenharmony_ci	first = info->cur;
428062306a36Sopenharmony_ci	desc = first;
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	dma_buf = DMA_BUFFER(desc);
428362306a36Sopenharmony_ci	if (last_frag) {
428462306a36Sopenharmony_ci		int frag;
428562306a36Sopenharmony_ci		skb_frag_t *this_frag;
428662306a36Sopenharmony_ci
428762306a36Sopenharmony_ci		dma_buf->len = skb_headlen(skb);
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_ci		dma_buf->dma = dma_map_single(&hw_priv->pdev->dev, skb->data,
429062306a36Sopenharmony_ci					      dma_buf->len, DMA_TO_DEVICE);
429162306a36Sopenharmony_ci		set_tx_buf(desc, dma_buf->dma);
429262306a36Sopenharmony_ci		set_tx_len(desc, dma_buf->len);
429362306a36Sopenharmony_ci
429462306a36Sopenharmony_ci		frag = 0;
429562306a36Sopenharmony_ci		do {
429662306a36Sopenharmony_ci			this_frag = &skb_shinfo(skb)->frags[frag];
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_ci			/* Get a new descriptor. */
429962306a36Sopenharmony_ci			get_tx_pkt(info, &desc);
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci			/* Keep track of descriptors used so far. */
430262306a36Sopenharmony_ci			++hw->tx_int_cnt;
430362306a36Sopenharmony_ci
430462306a36Sopenharmony_ci			dma_buf = DMA_BUFFER(desc);
430562306a36Sopenharmony_ci			dma_buf->len = skb_frag_size(this_frag);
430662306a36Sopenharmony_ci
430762306a36Sopenharmony_ci			dma_buf->dma = dma_map_single(&hw_priv->pdev->dev,
430862306a36Sopenharmony_ci						      skb_frag_address(this_frag),
430962306a36Sopenharmony_ci						      dma_buf->len,
431062306a36Sopenharmony_ci						      DMA_TO_DEVICE);
431162306a36Sopenharmony_ci			set_tx_buf(desc, dma_buf->dma);
431262306a36Sopenharmony_ci			set_tx_len(desc, dma_buf->len);
431362306a36Sopenharmony_ci
431462306a36Sopenharmony_ci			frag++;
431562306a36Sopenharmony_ci			if (frag == last_frag)
431662306a36Sopenharmony_ci				break;
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci			/* Do not release the last descriptor here. */
431962306a36Sopenharmony_ci			release_desc(desc);
432062306a36Sopenharmony_ci		} while (1);
432162306a36Sopenharmony_ci
432262306a36Sopenharmony_ci		/* current points to the last descriptor. */
432362306a36Sopenharmony_ci		info->cur = desc;
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci		/* Release the first descriptor. */
432662306a36Sopenharmony_ci		release_desc(first);
432762306a36Sopenharmony_ci	} else {
432862306a36Sopenharmony_ci		dma_buf->len = len;
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci		dma_buf->dma = dma_map_single(&hw_priv->pdev->dev, skb->data,
433162306a36Sopenharmony_ci					      dma_buf->len, DMA_TO_DEVICE);
433262306a36Sopenharmony_ci		set_tx_buf(desc, dma_buf->dma);
433362306a36Sopenharmony_ci		set_tx_len(desc, dma_buf->len);
433462306a36Sopenharmony_ci	}
433562306a36Sopenharmony_ci
433662306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
433762306a36Sopenharmony_ci		(desc)->sw.buf.tx.csum_gen_tcp = 1;
433862306a36Sopenharmony_ci		(desc)->sw.buf.tx.csum_gen_udp = 1;
433962306a36Sopenharmony_ci	}
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci	/*
434262306a36Sopenharmony_ci	 * The last descriptor holds the packet so that it can be returned to
434362306a36Sopenharmony_ci	 * network subsystem after all descriptors are transmitted.
434462306a36Sopenharmony_ci	 */
434562306a36Sopenharmony_ci	dma_buf->skb = skb;
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci	hw_send_pkt(hw);
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci	/* Update transmit statistics. */
435062306a36Sopenharmony_ci	dev->stats.tx_packets++;
435162306a36Sopenharmony_ci	dev->stats.tx_bytes += len;
435262306a36Sopenharmony_ci}
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci/**
435562306a36Sopenharmony_ci * transmit_cleanup - clean up transmit descriptors
435662306a36Sopenharmony_ci * @hw_priv:	Network device.
435762306a36Sopenharmony_ci * @normal:	break if owned
435862306a36Sopenharmony_ci *
435962306a36Sopenharmony_ci * This routine is called to clean up the transmitted buffers.
436062306a36Sopenharmony_ci */
436162306a36Sopenharmony_cistatic void transmit_cleanup(struct dev_info *hw_priv, int normal)
436262306a36Sopenharmony_ci{
436362306a36Sopenharmony_ci	int last;
436462306a36Sopenharmony_ci	union desc_stat status;
436562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
436662306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->tx_desc_info;
436762306a36Sopenharmony_ci	struct ksz_desc *desc;
436862306a36Sopenharmony_ci	struct ksz_dma_buf *dma_buf;
436962306a36Sopenharmony_ci	struct net_device *dev = NULL;
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	spin_lock_irq(&hw_priv->hwlock);
437262306a36Sopenharmony_ci	last = info->last;
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	while (info->avail < info->alloc) {
437562306a36Sopenharmony_ci		/* Get next descriptor which is not hardware owned. */
437662306a36Sopenharmony_ci		desc = &info->ring[last];
437762306a36Sopenharmony_ci		status.data = le32_to_cpu(desc->phw->ctrl.data);
437862306a36Sopenharmony_ci		if (status.tx.hw_owned) {
437962306a36Sopenharmony_ci			if (normal)
438062306a36Sopenharmony_ci				break;
438162306a36Sopenharmony_ci			else
438262306a36Sopenharmony_ci				reset_desc(desc, status);
438362306a36Sopenharmony_ci		}
438462306a36Sopenharmony_ci
438562306a36Sopenharmony_ci		dma_buf = DMA_BUFFER(desc);
438662306a36Sopenharmony_ci		dma_unmap_single(&hw_priv->pdev->dev, dma_buf->dma,
438762306a36Sopenharmony_ci				 dma_buf->len, DMA_TO_DEVICE);
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci		/* This descriptor contains the last buffer in the packet. */
439062306a36Sopenharmony_ci		if (dma_buf->skb) {
439162306a36Sopenharmony_ci			dev = dma_buf->skb->dev;
439262306a36Sopenharmony_ci
439362306a36Sopenharmony_ci			/* Release the packet back to network subsystem. */
439462306a36Sopenharmony_ci			dev_kfree_skb_irq(dma_buf->skb);
439562306a36Sopenharmony_ci			dma_buf->skb = NULL;
439662306a36Sopenharmony_ci		}
439762306a36Sopenharmony_ci
439862306a36Sopenharmony_ci		/* Free the transmitted descriptor. */
439962306a36Sopenharmony_ci		last++;
440062306a36Sopenharmony_ci		last &= info->mask;
440162306a36Sopenharmony_ci		info->avail++;
440262306a36Sopenharmony_ci	}
440362306a36Sopenharmony_ci	info->last = last;
440462306a36Sopenharmony_ci	spin_unlock_irq(&hw_priv->hwlock);
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_ci	/* Notify the network subsystem that the packet has been sent. */
440762306a36Sopenharmony_ci	if (dev)
440862306a36Sopenharmony_ci		netif_trans_update(dev);
440962306a36Sopenharmony_ci}
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci/**
441262306a36Sopenharmony_ci * tx_done - transmit done processing
441362306a36Sopenharmony_ci * @hw_priv:	Network device.
441462306a36Sopenharmony_ci *
441562306a36Sopenharmony_ci * This routine is called when the transmit interrupt is triggered, indicating
441662306a36Sopenharmony_ci * either a packet is sent successfully or there are transmit errors.
441762306a36Sopenharmony_ci */
441862306a36Sopenharmony_cistatic void tx_done(struct dev_info *hw_priv)
441962306a36Sopenharmony_ci{
442062306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
442162306a36Sopenharmony_ci	int port;
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci	transmit_cleanup(hw_priv, 1);
442462306a36Sopenharmony_ci
442562306a36Sopenharmony_ci	for (port = 0; port < hw->dev_count; port++) {
442662306a36Sopenharmony_ci		struct net_device *dev = hw->port_info[port].pdev;
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci		if (netif_running(dev) && netif_queue_stopped(dev))
442962306a36Sopenharmony_ci			netif_wake_queue(dev);
443062306a36Sopenharmony_ci	}
443162306a36Sopenharmony_ci}
443262306a36Sopenharmony_ci
443362306a36Sopenharmony_cistatic inline void copy_old_skb(struct sk_buff *old, struct sk_buff *skb)
443462306a36Sopenharmony_ci{
443562306a36Sopenharmony_ci	skb->dev = old->dev;
443662306a36Sopenharmony_ci	skb->protocol = old->protocol;
443762306a36Sopenharmony_ci	skb->ip_summed = old->ip_summed;
443862306a36Sopenharmony_ci	skb->csum = old->csum;
443962306a36Sopenharmony_ci	skb_set_network_header(skb, ETH_HLEN);
444062306a36Sopenharmony_ci
444162306a36Sopenharmony_ci	dev_consume_skb_any(old);
444262306a36Sopenharmony_ci}
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci/**
444562306a36Sopenharmony_ci * netdev_tx - send out packet
444662306a36Sopenharmony_ci * @skb:	Socket buffer.
444762306a36Sopenharmony_ci * @dev:	Network device.
444862306a36Sopenharmony_ci *
444962306a36Sopenharmony_ci * This function is used by the upper network layer to send out a packet.
445062306a36Sopenharmony_ci *
445162306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code indicating failure.
445262306a36Sopenharmony_ci */
445362306a36Sopenharmony_cistatic netdev_tx_t netdev_tx(struct sk_buff *skb, struct net_device *dev)
445462306a36Sopenharmony_ci{
445562306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
445662306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
445762306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
445862306a36Sopenharmony_ci	int left;
445962306a36Sopenharmony_ci	int num = 1;
446062306a36Sopenharmony_ci	int rc = 0;
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_ci	if (hw->features & SMALL_PACKET_TX_BUG) {
446362306a36Sopenharmony_ci		struct sk_buff *org_skb = skb;
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci		if (skb->len <= 48) {
446662306a36Sopenharmony_ci			if (skb_end_pointer(skb) - skb->data >= 50) {
446762306a36Sopenharmony_ci				memset(&skb->data[skb->len], 0, 50 - skb->len);
446862306a36Sopenharmony_ci				skb->len = 50;
446962306a36Sopenharmony_ci			} else {
447062306a36Sopenharmony_ci				skb = netdev_alloc_skb(dev, 50);
447162306a36Sopenharmony_ci				if (!skb)
447262306a36Sopenharmony_ci					return NETDEV_TX_BUSY;
447362306a36Sopenharmony_ci				memcpy(skb->data, org_skb->data, org_skb->len);
447462306a36Sopenharmony_ci				memset(&skb->data[org_skb->len], 0,
447562306a36Sopenharmony_ci					50 - org_skb->len);
447662306a36Sopenharmony_ci				skb->len = 50;
447762306a36Sopenharmony_ci				copy_old_skb(org_skb, skb);
447862306a36Sopenharmony_ci			}
447962306a36Sopenharmony_ci		}
448062306a36Sopenharmony_ci	}
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci	spin_lock_irq(&hw_priv->hwlock);
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	num = skb_shinfo(skb)->nr_frags + 1;
448562306a36Sopenharmony_ci	left = hw_alloc_pkt(hw, skb->len, num);
448662306a36Sopenharmony_ci	if (left) {
448762306a36Sopenharmony_ci		if (left < num ||
448862306a36Sopenharmony_ci		    (CHECKSUM_PARTIAL == skb->ip_summed &&
448962306a36Sopenharmony_ci		     skb->protocol == htons(ETH_P_IPV6))) {
449062306a36Sopenharmony_ci			struct sk_buff *org_skb = skb;
449162306a36Sopenharmony_ci
449262306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, org_skb->len);
449362306a36Sopenharmony_ci			if (!skb) {
449462306a36Sopenharmony_ci				rc = NETDEV_TX_BUSY;
449562306a36Sopenharmony_ci				goto unlock;
449662306a36Sopenharmony_ci			}
449762306a36Sopenharmony_ci			skb_copy_and_csum_dev(org_skb, skb->data);
449862306a36Sopenharmony_ci			org_skb->ip_summed = CHECKSUM_NONE;
449962306a36Sopenharmony_ci			skb->len = org_skb->len;
450062306a36Sopenharmony_ci			copy_old_skb(org_skb, skb);
450162306a36Sopenharmony_ci		}
450262306a36Sopenharmony_ci		send_packet(skb, dev);
450362306a36Sopenharmony_ci		if (left <= num)
450462306a36Sopenharmony_ci			netif_stop_queue(dev);
450562306a36Sopenharmony_ci	} else {
450662306a36Sopenharmony_ci		/* Stop the transmit queue until packet is allocated. */
450762306a36Sopenharmony_ci		netif_stop_queue(dev);
450862306a36Sopenharmony_ci		rc = NETDEV_TX_BUSY;
450962306a36Sopenharmony_ci	}
451062306a36Sopenharmony_ciunlock:
451162306a36Sopenharmony_ci	spin_unlock_irq(&hw_priv->hwlock);
451262306a36Sopenharmony_ci
451362306a36Sopenharmony_ci	return rc;
451462306a36Sopenharmony_ci}
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_ci/**
451762306a36Sopenharmony_ci * netdev_tx_timeout - transmit timeout processing
451862306a36Sopenharmony_ci * @dev:	Network device.
451962306a36Sopenharmony_ci * @txqueue:	index of hanging queue
452062306a36Sopenharmony_ci *
452162306a36Sopenharmony_ci * This routine is called when the transmit timer expires.  That indicates the
452262306a36Sopenharmony_ci * hardware is not running correctly because transmit interrupts are not
452362306a36Sopenharmony_ci * triggered to free up resources so that the transmit routine can continue
452462306a36Sopenharmony_ci * sending out packets.  The hardware is reset to correct the problem.
452562306a36Sopenharmony_ci */
452662306a36Sopenharmony_cistatic void netdev_tx_timeout(struct net_device *dev, unsigned int txqueue)
452762306a36Sopenharmony_ci{
452862306a36Sopenharmony_ci	static unsigned long last_reset;
452962306a36Sopenharmony_ci
453062306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
453162306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
453262306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
453362306a36Sopenharmony_ci	int port;
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci	if (hw->dev_count > 1) {
453662306a36Sopenharmony_ci		/*
453762306a36Sopenharmony_ci		 * Only reset the hardware if time between calls is long
453862306a36Sopenharmony_ci		 * enough.
453962306a36Sopenharmony_ci		 */
454062306a36Sopenharmony_ci		if (time_before_eq(jiffies, last_reset + dev->watchdog_timeo))
454162306a36Sopenharmony_ci			hw_priv = NULL;
454262306a36Sopenharmony_ci	}
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci	last_reset = jiffies;
454562306a36Sopenharmony_ci	if (hw_priv) {
454662306a36Sopenharmony_ci		hw_dis_intr(hw);
454762306a36Sopenharmony_ci		hw_disable(hw);
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci		transmit_cleanup(hw_priv, 0);
455062306a36Sopenharmony_ci		hw_reset_pkts(&hw->rx_desc_info);
455162306a36Sopenharmony_ci		hw_reset_pkts(&hw->tx_desc_info);
455262306a36Sopenharmony_ci		ksz_init_rx_buffers(hw_priv);
455362306a36Sopenharmony_ci
455462306a36Sopenharmony_ci		hw_reset(hw);
455562306a36Sopenharmony_ci
455662306a36Sopenharmony_ci		hw_set_desc_base(hw,
455762306a36Sopenharmony_ci			hw->tx_desc_info.ring_phys,
455862306a36Sopenharmony_ci			hw->rx_desc_info.ring_phys);
455962306a36Sopenharmony_ci		hw_set_addr(hw);
456062306a36Sopenharmony_ci		if (hw->all_multi)
456162306a36Sopenharmony_ci			hw_set_multicast(hw, hw->all_multi);
456262306a36Sopenharmony_ci		else if (hw->multi_list_size)
456362306a36Sopenharmony_ci			hw_set_grp_addr(hw);
456462306a36Sopenharmony_ci
456562306a36Sopenharmony_ci		if (hw->dev_count > 1) {
456662306a36Sopenharmony_ci			hw_set_add_addr(hw);
456762306a36Sopenharmony_ci			for (port = 0; port < SWITCH_PORT_NUM; port++) {
456862306a36Sopenharmony_ci				struct net_device *port_dev;
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_ci				port_set_stp_state(hw, port,
457162306a36Sopenharmony_ci					STP_STATE_DISABLED);
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci				port_dev = hw->port_info[port].pdev;
457462306a36Sopenharmony_ci				if (netif_running(port_dev))
457562306a36Sopenharmony_ci					port_set_stp_state(hw, port,
457662306a36Sopenharmony_ci						STP_STATE_SIMPLE);
457762306a36Sopenharmony_ci			}
457862306a36Sopenharmony_ci		}
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci		hw_enable(hw);
458162306a36Sopenharmony_ci		hw_ena_intr(hw);
458262306a36Sopenharmony_ci	}
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci	netif_trans_update(dev);
458562306a36Sopenharmony_ci	netif_wake_queue(dev);
458662306a36Sopenharmony_ci}
458762306a36Sopenharmony_ci
458862306a36Sopenharmony_cistatic inline void csum_verified(struct sk_buff *skb)
458962306a36Sopenharmony_ci{
459062306a36Sopenharmony_ci	unsigned short protocol;
459162306a36Sopenharmony_ci	struct iphdr *iph;
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	protocol = skb->protocol;
459462306a36Sopenharmony_ci	skb_reset_network_header(skb);
459562306a36Sopenharmony_ci	iph = (struct iphdr *) skb_network_header(skb);
459662306a36Sopenharmony_ci	if (protocol == htons(ETH_P_8021Q)) {
459762306a36Sopenharmony_ci		protocol = iph->tot_len;
459862306a36Sopenharmony_ci		skb_set_network_header(skb, VLAN_HLEN);
459962306a36Sopenharmony_ci		iph = (struct iphdr *) skb_network_header(skb);
460062306a36Sopenharmony_ci	}
460162306a36Sopenharmony_ci	if (protocol == htons(ETH_P_IP)) {
460262306a36Sopenharmony_ci		if (iph->protocol == IPPROTO_TCP)
460362306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
460462306a36Sopenharmony_ci	}
460562306a36Sopenharmony_ci}
460662306a36Sopenharmony_ci
460762306a36Sopenharmony_cistatic inline int rx_proc(struct net_device *dev, struct ksz_hw* hw,
460862306a36Sopenharmony_ci	struct ksz_desc *desc, union desc_stat status)
460962306a36Sopenharmony_ci{
461062306a36Sopenharmony_ci	int packet_len;
461162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
461262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
461362306a36Sopenharmony_ci	struct ksz_dma_buf *dma_buf;
461462306a36Sopenharmony_ci	struct sk_buff *skb;
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_ci	/* Received length includes 4-byte CRC. */
461762306a36Sopenharmony_ci	packet_len = status.rx.frame_len - 4;
461862306a36Sopenharmony_ci
461962306a36Sopenharmony_ci	dma_buf = DMA_BUFFER(desc);
462062306a36Sopenharmony_ci	dma_sync_single_for_cpu(&hw_priv->pdev->dev, dma_buf->dma,
462162306a36Sopenharmony_ci				packet_len + 4, DMA_FROM_DEVICE);
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci	do {
462462306a36Sopenharmony_ci		/* skb->data != skb->head */
462562306a36Sopenharmony_ci		skb = netdev_alloc_skb(dev, packet_len + 2);
462662306a36Sopenharmony_ci		if (!skb) {
462762306a36Sopenharmony_ci			dev->stats.rx_dropped++;
462862306a36Sopenharmony_ci			return -ENOMEM;
462962306a36Sopenharmony_ci		}
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_ci		/*
463262306a36Sopenharmony_ci		 * Align socket buffer in 4-byte boundary for better
463362306a36Sopenharmony_ci		 * performance.
463462306a36Sopenharmony_ci		 */
463562306a36Sopenharmony_ci		skb_reserve(skb, 2);
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_ci		skb_put_data(skb, dma_buf->skb->data, packet_len);
463862306a36Sopenharmony_ci	} while (0);
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci	if (hw->rx_cfg & (DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP))
464362306a36Sopenharmony_ci		csum_verified(skb);
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci	/* Update receive statistics. */
464662306a36Sopenharmony_ci	dev->stats.rx_packets++;
464762306a36Sopenharmony_ci	dev->stats.rx_bytes += packet_len;
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci	/* Notify upper layer for received packet. */
465062306a36Sopenharmony_ci	netif_rx(skb);
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci	return 0;
465362306a36Sopenharmony_ci}
465462306a36Sopenharmony_ci
465562306a36Sopenharmony_cistatic int dev_rcv_packets(struct dev_info *hw_priv)
465662306a36Sopenharmony_ci{
465762306a36Sopenharmony_ci	int next;
465862306a36Sopenharmony_ci	union desc_stat status;
465962306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
466062306a36Sopenharmony_ci	struct net_device *dev = hw->port_info[0].pdev;
466162306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->rx_desc_info;
466262306a36Sopenharmony_ci	int left = info->alloc;
466362306a36Sopenharmony_ci	struct ksz_desc *desc;
466462306a36Sopenharmony_ci	int received = 0;
466562306a36Sopenharmony_ci
466662306a36Sopenharmony_ci	next = info->next;
466762306a36Sopenharmony_ci	while (left--) {
466862306a36Sopenharmony_ci		/* Get next descriptor which is not hardware owned. */
466962306a36Sopenharmony_ci		desc = &info->ring[next];
467062306a36Sopenharmony_ci		status.data = le32_to_cpu(desc->phw->ctrl.data);
467162306a36Sopenharmony_ci		if (status.rx.hw_owned)
467262306a36Sopenharmony_ci			break;
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci		/* Status valid only when last descriptor bit is set. */
467562306a36Sopenharmony_ci		if (status.rx.last_desc && status.rx.first_desc) {
467662306a36Sopenharmony_ci			if (rx_proc(dev, hw, desc, status))
467762306a36Sopenharmony_ci				goto release_packet;
467862306a36Sopenharmony_ci			received++;
467962306a36Sopenharmony_ci		}
468062306a36Sopenharmony_ci
468162306a36Sopenharmony_cirelease_packet:
468262306a36Sopenharmony_ci		release_desc(desc);
468362306a36Sopenharmony_ci		next++;
468462306a36Sopenharmony_ci		next &= info->mask;
468562306a36Sopenharmony_ci	}
468662306a36Sopenharmony_ci	info->next = next;
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci	return received;
468962306a36Sopenharmony_ci}
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_cistatic int port_rcv_packets(struct dev_info *hw_priv)
469262306a36Sopenharmony_ci{
469362306a36Sopenharmony_ci	int next;
469462306a36Sopenharmony_ci	union desc_stat status;
469562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
469662306a36Sopenharmony_ci	struct net_device *dev = hw->port_info[0].pdev;
469762306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->rx_desc_info;
469862306a36Sopenharmony_ci	int left = info->alloc;
469962306a36Sopenharmony_ci	struct ksz_desc *desc;
470062306a36Sopenharmony_ci	int received = 0;
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci	next = info->next;
470362306a36Sopenharmony_ci	while (left--) {
470462306a36Sopenharmony_ci		/* Get next descriptor which is not hardware owned. */
470562306a36Sopenharmony_ci		desc = &info->ring[next];
470662306a36Sopenharmony_ci		status.data = le32_to_cpu(desc->phw->ctrl.data);
470762306a36Sopenharmony_ci		if (status.rx.hw_owned)
470862306a36Sopenharmony_ci			break;
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci		if (hw->dev_count > 1) {
471162306a36Sopenharmony_ci			/* Get received port number. */
471262306a36Sopenharmony_ci			int p = HW_TO_DEV_PORT(status.rx.src_port);
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci			dev = hw->port_info[p].pdev;
471562306a36Sopenharmony_ci			if (!netif_running(dev))
471662306a36Sopenharmony_ci				goto release_packet;
471762306a36Sopenharmony_ci		}
471862306a36Sopenharmony_ci
471962306a36Sopenharmony_ci		/* Status valid only when last descriptor bit is set. */
472062306a36Sopenharmony_ci		if (status.rx.last_desc && status.rx.first_desc) {
472162306a36Sopenharmony_ci			if (rx_proc(dev, hw, desc, status))
472262306a36Sopenharmony_ci				goto release_packet;
472362306a36Sopenharmony_ci			received++;
472462306a36Sopenharmony_ci		}
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_cirelease_packet:
472762306a36Sopenharmony_ci		release_desc(desc);
472862306a36Sopenharmony_ci		next++;
472962306a36Sopenharmony_ci		next &= info->mask;
473062306a36Sopenharmony_ci	}
473162306a36Sopenharmony_ci	info->next = next;
473262306a36Sopenharmony_ci
473362306a36Sopenharmony_ci	return received;
473462306a36Sopenharmony_ci}
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_cistatic int dev_rcv_special(struct dev_info *hw_priv)
473762306a36Sopenharmony_ci{
473862306a36Sopenharmony_ci	int next;
473962306a36Sopenharmony_ci	union desc_stat status;
474062306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
474162306a36Sopenharmony_ci	struct net_device *dev = hw->port_info[0].pdev;
474262306a36Sopenharmony_ci	struct ksz_desc_info *info = &hw->rx_desc_info;
474362306a36Sopenharmony_ci	int left = info->alloc;
474462306a36Sopenharmony_ci	struct ksz_desc *desc;
474562306a36Sopenharmony_ci	int received = 0;
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	next = info->next;
474862306a36Sopenharmony_ci	while (left--) {
474962306a36Sopenharmony_ci		/* Get next descriptor which is not hardware owned. */
475062306a36Sopenharmony_ci		desc = &info->ring[next];
475162306a36Sopenharmony_ci		status.data = le32_to_cpu(desc->phw->ctrl.data);
475262306a36Sopenharmony_ci		if (status.rx.hw_owned)
475362306a36Sopenharmony_ci			break;
475462306a36Sopenharmony_ci
475562306a36Sopenharmony_ci		if (hw->dev_count > 1) {
475662306a36Sopenharmony_ci			/* Get received port number. */
475762306a36Sopenharmony_ci			int p = HW_TO_DEV_PORT(status.rx.src_port);
475862306a36Sopenharmony_ci
475962306a36Sopenharmony_ci			dev = hw->port_info[p].pdev;
476062306a36Sopenharmony_ci			if (!netif_running(dev))
476162306a36Sopenharmony_ci				goto release_packet;
476262306a36Sopenharmony_ci		}
476362306a36Sopenharmony_ci
476462306a36Sopenharmony_ci		/* Status valid only when last descriptor bit is set. */
476562306a36Sopenharmony_ci		if (status.rx.last_desc && status.rx.first_desc) {
476662306a36Sopenharmony_ci			/*
476762306a36Sopenharmony_ci			 * Receive without error.  With receive errors
476862306a36Sopenharmony_ci			 * disabled, packets with receive errors will be
476962306a36Sopenharmony_ci			 * dropped, so no need to check the error bit.
477062306a36Sopenharmony_ci			 */
477162306a36Sopenharmony_ci			if (!status.rx.error || (status.data &
477262306a36Sopenharmony_ci					KS_DESC_RX_ERROR_COND) ==
477362306a36Sopenharmony_ci					KS_DESC_RX_ERROR_TOO_LONG) {
477462306a36Sopenharmony_ci				if (rx_proc(dev, hw, desc, status))
477562306a36Sopenharmony_ci					goto release_packet;
477662306a36Sopenharmony_ci				received++;
477762306a36Sopenharmony_ci			} else {
477862306a36Sopenharmony_ci				struct dev_priv *priv = netdev_priv(dev);
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci				/* Update receive error statistics. */
478162306a36Sopenharmony_ci				priv->port.counter[OID_COUNTER_RCV_ERROR]++;
478262306a36Sopenharmony_ci			}
478362306a36Sopenharmony_ci		}
478462306a36Sopenharmony_ci
478562306a36Sopenharmony_cirelease_packet:
478662306a36Sopenharmony_ci		release_desc(desc);
478762306a36Sopenharmony_ci		next++;
478862306a36Sopenharmony_ci		next &= info->mask;
478962306a36Sopenharmony_ci	}
479062306a36Sopenharmony_ci	info->next = next;
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	return received;
479362306a36Sopenharmony_ci}
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_cistatic void rx_proc_task(struct tasklet_struct *t)
479662306a36Sopenharmony_ci{
479762306a36Sopenharmony_ci	struct dev_info *hw_priv = from_tasklet(hw_priv, t, rx_tasklet);
479862306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
479962306a36Sopenharmony_ci
480062306a36Sopenharmony_ci	if (!hw->enabled)
480162306a36Sopenharmony_ci		return;
480262306a36Sopenharmony_ci	if (unlikely(!hw_priv->dev_rcv(hw_priv))) {
480362306a36Sopenharmony_ci
480462306a36Sopenharmony_ci		/* In case receive process is suspended because of overrun. */
480562306a36Sopenharmony_ci		hw_resume_rx(hw);
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci		/* tasklets are interruptible. */
480862306a36Sopenharmony_ci		spin_lock_irq(&hw_priv->hwlock);
480962306a36Sopenharmony_ci		hw_turn_on_intr(hw, KS884X_INT_RX_MASK);
481062306a36Sopenharmony_ci		spin_unlock_irq(&hw_priv->hwlock);
481162306a36Sopenharmony_ci	} else {
481262306a36Sopenharmony_ci		hw_ack_intr(hw, KS884X_INT_RX);
481362306a36Sopenharmony_ci		tasklet_schedule(&hw_priv->rx_tasklet);
481462306a36Sopenharmony_ci	}
481562306a36Sopenharmony_ci}
481662306a36Sopenharmony_ci
481762306a36Sopenharmony_cistatic void tx_proc_task(struct tasklet_struct *t)
481862306a36Sopenharmony_ci{
481962306a36Sopenharmony_ci	struct dev_info *hw_priv = from_tasklet(hw_priv, t, tx_tasklet);
482062306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
482162306a36Sopenharmony_ci
482262306a36Sopenharmony_ci	hw_ack_intr(hw, KS884X_INT_TX_MASK);
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ci	tx_done(hw_priv);
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	/* tasklets are interruptible. */
482762306a36Sopenharmony_ci	spin_lock_irq(&hw_priv->hwlock);
482862306a36Sopenharmony_ci	hw_turn_on_intr(hw, KS884X_INT_TX);
482962306a36Sopenharmony_ci	spin_unlock_irq(&hw_priv->hwlock);
483062306a36Sopenharmony_ci}
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_cistatic inline void handle_rx_stop(struct ksz_hw *hw)
483362306a36Sopenharmony_ci{
483462306a36Sopenharmony_ci	/* Receive just has been stopped. */
483562306a36Sopenharmony_ci	if (0 == hw->rx_stop)
483662306a36Sopenharmony_ci		hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
483762306a36Sopenharmony_ci	else if (hw->rx_stop > 1) {
483862306a36Sopenharmony_ci		if (hw->enabled && (hw->rx_cfg & DMA_RX_ENABLE)) {
483962306a36Sopenharmony_ci			hw_start_rx(hw);
484062306a36Sopenharmony_ci		} else {
484162306a36Sopenharmony_ci			hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
484262306a36Sopenharmony_ci			hw->rx_stop = 0;
484362306a36Sopenharmony_ci		}
484462306a36Sopenharmony_ci	} else
484562306a36Sopenharmony_ci		/* Receive just has been started. */
484662306a36Sopenharmony_ci		hw->rx_stop++;
484762306a36Sopenharmony_ci}
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci/**
485062306a36Sopenharmony_ci * netdev_intr - interrupt handling
485162306a36Sopenharmony_ci * @irq:	Interrupt number.
485262306a36Sopenharmony_ci * @dev_id:	Network device.
485362306a36Sopenharmony_ci *
485462306a36Sopenharmony_ci * This function is called by upper network layer to signal interrupt.
485562306a36Sopenharmony_ci *
485662306a36Sopenharmony_ci * Return IRQ_HANDLED if interrupt is handled.
485762306a36Sopenharmony_ci */
485862306a36Sopenharmony_cistatic irqreturn_t netdev_intr(int irq, void *dev_id)
485962306a36Sopenharmony_ci{
486062306a36Sopenharmony_ci	uint int_enable = 0;
486162306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) dev_id;
486262306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
486362306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
486462306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	spin_lock(&hw_priv->hwlock);
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_ci	hw_read_intr(hw, &int_enable);
486962306a36Sopenharmony_ci
487062306a36Sopenharmony_ci	/* Not our interrupt! */
487162306a36Sopenharmony_ci	if (!int_enable) {
487262306a36Sopenharmony_ci		spin_unlock(&hw_priv->hwlock);
487362306a36Sopenharmony_ci		return IRQ_NONE;
487462306a36Sopenharmony_ci	}
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci	do {
487762306a36Sopenharmony_ci		hw_ack_intr(hw, int_enable);
487862306a36Sopenharmony_ci		int_enable &= hw->intr_mask;
487962306a36Sopenharmony_ci
488062306a36Sopenharmony_ci		if (unlikely(int_enable & KS884X_INT_TX_MASK)) {
488162306a36Sopenharmony_ci			hw_dis_intr_bit(hw, KS884X_INT_TX_MASK);
488262306a36Sopenharmony_ci			tasklet_schedule(&hw_priv->tx_tasklet);
488362306a36Sopenharmony_ci		}
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci		if (likely(int_enable & KS884X_INT_RX)) {
488662306a36Sopenharmony_ci			hw_dis_intr_bit(hw, KS884X_INT_RX);
488762306a36Sopenharmony_ci			tasklet_schedule(&hw_priv->rx_tasklet);
488862306a36Sopenharmony_ci		}
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci		if (unlikely(int_enable & KS884X_INT_RX_OVERRUN)) {
489162306a36Sopenharmony_ci			dev->stats.rx_fifo_errors++;
489262306a36Sopenharmony_ci			hw_resume_rx(hw);
489362306a36Sopenharmony_ci		}
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_ci		if (unlikely(int_enable & KS884X_INT_PHY)) {
489662306a36Sopenharmony_ci			struct ksz_port *port = &priv->port;
489762306a36Sopenharmony_ci
489862306a36Sopenharmony_ci			hw->features |= LINK_INT_WORKING;
489962306a36Sopenharmony_ci			port_get_link_speed(port);
490062306a36Sopenharmony_ci		}
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci		if (unlikely(int_enable & KS884X_INT_RX_STOPPED)) {
490362306a36Sopenharmony_ci			handle_rx_stop(hw);
490462306a36Sopenharmony_ci			break;
490562306a36Sopenharmony_ci		}
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_ci		if (unlikely(int_enable & KS884X_INT_TX_STOPPED)) {
490862306a36Sopenharmony_ci			u32 data;
490962306a36Sopenharmony_ci
491062306a36Sopenharmony_ci			hw->intr_mask &= ~KS884X_INT_TX_STOPPED;
491162306a36Sopenharmony_ci			pr_info("Tx stopped\n");
491262306a36Sopenharmony_ci			data = readl(hw->io + KS_DMA_TX_CTRL);
491362306a36Sopenharmony_ci			if (!(data & DMA_TX_ENABLE))
491462306a36Sopenharmony_ci				pr_info("Tx disabled\n");
491562306a36Sopenharmony_ci			break;
491662306a36Sopenharmony_ci		}
491762306a36Sopenharmony_ci	} while (0);
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	hw_ena_intr(hw);
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ci	spin_unlock(&hw_priv->hwlock);
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_ci	return IRQ_HANDLED;
492462306a36Sopenharmony_ci}
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci/*
492762306a36Sopenharmony_ci * Linux network device functions
492862306a36Sopenharmony_ci */
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci
493162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
493262306a36Sopenharmony_cistatic void netdev_netpoll(struct net_device *dev)
493362306a36Sopenharmony_ci{
493462306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
493562306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
493662306a36Sopenharmony_ci
493762306a36Sopenharmony_ci	hw_dis_intr(&hw_priv->hw);
493862306a36Sopenharmony_ci	netdev_intr(dev->irq, dev);
493962306a36Sopenharmony_ci}
494062306a36Sopenharmony_ci#endif
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_cistatic void bridge_change(struct ksz_hw *hw)
494362306a36Sopenharmony_ci{
494462306a36Sopenharmony_ci	int port;
494562306a36Sopenharmony_ci	u8  member;
494662306a36Sopenharmony_ci	struct ksz_switch *sw = hw->ksz_switch;
494762306a36Sopenharmony_ci
494862306a36Sopenharmony_ci	/* No ports in forwarding state. */
494962306a36Sopenharmony_ci	if (!sw->member) {
495062306a36Sopenharmony_ci		port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
495162306a36Sopenharmony_ci		sw_block_addr(hw);
495262306a36Sopenharmony_ci	}
495362306a36Sopenharmony_ci	for (port = 0; port < SWITCH_PORT_NUM; port++) {
495462306a36Sopenharmony_ci		if (STP_STATE_FORWARDING == sw->port_cfg[port].stp_state)
495562306a36Sopenharmony_ci			member = HOST_MASK | sw->member;
495662306a36Sopenharmony_ci		else
495762306a36Sopenharmony_ci			member = HOST_MASK | (1 << port);
495862306a36Sopenharmony_ci		if (member != sw->port_cfg[port].member)
495962306a36Sopenharmony_ci			sw_cfg_port_base_vlan(hw, port, member);
496062306a36Sopenharmony_ci	}
496162306a36Sopenharmony_ci}
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_ci/**
496462306a36Sopenharmony_ci * netdev_close - close network device
496562306a36Sopenharmony_ci * @dev:	Network device.
496662306a36Sopenharmony_ci *
496762306a36Sopenharmony_ci * This function process the close operation of network device.  This is caused
496862306a36Sopenharmony_ci * by the user command "ifconfig ethX down."
496962306a36Sopenharmony_ci *
497062306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code indicating failure.
497162306a36Sopenharmony_ci */
497262306a36Sopenharmony_cistatic int netdev_close(struct net_device *dev)
497362306a36Sopenharmony_ci{
497462306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
497562306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
497662306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
497762306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
497862306a36Sopenharmony_ci	int pi;
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	netif_stop_queue(dev);
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci	ksz_stop_timer(&priv->monitor_timer_info);
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_ci	/* Need to shut the port manually in multiple device interfaces mode. */
498562306a36Sopenharmony_ci	if (hw->dev_count > 1) {
498662306a36Sopenharmony_ci		port_set_stp_state(hw, port->first_port, STP_STATE_DISABLED);
498762306a36Sopenharmony_ci
498862306a36Sopenharmony_ci		/* Port is closed.  Need to change bridge setting. */
498962306a36Sopenharmony_ci		if (hw->features & STP_SUPPORT) {
499062306a36Sopenharmony_ci			pi = 1 << port->first_port;
499162306a36Sopenharmony_ci			if (hw->ksz_switch->member & pi) {
499262306a36Sopenharmony_ci				hw->ksz_switch->member &= ~pi;
499362306a36Sopenharmony_ci				bridge_change(hw);
499462306a36Sopenharmony_ci			}
499562306a36Sopenharmony_ci		}
499662306a36Sopenharmony_ci	}
499762306a36Sopenharmony_ci	if (port->first_port > 0)
499862306a36Sopenharmony_ci		hw_del_addr(hw, dev->dev_addr);
499962306a36Sopenharmony_ci	if (!hw_priv->wol_enable)
500062306a36Sopenharmony_ci		port_set_power_saving(port, true);
500162306a36Sopenharmony_ci
500262306a36Sopenharmony_ci	if (priv->multicast)
500362306a36Sopenharmony_ci		--hw->all_multi;
500462306a36Sopenharmony_ci	if (priv->promiscuous)
500562306a36Sopenharmony_ci		--hw->promiscuous;
500662306a36Sopenharmony_ci
500762306a36Sopenharmony_ci	hw_priv->opened--;
500862306a36Sopenharmony_ci	if (!(hw_priv->opened)) {
500962306a36Sopenharmony_ci		ksz_stop_timer(&hw_priv->mib_timer_info);
501062306a36Sopenharmony_ci		flush_work(&hw_priv->mib_read);
501162306a36Sopenharmony_ci
501262306a36Sopenharmony_ci		hw_dis_intr(hw);
501362306a36Sopenharmony_ci		hw_disable(hw);
501462306a36Sopenharmony_ci		hw_clr_multicast(hw);
501562306a36Sopenharmony_ci
501662306a36Sopenharmony_ci		/* Delay for receive task to stop scheduling itself. */
501762306a36Sopenharmony_ci		msleep(2000 / HZ);
501862306a36Sopenharmony_ci
501962306a36Sopenharmony_ci		tasklet_kill(&hw_priv->rx_tasklet);
502062306a36Sopenharmony_ci		tasklet_kill(&hw_priv->tx_tasklet);
502162306a36Sopenharmony_ci		free_irq(dev->irq, hw_priv->dev);
502262306a36Sopenharmony_ci
502362306a36Sopenharmony_ci		transmit_cleanup(hw_priv, 0);
502462306a36Sopenharmony_ci		hw_reset_pkts(&hw->rx_desc_info);
502562306a36Sopenharmony_ci		hw_reset_pkts(&hw->tx_desc_info);
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci		/* Clean out static MAC table when the switch is shutdown. */
502862306a36Sopenharmony_ci		if (hw->features & STP_SUPPORT)
502962306a36Sopenharmony_ci			sw_clr_sta_mac_table(hw);
503062306a36Sopenharmony_ci	}
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci	return 0;
503362306a36Sopenharmony_ci}
503462306a36Sopenharmony_ci
503562306a36Sopenharmony_cistatic void hw_cfg_huge_frame(struct dev_info *hw_priv, struct ksz_hw *hw)
503662306a36Sopenharmony_ci{
503762306a36Sopenharmony_ci	if (hw->ksz_switch) {
503862306a36Sopenharmony_ci		u32 data;
503962306a36Sopenharmony_ci
504062306a36Sopenharmony_ci		data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
504162306a36Sopenharmony_ci		if (hw->features & RX_HUGE_FRAME)
504262306a36Sopenharmony_ci			data |= SWITCH_HUGE_PACKET;
504362306a36Sopenharmony_ci		else
504462306a36Sopenharmony_ci			data &= ~SWITCH_HUGE_PACKET;
504562306a36Sopenharmony_ci		writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
504662306a36Sopenharmony_ci	}
504762306a36Sopenharmony_ci	if (hw->features & RX_HUGE_FRAME) {
504862306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_ERROR;
504962306a36Sopenharmony_ci		hw_priv->dev_rcv = dev_rcv_special;
505062306a36Sopenharmony_ci	} else {
505162306a36Sopenharmony_ci		hw->rx_cfg &= ~DMA_RX_ERROR;
505262306a36Sopenharmony_ci		if (hw->dev_count > 1)
505362306a36Sopenharmony_ci			hw_priv->dev_rcv = port_rcv_packets;
505462306a36Sopenharmony_ci		else
505562306a36Sopenharmony_ci			hw_priv->dev_rcv = dev_rcv_packets;
505662306a36Sopenharmony_ci	}
505762306a36Sopenharmony_ci}
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_cistatic int prepare_hardware(struct net_device *dev)
506062306a36Sopenharmony_ci{
506162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
506262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
506362306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
506462306a36Sopenharmony_ci	int rc = 0;
506562306a36Sopenharmony_ci
506662306a36Sopenharmony_ci	/* Remember the network device that requests interrupts. */
506762306a36Sopenharmony_ci	hw_priv->dev = dev;
506862306a36Sopenharmony_ci	rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev);
506962306a36Sopenharmony_ci	if (rc)
507062306a36Sopenharmony_ci		return rc;
507162306a36Sopenharmony_ci	tasklet_setup(&hw_priv->rx_tasklet, rx_proc_task);
507262306a36Sopenharmony_ci	tasklet_setup(&hw_priv->tx_tasklet, tx_proc_task);
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci	hw->promiscuous = 0;
507562306a36Sopenharmony_ci	hw->all_multi = 0;
507662306a36Sopenharmony_ci	hw->multi_list_size = 0;
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci	hw_reset(hw);
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_ci	hw_set_desc_base(hw,
508162306a36Sopenharmony_ci		hw->tx_desc_info.ring_phys, hw->rx_desc_info.ring_phys);
508262306a36Sopenharmony_ci	hw_set_addr(hw);
508362306a36Sopenharmony_ci	hw_cfg_huge_frame(hw_priv, hw);
508462306a36Sopenharmony_ci	ksz_init_rx_buffers(hw_priv);
508562306a36Sopenharmony_ci	return 0;
508662306a36Sopenharmony_ci}
508762306a36Sopenharmony_ci
508862306a36Sopenharmony_cistatic void set_media_state(struct net_device *dev, int media_state)
508962306a36Sopenharmony_ci{
509062306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_ci	if (media_state == priv->media_state)
509362306a36Sopenharmony_ci		netif_carrier_on(dev);
509462306a36Sopenharmony_ci	else
509562306a36Sopenharmony_ci		netif_carrier_off(dev);
509662306a36Sopenharmony_ci	netif_info(priv, link, dev, "link %s\n",
509762306a36Sopenharmony_ci		   media_state == priv->media_state ? "on" : "off");
509862306a36Sopenharmony_ci}
509962306a36Sopenharmony_ci
510062306a36Sopenharmony_ci/**
510162306a36Sopenharmony_ci * netdev_open - open network device
510262306a36Sopenharmony_ci * @dev:	Network device.
510362306a36Sopenharmony_ci *
510462306a36Sopenharmony_ci * This function process the open operation of network device.  This is caused
510562306a36Sopenharmony_ci * by the user command "ifconfig ethX up."
510662306a36Sopenharmony_ci *
510762306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code indicating failure.
510862306a36Sopenharmony_ci */
510962306a36Sopenharmony_cistatic int netdev_open(struct net_device *dev)
511062306a36Sopenharmony_ci{
511162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
511262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
511362306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
511462306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
511562306a36Sopenharmony_ci	unsigned long next_jiffies;
511662306a36Sopenharmony_ci	int i;
511762306a36Sopenharmony_ci	int p;
511862306a36Sopenharmony_ci	int rc = 0;
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_ci	next_jiffies = jiffies + HZ * 2;
512162306a36Sopenharmony_ci	priv->multicast = 0;
512262306a36Sopenharmony_ci	priv->promiscuous = 0;
512362306a36Sopenharmony_ci
512462306a36Sopenharmony_ci	/* Reset device statistics. */
512562306a36Sopenharmony_ci	memset(&dev->stats, 0, sizeof(struct net_device_stats));
512662306a36Sopenharmony_ci	memset((void *) port->counter, 0,
512762306a36Sopenharmony_ci		(sizeof(u64) * OID_COUNTER_LAST));
512862306a36Sopenharmony_ci
512962306a36Sopenharmony_ci	if (!(hw_priv->opened)) {
513062306a36Sopenharmony_ci		rc = prepare_hardware(dev);
513162306a36Sopenharmony_ci		if (rc)
513262306a36Sopenharmony_ci			return rc;
513362306a36Sopenharmony_ci		for (i = 0; i < hw->mib_port_cnt; i++) {
513462306a36Sopenharmony_ci			next_jiffies += HZ * 1;
513562306a36Sopenharmony_ci			hw_priv->counter[i].time = next_jiffies;
513662306a36Sopenharmony_ci			hw->port_mib[i].state = media_disconnected;
513762306a36Sopenharmony_ci			port_init_cnt(hw, i);
513862306a36Sopenharmony_ci		}
513962306a36Sopenharmony_ci		if (hw->ksz_switch)
514062306a36Sopenharmony_ci			hw->port_mib[HOST_PORT].state = media_connected;
514162306a36Sopenharmony_ci		else {
514262306a36Sopenharmony_ci			hw_add_wol_bcast(hw);
514362306a36Sopenharmony_ci			hw_cfg_wol_pme(hw, 0);
514462306a36Sopenharmony_ci			hw_clr_wol_pme_status(&hw_priv->hw);
514562306a36Sopenharmony_ci		}
514662306a36Sopenharmony_ci	}
514762306a36Sopenharmony_ci	port_set_power_saving(port, false);
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
515062306a36Sopenharmony_ci		/*
515162306a36Sopenharmony_ci		 * Initialize to invalid value so that link detection
515262306a36Sopenharmony_ci		 * is done.
515362306a36Sopenharmony_ci		 */
515462306a36Sopenharmony_ci		hw->port_info[p].partner = 0xFF;
515562306a36Sopenharmony_ci		hw->port_info[p].state = media_disconnected;
515662306a36Sopenharmony_ci	}
515762306a36Sopenharmony_ci
515862306a36Sopenharmony_ci	/* Need to open the port in multiple device interfaces mode. */
515962306a36Sopenharmony_ci	if (hw->dev_count > 1) {
516062306a36Sopenharmony_ci		port_set_stp_state(hw, port->first_port, STP_STATE_SIMPLE);
516162306a36Sopenharmony_ci		if (port->first_port > 0)
516262306a36Sopenharmony_ci			hw_add_addr(hw, dev->dev_addr);
516362306a36Sopenharmony_ci	}
516462306a36Sopenharmony_ci
516562306a36Sopenharmony_ci	port_get_link_speed(port);
516662306a36Sopenharmony_ci	if (port->force_link)
516762306a36Sopenharmony_ci		port_force_link_speed(port);
516862306a36Sopenharmony_ci	else
516962306a36Sopenharmony_ci		port_set_link_speed(port);
517062306a36Sopenharmony_ci
517162306a36Sopenharmony_ci	if (!(hw_priv->opened)) {
517262306a36Sopenharmony_ci		hw_setup_intr(hw);
517362306a36Sopenharmony_ci		hw_enable(hw);
517462306a36Sopenharmony_ci		hw_ena_intr(hw);
517562306a36Sopenharmony_ci
517662306a36Sopenharmony_ci		if (hw->mib_port_cnt)
517762306a36Sopenharmony_ci			ksz_start_timer(&hw_priv->mib_timer_info,
517862306a36Sopenharmony_ci				hw_priv->mib_timer_info.period);
517962306a36Sopenharmony_ci	}
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci	hw_priv->opened++;
518262306a36Sopenharmony_ci
518362306a36Sopenharmony_ci	ksz_start_timer(&priv->monitor_timer_info,
518462306a36Sopenharmony_ci		priv->monitor_timer_info.period);
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci	priv->media_state = port->linked->state;
518762306a36Sopenharmony_ci
518862306a36Sopenharmony_ci	set_media_state(dev, media_connected);
518962306a36Sopenharmony_ci	netif_start_queue(dev);
519062306a36Sopenharmony_ci
519162306a36Sopenharmony_ci	return 0;
519262306a36Sopenharmony_ci}
519362306a36Sopenharmony_ci
519462306a36Sopenharmony_ci/* RX errors = rx_errors */
519562306a36Sopenharmony_ci/* RX dropped = rx_dropped */
519662306a36Sopenharmony_ci/* RX overruns = rx_fifo_errors */
519762306a36Sopenharmony_ci/* RX frame = rx_crc_errors + rx_frame_errors + rx_length_errors */
519862306a36Sopenharmony_ci/* TX errors = tx_errors */
519962306a36Sopenharmony_ci/* TX dropped = tx_dropped */
520062306a36Sopenharmony_ci/* TX overruns = tx_fifo_errors */
520162306a36Sopenharmony_ci/* TX carrier = tx_aborted_errors + tx_carrier_errors + tx_window_errors */
520262306a36Sopenharmony_ci/* collisions = collisions */
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci/**
520562306a36Sopenharmony_ci * netdev_query_statistics - query network device statistics
520662306a36Sopenharmony_ci * @dev:	Network device.
520762306a36Sopenharmony_ci *
520862306a36Sopenharmony_ci * This function returns the statistics of the network device.  The device
520962306a36Sopenharmony_ci * needs not be opened.
521062306a36Sopenharmony_ci *
521162306a36Sopenharmony_ci * Return network device statistics.
521262306a36Sopenharmony_ci */
521362306a36Sopenharmony_cistatic struct net_device_stats *netdev_query_statistics(struct net_device *dev)
521462306a36Sopenharmony_ci{
521562306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
521662306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
521762306a36Sopenharmony_ci	struct ksz_hw *hw = &priv->adapter->hw;
521862306a36Sopenharmony_ci	struct ksz_port_mib *mib;
521962306a36Sopenharmony_ci	int i;
522062306a36Sopenharmony_ci	int p;
522162306a36Sopenharmony_ci
522262306a36Sopenharmony_ci	dev->stats.rx_errors = port->counter[OID_COUNTER_RCV_ERROR];
522362306a36Sopenharmony_ci	dev->stats.tx_errors = port->counter[OID_COUNTER_XMIT_ERROR];
522462306a36Sopenharmony_ci
522562306a36Sopenharmony_ci	/* Reset to zero to add count later. */
522662306a36Sopenharmony_ci	dev->stats.multicast = 0;
522762306a36Sopenharmony_ci	dev->stats.collisions = 0;
522862306a36Sopenharmony_ci	dev->stats.rx_length_errors = 0;
522962306a36Sopenharmony_ci	dev->stats.rx_crc_errors = 0;
523062306a36Sopenharmony_ci	dev->stats.rx_frame_errors = 0;
523162306a36Sopenharmony_ci	dev->stats.tx_window_errors = 0;
523262306a36Sopenharmony_ci
523362306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
523462306a36Sopenharmony_ci		mib = &hw->port_mib[p];
523562306a36Sopenharmony_ci
523662306a36Sopenharmony_ci		dev->stats.multicast += (unsigned long)
523762306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_MULTICAST];
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci		dev->stats.collisions += (unsigned long)
524062306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_TX_TOTAL_COLLISION];
524162306a36Sopenharmony_ci
524262306a36Sopenharmony_ci		dev->stats.rx_length_errors += (unsigned long)(
524362306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_UNDERSIZE] +
524462306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_FRAGMENT] +
524562306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_OVERSIZE] +
524662306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_JABBER]);
524762306a36Sopenharmony_ci		dev->stats.rx_crc_errors += (unsigned long)
524862306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_CRC_ERR];
524962306a36Sopenharmony_ci		dev->stats.rx_frame_errors += (unsigned long)(
525062306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_ALIGNMENT_ERR] +
525162306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_RX_SYMBOL_ERR]);
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci		dev->stats.tx_window_errors += (unsigned long)
525462306a36Sopenharmony_ci			mib->counter[MIB_COUNTER_TX_LATE_COLLISION];
525562306a36Sopenharmony_ci	}
525662306a36Sopenharmony_ci
525762306a36Sopenharmony_ci	return &dev->stats;
525862306a36Sopenharmony_ci}
525962306a36Sopenharmony_ci
526062306a36Sopenharmony_ci/**
526162306a36Sopenharmony_ci * netdev_set_mac_address - set network device MAC address
526262306a36Sopenharmony_ci * @dev:	Network device.
526362306a36Sopenharmony_ci * @addr:	Buffer of MAC address.
526462306a36Sopenharmony_ci *
526562306a36Sopenharmony_ci * This function is used to set the MAC address of the network device.
526662306a36Sopenharmony_ci *
526762306a36Sopenharmony_ci * Return 0 to indicate success.
526862306a36Sopenharmony_ci */
526962306a36Sopenharmony_cistatic int netdev_set_mac_address(struct net_device *dev, void *addr)
527062306a36Sopenharmony_ci{
527162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
527262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
527362306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
527462306a36Sopenharmony_ci	struct sockaddr *mac = addr;
527562306a36Sopenharmony_ci	uint interrupt;
527662306a36Sopenharmony_ci
527762306a36Sopenharmony_ci	if (priv->port.first_port > 0)
527862306a36Sopenharmony_ci		hw_del_addr(hw, dev->dev_addr);
527962306a36Sopenharmony_ci	else {
528062306a36Sopenharmony_ci		hw->mac_override = 1;
528162306a36Sopenharmony_ci		memcpy(hw->override_addr, mac->sa_data, ETH_ALEN);
528262306a36Sopenharmony_ci	}
528362306a36Sopenharmony_ci
528462306a36Sopenharmony_ci	eth_hw_addr_set(dev, mac->sa_data);
528562306a36Sopenharmony_ci
528662306a36Sopenharmony_ci	interrupt = hw_block_intr(hw);
528762306a36Sopenharmony_ci
528862306a36Sopenharmony_ci	if (priv->port.first_port > 0)
528962306a36Sopenharmony_ci		hw_add_addr(hw, dev->dev_addr);
529062306a36Sopenharmony_ci	else
529162306a36Sopenharmony_ci		hw_set_addr(hw);
529262306a36Sopenharmony_ci	hw_restore_intr(hw, interrupt);
529362306a36Sopenharmony_ci
529462306a36Sopenharmony_ci	return 0;
529562306a36Sopenharmony_ci}
529662306a36Sopenharmony_ci
529762306a36Sopenharmony_cistatic void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
529862306a36Sopenharmony_ci	struct ksz_hw *hw, int promiscuous)
529962306a36Sopenharmony_ci{
530062306a36Sopenharmony_ci	if (promiscuous != priv->promiscuous) {
530162306a36Sopenharmony_ci		u8 prev_state = hw->promiscuous;
530262306a36Sopenharmony_ci
530362306a36Sopenharmony_ci		if (promiscuous)
530462306a36Sopenharmony_ci			++hw->promiscuous;
530562306a36Sopenharmony_ci		else
530662306a36Sopenharmony_ci			--hw->promiscuous;
530762306a36Sopenharmony_ci		priv->promiscuous = promiscuous;
530862306a36Sopenharmony_ci
530962306a36Sopenharmony_ci		/* Turn on/off promiscuous mode. */
531062306a36Sopenharmony_ci		if (hw->promiscuous <= 1 && prev_state <= 1)
531162306a36Sopenharmony_ci			hw_set_promiscuous(hw, hw->promiscuous);
531262306a36Sopenharmony_ci
531362306a36Sopenharmony_ci		/*
531462306a36Sopenharmony_ci		 * Port is not in promiscuous mode, meaning it is released
531562306a36Sopenharmony_ci		 * from the bridge.
531662306a36Sopenharmony_ci		 */
531762306a36Sopenharmony_ci		if ((hw->features & STP_SUPPORT) && !promiscuous &&
531862306a36Sopenharmony_ci		    netif_is_bridge_port(dev)) {
531962306a36Sopenharmony_ci			struct ksz_switch *sw = hw->ksz_switch;
532062306a36Sopenharmony_ci			int port = priv->port.first_port;
532162306a36Sopenharmony_ci
532262306a36Sopenharmony_ci			port_set_stp_state(hw, port, STP_STATE_DISABLED);
532362306a36Sopenharmony_ci			port = 1 << port;
532462306a36Sopenharmony_ci			if (sw->member & port) {
532562306a36Sopenharmony_ci				sw->member &= ~port;
532662306a36Sopenharmony_ci				bridge_change(hw);
532762306a36Sopenharmony_ci			}
532862306a36Sopenharmony_ci		}
532962306a36Sopenharmony_ci	}
533062306a36Sopenharmony_ci}
533162306a36Sopenharmony_ci
533262306a36Sopenharmony_cistatic void dev_set_multicast(struct dev_priv *priv, struct ksz_hw *hw,
533362306a36Sopenharmony_ci	int multicast)
533462306a36Sopenharmony_ci{
533562306a36Sopenharmony_ci	if (multicast != priv->multicast) {
533662306a36Sopenharmony_ci		u8 all_multi = hw->all_multi;
533762306a36Sopenharmony_ci
533862306a36Sopenharmony_ci		if (multicast)
533962306a36Sopenharmony_ci			++hw->all_multi;
534062306a36Sopenharmony_ci		else
534162306a36Sopenharmony_ci			--hw->all_multi;
534262306a36Sopenharmony_ci		priv->multicast = multicast;
534362306a36Sopenharmony_ci
534462306a36Sopenharmony_ci		/* Turn on/off all multicast mode. */
534562306a36Sopenharmony_ci		if (hw->all_multi <= 1 && all_multi <= 1)
534662306a36Sopenharmony_ci			hw_set_multicast(hw, hw->all_multi);
534762306a36Sopenharmony_ci	}
534862306a36Sopenharmony_ci}
534962306a36Sopenharmony_ci
535062306a36Sopenharmony_ci/**
535162306a36Sopenharmony_ci * netdev_set_rx_mode
535262306a36Sopenharmony_ci * @dev:	Network device.
535362306a36Sopenharmony_ci *
535462306a36Sopenharmony_ci * This routine is used to set multicast addresses or put the network device
535562306a36Sopenharmony_ci * into promiscuous mode.
535662306a36Sopenharmony_ci */
535762306a36Sopenharmony_cistatic void netdev_set_rx_mode(struct net_device *dev)
535862306a36Sopenharmony_ci{
535962306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
536062306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
536162306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
536262306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
536362306a36Sopenharmony_ci	int multicast = (dev->flags & IFF_ALLMULTI);
536462306a36Sopenharmony_ci
536562306a36Sopenharmony_ci	dev_set_promiscuous(dev, priv, hw, (dev->flags & IFF_PROMISC));
536662306a36Sopenharmony_ci
536762306a36Sopenharmony_ci	if (hw_priv->hw.dev_count > 1)
536862306a36Sopenharmony_ci		multicast |= (dev->flags & IFF_MULTICAST);
536962306a36Sopenharmony_ci	dev_set_multicast(priv, hw, multicast);
537062306a36Sopenharmony_ci
537162306a36Sopenharmony_ci	/* Cannot use different hashes in multiple device interfaces mode. */
537262306a36Sopenharmony_ci	if (hw_priv->hw.dev_count > 1)
537362306a36Sopenharmony_ci		return;
537462306a36Sopenharmony_ci
537562306a36Sopenharmony_ci	if ((dev->flags & IFF_MULTICAST) && !netdev_mc_empty(dev)) {
537662306a36Sopenharmony_ci		int i = 0;
537762306a36Sopenharmony_ci
537862306a36Sopenharmony_ci		/* List too big to support so turn on all multicast mode. */
537962306a36Sopenharmony_ci		if (netdev_mc_count(dev) > MAX_MULTICAST_LIST) {
538062306a36Sopenharmony_ci			if (MAX_MULTICAST_LIST != hw->multi_list_size) {
538162306a36Sopenharmony_ci				hw->multi_list_size = MAX_MULTICAST_LIST;
538262306a36Sopenharmony_ci				++hw->all_multi;
538362306a36Sopenharmony_ci				hw_set_multicast(hw, hw->all_multi);
538462306a36Sopenharmony_ci			}
538562306a36Sopenharmony_ci			return;
538662306a36Sopenharmony_ci		}
538762306a36Sopenharmony_ci
538862306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
538962306a36Sopenharmony_ci			if (i >= MAX_MULTICAST_LIST)
539062306a36Sopenharmony_ci				break;
539162306a36Sopenharmony_ci			memcpy(hw->multi_list[i++], ha->addr, ETH_ALEN);
539262306a36Sopenharmony_ci		}
539362306a36Sopenharmony_ci		hw->multi_list_size = (u8) i;
539462306a36Sopenharmony_ci		hw_set_grp_addr(hw);
539562306a36Sopenharmony_ci	} else {
539662306a36Sopenharmony_ci		if (MAX_MULTICAST_LIST == hw->multi_list_size) {
539762306a36Sopenharmony_ci			--hw->all_multi;
539862306a36Sopenharmony_ci			hw_set_multicast(hw, hw->all_multi);
539962306a36Sopenharmony_ci		}
540062306a36Sopenharmony_ci		hw->multi_list_size = 0;
540162306a36Sopenharmony_ci		hw_clr_multicast(hw);
540262306a36Sopenharmony_ci	}
540362306a36Sopenharmony_ci}
540462306a36Sopenharmony_ci
540562306a36Sopenharmony_cistatic int netdev_change_mtu(struct net_device *dev, int new_mtu)
540662306a36Sopenharmony_ci{
540762306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
540862306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
540962306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
541062306a36Sopenharmony_ci	int hw_mtu;
541162306a36Sopenharmony_ci
541262306a36Sopenharmony_ci	if (netif_running(dev))
541362306a36Sopenharmony_ci		return -EBUSY;
541462306a36Sopenharmony_ci
541562306a36Sopenharmony_ci	/* Cannot use different MTU in multiple device interfaces mode. */
541662306a36Sopenharmony_ci	if (hw->dev_count > 1)
541762306a36Sopenharmony_ci		if (dev != hw_priv->dev)
541862306a36Sopenharmony_ci			return 0;
541962306a36Sopenharmony_ci
542062306a36Sopenharmony_ci	hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4;
542162306a36Sopenharmony_ci	if (hw_mtu > REGULAR_RX_BUF_SIZE) {
542262306a36Sopenharmony_ci		hw->features |= RX_HUGE_FRAME;
542362306a36Sopenharmony_ci		hw_mtu = MAX_RX_BUF_SIZE;
542462306a36Sopenharmony_ci	} else {
542562306a36Sopenharmony_ci		hw->features &= ~RX_HUGE_FRAME;
542662306a36Sopenharmony_ci		hw_mtu = REGULAR_RX_BUF_SIZE;
542762306a36Sopenharmony_ci	}
542862306a36Sopenharmony_ci	hw_mtu = (hw_mtu + 3) & ~3;
542962306a36Sopenharmony_ci	hw_priv->mtu = hw_mtu;
543062306a36Sopenharmony_ci	dev->mtu = new_mtu;
543162306a36Sopenharmony_ci
543262306a36Sopenharmony_ci	return 0;
543362306a36Sopenharmony_ci}
543462306a36Sopenharmony_ci
543562306a36Sopenharmony_ci/**
543662306a36Sopenharmony_ci * netdev_ioctl - I/O control processing
543762306a36Sopenharmony_ci * @dev:	Network device.
543862306a36Sopenharmony_ci * @ifr:	Interface request structure.
543962306a36Sopenharmony_ci * @cmd:	I/O control code.
544062306a36Sopenharmony_ci *
544162306a36Sopenharmony_ci * This function is used to process I/O control calls.
544262306a36Sopenharmony_ci *
544362306a36Sopenharmony_ci * Return 0 to indicate success.
544462306a36Sopenharmony_ci */
544562306a36Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
544662306a36Sopenharmony_ci{
544762306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
544862306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
544962306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
545062306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
545162306a36Sopenharmony_ci	int result = 0;
545262306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_ci	if (down_interruptible(&priv->proc_sem))
545562306a36Sopenharmony_ci		return -ERESTARTSYS;
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_ci	switch (cmd) {
545862306a36Sopenharmony_ci	/* Get address of MII PHY in use. */
545962306a36Sopenharmony_ci	case SIOCGMIIPHY:
546062306a36Sopenharmony_ci		data->phy_id = priv->id;
546162306a36Sopenharmony_ci		fallthrough;
546262306a36Sopenharmony_ci
546362306a36Sopenharmony_ci	/* Read MII PHY register. */
546462306a36Sopenharmony_ci	case SIOCGMIIREG:
546562306a36Sopenharmony_ci		if (data->phy_id != priv->id || data->reg_num >= 6)
546662306a36Sopenharmony_ci			result = -EIO;
546762306a36Sopenharmony_ci		else
546862306a36Sopenharmony_ci			hw_r_phy(hw, port->linked->port_id, data->reg_num,
546962306a36Sopenharmony_ci				&data->val_out);
547062306a36Sopenharmony_ci		break;
547162306a36Sopenharmony_ci
547262306a36Sopenharmony_ci	/* Write MII PHY register. */
547362306a36Sopenharmony_ci	case SIOCSMIIREG:
547462306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
547562306a36Sopenharmony_ci			result = -EPERM;
547662306a36Sopenharmony_ci		else if (data->phy_id != priv->id || data->reg_num >= 6)
547762306a36Sopenharmony_ci			result = -EIO;
547862306a36Sopenharmony_ci		else
547962306a36Sopenharmony_ci			hw_w_phy(hw, port->linked->port_id, data->reg_num,
548062306a36Sopenharmony_ci				data->val_in);
548162306a36Sopenharmony_ci		break;
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_ci	default:
548462306a36Sopenharmony_ci		result = -EOPNOTSUPP;
548562306a36Sopenharmony_ci	}
548662306a36Sopenharmony_ci
548762306a36Sopenharmony_ci	up(&priv->proc_sem);
548862306a36Sopenharmony_ci
548962306a36Sopenharmony_ci	return result;
549062306a36Sopenharmony_ci}
549162306a36Sopenharmony_ci
549262306a36Sopenharmony_ci/*
549362306a36Sopenharmony_ci * MII support
549462306a36Sopenharmony_ci */
549562306a36Sopenharmony_ci
549662306a36Sopenharmony_ci/**
549762306a36Sopenharmony_ci * mdio_read - read PHY register
549862306a36Sopenharmony_ci * @dev:	Network device.
549962306a36Sopenharmony_ci * @phy_id:	The PHY id.
550062306a36Sopenharmony_ci * @reg_num:	The register number.
550162306a36Sopenharmony_ci *
550262306a36Sopenharmony_ci * This function returns the PHY register value.
550362306a36Sopenharmony_ci *
550462306a36Sopenharmony_ci * Return the register value.
550562306a36Sopenharmony_ci */
550662306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int reg_num)
550762306a36Sopenharmony_ci{
550862306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
550962306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
551062306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
551162306a36Sopenharmony_ci	u16 val_out;
551262306a36Sopenharmony_ci
551362306a36Sopenharmony_ci	hw_r_phy(hw, port->linked->port_id, reg_num << 1, &val_out);
551462306a36Sopenharmony_ci	return val_out;
551562306a36Sopenharmony_ci}
551662306a36Sopenharmony_ci
551762306a36Sopenharmony_ci/**
551862306a36Sopenharmony_ci * mdio_write - set PHY register
551962306a36Sopenharmony_ci * @dev:	Network device.
552062306a36Sopenharmony_ci * @phy_id:	The PHY id.
552162306a36Sopenharmony_ci * @reg_num:	The register number.
552262306a36Sopenharmony_ci * @val:	The register value.
552362306a36Sopenharmony_ci *
552462306a36Sopenharmony_ci * This procedure sets the PHY register value.
552562306a36Sopenharmony_ci */
552662306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
552762306a36Sopenharmony_ci{
552862306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
552962306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
553062306a36Sopenharmony_ci	struct ksz_hw *hw = port->hw;
553162306a36Sopenharmony_ci	int i;
553262306a36Sopenharmony_ci	int pi;
553362306a36Sopenharmony_ci
553462306a36Sopenharmony_ci	for (i = 0, pi = port->first_port; i < port->port_cnt; i++, pi++)
553562306a36Sopenharmony_ci		hw_w_phy(hw, pi, reg_num << 1, val);
553662306a36Sopenharmony_ci}
553762306a36Sopenharmony_ci
553862306a36Sopenharmony_ci/*
553962306a36Sopenharmony_ci * ethtool support
554062306a36Sopenharmony_ci */
554162306a36Sopenharmony_ci
554262306a36Sopenharmony_ci#define EEPROM_SIZE			0x40
554362306a36Sopenharmony_ci
554462306a36Sopenharmony_cistatic u16 eeprom_data[EEPROM_SIZE] = { 0 };
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_ci#define ADVERTISED_ALL			\
554762306a36Sopenharmony_ci	(ADVERTISED_10baseT_Half |	\
554862306a36Sopenharmony_ci	ADVERTISED_10baseT_Full |	\
554962306a36Sopenharmony_ci	ADVERTISED_100baseT_Half |	\
555062306a36Sopenharmony_ci	ADVERTISED_100baseT_Full)
555162306a36Sopenharmony_ci
555262306a36Sopenharmony_ci/* These functions use the MII functions in mii.c. */
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_ci/**
555562306a36Sopenharmony_ci * netdev_get_link_ksettings - get network device settings
555662306a36Sopenharmony_ci * @dev:	Network device.
555762306a36Sopenharmony_ci * @cmd:	Ethtool command.
555862306a36Sopenharmony_ci *
555962306a36Sopenharmony_ci * This function queries the PHY and returns its state in the ethtool command.
556062306a36Sopenharmony_ci *
556162306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
556262306a36Sopenharmony_ci */
556362306a36Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
556462306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
556562306a36Sopenharmony_ci{
556662306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
556762306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
556862306a36Sopenharmony_ci
556962306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
557062306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&priv->mii_if, cmd);
557162306a36Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
557262306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
557362306a36Sopenharmony_ci
557462306a36Sopenharmony_ci	/* Save advertised settings for workaround in next function. */
557562306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&priv->advertising,
557662306a36Sopenharmony_ci						cmd->link_modes.advertising);
557762306a36Sopenharmony_ci
557862306a36Sopenharmony_ci	return 0;
557962306a36Sopenharmony_ci}
558062306a36Sopenharmony_ci
558162306a36Sopenharmony_ci/**
558262306a36Sopenharmony_ci * netdev_set_link_ksettings - set network device settings
558362306a36Sopenharmony_ci * @dev:	Network device.
558462306a36Sopenharmony_ci * @cmd:	Ethtool command.
558562306a36Sopenharmony_ci *
558662306a36Sopenharmony_ci * This function sets the PHY according to the ethtool command.
558762306a36Sopenharmony_ci *
558862306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
558962306a36Sopenharmony_ci */
559062306a36Sopenharmony_cistatic int netdev_set_link_ksettings(struct net_device *dev,
559162306a36Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
559262306a36Sopenharmony_ci{
559362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
559462306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
559562306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
559662306a36Sopenharmony_ci	struct ethtool_link_ksettings copy_cmd;
559762306a36Sopenharmony_ci	u32 speed = cmd->base.speed;
559862306a36Sopenharmony_ci	u32 advertising;
559962306a36Sopenharmony_ci	int rc;
560062306a36Sopenharmony_ci
560162306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
560262306a36Sopenharmony_ci						cmd->link_modes.advertising);
560362306a36Sopenharmony_ci
560462306a36Sopenharmony_ci	/*
560562306a36Sopenharmony_ci	 * ethtool utility does not change advertised setting if auto
560662306a36Sopenharmony_ci	 * negotiation is not specified explicitly.
560762306a36Sopenharmony_ci	 */
560862306a36Sopenharmony_ci	if (cmd->base.autoneg && priv->advertising == advertising) {
560962306a36Sopenharmony_ci		advertising |= ADVERTISED_ALL;
561062306a36Sopenharmony_ci		if (10 == speed)
561162306a36Sopenharmony_ci			advertising &=
561262306a36Sopenharmony_ci				~(ADVERTISED_100baseT_Full |
561362306a36Sopenharmony_ci				ADVERTISED_100baseT_Half);
561462306a36Sopenharmony_ci		else if (100 == speed)
561562306a36Sopenharmony_ci			advertising &=
561662306a36Sopenharmony_ci				~(ADVERTISED_10baseT_Full |
561762306a36Sopenharmony_ci				ADVERTISED_10baseT_Half);
561862306a36Sopenharmony_ci		if (0 == cmd->base.duplex)
561962306a36Sopenharmony_ci			advertising &=
562062306a36Sopenharmony_ci				~(ADVERTISED_100baseT_Full |
562162306a36Sopenharmony_ci				ADVERTISED_10baseT_Full);
562262306a36Sopenharmony_ci		else if (1 == cmd->base.duplex)
562362306a36Sopenharmony_ci			advertising &=
562462306a36Sopenharmony_ci				~(ADVERTISED_100baseT_Half |
562562306a36Sopenharmony_ci				ADVERTISED_10baseT_Half);
562662306a36Sopenharmony_ci	}
562762306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
562862306a36Sopenharmony_ci	if (cmd->base.autoneg &&
562962306a36Sopenharmony_ci	    (advertising & ADVERTISED_ALL) == ADVERTISED_ALL) {
563062306a36Sopenharmony_ci		port->duplex = 0;
563162306a36Sopenharmony_ci		port->speed = 0;
563262306a36Sopenharmony_ci		port->force_link = 0;
563362306a36Sopenharmony_ci	} else {
563462306a36Sopenharmony_ci		port->duplex = cmd->base.duplex + 1;
563562306a36Sopenharmony_ci		if (1000 != speed)
563662306a36Sopenharmony_ci			port->speed = speed;
563762306a36Sopenharmony_ci		if (cmd->base.autoneg)
563862306a36Sopenharmony_ci			port->force_link = 0;
563962306a36Sopenharmony_ci		else
564062306a36Sopenharmony_ci			port->force_link = 1;
564162306a36Sopenharmony_ci	}
564262306a36Sopenharmony_ci
564362306a36Sopenharmony_ci	memcpy(&copy_cmd, cmd, sizeof(copy_cmd));
564462306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(copy_cmd.link_modes.advertising,
564562306a36Sopenharmony_ci						advertising);
564662306a36Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(
564762306a36Sopenharmony_ci		&priv->mii_if,
564862306a36Sopenharmony_ci		(const struct ethtool_link_ksettings *)&copy_cmd);
564962306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
565062306a36Sopenharmony_ci	return rc;
565162306a36Sopenharmony_ci}
565262306a36Sopenharmony_ci
565362306a36Sopenharmony_ci/**
565462306a36Sopenharmony_ci * netdev_nway_reset - restart auto-negotiation
565562306a36Sopenharmony_ci * @dev:	Network device.
565662306a36Sopenharmony_ci *
565762306a36Sopenharmony_ci * This function restarts the PHY for auto-negotiation.
565862306a36Sopenharmony_ci *
565962306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
566062306a36Sopenharmony_ci */
566162306a36Sopenharmony_cistatic int netdev_nway_reset(struct net_device *dev)
566262306a36Sopenharmony_ci{
566362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
566462306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
566562306a36Sopenharmony_ci	int rc;
566662306a36Sopenharmony_ci
566762306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
566862306a36Sopenharmony_ci	rc = mii_nway_restart(&priv->mii_if);
566962306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
567062306a36Sopenharmony_ci	return rc;
567162306a36Sopenharmony_ci}
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_ci/**
567462306a36Sopenharmony_ci * netdev_get_link - get network device link status
567562306a36Sopenharmony_ci * @dev:	Network device.
567662306a36Sopenharmony_ci *
567762306a36Sopenharmony_ci * This function gets the link status from the PHY.
567862306a36Sopenharmony_ci *
567962306a36Sopenharmony_ci * Return true if PHY is linked and false otherwise.
568062306a36Sopenharmony_ci */
568162306a36Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev)
568262306a36Sopenharmony_ci{
568362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
568462306a36Sopenharmony_ci	int rc;
568562306a36Sopenharmony_ci
568662306a36Sopenharmony_ci	rc = mii_link_ok(&priv->mii_if);
568762306a36Sopenharmony_ci	return rc;
568862306a36Sopenharmony_ci}
568962306a36Sopenharmony_ci
569062306a36Sopenharmony_ci/**
569162306a36Sopenharmony_ci * netdev_get_drvinfo - get network driver information
569262306a36Sopenharmony_ci * @dev:	Network device.
569362306a36Sopenharmony_ci * @info:	Ethtool driver info data structure.
569462306a36Sopenharmony_ci *
569562306a36Sopenharmony_ci * This procedure returns the driver information.
569662306a36Sopenharmony_ci */
569762306a36Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev,
569862306a36Sopenharmony_ci	struct ethtool_drvinfo *info)
569962306a36Sopenharmony_ci{
570062306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
570162306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
570262306a36Sopenharmony_ci
570362306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
570462306a36Sopenharmony_ci	strscpy(info->version, DRV_VERSION, sizeof(info->version));
570562306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(hw_priv->pdev),
570662306a36Sopenharmony_ci		sizeof(info->bus_info));
570762306a36Sopenharmony_ci}
570862306a36Sopenharmony_ci
570962306a36Sopenharmony_cistatic struct hw_regs {
571062306a36Sopenharmony_ci	int start;
571162306a36Sopenharmony_ci	int end;
571262306a36Sopenharmony_ci} hw_regs_range[] = {
571362306a36Sopenharmony_ci	{ KS_DMA_TX_CTRL,	KS884X_INTERRUPTS_STATUS },
571462306a36Sopenharmony_ci	{ KS_ADD_ADDR_0_LO,	KS_ADD_ADDR_F_HI },
571562306a36Sopenharmony_ci	{ KS884X_ADDR_0_OFFSET,	KS8841_WOL_FRAME_BYTE2_OFFSET },
571662306a36Sopenharmony_ci	{ KS884X_SIDER_P,	KS8842_SGCR7_P },
571762306a36Sopenharmony_ci	{ KS8842_MACAR1_P,	KS8842_TOSR8_P },
571862306a36Sopenharmony_ci	{ KS884X_P1MBCR_P,	KS8842_P3ERCR_P },
571962306a36Sopenharmony_ci	{ 0, 0 }
572062306a36Sopenharmony_ci};
572162306a36Sopenharmony_ci
572262306a36Sopenharmony_ci/**
572362306a36Sopenharmony_ci * netdev_get_regs_len - get length of register dump
572462306a36Sopenharmony_ci * @dev:	Network device.
572562306a36Sopenharmony_ci *
572662306a36Sopenharmony_ci * This function returns the length of the register dump.
572762306a36Sopenharmony_ci *
572862306a36Sopenharmony_ci * Return length of the register dump.
572962306a36Sopenharmony_ci */
573062306a36Sopenharmony_cistatic int netdev_get_regs_len(struct net_device *dev)
573162306a36Sopenharmony_ci{
573262306a36Sopenharmony_ci	struct hw_regs *range = hw_regs_range;
573362306a36Sopenharmony_ci	int regs_len = 0x10 * sizeof(u32);
573462306a36Sopenharmony_ci
573562306a36Sopenharmony_ci	while (range->end > range->start) {
573662306a36Sopenharmony_ci		regs_len += (range->end - range->start + 3) / 4 * 4;
573762306a36Sopenharmony_ci		range++;
573862306a36Sopenharmony_ci	}
573962306a36Sopenharmony_ci	return regs_len;
574062306a36Sopenharmony_ci}
574162306a36Sopenharmony_ci
574262306a36Sopenharmony_ci/**
574362306a36Sopenharmony_ci * netdev_get_regs - get register dump
574462306a36Sopenharmony_ci * @dev:	Network device.
574562306a36Sopenharmony_ci * @regs:	Ethtool registers data structure.
574662306a36Sopenharmony_ci * @ptr:	Buffer to store the register values.
574762306a36Sopenharmony_ci *
574862306a36Sopenharmony_ci * This procedure dumps the register values in the provided buffer.
574962306a36Sopenharmony_ci */
575062306a36Sopenharmony_cistatic void netdev_get_regs(struct net_device *dev, struct ethtool_regs *regs,
575162306a36Sopenharmony_ci	void *ptr)
575262306a36Sopenharmony_ci{
575362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
575462306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
575562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
575662306a36Sopenharmony_ci	int *buf = (int *) ptr;
575762306a36Sopenharmony_ci	struct hw_regs *range = hw_regs_range;
575862306a36Sopenharmony_ci	int len;
575962306a36Sopenharmony_ci
576062306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
576162306a36Sopenharmony_ci	regs->version = 0;
576262306a36Sopenharmony_ci	for (len = 0; len < 0x40; len += 4) {
576362306a36Sopenharmony_ci		pci_read_config_dword(hw_priv->pdev, len, buf);
576462306a36Sopenharmony_ci		buf++;
576562306a36Sopenharmony_ci	}
576662306a36Sopenharmony_ci	while (range->end > range->start) {
576762306a36Sopenharmony_ci		for (len = range->start; len < range->end; len += 4) {
576862306a36Sopenharmony_ci			*buf = readl(hw->io + len);
576962306a36Sopenharmony_ci			buf++;
577062306a36Sopenharmony_ci		}
577162306a36Sopenharmony_ci		range++;
577262306a36Sopenharmony_ci	}
577362306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
577462306a36Sopenharmony_ci}
577562306a36Sopenharmony_ci
577662306a36Sopenharmony_ci#define WOL_SUPPORT			\
577762306a36Sopenharmony_ci	(WAKE_PHY | WAKE_MAGIC |	\
577862306a36Sopenharmony_ci	WAKE_UCAST | WAKE_MCAST |	\
577962306a36Sopenharmony_ci	WAKE_BCAST | WAKE_ARP)
578062306a36Sopenharmony_ci
578162306a36Sopenharmony_ci/**
578262306a36Sopenharmony_ci * netdev_get_wol - get Wake-on-LAN support
578362306a36Sopenharmony_ci * @dev:	Network device.
578462306a36Sopenharmony_ci * @wol:	Ethtool Wake-on-LAN data structure.
578562306a36Sopenharmony_ci *
578662306a36Sopenharmony_ci * This procedure returns Wake-on-LAN support.
578762306a36Sopenharmony_ci */
578862306a36Sopenharmony_cistatic void netdev_get_wol(struct net_device *dev,
578962306a36Sopenharmony_ci	struct ethtool_wolinfo *wol)
579062306a36Sopenharmony_ci{
579162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
579262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci	wol->supported = hw_priv->wol_support;
579562306a36Sopenharmony_ci	wol->wolopts = hw_priv->wol_enable;
579662306a36Sopenharmony_ci	memset(&wol->sopass, 0, sizeof(wol->sopass));
579762306a36Sopenharmony_ci}
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_ci/**
580062306a36Sopenharmony_ci * netdev_set_wol - set Wake-on-LAN support
580162306a36Sopenharmony_ci * @dev:	Network device.
580262306a36Sopenharmony_ci * @wol:	Ethtool Wake-on-LAN data structure.
580362306a36Sopenharmony_ci *
580462306a36Sopenharmony_ci * This function sets Wake-on-LAN support.
580562306a36Sopenharmony_ci *
580662306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
580762306a36Sopenharmony_ci */
580862306a36Sopenharmony_cistatic int netdev_set_wol(struct net_device *dev,
580962306a36Sopenharmony_ci	struct ethtool_wolinfo *wol)
581062306a36Sopenharmony_ci{
581162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
581262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
581362306a36Sopenharmony_ci
581462306a36Sopenharmony_ci	/* Need to find a way to retrieve the device IP address. */
581562306a36Sopenharmony_ci	static const u8 net_addr[] = { 192, 168, 1, 1 };
581662306a36Sopenharmony_ci
581762306a36Sopenharmony_ci	if (wol->wolopts & ~hw_priv->wol_support)
581862306a36Sopenharmony_ci		return -EINVAL;
581962306a36Sopenharmony_ci
582062306a36Sopenharmony_ci	hw_priv->wol_enable = wol->wolopts;
582162306a36Sopenharmony_ci
582262306a36Sopenharmony_ci	/* Link wakeup cannot really be disabled. */
582362306a36Sopenharmony_ci	if (wol->wolopts)
582462306a36Sopenharmony_ci		hw_priv->wol_enable |= WAKE_PHY;
582562306a36Sopenharmony_ci	hw_enable_wol(&hw_priv->hw, hw_priv->wol_enable, net_addr);
582662306a36Sopenharmony_ci	return 0;
582762306a36Sopenharmony_ci}
582862306a36Sopenharmony_ci
582962306a36Sopenharmony_ci/**
583062306a36Sopenharmony_ci * netdev_get_msglevel - get debug message level
583162306a36Sopenharmony_ci * @dev:	Network device.
583262306a36Sopenharmony_ci *
583362306a36Sopenharmony_ci * This function returns current debug message level.
583462306a36Sopenharmony_ci *
583562306a36Sopenharmony_ci * Return current debug message flags.
583662306a36Sopenharmony_ci */
583762306a36Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev)
583862306a36Sopenharmony_ci{
583962306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	return priv->msg_enable;
584262306a36Sopenharmony_ci}
584362306a36Sopenharmony_ci
584462306a36Sopenharmony_ci/**
584562306a36Sopenharmony_ci * netdev_set_msglevel - set debug message level
584662306a36Sopenharmony_ci * @dev:	Network device.
584762306a36Sopenharmony_ci * @value:	Debug message flags.
584862306a36Sopenharmony_ci *
584962306a36Sopenharmony_ci * This procedure sets debug message level.
585062306a36Sopenharmony_ci */
585162306a36Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 value)
585262306a36Sopenharmony_ci{
585362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
585462306a36Sopenharmony_ci
585562306a36Sopenharmony_ci	priv->msg_enable = value;
585662306a36Sopenharmony_ci}
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci/**
585962306a36Sopenharmony_ci * netdev_get_eeprom_len - get EEPROM length
586062306a36Sopenharmony_ci * @dev:	Network device.
586162306a36Sopenharmony_ci *
586262306a36Sopenharmony_ci * This function returns the length of the EEPROM.
586362306a36Sopenharmony_ci *
586462306a36Sopenharmony_ci * Return length of the EEPROM.
586562306a36Sopenharmony_ci */
586662306a36Sopenharmony_cistatic int netdev_get_eeprom_len(struct net_device *dev)
586762306a36Sopenharmony_ci{
586862306a36Sopenharmony_ci	return EEPROM_SIZE * 2;
586962306a36Sopenharmony_ci}
587062306a36Sopenharmony_ci
587162306a36Sopenharmony_ci#define EEPROM_MAGIC			0x10A18842
587262306a36Sopenharmony_ci
587362306a36Sopenharmony_ci/**
587462306a36Sopenharmony_ci * netdev_get_eeprom - get EEPROM data
587562306a36Sopenharmony_ci * @dev:	Network device.
587662306a36Sopenharmony_ci * @eeprom:	Ethtool EEPROM data structure.
587762306a36Sopenharmony_ci * @data:	Buffer to store the EEPROM data.
587862306a36Sopenharmony_ci *
587962306a36Sopenharmony_ci * This function dumps the EEPROM data in the provided buffer.
588062306a36Sopenharmony_ci *
588162306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
588262306a36Sopenharmony_ci */
588362306a36Sopenharmony_cistatic int netdev_get_eeprom(struct net_device *dev,
588462306a36Sopenharmony_ci	struct ethtool_eeprom *eeprom, u8 *data)
588562306a36Sopenharmony_ci{
588662306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
588762306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
588862306a36Sopenharmony_ci	u8 *eeprom_byte = (u8 *) eeprom_data;
588962306a36Sopenharmony_ci	int i;
589062306a36Sopenharmony_ci	int len;
589162306a36Sopenharmony_ci
589262306a36Sopenharmony_ci	len = (eeprom->offset + eeprom->len + 1) / 2;
589362306a36Sopenharmony_ci	for (i = eeprom->offset / 2; i < len; i++)
589462306a36Sopenharmony_ci		eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
589562306a36Sopenharmony_ci	eeprom->magic = EEPROM_MAGIC;
589662306a36Sopenharmony_ci	memcpy(data, &eeprom_byte[eeprom->offset], eeprom->len);
589762306a36Sopenharmony_ci
589862306a36Sopenharmony_ci	return 0;
589962306a36Sopenharmony_ci}
590062306a36Sopenharmony_ci
590162306a36Sopenharmony_ci/**
590262306a36Sopenharmony_ci * netdev_set_eeprom - write EEPROM data
590362306a36Sopenharmony_ci * @dev:	Network device.
590462306a36Sopenharmony_ci * @eeprom:	Ethtool EEPROM data structure.
590562306a36Sopenharmony_ci * @data:	Data buffer.
590662306a36Sopenharmony_ci *
590762306a36Sopenharmony_ci * This function modifies the EEPROM data one byte at a time.
590862306a36Sopenharmony_ci *
590962306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
591062306a36Sopenharmony_ci */
591162306a36Sopenharmony_cistatic int netdev_set_eeprom(struct net_device *dev,
591262306a36Sopenharmony_ci	struct ethtool_eeprom *eeprom, u8 *data)
591362306a36Sopenharmony_ci{
591462306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
591562306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
591662306a36Sopenharmony_ci	u16 eeprom_word[EEPROM_SIZE];
591762306a36Sopenharmony_ci	u8 *eeprom_byte = (u8 *) eeprom_word;
591862306a36Sopenharmony_ci	int i;
591962306a36Sopenharmony_ci	int len;
592062306a36Sopenharmony_ci
592162306a36Sopenharmony_ci	if (eeprom->magic != EEPROM_MAGIC)
592262306a36Sopenharmony_ci		return -EINVAL;
592362306a36Sopenharmony_ci
592462306a36Sopenharmony_ci	len = (eeprom->offset + eeprom->len + 1) / 2;
592562306a36Sopenharmony_ci	for (i = eeprom->offset / 2; i < len; i++)
592662306a36Sopenharmony_ci		eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
592762306a36Sopenharmony_ci	memcpy(eeprom_word, eeprom_data, EEPROM_SIZE * 2);
592862306a36Sopenharmony_ci	memcpy(&eeprom_byte[eeprom->offset], data, eeprom->len);
592962306a36Sopenharmony_ci	for (i = 0; i < EEPROM_SIZE; i++)
593062306a36Sopenharmony_ci		if (eeprom_word[i] != eeprom_data[i]) {
593162306a36Sopenharmony_ci			eeprom_data[i] = eeprom_word[i];
593262306a36Sopenharmony_ci			eeprom_write(&hw_priv->hw, i, eeprom_data[i]);
593362306a36Sopenharmony_ci	}
593462306a36Sopenharmony_ci
593562306a36Sopenharmony_ci	return 0;
593662306a36Sopenharmony_ci}
593762306a36Sopenharmony_ci
593862306a36Sopenharmony_ci/**
593962306a36Sopenharmony_ci * netdev_get_pauseparam - get flow control parameters
594062306a36Sopenharmony_ci * @dev:	Network device.
594162306a36Sopenharmony_ci * @pause:	Ethtool PAUSE settings data structure.
594262306a36Sopenharmony_ci *
594362306a36Sopenharmony_ci * This procedure returns the PAUSE control flow settings.
594462306a36Sopenharmony_ci */
594562306a36Sopenharmony_cistatic void netdev_get_pauseparam(struct net_device *dev,
594662306a36Sopenharmony_ci	struct ethtool_pauseparam *pause)
594762306a36Sopenharmony_ci{
594862306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
594962306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
595062306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
595162306a36Sopenharmony_ci
595262306a36Sopenharmony_ci	pause->autoneg = (hw->overrides & PAUSE_FLOW_CTRL) ? 0 : 1;
595362306a36Sopenharmony_ci	if (!hw->ksz_switch) {
595462306a36Sopenharmony_ci		pause->rx_pause =
595562306a36Sopenharmony_ci			(hw->rx_cfg & DMA_RX_FLOW_ENABLE) ? 1 : 0;
595662306a36Sopenharmony_ci		pause->tx_pause =
595762306a36Sopenharmony_ci			(hw->tx_cfg & DMA_TX_FLOW_ENABLE) ? 1 : 0;
595862306a36Sopenharmony_ci	} else {
595962306a36Sopenharmony_ci		pause->rx_pause =
596062306a36Sopenharmony_ci			(sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
596162306a36Sopenharmony_ci				SWITCH_RX_FLOW_CTRL)) ? 1 : 0;
596262306a36Sopenharmony_ci		pause->tx_pause =
596362306a36Sopenharmony_ci			(sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
596462306a36Sopenharmony_ci				SWITCH_TX_FLOW_CTRL)) ? 1 : 0;
596562306a36Sopenharmony_ci	}
596662306a36Sopenharmony_ci}
596762306a36Sopenharmony_ci
596862306a36Sopenharmony_ci/**
596962306a36Sopenharmony_ci * netdev_set_pauseparam - set flow control parameters
597062306a36Sopenharmony_ci * @dev:	Network device.
597162306a36Sopenharmony_ci * @pause:	Ethtool PAUSE settings data structure.
597262306a36Sopenharmony_ci *
597362306a36Sopenharmony_ci * This function sets the PAUSE control flow settings.
597462306a36Sopenharmony_ci * Not implemented yet.
597562306a36Sopenharmony_ci *
597662306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
597762306a36Sopenharmony_ci */
597862306a36Sopenharmony_cistatic int netdev_set_pauseparam(struct net_device *dev,
597962306a36Sopenharmony_ci	struct ethtool_pauseparam *pause)
598062306a36Sopenharmony_ci{
598162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
598262306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
598362306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
598462306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
598562306a36Sopenharmony_ci
598662306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
598762306a36Sopenharmony_ci	if (pause->autoneg) {
598862306a36Sopenharmony_ci		if (!pause->rx_pause && !pause->tx_pause)
598962306a36Sopenharmony_ci			port->flow_ctrl = PHY_NO_FLOW_CTRL;
599062306a36Sopenharmony_ci		else
599162306a36Sopenharmony_ci			port->flow_ctrl = PHY_FLOW_CTRL;
599262306a36Sopenharmony_ci		hw->overrides &= ~PAUSE_FLOW_CTRL;
599362306a36Sopenharmony_ci		port->force_link = 0;
599462306a36Sopenharmony_ci		if (hw->ksz_switch) {
599562306a36Sopenharmony_ci			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
599662306a36Sopenharmony_ci				SWITCH_RX_FLOW_CTRL, 1);
599762306a36Sopenharmony_ci			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
599862306a36Sopenharmony_ci				SWITCH_TX_FLOW_CTRL, 1);
599962306a36Sopenharmony_ci		}
600062306a36Sopenharmony_ci		port_set_link_speed(port);
600162306a36Sopenharmony_ci	} else {
600262306a36Sopenharmony_ci		hw->overrides |= PAUSE_FLOW_CTRL;
600362306a36Sopenharmony_ci		if (hw->ksz_switch) {
600462306a36Sopenharmony_ci			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
600562306a36Sopenharmony_ci				SWITCH_RX_FLOW_CTRL, pause->rx_pause);
600662306a36Sopenharmony_ci			sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
600762306a36Sopenharmony_ci				SWITCH_TX_FLOW_CTRL, pause->tx_pause);
600862306a36Sopenharmony_ci		} else
600962306a36Sopenharmony_ci			set_flow_ctrl(hw, pause->rx_pause, pause->tx_pause);
601062306a36Sopenharmony_ci	}
601162306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
601262306a36Sopenharmony_ci
601362306a36Sopenharmony_ci	return 0;
601462306a36Sopenharmony_ci}
601562306a36Sopenharmony_ci
601662306a36Sopenharmony_ci/**
601762306a36Sopenharmony_ci * netdev_get_ringparam - get tx/rx ring parameters
601862306a36Sopenharmony_ci * @dev:	Network device.
601962306a36Sopenharmony_ci * @ring:	Ethtool RING settings data structure.
602062306a36Sopenharmony_ci * @kernel_ring:	Ethtool external RING settings data structure.
602162306a36Sopenharmony_ci * @extack:	Netlink handle.
602262306a36Sopenharmony_ci *
602362306a36Sopenharmony_ci * This procedure returns the TX/RX ring settings.
602462306a36Sopenharmony_ci */
602562306a36Sopenharmony_cistatic void netdev_get_ringparam(struct net_device *dev,
602662306a36Sopenharmony_ci				 struct ethtool_ringparam *ring,
602762306a36Sopenharmony_ci				 struct kernel_ethtool_ringparam *kernel_ring,
602862306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
602962306a36Sopenharmony_ci{
603062306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
603162306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
603262306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
603362306a36Sopenharmony_ci
603462306a36Sopenharmony_ci	ring->tx_max_pending = (1 << 9);
603562306a36Sopenharmony_ci	ring->tx_pending = hw->tx_desc_info.alloc;
603662306a36Sopenharmony_ci	ring->rx_max_pending = (1 << 9);
603762306a36Sopenharmony_ci	ring->rx_pending = hw->rx_desc_info.alloc;
603862306a36Sopenharmony_ci}
603962306a36Sopenharmony_ci
604062306a36Sopenharmony_ci#define STATS_LEN			(TOTAL_PORT_COUNTER_NUM)
604162306a36Sopenharmony_ci
604262306a36Sopenharmony_cistatic struct {
604362306a36Sopenharmony_ci	char string[ETH_GSTRING_LEN];
604462306a36Sopenharmony_ci} ethtool_stats_keys[STATS_LEN] = {
604562306a36Sopenharmony_ci	{ "rx_lo_priority_octets" },
604662306a36Sopenharmony_ci	{ "rx_hi_priority_octets" },
604762306a36Sopenharmony_ci	{ "rx_undersize_packets" },
604862306a36Sopenharmony_ci	{ "rx_fragments" },
604962306a36Sopenharmony_ci	{ "rx_oversize_packets" },
605062306a36Sopenharmony_ci	{ "rx_jabbers" },
605162306a36Sopenharmony_ci	{ "rx_symbol_errors" },
605262306a36Sopenharmony_ci	{ "rx_crc_errors" },
605362306a36Sopenharmony_ci	{ "rx_align_errors" },
605462306a36Sopenharmony_ci	{ "rx_mac_ctrl_packets" },
605562306a36Sopenharmony_ci	{ "rx_pause_packets" },
605662306a36Sopenharmony_ci	{ "rx_bcast_packets" },
605762306a36Sopenharmony_ci	{ "rx_mcast_packets" },
605862306a36Sopenharmony_ci	{ "rx_ucast_packets" },
605962306a36Sopenharmony_ci	{ "rx_64_or_less_octet_packets" },
606062306a36Sopenharmony_ci	{ "rx_65_to_127_octet_packets" },
606162306a36Sopenharmony_ci	{ "rx_128_to_255_octet_packets" },
606262306a36Sopenharmony_ci	{ "rx_256_to_511_octet_packets" },
606362306a36Sopenharmony_ci	{ "rx_512_to_1023_octet_packets" },
606462306a36Sopenharmony_ci	{ "rx_1024_to_1522_octet_packets" },
606562306a36Sopenharmony_ci
606662306a36Sopenharmony_ci	{ "tx_lo_priority_octets" },
606762306a36Sopenharmony_ci	{ "tx_hi_priority_octets" },
606862306a36Sopenharmony_ci	{ "tx_late_collisions" },
606962306a36Sopenharmony_ci	{ "tx_pause_packets" },
607062306a36Sopenharmony_ci	{ "tx_bcast_packets" },
607162306a36Sopenharmony_ci	{ "tx_mcast_packets" },
607262306a36Sopenharmony_ci	{ "tx_ucast_packets" },
607362306a36Sopenharmony_ci	{ "tx_deferred" },
607462306a36Sopenharmony_ci	{ "tx_total_collisions" },
607562306a36Sopenharmony_ci	{ "tx_excessive_collisions" },
607662306a36Sopenharmony_ci	{ "tx_single_collisions" },
607762306a36Sopenharmony_ci	{ "tx_mult_collisions" },
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci	{ "rx_discards" },
608062306a36Sopenharmony_ci	{ "tx_discards" },
608162306a36Sopenharmony_ci};
608262306a36Sopenharmony_ci
608362306a36Sopenharmony_ci/**
608462306a36Sopenharmony_ci * netdev_get_strings - get statistics identity strings
608562306a36Sopenharmony_ci * @dev:	Network device.
608662306a36Sopenharmony_ci * @stringset:	String set identifier.
608762306a36Sopenharmony_ci * @buf:	Buffer to store the strings.
608862306a36Sopenharmony_ci *
608962306a36Sopenharmony_ci * This procedure returns the strings used to identify the statistics.
609062306a36Sopenharmony_ci */
609162306a36Sopenharmony_cistatic void netdev_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
609262306a36Sopenharmony_ci{
609362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
609462306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
609562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
609662306a36Sopenharmony_ci
609762306a36Sopenharmony_ci	if (ETH_SS_STATS == stringset)
609862306a36Sopenharmony_ci		memcpy(buf, &ethtool_stats_keys,
609962306a36Sopenharmony_ci			ETH_GSTRING_LEN * hw->mib_cnt);
610062306a36Sopenharmony_ci}
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci/**
610362306a36Sopenharmony_ci * netdev_get_sset_count - get statistics size
610462306a36Sopenharmony_ci * @dev:	Network device.
610562306a36Sopenharmony_ci * @sset:	The statistics set number.
610662306a36Sopenharmony_ci *
610762306a36Sopenharmony_ci * This function returns the size of the statistics to be reported.
610862306a36Sopenharmony_ci *
610962306a36Sopenharmony_ci * Return size of the statistics to be reported.
611062306a36Sopenharmony_ci */
611162306a36Sopenharmony_cistatic int netdev_get_sset_count(struct net_device *dev, int sset)
611262306a36Sopenharmony_ci{
611362306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
611462306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
611562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
611662306a36Sopenharmony_ci
611762306a36Sopenharmony_ci	switch (sset) {
611862306a36Sopenharmony_ci	case ETH_SS_STATS:
611962306a36Sopenharmony_ci		return hw->mib_cnt;
612062306a36Sopenharmony_ci	default:
612162306a36Sopenharmony_ci		return -EOPNOTSUPP;
612262306a36Sopenharmony_ci	}
612362306a36Sopenharmony_ci}
612462306a36Sopenharmony_ci
612562306a36Sopenharmony_ci/**
612662306a36Sopenharmony_ci * netdev_get_ethtool_stats - get network device statistics
612762306a36Sopenharmony_ci * @dev:	Network device.
612862306a36Sopenharmony_ci * @stats:	Ethtool statistics data structure.
612962306a36Sopenharmony_ci * @data:	Buffer to store the statistics.
613062306a36Sopenharmony_ci *
613162306a36Sopenharmony_ci * This procedure returns the statistics.
613262306a36Sopenharmony_ci */
613362306a36Sopenharmony_cistatic void netdev_get_ethtool_stats(struct net_device *dev,
613462306a36Sopenharmony_ci	struct ethtool_stats *stats, u64 *data)
613562306a36Sopenharmony_ci{
613662306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
613762306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
613862306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
613962306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
614062306a36Sopenharmony_ci	int n_stats = stats->n_stats;
614162306a36Sopenharmony_ci	int i;
614262306a36Sopenharmony_ci	int n;
614362306a36Sopenharmony_ci	int p;
614462306a36Sopenharmony_ci	u64 counter[TOTAL_PORT_COUNTER_NUM];
614562306a36Sopenharmony_ci
614662306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
614762306a36Sopenharmony_ci	n = SWITCH_PORT_NUM;
614862306a36Sopenharmony_ci	for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
614962306a36Sopenharmony_ci		if (media_connected == hw->port_mib[p].state) {
615062306a36Sopenharmony_ci			hw_priv->counter[p].read = 1;
615162306a36Sopenharmony_ci
615262306a36Sopenharmony_ci			/* Remember first port that requests read. */
615362306a36Sopenharmony_ci			if (n == SWITCH_PORT_NUM)
615462306a36Sopenharmony_ci				n = p;
615562306a36Sopenharmony_ci		}
615662306a36Sopenharmony_ci	}
615762306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
615862306a36Sopenharmony_ci
615962306a36Sopenharmony_ci	if (n < SWITCH_PORT_NUM)
616062306a36Sopenharmony_ci		schedule_work(&hw_priv->mib_read);
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_ci	if (1 == port->mib_port_cnt && n < SWITCH_PORT_NUM) {
616362306a36Sopenharmony_ci		p = n;
616462306a36Sopenharmony_ci		wait_event_interruptible_timeout(
616562306a36Sopenharmony_ci			hw_priv->counter[p].counter,
616662306a36Sopenharmony_ci			2 == hw_priv->counter[p].read,
616762306a36Sopenharmony_ci			HZ * 1);
616862306a36Sopenharmony_ci	} else
616962306a36Sopenharmony_ci		for (i = 0, p = n; i < port->mib_port_cnt - n; i++, p++) {
617062306a36Sopenharmony_ci			if (0 == i) {
617162306a36Sopenharmony_ci				wait_event_interruptible_timeout(
617262306a36Sopenharmony_ci					hw_priv->counter[p].counter,
617362306a36Sopenharmony_ci					2 == hw_priv->counter[p].read,
617462306a36Sopenharmony_ci					HZ * 2);
617562306a36Sopenharmony_ci			} else if (hw->port_mib[p].cnt_ptr) {
617662306a36Sopenharmony_ci				wait_event_interruptible_timeout(
617762306a36Sopenharmony_ci					hw_priv->counter[p].counter,
617862306a36Sopenharmony_ci					2 == hw_priv->counter[p].read,
617962306a36Sopenharmony_ci					HZ * 1);
618062306a36Sopenharmony_ci			}
618162306a36Sopenharmony_ci		}
618262306a36Sopenharmony_ci
618362306a36Sopenharmony_ci	get_mib_counters(hw, port->first_port, port->mib_port_cnt, counter);
618462306a36Sopenharmony_ci	n = hw->mib_cnt;
618562306a36Sopenharmony_ci	if (n > n_stats)
618662306a36Sopenharmony_ci		n = n_stats;
618762306a36Sopenharmony_ci	n_stats -= n;
618862306a36Sopenharmony_ci	for (i = 0; i < n; i++)
618962306a36Sopenharmony_ci		*data++ = counter[i];
619062306a36Sopenharmony_ci}
619162306a36Sopenharmony_ci
619262306a36Sopenharmony_ci/**
619362306a36Sopenharmony_ci * netdev_set_features - set receive checksum support
619462306a36Sopenharmony_ci * @dev:	Network device.
619562306a36Sopenharmony_ci * @features:	New device features (offloads).
619662306a36Sopenharmony_ci *
619762306a36Sopenharmony_ci * This function sets receive checksum support setting.
619862306a36Sopenharmony_ci *
619962306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code.
620062306a36Sopenharmony_ci */
620162306a36Sopenharmony_cistatic int netdev_set_features(struct net_device *dev,
620262306a36Sopenharmony_ci	netdev_features_t features)
620362306a36Sopenharmony_ci{
620462306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
620562306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
620662306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
620762306a36Sopenharmony_ci
620862306a36Sopenharmony_ci	mutex_lock(&hw_priv->lock);
620962306a36Sopenharmony_ci
621062306a36Sopenharmony_ci	/* see note in hw_setup() */
621162306a36Sopenharmony_ci	if (features & NETIF_F_RXCSUM)
621262306a36Sopenharmony_ci		hw->rx_cfg |= DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP;
621362306a36Sopenharmony_ci	else
621462306a36Sopenharmony_ci		hw->rx_cfg &= ~(DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
621562306a36Sopenharmony_ci
621662306a36Sopenharmony_ci	if (hw->enabled)
621762306a36Sopenharmony_ci		writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
621862306a36Sopenharmony_ci
621962306a36Sopenharmony_ci	mutex_unlock(&hw_priv->lock);
622062306a36Sopenharmony_ci
622162306a36Sopenharmony_ci	return 0;
622262306a36Sopenharmony_ci}
622362306a36Sopenharmony_ci
622462306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
622562306a36Sopenharmony_ci	.nway_reset		= netdev_nway_reset,
622662306a36Sopenharmony_ci	.get_link		= netdev_get_link,
622762306a36Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
622862306a36Sopenharmony_ci	.get_regs_len		= netdev_get_regs_len,
622962306a36Sopenharmony_ci	.get_regs		= netdev_get_regs,
623062306a36Sopenharmony_ci	.get_wol		= netdev_get_wol,
623162306a36Sopenharmony_ci	.set_wol		= netdev_set_wol,
623262306a36Sopenharmony_ci	.get_msglevel		= netdev_get_msglevel,
623362306a36Sopenharmony_ci	.set_msglevel		= netdev_set_msglevel,
623462306a36Sopenharmony_ci	.get_eeprom_len		= netdev_get_eeprom_len,
623562306a36Sopenharmony_ci	.get_eeprom		= netdev_get_eeprom,
623662306a36Sopenharmony_ci	.set_eeprom		= netdev_set_eeprom,
623762306a36Sopenharmony_ci	.get_pauseparam		= netdev_get_pauseparam,
623862306a36Sopenharmony_ci	.set_pauseparam		= netdev_set_pauseparam,
623962306a36Sopenharmony_ci	.get_ringparam		= netdev_get_ringparam,
624062306a36Sopenharmony_ci	.get_strings		= netdev_get_strings,
624162306a36Sopenharmony_ci	.get_sset_count		= netdev_get_sset_count,
624262306a36Sopenharmony_ci	.get_ethtool_stats	= netdev_get_ethtool_stats,
624362306a36Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
624462306a36Sopenharmony_ci	.set_link_ksettings	= netdev_set_link_ksettings,
624562306a36Sopenharmony_ci};
624662306a36Sopenharmony_ci
624762306a36Sopenharmony_ci/*
624862306a36Sopenharmony_ci * Hardware monitoring
624962306a36Sopenharmony_ci */
625062306a36Sopenharmony_ci
625162306a36Sopenharmony_cistatic void update_link(struct net_device *dev, struct dev_priv *priv,
625262306a36Sopenharmony_ci	struct ksz_port *port)
625362306a36Sopenharmony_ci{
625462306a36Sopenharmony_ci	if (priv->media_state != port->linked->state) {
625562306a36Sopenharmony_ci		priv->media_state = port->linked->state;
625662306a36Sopenharmony_ci		if (netif_running(dev))
625762306a36Sopenharmony_ci			set_media_state(dev, media_connected);
625862306a36Sopenharmony_ci	}
625962306a36Sopenharmony_ci}
626062306a36Sopenharmony_ci
626162306a36Sopenharmony_cistatic void mib_read_work(struct work_struct *work)
626262306a36Sopenharmony_ci{
626362306a36Sopenharmony_ci	struct dev_info *hw_priv =
626462306a36Sopenharmony_ci		container_of(work, struct dev_info, mib_read);
626562306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
626662306a36Sopenharmony_ci	unsigned long next_jiffies;
626762306a36Sopenharmony_ci	struct ksz_port_mib *mib;
626862306a36Sopenharmony_ci	int i;
626962306a36Sopenharmony_ci
627062306a36Sopenharmony_ci	next_jiffies = jiffies;
627162306a36Sopenharmony_ci	for (i = 0; i < hw->mib_port_cnt; i++) {
627262306a36Sopenharmony_ci		mib = &hw->port_mib[i];
627362306a36Sopenharmony_ci
627462306a36Sopenharmony_ci		/* Reading MIB counters or requested to read. */
627562306a36Sopenharmony_ci		if (mib->cnt_ptr || 1 == hw_priv->counter[i].read) {
627662306a36Sopenharmony_ci
627762306a36Sopenharmony_ci			/* Need to process receive interrupt. */
627862306a36Sopenharmony_ci			if (port_r_cnt(hw, i))
627962306a36Sopenharmony_ci				break;
628062306a36Sopenharmony_ci			hw_priv->counter[i].read = 0;
628162306a36Sopenharmony_ci
628262306a36Sopenharmony_ci			/* Finish reading counters. */
628362306a36Sopenharmony_ci			if (0 == mib->cnt_ptr) {
628462306a36Sopenharmony_ci				hw_priv->counter[i].read = 2;
628562306a36Sopenharmony_ci				wake_up_interruptible(
628662306a36Sopenharmony_ci					&hw_priv->counter[i].counter);
628762306a36Sopenharmony_ci			}
628862306a36Sopenharmony_ci		} else if (time_after_eq(jiffies, hw_priv->counter[i].time)) {
628962306a36Sopenharmony_ci			/* Only read MIB counters when the port is connected. */
629062306a36Sopenharmony_ci			if (media_connected == mib->state)
629162306a36Sopenharmony_ci				hw_priv->counter[i].read = 1;
629262306a36Sopenharmony_ci			next_jiffies += HZ * 1 * hw->mib_port_cnt;
629362306a36Sopenharmony_ci			hw_priv->counter[i].time = next_jiffies;
629462306a36Sopenharmony_ci
629562306a36Sopenharmony_ci		/* Port is just disconnected. */
629662306a36Sopenharmony_ci		} else if (mib->link_down) {
629762306a36Sopenharmony_ci			mib->link_down = 0;
629862306a36Sopenharmony_ci
629962306a36Sopenharmony_ci			/* Read counters one last time after link is lost. */
630062306a36Sopenharmony_ci			hw_priv->counter[i].read = 1;
630162306a36Sopenharmony_ci		}
630262306a36Sopenharmony_ci	}
630362306a36Sopenharmony_ci}
630462306a36Sopenharmony_ci
630562306a36Sopenharmony_cistatic void mib_monitor(struct timer_list *t)
630662306a36Sopenharmony_ci{
630762306a36Sopenharmony_ci	struct dev_info *hw_priv = from_timer(hw_priv, t, mib_timer_info.timer);
630862306a36Sopenharmony_ci
630962306a36Sopenharmony_ci	mib_read_work(&hw_priv->mib_read);
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ci	/* This is used to verify Wake-on-LAN is working. */
631262306a36Sopenharmony_ci	if (hw_priv->pme_wait) {
631362306a36Sopenharmony_ci		if (time_is_before_eq_jiffies(hw_priv->pme_wait)) {
631462306a36Sopenharmony_ci			hw_clr_wol_pme_status(&hw_priv->hw);
631562306a36Sopenharmony_ci			hw_priv->pme_wait = 0;
631662306a36Sopenharmony_ci		}
631762306a36Sopenharmony_ci	} else if (hw_chk_wol_pme_status(&hw_priv->hw)) {
631862306a36Sopenharmony_ci
631962306a36Sopenharmony_ci		/* PME is asserted.  Wait 2 seconds to clear it. */
632062306a36Sopenharmony_ci		hw_priv->pme_wait = jiffies + HZ * 2;
632162306a36Sopenharmony_ci	}
632262306a36Sopenharmony_ci
632362306a36Sopenharmony_ci	ksz_update_timer(&hw_priv->mib_timer_info);
632462306a36Sopenharmony_ci}
632562306a36Sopenharmony_ci
632662306a36Sopenharmony_ci/**
632762306a36Sopenharmony_ci * dev_monitor - periodic monitoring
632862306a36Sopenharmony_ci * @t:	timer list containing a network device pointer.
632962306a36Sopenharmony_ci *
633062306a36Sopenharmony_ci * This routine is run in a kernel timer to monitor the network device.
633162306a36Sopenharmony_ci */
633262306a36Sopenharmony_cistatic void dev_monitor(struct timer_list *t)
633362306a36Sopenharmony_ci{
633462306a36Sopenharmony_ci	struct dev_priv *priv = from_timer(priv, t, monitor_timer_info.timer);
633562306a36Sopenharmony_ci	struct net_device *dev = priv->mii_if.dev;
633662306a36Sopenharmony_ci	struct dev_info *hw_priv = priv->adapter;
633762306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
633862306a36Sopenharmony_ci	struct ksz_port *port = &priv->port;
633962306a36Sopenharmony_ci
634062306a36Sopenharmony_ci	if (!(hw->features & LINK_INT_WORKING))
634162306a36Sopenharmony_ci		port_get_link_speed(port);
634262306a36Sopenharmony_ci	update_link(dev, priv, port);
634362306a36Sopenharmony_ci
634462306a36Sopenharmony_ci	ksz_update_timer(&priv->monitor_timer_info);
634562306a36Sopenharmony_ci}
634662306a36Sopenharmony_ci
634762306a36Sopenharmony_ci/*
634862306a36Sopenharmony_ci * Linux network device interface functions
634962306a36Sopenharmony_ci */
635062306a36Sopenharmony_ci
635162306a36Sopenharmony_ci/* Driver exported variables */
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_cistatic int msg_enable;
635462306a36Sopenharmony_ci
635562306a36Sopenharmony_cistatic char *macaddr = ":";
635662306a36Sopenharmony_cistatic char *mac1addr = ":";
635762306a36Sopenharmony_ci
635862306a36Sopenharmony_ci/*
635962306a36Sopenharmony_ci * This enables multiple network device mode for KSZ8842, which contains a
636062306a36Sopenharmony_ci * switch with two physical ports.  Some users like to take control of the
636162306a36Sopenharmony_ci * ports for running Spanning Tree Protocol.  The driver will create an
636262306a36Sopenharmony_ci * additional eth? device for the other port.
636362306a36Sopenharmony_ci *
636462306a36Sopenharmony_ci * Some limitations are the network devices cannot have different MTU and
636562306a36Sopenharmony_ci * multicast hash tables.
636662306a36Sopenharmony_ci */
636762306a36Sopenharmony_cistatic int multi_dev;
636862306a36Sopenharmony_ci
636962306a36Sopenharmony_ci/*
637062306a36Sopenharmony_ci * As most users select multiple network device mode to use Spanning Tree
637162306a36Sopenharmony_ci * Protocol, this enables a feature in which most unicast and multicast packets
637262306a36Sopenharmony_ci * are forwarded inside the switch and not passed to the host.  Only packets
637362306a36Sopenharmony_ci * that need the host's attention are passed to it.  This prevents the host
637462306a36Sopenharmony_ci * wasting CPU time to examine each and every incoming packets and do the
637562306a36Sopenharmony_ci * forwarding itself.
637662306a36Sopenharmony_ci *
637762306a36Sopenharmony_ci * As the hack requires the private bridge header, the driver cannot compile
637862306a36Sopenharmony_ci * with just the kernel headers.
637962306a36Sopenharmony_ci *
638062306a36Sopenharmony_ci * Enabling STP support also turns on multiple network device mode.
638162306a36Sopenharmony_ci */
638262306a36Sopenharmony_cistatic int stp;
638362306a36Sopenharmony_ci
638462306a36Sopenharmony_ci/*
638562306a36Sopenharmony_ci * This enables fast aging in the KSZ8842 switch.  Not sure what situation
638662306a36Sopenharmony_ci * needs that.  However, fast aging is used to flush the dynamic MAC table when
638762306a36Sopenharmony_ci * STP support is enabled.
638862306a36Sopenharmony_ci */
638962306a36Sopenharmony_cistatic int fast_aging;
639062306a36Sopenharmony_ci
639162306a36Sopenharmony_ci/**
639262306a36Sopenharmony_ci * netdev_init - initialize network device.
639362306a36Sopenharmony_ci * @dev:	Network device.
639462306a36Sopenharmony_ci *
639562306a36Sopenharmony_ci * This function initializes the network device.
639662306a36Sopenharmony_ci *
639762306a36Sopenharmony_ci * Return 0 if successful; otherwise an error code indicating failure.
639862306a36Sopenharmony_ci */
639962306a36Sopenharmony_cistatic int __init netdev_init(struct net_device *dev)
640062306a36Sopenharmony_ci{
640162306a36Sopenharmony_ci	struct dev_priv *priv = netdev_priv(dev);
640262306a36Sopenharmony_ci
640362306a36Sopenharmony_ci	/* 500 ms timeout */
640462306a36Sopenharmony_ci	ksz_init_timer(&priv->monitor_timer_info, 500 * HZ / 1000,
640562306a36Sopenharmony_ci		dev_monitor);
640662306a36Sopenharmony_ci
640762306a36Sopenharmony_ci	/* 500 ms timeout */
640862306a36Sopenharmony_ci	dev->watchdog_timeo = HZ / 2;
640962306a36Sopenharmony_ci
641062306a36Sopenharmony_ci	dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_RXCSUM;
641162306a36Sopenharmony_ci
641262306a36Sopenharmony_ci	/*
641362306a36Sopenharmony_ci	 * Hardware does not really support IPv6 checksum generation, but
641462306a36Sopenharmony_ci	 * driver actually runs faster with this on.
641562306a36Sopenharmony_ci	 */
641662306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_IPV6_CSUM;
641762306a36Sopenharmony_ci
641862306a36Sopenharmony_ci	dev->features |= dev->hw_features;
641962306a36Sopenharmony_ci
642062306a36Sopenharmony_ci	sema_init(&priv->proc_sem, 1);
642162306a36Sopenharmony_ci
642262306a36Sopenharmony_ci	priv->mii_if.phy_id_mask = 0x1;
642362306a36Sopenharmony_ci	priv->mii_if.reg_num_mask = 0x7;
642462306a36Sopenharmony_ci	priv->mii_if.dev = dev;
642562306a36Sopenharmony_ci	priv->mii_if.mdio_read = mdio_read;
642662306a36Sopenharmony_ci	priv->mii_if.mdio_write = mdio_write;
642762306a36Sopenharmony_ci	priv->mii_if.phy_id = priv->port.first_port + 1;
642862306a36Sopenharmony_ci
642962306a36Sopenharmony_ci	priv->msg_enable = netif_msg_init(msg_enable,
643062306a36Sopenharmony_ci		(NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK));
643162306a36Sopenharmony_ci
643262306a36Sopenharmony_ci	return 0;
643362306a36Sopenharmony_ci}
643462306a36Sopenharmony_ci
643562306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
643662306a36Sopenharmony_ci	.ndo_init		= netdev_init,
643762306a36Sopenharmony_ci	.ndo_open		= netdev_open,
643862306a36Sopenharmony_ci	.ndo_stop		= netdev_close,
643962306a36Sopenharmony_ci	.ndo_get_stats		= netdev_query_statistics,
644062306a36Sopenharmony_ci	.ndo_start_xmit		= netdev_tx,
644162306a36Sopenharmony_ci	.ndo_tx_timeout		= netdev_tx_timeout,
644262306a36Sopenharmony_ci	.ndo_change_mtu		= netdev_change_mtu,
644362306a36Sopenharmony_ci	.ndo_set_features	= netdev_set_features,
644462306a36Sopenharmony_ci	.ndo_set_mac_address	= netdev_set_mac_address,
644562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
644662306a36Sopenharmony_ci	.ndo_eth_ioctl		= netdev_ioctl,
644762306a36Sopenharmony_ci	.ndo_set_rx_mode	= netdev_set_rx_mode,
644862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
644962306a36Sopenharmony_ci	.ndo_poll_controller	= netdev_netpoll,
645062306a36Sopenharmony_ci#endif
645162306a36Sopenharmony_ci};
645262306a36Sopenharmony_ci
645362306a36Sopenharmony_cistatic void netdev_free(struct net_device *dev)
645462306a36Sopenharmony_ci{
645562306a36Sopenharmony_ci	if (dev->watchdog_timeo)
645662306a36Sopenharmony_ci		unregister_netdev(dev);
645762306a36Sopenharmony_ci
645862306a36Sopenharmony_ci	free_netdev(dev);
645962306a36Sopenharmony_ci}
646062306a36Sopenharmony_ci
646162306a36Sopenharmony_cistruct platform_info {
646262306a36Sopenharmony_ci	struct dev_info dev_info;
646362306a36Sopenharmony_ci	struct net_device *netdev[SWITCH_PORT_NUM];
646462306a36Sopenharmony_ci};
646562306a36Sopenharmony_ci
646662306a36Sopenharmony_cistatic int net_device_present;
646762306a36Sopenharmony_ci
646862306a36Sopenharmony_cistatic void get_mac_addr(struct dev_info *hw_priv, u8 *macaddr, int port)
646962306a36Sopenharmony_ci{
647062306a36Sopenharmony_ci	int i;
647162306a36Sopenharmony_ci	int j;
647262306a36Sopenharmony_ci	int got_num;
647362306a36Sopenharmony_ci	int num;
647462306a36Sopenharmony_ci
647562306a36Sopenharmony_ci	i = j = num = got_num = 0;
647662306a36Sopenharmony_ci	while (j < ETH_ALEN) {
647762306a36Sopenharmony_ci		if (macaddr[i]) {
647862306a36Sopenharmony_ci			int digit;
647962306a36Sopenharmony_ci
648062306a36Sopenharmony_ci			got_num = 1;
648162306a36Sopenharmony_ci			digit = hex_to_bin(macaddr[i]);
648262306a36Sopenharmony_ci			if (digit >= 0)
648362306a36Sopenharmony_ci				num = num * 16 + digit;
648462306a36Sopenharmony_ci			else if (':' == macaddr[i])
648562306a36Sopenharmony_ci				got_num = 2;
648662306a36Sopenharmony_ci			else
648762306a36Sopenharmony_ci				break;
648862306a36Sopenharmony_ci		} else if (got_num)
648962306a36Sopenharmony_ci			got_num = 2;
649062306a36Sopenharmony_ci		else
649162306a36Sopenharmony_ci			break;
649262306a36Sopenharmony_ci		if (2 == got_num) {
649362306a36Sopenharmony_ci			if (MAIN_PORT == port) {
649462306a36Sopenharmony_ci				hw_priv->hw.override_addr[j++] = (u8) num;
649562306a36Sopenharmony_ci				hw_priv->hw.override_addr[5] +=
649662306a36Sopenharmony_ci					hw_priv->hw.id;
649762306a36Sopenharmony_ci			} else {
649862306a36Sopenharmony_ci				hw_priv->hw.ksz_switch->other_addr[j++] =
649962306a36Sopenharmony_ci					(u8) num;
650062306a36Sopenharmony_ci				hw_priv->hw.ksz_switch->other_addr[5] +=
650162306a36Sopenharmony_ci					hw_priv->hw.id;
650262306a36Sopenharmony_ci			}
650362306a36Sopenharmony_ci			num = got_num = 0;
650462306a36Sopenharmony_ci		}
650562306a36Sopenharmony_ci		i++;
650662306a36Sopenharmony_ci	}
650762306a36Sopenharmony_ci	if (ETH_ALEN == j) {
650862306a36Sopenharmony_ci		if (MAIN_PORT == port)
650962306a36Sopenharmony_ci			hw_priv->hw.mac_override = 1;
651062306a36Sopenharmony_ci	}
651162306a36Sopenharmony_ci}
651262306a36Sopenharmony_ci
651362306a36Sopenharmony_ci#define KS884X_DMA_MASK			(~0x0UL)
651462306a36Sopenharmony_ci
651562306a36Sopenharmony_cistatic void read_other_addr(struct ksz_hw *hw)
651662306a36Sopenharmony_ci{
651762306a36Sopenharmony_ci	int i;
651862306a36Sopenharmony_ci	u16 data[3];
651962306a36Sopenharmony_ci	struct ksz_switch *sw = hw->ksz_switch;
652062306a36Sopenharmony_ci
652162306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
652262306a36Sopenharmony_ci		data[i] = eeprom_read(hw, i + EEPROM_DATA_OTHER_MAC_ADDR);
652362306a36Sopenharmony_ci	if ((data[0] || data[1] || data[2]) && data[0] != 0xffff) {
652462306a36Sopenharmony_ci		sw->other_addr[5] = (u8) data[0];
652562306a36Sopenharmony_ci		sw->other_addr[4] = (u8)(data[0] >> 8);
652662306a36Sopenharmony_ci		sw->other_addr[3] = (u8) data[1];
652762306a36Sopenharmony_ci		sw->other_addr[2] = (u8)(data[1] >> 8);
652862306a36Sopenharmony_ci		sw->other_addr[1] = (u8) data[2];
652962306a36Sopenharmony_ci		sw->other_addr[0] = (u8)(data[2] >> 8);
653062306a36Sopenharmony_ci	}
653162306a36Sopenharmony_ci}
653262306a36Sopenharmony_ci
653362306a36Sopenharmony_ci#ifndef PCI_VENDOR_ID_MICREL_KS
653462306a36Sopenharmony_ci#define PCI_VENDOR_ID_MICREL_KS		0x16c6
653562306a36Sopenharmony_ci#endif
653662306a36Sopenharmony_ci
653762306a36Sopenharmony_cistatic int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id)
653862306a36Sopenharmony_ci{
653962306a36Sopenharmony_ci	struct net_device *dev;
654062306a36Sopenharmony_ci	struct dev_priv *priv;
654162306a36Sopenharmony_ci	struct dev_info *hw_priv;
654262306a36Sopenharmony_ci	struct ksz_hw *hw;
654362306a36Sopenharmony_ci	struct platform_info *info;
654462306a36Sopenharmony_ci	struct ksz_port *port;
654562306a36Sopenharmony_ci	unsigned long reg_base;
654662306a36Sopenharmony_ci	unsigned long reg_len;
654762306a36Sopenharmony_ci	int cnt;
654862306a36Sopenharmony_ci	int i;
654962306a36Sopenharmony_ci	int mib_port_count;
655062306a36Sopenharmony_ci	int pi;
655162306a36Sopenharmony_ci	int port_count;
655262306a36Sopenharmony_ci	int result;
655362306a36Sopenharmony_ci	char banner[sizeof(version)];
655462306a36Sopenharmony_ci	struct ksz_switch *sw = NULL;
655562306a36Sopenharmony_ci
655662306a36Sopenharmony_ci	result = pcim_enable_device(pdev);
655762306a36Sopenharmony_ci	if (result)
655862306a36Sopenharmony_ci		return result;
655962306a36Sopenharmony_ci
656062306a36Sopenharmony_ci	result = -ENODEV;
656162306a36Sopenharmony_ci
656262306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) ||
656362306a36Sopenharmony_ci	    dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)))
656462306a36Sopenharmony_ci		return result;
656562306a36Sopenharmony_ci
656662306a36Sopenharmony_ci	reg_base = pci_resource_start(pdev, 0);
656762306a36Sopenharmony_ci	reg_len = pci_resource_len(pdev, 0);
656862306a36Sopenharmony_ci	if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0)
656962306a36Sopenharmony_ci		return result;
657062306a36Sopenharmony_ci
657162306a36Sopenharmony_ci	if (!request_mem_region(reg_base, reg_len, DRV_NAME))
657262306a36Sopenharmony_ci		return result;
657362306a36Sopenharmony_ci	pci_set_master(pdev);
657462306a36Sopenharmony_ci
657562306a36Sopenharmony_ci	result = -ENOMEM;
657662306a36Sopenharmony_ci
657762306a36Sopenharmony_ci	info = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
657862306a36Sopenharmony_ci	if (!info)
657962306a36Sopenharmony_ci		goto pcidev_init_dev_err;
658062306a36Sopenharmony_ci
658162306a36Sopenharmony_ci	hw_priv = &info->dev_info;
658262306a36Sopenharmony_ci	hw_priv->pdev = pdev;
658362306a36Sopenharmony_ci
658462306a36Sopenharmony_ci	hw = &hw_priv->hw;
658562306a36Sopenharmony_ci
658662306a36Sopenharmony_ci	hw->io = ioremap(reg_base, reg_len);
658762306a36Sopenharmony_ci	if (!hw->io)
658862306a36Sopenharmony_ci		goto pcidev_init_io_err;
658962306a36Sopenharmony_ci
659062306a36Sopenharmony_ci	cnt = hw_init(hw);
659162306a36Sopenharmony_ci	if (!cnt) {
659262306a36Sopenharmony_ci		if (msg_enable & NETIF_MSG_PROBE)
659362306a36Sopenharmony_ci			pr_alert("chip not detected\n");
659462306a36Sopenharmony_ci		result = -ENODEV;
659562306a36Sopenharmony_ci		goto pcidev_init_alloc_err;
659662306a36Sopenharmony_ci	}
659762306a36Sopenharmony_ci
659862306a36Sopenharmony_ci	snprintf(banner, sizeof(banner), "%s", version);
659962306a36Sopenharmony_ci	banner[13] = cnt + '0';		/* Replace x in "Micrel KSZ884x" */
660062306a36Sopenharmony_ci	dev_info(&hw_priv->pdev->dev, "%s\n", banner);
660162306a36Sopenharmony_ci	dev_dbg(&hw_priv->pdev->dev, "Mem = %p; IRQ = %d\n", hw->io, pdev->irq);
660262306a36Sopenharmony_ci
660362306a36Sopenharmony_ci	/* Assume device is KSZ8841. */
660462306a36Sopenharmony_ci	hw->dev_count = 1;
660562306a36Sopenharmony_ci	port_count = 1;
660662306a36Sopenharmony_ci	mib_port_count = 1;
660762306a36Sopenharmony_ci	hw->addr_list_size = 0;
660862306a36Sopenharmony_ci	hw->mib_cnt = PORT_COUNTER_NUM;
660962306a36Sopenharmony_ci	hw->mib_port_cnt = 1;
661062306a36Sopenharmony_ci
661162306a36Sopenharmony_ci	/* KSZ8842 has a switch with multiple ports. */
661262306a36Sopenharmony_ci	if (2 == cnt) {
661362306a36Sopenharmony_ci		if (fast_aging)
661462306a36Sopenharmony_ci			hw->overrides |= FAST_AGING;
661562306a36Sopenharmony_ci
661662306a36Sopenharmony_ci		hw->mib_cnt = TOTAL_PORT_COUNTER_NUM;
661762306a36Sopenharmony_ci
661862306a36Sopenharmony_ci		/* Multiple network device interfaces are required. */
661962306a36Sopenharmony_ci		if (multi_dev) {
662062306a36Sopenharmony_ci			hw->dev_count = SWITCH_PORT_NUM;
662162306a36Sopenharmony_ci			hw->addr_list_size = SWITCH_PORT_NUM - 1;
662262306a36Sopenharmony_ci		}
662362306a36Sopenharmony_ci
662462306a36Sopenharmony_ci		/* Single network device has multiple ports. */
662562306a36Sopenharmony_ci		if (1 == hw->dev_count) {
662662306a36Sopenharmony_ci			port_count = SWITCH_PORT_NUM;
662762306a36Sopenharmony_ci			mib_port_count = SWITCH_PORT_NUM;
662862306a36Sopenharmony_ci		}
662962306a36Sopenharmony_ci		hw->mib_port_cnt = TOTAL_PORT_NUM;
663062306a36Sopenharmony_ci		hw->ksz_switch = kzalloc(sizeof(struct ksz_switch), GFP_KERNEL);
663162306a36Sopenharmony_ci		if (!hw->ksz_switch)
663262306a36Sopenharmony_ci			goto pcidev_init_alloc_err;
663362306a36Sopenharmony_ci
663462306a36Sopenharmony_ci		sw = hw->ksz_switch;
663562306a36Sopenharmony_ci	}
663662306a36Sopenharmony_ci	for (i = 0; i < hw->mib_port_cnt; i++)
663762306a36Sopenharmony_ci		hw->port_mib[i].mib_start = 0;
663862306a36Sopenharmony_ci
663962306a36Sopenharmony_ci	hw->parent = hw_priv;
664062306a36Sopenharmony_ci
664162306a36Sopenharmony_ci	/* Default MTU is 1500. */
664262306a36Sopenharmony_ci	hw_priv->mtu = (REGULAR_RX_BUF_SIZE + 3) & ~3;
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_ci	if (ksz_alloc_mem(hw_priv))
664562306a36Sopenharmony_ci		goto pcidev_init_mem_err;
664662306a36Sopenharmony_ci
664762306a36Sopenharmony_ci	hw_priv->hw.id = net_device_present;
664862306a36Sopenharmony_ci
664962306a36Sopenharmony_ci	spin_lock_init(&hw_priv->hwlock);
665062306a36Sopenharmony_ci	mutex_init(&hw_priv->lock);
665162306a36Sopenharmony_ci
665262306a36Sopenharmony_ci	for (i = 0; i < TOTAL_PORT_NUM; i++)
665362306a36Sopenharmony_ci		init_waitqueue_head(&hw_priv->counter[i].counter);
665462306a36Sopenharmony_ci
665562306a36Sopenharmony_ci	if (macaddr[0] != ':')
665662306a36Sopenharmony_ci		get_mac_addr(hw_priv, macaddr, MAIN_PORT);
665762306a36Sopenharmony_ci
665862306a36Sopenharmony_ci	/* Read MAC address and initialize override address if not overridden. */
665962306a36Sopenharmony_ci	hw_read_addr(hw);
666062306a36Sopenharmony_ci
666162306a36Sopenharmony_ci	/* Multiple device interfaces mode requires a second MAC address. */
666262306a36Sopenharmony_ci	if (hw->dev_count > 1) {
666362306a36Sopenharmony_ci		memcpy(sw->other_addr, hw->override_addr, ETH_ALEN);
666462306a36Sopenharmony_ci		read_other_addr(hw);
666562306a36Sopenharmony_ci		if (mac1addr[0] != ':')
666662306a36Sopenharmony_ci			get_mac_addr(hw_priv, mac1addr, OTHER_PORT);
666762306a36Sopenharmony_ci	}
666862306a36Sopenharmony_ci
666962306a36Sopenharmony_ci	hw_setup(hw);
667062306a36Sopenharmony_ci	if (hw->ksz_switch)
667162306a36Sopenharmony_ci		sw_setup(hw);
667262306a36Sopenharmony_ci	else {
667362306a36Sopenharmony_ci		hw_priv->wol_support = WOL_SUPPORT;
667462306a36Sopenharmony_ci		hw_priv->wol_enable = 0;
667562306a36Sopenharmony_ci	}
667662306a36Sopenharmony_ci
667762306a36Sopenharmony_ci	INIT_WORK(&hw_priv->mib_read, mib_read_work);
667862306a36Sopenharmony_ci
667962306a36Sopenharmony_ci	/* 500 ms timeout */
668062306a36Sopenharmony_ci	ksz_init_timer(&hw_priv->mib_timer_info, 500 * HZ / 1000,
668162306a36Sopenharmony_ci		mib_monitor);
668262306a36Sopenharmony_ci
668362306a36Sopenharmony_ci	for (i = 0; i < hw->dev_count; i++) {
668462306a36Sopenharmony_ci		dev = alloc_etherdev(sizeof(struct dev_priv));
668562306a36Sopenharmony_ci		if (!dev)
668662306a36Sopenharmony_ci			goto pcidev_init_reg_err;
668762306a36Sopenharmony_ci		SET_NETDEV_DEV(dev, &pdev->dev);
668862306a36Sopenharmony_ci		info->netdev[i] = dev;
668962306a36Sopenharmony_ci
669062306a36Sopenharmony_ci		priv = netdev_priv(dev);
669162306a36Sopenharmony_ci		priv->adapter = hw_priv;
669262306a36Sopenharmony_ci		priv->id = net_device_present++;
669362306a36Sopenharmony_ci
669462306a36Sopenharmony_ci		port = &priv->port;
669562306a36Sopenharmony_ci		port->port_cnt = port_count;
669662306a36Sopenharmony_ci		port->mib_port_cnt = mib_port_count;
669762306a36Sopenharmony_ci		port->first_port = i;
669862306a36Sopenharmony_ci		port->flow_ctrl = PHY_FLOW_CTRL;
669962306a36Sopenharmony_ci
670062306a36Sopenharmony_ci		port->hw = hw;
670162306a36Sopenharmony_ci		port->linked = &hw->port_info[port->first_port];
670262306a36Sopenharmony_ci
670362306a36Sopenharmony_ci		for (cnt = 0, pi = i; cnt < port_count; cnt++, pi++) {
670462306a36Sopenharmony_ci			hw->port_info[pi].port_id = pi;
670562306a36Sopenharmony_ci			hw->port_info[pi].pdev = dev;
670662306a36Sopenharmony_ci			hw->port_info[pi].state = media_disconnected;
670762306a36Sopenharmony_ci		}
670862306a36Sopenharmony_ci
670962306a36Sopenharmony_ci		dev->mem_start = (unsigned long) hw->io;
671062306a36Sopenharmony_ci		dev->mem_end = dev->mem_start + reg_len - 1;
671162306a36Sopenharmony_ci		dev->irq = pdev->irq;
671262306a36Sopenharmony_ci		if (MAIN_PORT == i)
671362306a36Sopenharmony_ci			eth_hw_addr_set(dev, hw_priv->hw.override_addr);
671462306a36Sopenharmony_ci		else {
671562306a36Sopenharmony_ci			u8 addr[ETH_ALEN];
671662306a36Sopenharmony_ci
671762306a36Sopenharmony_ci			ether_addr_copy(addr, sw->other_addr);
671862306a36Sopenharmony_ci			if (ether_addr_equal(sw->other_addr, hw->override_addr))
671962306a36Sopenharmony_ci				addr[5] += port->first_port;
672062306a36Sopenharmony_ci			eth_hw_addr_set(dev, addr);
672162306a36Sopenharmony_ci		}
672262306a36Sopenharmony_ci
672362306a36Sopenharmony_ci		dev->netdev_ops = &netdev_ops;
672462306a36Sopenharmony_ci		dev->ethtool_ops = &netdev_ethtool_ops;
672562306a36Sopenharmony_ci
672662306a36Sopenharmony_ci		/* MTU range: 60 - 1894 */
672762306a36Sopenharmony_ci		dev->min_mtu = ETH_ZLEN;
672862306a36Sopenharmony_ci		dev->max_mtu = MAX_RX_BUF_SIZE -
672962306a36Sopenharmony_ci			       (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
673062306a36Sopenharmony_ci
673162306a36Sopenharmony_ci		if (register_netdev(dev))
673262306a36Sopenharmony_ci			goto pcidev_init_reg_err;
673362306a36Sopenharmony_ci		port_set_power_saving(port, true);
673462306a36Sopenharmony_ci	}
673562306a36Sopenharmony_ci
673662306a36Sopenharmony_ci	pci_dev_get(hw_priv->pdev);
673762306a36Sopenharmony_ci	pci_set_drvdata(pdev, info);
673862306a36Sopenharmony_ci	return 0;
673962306a36Sopenharmony_ci
674062306a36Sopenharmony_cipcidev_init_reg_err:
674162306a36Sopenharmony_ci	for (i = 0; i < hw->dev_count; i++) {
674262306a36Sopenharmony_ci		if (info->netdev[i]) {
674362306a36Sopenharmony_ci			netdev_free(info->netdev[i]);
674462306a36Sopenharmony_ci			info->netdev[i] = NULL;
674562306a36Sopenharmony_ci		}
674662306a36Sopenharmony_ci	}
674762306a36Sopenharmony_ci
674862306a36Sopenharmony_cipcidev_init_mem_err:
674962306a36Sopenharmony_ci	ksz_free_mem(hw_priv);
675062306a36Sopenharmony_ci	kfree(hw->ksz_switch);
675162306a36Sopenharmony_ci
675262306a36Sopenharmony_cipcidev_init_alloc_err:
675362306a36Sopenharmony_ci	iounmap(hw->io);
675462306a36Sopenharmony_ci
675562306a36Sopenharmony_cipcidev_init_io_err:
675662306a36Sopenharmony_ci	kfree(info);
675762306a36Sopenharmony_ci
675862306a36Sopenharmony_cipcidev_init_dev_err:
675962306a36Sopenharmony_ci	release_mem_region(reg_base, reg_len);
676062306a36Sopenharmony_ci
676162306a36Sopenharmony_ci	return result;
676262306a36Sopenharmony_ci}
676362306a36Sopenharmony_ci
676462306a36Sopenharmony_cistatic void pcidev_exit(struct pci_dev *pdev)
676562306a36Sopenharmony_ci{
676662306a36Sopenharmony_ci	int i;
676762306a36Sopenharmony_ci	struct platform_info *info = pci_get_drvdata(pdev);
676862306a36Sopenharmony_ci	struct dev_info *hw_priv = &info->dev_info;
676962306a36Sopenharmony_ci
677062306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 0),
677162306a36Sopenharmony_ci		pci_resource_len(pdev, 0));
677262306a36Sopenharmony_ci	for (i = 0; i < hw_priv->hw.dev_count; i++) {
677362306a36Sopenharmony_ci		if (info->netdev[i])
677462306a36Sopenharmony_ci			netdev_free(info->netdev[i]);
677562306a36Sopenharmony_ci	}
677662306a36Sopenharmony_ci	if (hw_priv->hw.io)
677762306a36Sopenharmony_ci		iounmap(hw_priv->hw.io);
677862306a36Sopenharmony_ci	ksz_free_mem(hw_priv);
677962306a36Sopenharmony_ci	kfree(hw_priv->hw.ksz_switch);
678062306a36Sopenharmony_ci	pci_dev_put(hw_priv->pdev);
678162306a36Sopenharmony_ci	kfree(info);
678262306a36Sopenharmony_ci}
678362306a36Sopenharmony_ci
678462306a36Sopenharmony_cistatic int __maybe_unused pcidev_resume(struct device *dev_d)
678562306a36Sopenharmony_ci{
678662306a36Sopenharmony_ci	int i;
678762306a36Sopenharmony_ci	struct platform_info *info = dev_get_drvdata(dev_d);
678862306a36Sopenharmony_ci	struct dev_info *hw_priv = &info->dev_info;
678962306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
679062306a36Sopenharmony_ci
679162306a36Sopenharmony_ci	device_wakeup_disable(dev_d);
679262306a36Sopenharmony_ci
679362306a36Sopenharmony_ci	if (hw_priv->wol_enable)
679462306a36Sopenharmony_ci		hw_cfg_wol_pme(hw, 0);
679562306a36Sopenharmony_ci	for (i = 0; i < hw->dev_count; i++) {
679662306a36Sopenharmony_ci		if (info->netdev[i]) {
679762306a36Sopenharmony_ci			struct net_device *dev = info->netdev[i];
679862306a36Sopenharmony_ci
679962306a36Sopenharmony_ci			if (netif_running(dev)) {
680062306a36Sopenharmony_ci				netdev_open(dev);
680162306a36Sopenharmony_ci				netif_device_attach(dev);
680262306a36Sopenharmony_ci			}
680362306a36Sopenharmony_ci		}
680462306a36Sopenharmony_ci	}
680562306a36Sopenharmony_ci	return 0;
680662306a36Sopenharmony_ci}
680762306a36Sopenharmony_ci
680862306a36Sopenharmony_cistatic int __maybe_unused pcidev_suspend(struct device *dev_d)
680962306a36Sopenharmony_ci{
681062306a36Sopenharmony_ci	int i;
681162306a36Sopenharmony_ci	struct platform_info *info = dev_get_drvdata(dev_d);
681262306a36Sopenharmony_ci	struct dev_info *hw_priv = &info->dev_info;
681362306a36Sopenharmony_ci	struct ksz_hw *hw = &hw_priv->hw;
681462306a36Sopenharmony_ci
681562306a36Sopenharmony_ci	/* Need to find a way to retrieve the device IP address. */
681662306a36Sopenharmony_ci	static const u8 net_addr[] = { 192, 168, 1, 1 };
681762306a36Sopenharmony_ci
681862306a36Sopenharmony_ci	for (i = 0; i < hw->dev_count; i++) {
681962306a36Sopenharmony_ci		if (info->netdev[i]) {
682062306a36Sopenharmony_ci			struct net_device *dev = info->netdev[i];
682162306a36Sopenharmony_ci
682262306a36Sopenharmony_ci			if (netif_running(dev)) {
682362306a36Sopenharmony_ci				netif_device_detach(dev);
682462306a36Sopenharmony_ci				netdev_close(dev);
682562306a36Sopenharmony_ci			}
682662306a36Sopenharmony_ci		}
682762306a36Sopenharmony_ci	}
682862306a36Sopenharmony_ci	if (hw_priv->wol_enable) {
682962306a36Sopenharmony_ci		hw_enable_wol(hw, hw_priv->wol_enable, net_addr);
683062306a36Sopenharmony_ci		hw_cfg_wol_pme(hw, 1);
683162306a36Sopenharmony_ci	}
683262306a36Sopenharmony_ci
683362306a36Sopenharmony_ci	device_wakeup_enable(dev_d);
683462306a36Sopenharmony_ci	return 0;
683562306a36Sopenharmony_ci}
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_cistatic char pcidev_name[] = "ksz884xp";
683862306a36Sopenharmony_ci
683962306a36Sopenharmony_cistatic const struct pci_device_id pcidev_table[] = {
684062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_MICREL_KS, 0x8841,
684162306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
684262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_MICREL_KS, 0x8842,
684362306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
684462306a36Sopenharmony_ci	{ 0 }
684562306a36Sopenharmony_ci};
684662306a36Sopenharmony_ci
684762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pcidev_table);
684862306a36Sopenharmony_ci
684962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pcidev_pm_ops, pcidev_suspend, pcidev_resume);
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_cistatic struct pci_driver pci_device_driver = {
685262306a36Sopenharmony_ci	.driver.pm	= &pcidev_pm_ops,
685362306a36Sopenharmony_ci	.name		= pcidev_name,
685462306a36Sopenharmony_ci	.id_table	= pcidev_table,
685562306a36Sopenharmony_ci	.probe		= pcidev_init,
685662306a36Sopenharmony_ci	.remove		= pcidev_exit
685762306a36Sopenharmony_ci};
685862306a36Sopenharmony_ci
685962306a36Sopenharmony_cimodule_pci_driver(pci_device_driver);
686062306a36Sopenharmony_ci
686162306a36Sopenharmony_ciMODULE_DESCRIPTION("KSZ8841/2 PCI network driver");
686262306a36Sopenharmony_ciMODULE_AUTHOR("Tristram Ha <Tristram.Ha@micrel.com>");
686362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
686462306a36Sopenharmony_ci
686562306a36Sopenharmony_cimodule_param_named(message, msg_enable, int, 0);
686662306a36Sopenharmony_ciMODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
686762306a36Sopenharmony_ci
686862306a36Sopenharmony_cimodule_param(macaddr, charp, 0);
686962306a36Sopenharmony_cimodule_param(mac1addr, charp, 0);
687062306a36Sopenharmony_cimodule_param(fast_aging, int, 0);
687162306a36Sopenharmony_cimodule_param(multi_dev, int, 0);
687262306a36Sopenharmony_cimodule_param(stp, int, 0);
687362306a36Sopenharmony_ciMODULE_PARM_DESC(macaddr, "MAC address");
687462306a36Sopenharmony_ciMODULE_PARM_DESC(mac1addr, "Second MAC address");
687562306a36Sopenharmony_ciMODULE_PARM_DESC(fast_aging, "Fast aging");
687662306a36Sopenharmony_ciMODULE_PARM_DESC(multi_dev, "Multiple device interfaces");
687762306a36Sopenharmony_ciMODULE_PARM_DESC(stp, "STP support");
6878