Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/micrel/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 169 kB image not shown  

Quelle  ksz884x.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * drivers/net/ethernet/micrel/ksx884x.c - Micrel KSZ8841/2 PCI Ethernet driver
 *
 * Copyright (c) 2009-2010 Micrel, Inc.
 *  Tristram Ha <Tristram.Ha@micrel.com>
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/mii.h>
#include <linux/platform_device.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/micrel_phy.h>


/* DMA Registers */

#define KS_DMA_TX_CTRL   0x0000
#define DMA_TX_ENABLE   0x00000001
#define DMA_TX_CRC_ENABLE  0x00000002
#define DMA_TX_PAD_ENABLE  0x00000004
#define DMA_TX_LOOPBACK   0x00000100
#define DMA_TX_FLOW_ENABLE  0x00000200
#define DMA_TX_CSUM_IP   0x00010000
#define DMA_TX_CSUM_TCP   0x00020000
#define DMA_TX_CSUM_UDP   0x00040000
#define DMA_TX_BURST_SIZE  0x3F000000

#define KS_DMA_RX_CTRL   0x0004
#define DMA_RX_ENABLE   0x00000001
#define KS884X_DMA_RX_MULTICAST  0x00000002
#define DMA_RX_PROMISCUOUS  0x00000004
#define DMA_RX_ERROR   0x00000008
#define DMA_RX_UNICAST   0x00000010
#define DMA_RX_ALL_MULTICAST  0x00000020
#define DMA_RX_BROADCAST  0x00000040
#define DMA_RX_FLOW_ENABLE  0x00000200
#define DMA_RX_CSUM_IP   0x00010000
#define DMA_RX_CSUM_TCP   0x00020000
#define DMA_RX_CSUM_UDP   0x00040000
#define DMA_RX_BURST_SIZE  0x3F000000

#define DMA_BURST_SHIFT   24
#define DMA_BURST_DEFAULT  8

#define KS_DMA_TX_START   0x0008
#define KS_DMA_RX_START   0x000C
#define DMA_START   0x00000001

#define KS_DMA_TX_ADDR   0x0010
#define KS_DMA_RX_ADDR   0x0014

#define DMA_ADDR_LIST_MASK  0xFFFFFFFC
#define DMA_ADDR_LIST_SHIFT  2

/* MTR0 */
#define KS884X_MULTICAST_0_OFFSET 0x0020
#define KS884X_MULTICAST_1_OFFSET 0x0021
#define KS884X_MULTICAST_2_OFFSET 0x0022
#define KS884x_MULTICAST_3_OFFSET 0x0023
/* MTR1 */
#define KS884X_MULTICAST_4_OFFSET 0x0024
#define KS884X_MULTICAST_5_OFFSET 0x0025
#define KS884X_MULTICAST_6_OFFSET 0x0026
#define KS884X_MULTICAST_7_OFFSET 0x0027

/* Interrupt Registers */

/* INTEN */
#define KS884X_INTERRUPTS_ENABLE 0x0028
/* INTST */
#define KS884X_INTERRUPTS_STATUS 0x002C

#define KS884X_INT_RX_STOPPED  0x02000000
#define KS884X_INT_TX_STOPPED  0x04000000
#define KS884X_INT_RX_OVERRUN  0x08000000
#define KS884X_INT_TX_EMPTY  0x10000000
#define KS884X_INT_RX   0x20000000
#define KS884X_INT_TX   0x40000000
#define KS884X_INT_PHY   0x80000000

#define KS884X_INT_RX_MASK  \
 (KS884X_INT_RX | KS884X_INT_RX_OVERRUN)
#define KS884X_INT_TX_MASK  \
 (KS884X_INT_TX | KS884X_INT_TX_EMPTY)
#define KS884X_INT_MASK (KS884X_INT_RX | KS884X_INT_TX | KS884X_INT_PHY)

/* MAC Additional Station Address */

/* MAAL0 */
#define KS_ADD_ADDR_0_LO  0x0080
/* MAAH0 */
#define KS_ADD_ADDR_0_HI  0x0084
/* MAAL1 */
#define KS_ADD_ADDR_1_LO  0x0088
/* MAAH1 */
#define KS_ADD_ADDR_1_HI  0x008C
/* MAAL2 */
#define KS_ADD_ADDR_2_LO  0x0090
/* MAAH2 */
#define KS_ADD_ADDR_2_HI  0x0094
/* MAAL3 */
#define KS_ADD_ADDR_3_LO  0x0098
/* MAAH3 */
#define KS_ADD_ADDR_3_HI  0x009C
/* MAAL4 */
#define KS_ADD_ADDR_4_LO  0x00A0
/* MAAH4 */
#define KS_ADD_ADDR_4_HI  0x00A4
/* MAAL5 */
#define KS_ADD_ADDR_5_LO  0x00A8
/* MAAH5 */
#define KS_ADD_ADDR_5_HI  0x00AC
/* MAAL6 */
#define KS_ADD_ADDR_6_LO  0x00B0
/* MAAH6 */
#define KS_ADD_ADDR_6_HI  0x00B4
/* MAAL7 */
#define KS_ADD_ADDR_7_LO  0x00B8
/* MAAH7 */
#define KS_ADD_ADDR_7_HI  0x00BC
/* MAAL8 */
#define KS_ADD_ADDR_8_LO  0x00C0
/* MAAH8 */
#define KS_ADD_ADDR_8_HI  0x00C4
/* MAAL9 */
#define KS_ADD_ADDR_9_LO  0x00C8
/* MAAH9 */
#define KS_ADD_ADDR_9_HI  0x00CC
/* MAAL10 */
#define KS_ADD_ADDR_A_LO  0x00D0
/* MAAH10 */
#define KS_ADD_ADDR_A_HI  0x00D4
/* MAAL11 */
#define KS_ADD_ADDR_B_LO  0x00D8
/* MAAH11 */
#define KS_ADD_ADDR_B_HI  0x00DC
/* MAAL12 */
#define KS_ADD_ADDR_C_LO  0x00E0
/* MAAH12 */
#define KS_ADD_ADDR_C_HI  0x00E4
/* MAAL13 */
#define KS_ADD_ADDR_D_LO  0x00E8
/* MAAH13 */
#define KS_ADD_ADDR_D_HI  0x00EC
/* MAAL14 */
#define KS_ADD_ADDR_E_LO  0x00F0
/* MAAH14 */
#define KS_ADD_ADDR_E_HI  0x00F4
/* MAAL15 */
#define KS_ADD_ADDR_F_LO  0x00F8
/* MAAH15 */
#define KS_ADD_ADDR_F_HI  0x00FC

#define ADD_ADDR_HI_MASK  0x0000FFFF
#define ADD_ADDR_ENABLE   0x80000000
#define ADD_ADDR_INCR   8

/* Miscellaneous Registers */

/* MARL */
#define KS884X_ADDR_0_OFFSET  0x0200
#define KS884X_ADDR_1_OFFSET  0x0201
/* MARM */
#define KS884X_ADDR_2_OFFSET  0x0202
#define KS884X_ADDR_3_OFFSET  0x0203
/* MARH */
#define KS884X_ADDR_4_OFFSET  0x0204
#define KS884X_ADDR_5_OFFSET  0x0205

/* OBCR */
#define KS884X_BUS_CTRL_OFFSET  0x0210

#define BUS_SPEED_125_MHZ  0x0000
#define BUS_SPEED_62_5_MHZ  0x0001
#define BUS_SPEED_41_66_MHZ  0x0002
#define BUS_SPEED_25_MHZ  0x0003

/* EEPCR */
#define KS884X_EEPROM_CTRL_OFFSET 0x0212

#define EEPROM_CHIP_SELECT  0x0001
#define EEPROM_SERIAL_CLOCK  0x0002
#define EEPROM_DATA_OUT   0x0004
#define EEPROM_DATA_IN   0x0008
#define EEPROM_ACCESS_ENABLE  0x0010

/* MBIR */
#define KS884X_MEM_INFO_OFFSET  0x0214

#define RX_MEM_TEST_FAILED  0x0008
#define RX_MEM_TEST_FINISHED  0x0010
#define TX_MEM_TEST_FAILED  0x0800
#define TX_MEM_TEST_FINISHED  0x1000

/* GCR */
#define KS884X_GLOBAL_CTRL_OFFSET 0x0216
#define GLOBAL_SOFTWARE_RESET  0x0001

#define KS8841_POWER_MANAGE_OFFSET 0x0218

/* WFCR */
#define KS8841_WOL_CTRL_OFFSET  0x021A
#define KS8841_WOL_MAGIC_ENABLE  0x0080
#define KS8841_WOL_FRAME3_ENABLE 0x0008
#define KS8841_WOL_FRAME2_ENABLE 0x0004
#define KS8841_WOL_FRAME1_ENABLE 0x0002
#define KS8841_WOL_FRAME0_ENABLE 0x0001

/* WF0 */
#define KS8841_WOL_FRAME_CRC_OFFSET 0x0220
#define KS8841_WOL_FRAME_BYTE0_OFFSET 0x0224
#define KS8841_WOL_FRAME_BYTE2_OFFSET 0x0228

/* IACR */
#define KS884X_IACR_P   0x04A0
#define KS884X_IACR_OFFSET  KS884X_IACR_P

/* IADR1 */
#define KS884X_IADR1_P   0x04A2
#define KS884X_IADR2_P   0x04A4
#define KS884X_IADR3_P   0x04A6
#define KS884X_IADR4_P   0x04A8
#define KS884X_IADR5_P   0x04AA

#define KS884X_ACC_CTRL_SEL_OFFSET KS884X_IACR_P
#define KS884X_ACC_CTRL_INDEX_OFFSET (KS884X_ACC_CTRL_SEL_OFFSET + 1)

#define KS884X_ACC_DATA_0_OFFSET KS884X_IADR4_P
#define KS884X_ACC_DATA_1_OFFSET (KS884X_ACC_DATA_0_OFFSET + 1)
#define KS884X_ACC_DATA_2_OFFSET KS884X_IADR5_P
#define KS884X_ACC_DATA_3_OFFSET (KS884X_ACC_DATA_2_OFFSET + 1)
#define KS884X_ACC_DATA_4_OFFSET KS884X_IADR2_P
#define KS884X_ACC_DATA_5_OFFSET (KS884X_ACC_DATA_4_OFFSET + 1)
#define KS884X_ACC_DATA_6_OFFSET KS884X_IADR3_P
#define KS884X_ACC_DATA_7_OFFSET (KS884X_ACC_DATA_6_OFFSET + 1)
#define KS884X_ACC_DATA_8_OFFSET KS884X_IADR1_P

/* P1MBCR */
#define KS884X_P1MBCR_P   0x04D0
#define KS884X_P1MBSR_P   0x04D2
#define KS884X_PHY1ILR_P  0x04D4
#define KS884X_PHY1IHR_P  0x04D6
#define KS884X_P1ANAR_P   0x04D8
#define KS884X_P1ANLPR_P  0x04DA

/* P2MBCR */
#define KS884X_P2MBCR_P   0x04E0
#define KS884X_P2MBSR_P   0x04E2
#define KS884X_PHY2ILR_P  0x04E4
#define KS884X_PHY2IHR_P  0x04E6
#define KS884X_P2ANAR_P   0x04E8
#define KS884X_P2ANLPR_P  0x04EA

#define KS884X_PHY_1_CTRL_OFFSET KS884X_P1MBCR_P
#define PHY_CTRL_INTERVAL  (KS884X_P2MBCR_P - KS884X_P1MBCR_P)

#define KS884X_PHY_CTRL_OFFSET  0x00

#define KS884X_PHY_STATUS_OFFSET 0x02

#define KS884X_PHY_ID_1_OFFSET  0x04
#define KS884X_PHY_ID_2_OFFSET  0x06

#define KS884X_PHY_AUTO_NEG_OFFSET 0x08

#define KS884X_PHY_REMOTE_CAP_OFFSET 0x0A

/* P1VCT */
#define KS884X_P1VCT_P   0x04F0
#define KS884X_P1PHYCTRL_P  0x04F2

/* P2VCT */
#define KS884X_P2VCT_P   0x04F4
#define KS884X_P2PHYCTRL_P  0x04F6

#define KS884X_PHY_SPECIAL_OFFSET KS884X_P1VCT_P
#define PHY_SPECIAL_INTERVAL  (KS884X_P2VCT_P - KS884X_P1VCT_P)

#define KS884X_PHY_LINK_MD_OFFSET 0x00

#define PHY_START_CABLE_DIAG  0x8000
#define PHY_CABLE_DIAG_RESULT  0x6000
#define PHY_CABLE_STAT_NORMAL  0x0000
#define PHY_CABLE_STAT_OPEN  0x2000
#define PHY_CABLE_STAT_SHORT  0x4000
#define PHY_CABLE_STAT_FAILED  0x6000
#define PHY_CABLE_10M_SHORT  0x1000
#define PHY_CABLE_FAULT_COUNTER  0x01FF

#define KS884X_PHY_PHY_CTRL_OFFSET 0x02

#define PHY_STAT_REVERSED_POLARITY 0x0020
#define PHY_STAT_MDIX   0x0010
#define PHY_FORCE_LINK   0x0008
#define PHY_POWER_SAVING_DISABLE 0x0004
#define PHY_REMOTE_LOOPBACK  0x0002

/* SIDER */
#define KS884X_SIDER_P   0x0400
#define KS884X_CHIP_ID_OFFSET  KS884X_SIDER_P
#define KS884X_FAMILY_ID_OFFSET  (KS884X_CHIP_ID_OFFSET + 1)

#define REG_FAMILY_ID   0x88

#define REG_CHIP_ID_41   0x8810
#define REG_CHIP_ID_42   0x8800

#define KS884X_CHIP_ID_MASK_41  0xFF10
#define KS884X_CHIP_ID_MASK  0xFFF0
#define KS884X_CHIP_ID_SHIFT  4
#define KS884X_REVISION_MASK  0x000E
#define KS884X_REVISION_SHIFT  1
#define KS8842_START   0x0001

#define CHIP_IP_41_M   0x8810
#define CHIP_IP_42_M   0x8800
#define CHIP_IP_61_M   0x8890
#define CHIP_IP_62_M   0x8880

#define CHIP_IP_41_P   0x8850
#define CHIP_IP_42_P   0x8840
#define CHIP_IP_61_P   0x88D0
#define CHIP_IP_62_P   0x88C0

/* SGCR1 */
#define KS8842_SGCR1_P   0x0402
#define KS8842_SWITCH_CTRL_1_OFFSET KS8842_SGCR1_P

#define SWITCH_PASS_ALL   0x8000
#define SWITCH_TX_FLOW_CTRL  0x2000
#define SWITCH_RX_FLOW_CTRL  0x1000
#define SWITCH_CHECK_LENGTH  0x0800
#define SWITCH_AGING_ENABLE  0x0400
#define SWITCH_FAST_AGING  0x0200
#define SWITCH_AGGR_BACKOFF  0x0100
#define SWITCH_PASS_PAUSE  0x0008
#define SWITCH_LINK_AUTO_AGING  0x0001

/* SGCR2 */
#define KS8842_SGCR2_P   0x0404
#define KS8842_SWITCH_CTRL_2_OFFSET KS8842_SGCR2_P

#define SWITCH_VLAN_ENABLE  0x8000
#define SWITCH_IGMP_SNOOP  0x4000
#define IPV6_MLD_SNOOP_ENABLE  0x2000
#define IPV6_MLD_SNOOP_OPTION  0x1000
#define PRIORITY_SCHEME_SELECT  0x0800
#define SWITCH_MIRROR_RX_TX  0x0100
#define UNICAST_VLAN_BOUNDARY  0x0080
#define MULTICAST_STORM_DISABLE  0x0040
#define SWITCH_BACK_PRESSURE  0x0020
#define FAIR_FLOW_CTRL   0x0010
#define NO_EXC_COLLISION_DROP  0x0008
#define SWITCH_HUGE_PACKET  0x0004
#define SWITCH_LEGAL_PACKET  0x0002
#define SWITCH_BUF_RESERVE  0x0001

/* SGCR3 */
#define KS8842_SGCR3_P   0x0406
#define KS8842_SWITCH_CTRL_3_OFFSET KS8842_SGCR3_P

#define BROADCAST_STORM_RATE_LO  0xFF00
#define SWITCH_REPEATER   0x0080
#define SWITCH_HALF_DUPLEX  0x0040
#define SWITCH_FLOW_CTRL  0x0020
#define SWITCH_10_MBIT   0x0010
#define SWITCH_REPLACE_NULL_VID  0x0008
#define BROADCAST_STORM_RATE_HI  0x0007

#define BROADCAST_STORM_RATE  0x07FF

/* SGCR4 */
#define KS8842_SGCR4_P   0x0408

/* SGCR5 */
#define KS8842_SGCR5_P   0x040A
#define KS8842_SWITCH_CTRL_5_OFFSET KS8842_SGCR5_P

#define LED_MODE   0x8200
#define LED_SPEED_DUPLEX_ACT  0x0000
#define LED_SPEED_DUPLEX_LINK_ACT 0x8000
#define LED_DUPLEX_10_100  0x0200

/* SGCR6 */
#define KS8842_SGCR6_P   0x0410
#define KS8842_SWITCH_CTRL_6_OFFSET KS8842_SGCR6_P

#define KS8842_PRIORITY_MASK  3
#define KS8842_PRIORITY_SHIFT  2

/* SGCR7 */
#define KS8842_SGCR7_P   0x0412
#define KS8842_SWITCH_CTRL_7_OFFSET KS8842_SGCR7_P

#define SWITCH_UNK_DEF_PORT_ENABLE 0x0008
#define SWITCH_UNK_DEF_PORT_3  0x0004
#define SWITCH_UNK_DEF_PORT_2  0x0002
#define SWITCH_UNK_DEF_PORT_1  0x0001

/* MACAR1 */
#define KS8842_MACAR1_P   0x0470
#define KS8842_MACAR2_P   0x0472
#define KS8842_MACAR3_P   0x0474
#define KS8842_MAC_ADDR_1_OFFSET KS8842_MACAR1_P
#define KS8842_MAC_ADDR_0_OFFSET (KS8842_MAC_ADDR_1_OFFSET + 1)
#define KS8842_MAC_ADDR_3_OFFSET KS8842_MACAR2_P
#define KS8842_MAC_ADDR_2_OFFSET (KS8842_MAC_ADDR_3_OFFSET + 1)
#define KS8842_MAC_ADDR_5_OFFSET KS8842_MACAR3_P
#define KS8842_MAC_ADDR_4_OFFSET (KS8842_MAC_ADDR_5_OFFSET + 1)

/* TOSR1 */
#define KS8842_TOSR1_P   0x0480
#define KS8842_TOSR2_P   0x0482
#define KS8842_TOSR3_P   0x0484
#define KS8842_TOSR4_P   0x0486
#define KS8842_TOSR5_P   0x0488
#define KS8842_TOSR6_P   0x048A
#define KS8842_TOSR7_P   0x0490
#define KS8842_TOSR8_P   0x0492
#define KS8842_TOS_1_OFFSET  KS8842_TOSR1_P
#define KS8842_TOS_2_OFFSET  KS8842_TOSR2_P
#define KS8842_TOS_3_OFFSET  KS8842_TOSR3_P
#define KS8842_TOS_4_OFFSET  KS8842_TOSR4_P
#define KS8842_TOS_5_OFFSET  KS8842_TOSR5_P
#define KS8842_TOS_6_OFFSET  KS8842_TOSR6_P

#define KS8842_TOS_7_OFFSET  KS8842_TOSR7_P
#define KS8842_TOS_8_OFFSET  KS8842_TOSR8_P

/* P1CR1 */
#define KS8842_P1CR1_P   0x0500
#define KS8842_P1CR2_P   0x0502
#define KS8842_P1VIDR_P   0x0504
#define KS8842_P1CR3_P   0x0506
#define KS8842_P1IRCR_P   0x0508
#define KS8842_P1ERCR_P   0x050A
#define KS884X_P1SCSLMD_P  0x0510
#define KS884X_P1CR4_P   0x0512
#define KS884X_P1SR_P   0x0514

/* P2CR1 */
#define KS8842_P2CR1_P   0x0520
#define KS8842_P2CR2_P   0x0522
#define KS8842_P2VIDR_P   0x0524
#define KS8842_P2CR3_P   0x0526
#define KS8842_P2IRCR_P   0x0528
#define KS8842_P2ERCR_P   0x052A
#define KS884X_P2SCSLMD_P  0x0530
#define KS884X_P2CR4_P   0x0532
#define KS884X_P2SR_P   0x0534

/* P3CR1 */
#define KS8842_P3CR1_P   0x0540
#define KS8842_P3CR2_P   0x0542
#define KS8842_P3VIDR_P   0x0544
#define KS8842_P3CR3_P   0x0546
#define KS8842_P3IRCR_P   0x0548
#define KS8842_P3ERCR_P   0x054A

#define KS8842_PORT_1_CTRL_1  KS8842_P1CR1_P
#define KS8842_PORT_2_CTRL_1  KS8842_P2CR1_P
#define KS8842_PORT_3_CTRL_1  KS8842_P3CR1_P

#define PORT_CTRL_ADDR(port, addr)  \
 (addr = KS8842_PORT_1_CTRL_1 + (port) * \
  (KS8842_PORT_2_CTRL_1 - KS8842_PORT_1_CTRL_1))

#define KS8842_PORT_CTRL_1_OFFSET 0x00

#define PORT_BROADCAST_STORM  0x0080
#define PORT_DIFFSERV_ENABLE  0x0040
#define PORT_802_1P_ENABLE  0x0020
#define PORT_BASED_PRIORITY_MASK 0x0018
#define PORT_BASED_PRIORITY_BASE 0x0003
#define PORT_BASED_PRIORITY_SHIFT 3
#define PORT_BASED_PRIORITY_0  0x0000
#define PORT_BASED_PRIORITY_1  0x0008
#define PORT_BASED_PRIORITY_2  0x0010
#define PORT_BASED_PRIORITY_3  0x0018
#define PORT_INSERT_TAG   0x0004
#define PORT_REMOVE_TAG   0x0002
#define PORT_PRIO_QUEUE_ENABLE  0x0001

#define KS8842_PORT_CTRL_2_OFFSET 0x02

#define PORT_INGRESS_VLAN_FILTER 0x4000
#define PORT_DISCARD_NON_VID  0x2000
#define PORT_FORCE_FLOW_CTRL  0x1000
#define PORT_BACK_PRESSURE  0x0800
#define PORT_TX_ENABLE   0x0400
#define PORT_RX_ENABLE   0x0200
#define PORT_LEARN_DISABLE  0x0100
#define PORT_MIRROR_SNIFFER  0x0080
#define PORT_MIRROR_RX   0x0040
#define PORT_MIRROR_TX   0x0020
#define PORT_USER_PRIORITY_CEILING 0x0008
#define PORT_VLAN_MEMBERSHIP  0x0007

#define KS8842_PORT_CTRL_VID_OFFSET 0x04

#define PORT_DEFAULT_VID  0x0001

#define KS8842_PORT_CTRL_3_OFFSET 0x06

#define PORT_INGRESS_LIMIT_MODE  0x000C
#define PORT_INGRESS_ALL  0x0000
#define PORT_INGRESS_UNICAST  0x0004
#define PORT_INGRESS_MULTICAST  0x0008
#define PORT_INGRESS_BROADCAST  0x000C
#define PORT_COUNT_IFG   0x0002
#define PORT_COUNT_PREAMBLE  0x0001

#define KS8842_PORT_IN_RATE_OFFSET 0x08
#define KS8842_PORT_OUT_RATE_OFFSET 0x0A

#define PORT_PRIORITY_RATE  0x0F
#define PORT_PRIORITY_RATE_SHIFT 4

#define KS884X_PORT_LINK_MD  0x10

#define PORT_CABLE_10M_SHORT  0x8000
#define PORT_CABLE_DIAG_RESULT  0x6000
#define PORT_CABLE_STAT_NORMAL  0x0000
#define PORT_CABLE_STAT_OPEN  0x2000
#define PORT_CABLE_STAT_SHORT  0x4000
#define PORT_CABLE_STAT_FAILED  0x6000
#define PORT_START_CABLE_DIAG  0x1000
#define PORT_FORCE_LINK   0x0800
#define PORT_POWER_SAVING_DISABLE 0x0400
#define PORT_PHY_REMOTE_LOOPBACK 0x0200
#define PORT_CABLE_FAULT_COUNTER 0x01FF

#define KS884X_PORT_CTRL_4_OFFSET 0x12

#define PORT_LED_OFF   0x8000
#define PORT_TX_DISABLE   0x4000
#define PORT_AUTO_NEG_RESTART  0x2000
#define PORT_REMOTE_FAULT_DISABLE 0x1000
#define PORT_POWER_DOWN   0x0800
#define PORT_AUTO_MDIX_DISABLE  0x0400
#define PORT_FORCE_MDIX   0x0200
#define PORT_LOOPBACK   0x0100
#define PORT_AUTO_NEG_ENABLE  0x0080
#define PORT_FORCE_100_MBIT  0x0040
#define PORT_FORCE_FULL_DUPLEX  0x0020
#define PORT_AUTO_NEG_SYM_PAUSE  0x0010
#define PORT_AUTO_NEG_100BTX_FD  0x0008
#define PORT_AUTO_NEG_100BTX  0x0004
#define PORT_AUTO_NEG_10BT_FD  0x0002
#define PORT_AUTO_NEG_10BT  0x0001

#define KS884X_PORT_STATUS_OFFSET 0x14

#define PORT_HP_MDIX   0x8000
#define PORT_REVERSED_POLARITY  0x2000
#define PORT_RX_FLOW_CTRL  0x0800
#define PORT_TX_FLOW_CTRL  0x1000
#define PORT_STATUS_SPEED_100MBIT 0x0400
#define PORT_STATUS_FULL_DUPLEX  0x0200
#define PORT_REMOTE_FAULT  0x0100
#define PORT_MDIX_STATUS  0x0080
#define PORT_AUTO_NEG_COMPLETE  0x0040
#define PORT_STATUS_LINK_GOOD  0x0020
#define PORT_REMOTE_SYM_PAUSE  0x0010
#define PORT_REMOTE_100BTX_FD  0x0008
#define PORT_REMOTE_100BTX  0x0004
#define PORT_REMOTE_10BT_FD  0x0002
#define PORT_REMOTE_10BT  0x0001

/*
#define STATIC_MAC_TABLE_ADDR 00-0000FFFF-FFFFFFFF
#define STATIC_MAC_TABLE_FWD_PORTS 00-00070000-00000000
#define STATIC_MAC_TABLE_VALID 00-00080000-00000000
#define STATIC_MAC_TABLE_OVERRIDE 00-00100000-00000000
#define STATIC_MAC_TABLE_USE_FID 00-00200000-00000000
#define STATIC_MAC_TABLE_FID 00-03C00000-00000000
*/


#define STATIC_MAC_TABLE_ADDR  0x0000FFFF
#define STATIC_MAC_TABLE_FWD_PORTS 0x00070000
#define STATIC_MAC_TABLE_VALID  0x00080000
#define STATIC_MAC_TABLE_OVERRIDE 0x00100000
#define STATIC_MAC_TABLE_USE_FID 0x00200000
#define STATIC_MAC_TABLE_FID  0x03C00000

#define STATIC_MAC_FWD_PORTS_SHIFT 16
#define STATIC_MAC_FID_SHIFT  22

/*
#define VLAN_TABLE_VID 00-00000000-00000FFF
#define VLAN_TABLE_FID 00-00000000-0000F000
#define VLAN_TABLE_MEMBERSHIP 00-00000000-00070000
#define VLAN_TABLE_VALID 00-00000000-00080000
*/


#define VLAN_TABLE_VID   0x00000FFF
#define VLAN_TABLE_FID   0x0000F000
#define VLAN_TABLE_MEMBERSHIP  0x00070000
#define VLAN_TABLE_VALID  0x00080000

#define VLAN_TABLE_FID_SHIFT  12
#define VLAN_TABLE_MEMBERSHIP_SHIFT 16

/*
#define DYNAMIC_MAC_TABLE_ADDR 00-0000FFFF-FFFFFFFF
#define DYNAMIC_MAC_TABLE_FID 00-000F0000-00000000
#define DYNAMIC_MAC_TABLE_SRC_PORT 00-00300000-00000000
#define DYNAMIC_MAC_TABLE_TIMESTAMP 00-00C00000-00000000
#define DYNAMIC_MAC_TABLE_ENTRIES 03-FF000000-00000000
#define DYNAMIC_MAC_TABLE_MAC_EMPTY 04-00000000-00000000
#define DYNAMIC_MAC_TABLE_RESERVED 78-00000000-00000000
#define DYNAMIC_MAC_TABLE_NOT_READY 80-00000000-00000000
*/


#define DYNAMIC_MAC_TABLE_ADDR  0x0000FFFF
#define DYNAMIC_MAC_TABLE_FID  0x000F0000
#define DYNAMIC_MAC_TABLE_SRC_PORT 0x00300000
#define DYNAMIC_MAC_TABLE_TIMESTAMP 0x00C00000
#define DYNAMIC_MAC_TABLE_ENTRIES 0xFF000000

#define DYNAMIC_MAC_TABLE_ENTRIES_H 0x03
#define DYNAMIC_MAC_TABLE_MAC_EMPTY 0x04
#define DYNAMIC_MAC_TABLE_RESERVED 0x78
#define DYNAMIC_MAC_TABLE_NOT_READY 0x80

#define DYNAMIC_MAC_FID_SHIFT  16
#define DYNAMIC_MAC_SRC_PORT_SHIFT 20
#define DYNAMIC_MAC_TIMESTAMP_SHIFT 22
#define DYNAMIC_MAC_ENTRIES_SHIFT 24
#define DYNAMIC_MAC_ENTRIES_H_SHIFT 8

/*
#define MIB_COUNTER_VALUE 00-00000000-3FFFFFFF
#define MIB_COUNTER_VALID 00-00000000-40000000
#define MIB_COUNTER_OVERFLOW 00-00000000-80000000
*/


#define MIB_COUNTER_VALUE  0x3FFFFFFF
#define MIB_COUNTER_VALID  0x40000000
#define MIB_COUNTER_OVERFLOW  0x80000000

#define MIB_PACKET_DROPPED  0x0000FFFF

#define KS_MIB_PACKET_DROPPED_TX_0 0x100
#define KS_MIB_PACKET_DROPPED_TX_1 0x101
#define KS_MIB_PACKET_DROPPED_TX 0x102
#define KS_MIB_PACKET_DROPPED_RX_0 0x103
#define KS_MIB_PACKET_DROPPED_RX_1 0x104
#define KS_MIB_PACKET_DROPPED_RX 0x105

/* Change default LED mode. */
#define SET_DEFAULT_LED   LED_SPEED_DUPLEX_ACT

#define MAC_ADDR_ORDER(i)  (ETH_ALEN - 1 - (i))

#define MAX_ETHERNET_BODY_SIZE  1500
#define ETHERNET_HEADER_SIZE  (14 + VLAN_HLEN)

#define MAX_ETHERNET_PACKET_SIZE \
 (MAX_ETHERNET_BODY_SIZE + ETHERNET_HEADER_SIZE)

#define REGULAR_RX_BUF_SIZE  (MAX_ETHERNET_PACKET_SIZE + 4)
#define MAX_RX_BUF_SIZE   (1912 + 4)

#define ADDITIONAL_ENTRIES  16
#define MAX_MULTICAST_LIST  32

#define HW_MULTICAST_SIZE  8

#define HW_TO_DEV_PORT(port)  (port - 1)

enum {
 media_connected,
 media_disconnected
};

enum {
 OID_COUNTER_UNKOWN,

 OID_COUNTER_FIRST,

 /* total transmit errors */
 OID_COUNTER_XMIT_ERROR,

 /* total receive errors */
 OID_COUNTER_RCV_ERROR,

 OID_COUNTER_LAST
};

/*
 * Hardware descriptor definitions
 */


#define DESC_ALIGNMENT   16
#define BUFFER_ALIGNMENT  8

#define NUM_OF_RX_DESC   64
#define NUM_OF_TX_DESC   64

#define KS_DESC_RX_FRAME_LEN  0x000007FF
#define KS_DESC_RX_FRAME_TYPE  0x00008000
#define KS_DESC_RX_ERROR_CRC  0x00010000
#define KS_DESC_RX_ERROR_RUNT  0x00020000
#define KS_DESC_RX_ERROR_TOO_LONG 0x00040000
#define KS_DESC_RX_ERROR_PHY  0x00080000
#define KS884X_DESC_RX_PORT_MASK 0x00300000
#define KS_DESC_RX_MULTICAST  0x01000000
#define KS_DESC_RX_ERROR  0x02000000
#define KS_DESC_RX_ERROR_CSUM_UDP 0x04000000
#define KS_DESC_RX_ERROR_CSUM_TCP 0x08000000
#define KS_DESC_RX_ERROR_CSUM_IP 0x10000000
#define KS_DESC_RX_LAST   0x20000000
#define KS_DESC_RX_FIRST  0x40000000
#define KS_DESC_RX_ERROR_COND  \
 (KS_DESC_RX_ERROR_CRC |  \
 KS_DESC_RX_ERROR_RUNT |  \
 KS_DESC_RX_ERROR_PHY |  \
 KS_DESC_RX_ERROR_TOO_LONG)

#define KS_DESC_HW_OWNED  0x80000000

#define KS_DESC_BUF_SIZE  0x000007FF
#define KS884X_DESC_TX_PORT_MASK 0x00300000
#define KS_DESC_END_OF_RING  0x02000000
#define KS_DESC_TX_CSUM_GEN_UDP  0x04000000
#define KS_DESC_TX_CSUM_GEN_TCP  0x08000000
#define KS_DESC_TX_CSUM_GEN_IP  0x10000000
#define KS_DESC_TX_LAST   0x20000000
#define KS_DESC_TX_FIRST  0x40000000
#define KS_DESC_TX_INTERRUPT  0x80000000

#define KS_DESC_PORT_SHIFT  20

#define KS_DESC_RX_MASK   (KS_DESC_BUF_SIZE)

#define KS_DESC_TX_MASK   \
 (KS_DESC_TX_INTERRUPT |  \
 KS_DESC_TX_FIRST |  \
 KS_DESC_TX_LAST |  \
 KS_DESC_TX_CSUM_GEN_IP | \
 KS_DESC_TX_CSUM_GEN_TCP | \
 KS_DESC_TX_CSUM_GEN_UDP | \
 KS_DESC_BUF_SIZE)

struct ksz_desc_rx_stat {
#ifdef __BIG_ENDIAN_BITFIELD
 u32 hw_owned:1;
 u32 first_desc:1;
 u32 last_desc:1;
 u32 csum_err_ip:1;
 u32 csum_err_tcp:1;
 u32 csum_err_udp:1;
 u32 error:1;
 u32 multicast:1;
 u32 src_port:4;
 u32 err_phy:1;
 u32 err_too_long:1;
 u32 err_runt:1;
 u32 err_crc:1;
 u32 frame_type:1;
 u32 reserved1:4;
 u32 frame_len:11;
#else
 u32 frame_len:11;
 u32 reserved1:4;
 u32 frame_type:1;
 u32 err_crc:1;
 u32 err_runt:1;
 u32 err_too_long:1;
 u32 err_phy:1;
 u32 src_port:4;
 u32 multicast:1;
 u32 error:1;
 u32 csum_err_udp:1;
 u32 csum_err_tcp:1;
 u32 csum_err_ip:1;
 u32 last_desc:1;
 u32 first_desc:1;
 u32 hw_owned:1;
#endif
};

struct ksz_desc_tx_stat {
#ifdef __BIG_ENDIAN_BITFIELD
 u32 hw_owned:1;
 u32 reserved1:31;
#else
 u32 reserved1:31;
 u32 hw_owned:1;
#endif
};

struct ksz_desc_rx_buf {
#ifdef __BIG_ENDIAN_BITFIELD
 u32 reserved4:6;
 u32 end_of_ring:1;
 u32 reserved3:14;
 u32 buf_size:11;
#else
 u32 buf_size:11;
 u32 reserved3:14;
 u32 end_of_ring:1;
 u32 reserved4:6;
#endif
};

struct ksz_desc_tx_buf {
#ifdef __BIG_ENDIAN_BITFIELD
 u32 intr:1;
 u32 first_seg:1;
 u32 last_seg:1;
 u32 csum_gen_ip:1;
 u32 csum_gen_tcp:1;
 u32 csum_gen_udp:1;
 u32 end_of_ring:1;
 u32 reserved4:1;
 u32 dest_port:4;
 u32 reserved3:9;
 u32 buf_size:11;
#else
 u32 buf_size:11;
 u32 reserved3:9;
 u32 dest_port:4;
 u32 reserved4:1;
 u32 end_of_ring:1;
 u32 csum_gen_udp:1;
 u32 csum_gen_tcp:1;
 u32 csum_gen_ip:1;
 u32 last_seg:1;
 u32 first_seg:1;
 u32 intr:1;
#endif
};

union desc_stat {
 struct ksz_desc_rx_stat rx;
 struct ksz_desc_tx_stat tx;
 u32 data;
};

union desc_buf {
 struct ksz_desc_rx_buf rx;
 struct ksz_desc_tx_buf tx;
 u32 data;
};

/**
 * struct ksz_hw_desc - Hardware descriptor data structure
 * @ctrl: Descriptor control value.
 * @buf: Descriptor buffer value.
 * @addr: Physical address of memory buffer.
 * @next: Pointer to next hardware descriptor.
 */

struct ksz_hw_desc {
 union desc_stat ctrl;
 union desc_buf buf;
 u32 addr;
 u32 next;
};

/**
 * struct ksz_sw_desc - Software descriptor data structure
 * @ctrl: Descriptor control value.
 * @buf: Descriptor buffer value.
 * @buf_size: Current buffers size value in hardware descriptor.
 */

struct ksz_sw_desc {
 union desc_stat ctrl;
 union desc_buf buf;
 u32 buf_size;
};

/**
 * struct ksz_dma_buf - OS dependent DMA buffer data structure
 * @skb: Associated socket buffer.
 * @dma: Associated physical DMA address.
 * @len: Actual len used.
 */

struct ksz_dma_buf {
 struct sk_buff *skb;
 dma_addr_t dma;
 int len;
};

/**
 * struct ksz_desc - Descriptor structure
 * @phw: Hardware descriptor pointer to uncached physical memory.
 * @sw: Cached memory to hold hardware descriptor values for
 *  manipulation.
 * @dma_buf: Operating system dependent data structure to hold physical
 *  memory buffer allocation information.
 */

struct ksz_desc {
 struct ksz_hw_desc *phw;
 struct ksz_sw_desc sw;
 struct ksz_dma_buf dma_buf;
};

#define DMA_BUFFER(desc)  ((struct ksz_dma_buf *)(&(desc)->dma_buf))

/**
 * struct ksz_desc_info - Descriptor information data structure
 * @ring: First descriptor in the ring.
 * @cur: Current descriptor being manipulated.
 * @ring_virt: First hardware descriptor in the ring.
 * @ring_phys: The physical address of the first descriptor of the ring.
 * @size: Size of hardware descriptor.
 * @alloc: Number of descriptors allocated.
 * @avail: Number of descriptors available for use.
 * @last: Index for last descriptor released to hardware.
 * @next: Index for next descriptor available for use.
 * @mask: Mask for index wrapping.
 */

struct ksz_desc_info {
 struct ksz_desc *ring;
 struct ksz_desc *cur;
 struct ksz_hw_desc *ring_virt;
 u32 ring_phys;
 int size;
 int alloc;
 int avail;
 int last;
 int next;
 int mask;
};

/*
 * KSZ8842 switch definitions
 */


enum {
 TABLE_STATIC_MAC = 0,
 TABLE_VLAN,
 TABLE_DYNAMIC_MAC,
 TABLE_MIB
};

#define LEARNED_MAC_TABLE_ENTRIES 1024
#define STATIC_MAC_TABLE_ENTRIES 8

/**
 * struct ksz_mac_table - Static MAC table data structure
 * @mac_addr: MAC address to filter.
 * @vid: VID value.
 * @fid: FID value.
 * @ports: Port membership.
 * @override: Override setting.
 * @use_fid: FID use setting.
 * @valid: Valid setting indicating the entry is being used.
 */

struct ksz_mac_table {
 u8 mac_addr[ETH_ALEN];
 u16 vid;
 u8 fid;
 u8 ports;
 u8 override:1;
 u8 use_fid:1;
 u8 valid:1;
};

#define VLAN_TABLE_ENTRIES  16

/**
 * struct ksz_vlan_table - VLAN table data structure
 * @vid: VID value.
 * @fid: FID value.
 * @member: Port membership.
 */

struct ksz_vlan_table {
 u16 vid;
 u8 fid;
 u8 member;
};

#define DIFFSERV_ENTRIES  64
#define PRIO_802_1P_ENTRIES  8
#define PRIO_QUEUES   4

#define SWITCH_PORT_NUM   2
#define TOTAL_PORT_NUM   (SWITCH_PORT_NUM + 1)
#define HOST_MASK   (1 << SWITCH_PORT_NUM)
#define PORT_MASK   7

#define MAIN_PORT   0
#define OTHER_PORT   1
#define HOST_PORT   SWITCH_PORT_NUM

#define PORT_COUNTER_NUM  0x20
#define TOTAL_PORT_COUNTER_NUM  (PORT_COUNTER_NUM + 2)

#define MIB_COUNTER_RX_LO_PRIORITY 0x00
#define MIB_COUNTER_RX_HI_PRIORITY 0x01
#define MIB_COUNTER_RX_UNDERSIZE 0x02
#define MIB_COUNTER_RX_FRAGMENT  0x03
#define MIB_COUNTER_RX_OVERSIZE  0x04
#define MIB_COUNTER_RX_JABBER  0x05
#define MIB_COUNTER_RX_SYMBOL_ERR 0x06
#define MIB_COUNTER_RX_CRC_ERR  0x07
#define MIB_COUNTER_RX_ALIGNMENT_ERR 0x08
#define MIB_COUNTER_RX_CTRL_8808 0x09
#define MIB_COUNTER_RX_PAUSE  0x0A
#define MIB_COUNTER_RX_BROADCAST 0x0B
#define MIB_COUNTER_RX_MULTICAST 0x0C
#define MIB_COUNTER_RX_UNICAST  0x0D
#define MIB_COUNTER_RX_OCTET_64  0x0E
#define MIB_COUNTER_RX_OCTET_65_127 0x0F
#define MIB_COUNTER_RX_OCTET_128_255 0x10
#define MIB_COUNTER_RX_OCTET_256_511 0x11
#define MIB_COUNTER_RX_OCTET_512_1023 0x12
#define MIB_COUNTER_RX_OCTET_1024_1522 0x13
#define MIB_COUNTER_TX_LO_PRIORITY 0x14
#define MIB_COUNTER_TX_HI_PRIORITY 0x15
#define MIB_COUNTER_TX_LATE_COLLISION 0x16
#define MIB_COUNTER_TX_PAUSE  0x17
#define MIB_COUNTER_TX_BROADCAST 0x18
#define MIB_COUNTER_TX_MULTICAST 0x19
#define MIB_COUNTER_TX_UNICAST  0x1A
#define MIB_COUNTER_TX_DEFERRED  0x1B
#define MIB_COUNTER_TX_TOTAL_COLLISION 0x1C
#define MIB_COUNTER_TX_EXCESS_COLLISION 0x1D
#define MIB_COUNTER_TX_SINGLE_COLLISION 0x1E
#define MIB_COUNTER_TX_MULTI_COLLISION 0x1F

#define MIB_COUNTER_RX_DROPPED_PACKET 0x20
#define MIB_COUNTER_TX_DROPPED_PACKET 0x21

/**
 * struct ksz_port_mib - Port MIB data structure
 * @cnt_ptr: Current pointer to MIB counter index.
 * @link_down: Indication the link has just gone down.
 * @state: Connection status of the port.
 * @mib_start: The starting counter index.  Some ports do not start at 0.
 * @counter: 64-bit MIB counter value.
 * @dropped: Temporary buffer to remember last read packet dropped values.
 *
 * MIB counters needs to be read periodically so that counters do not get
 * overflowed and give incorrect values.  A right balance is needed to
 * satisfy this condition and not waste too much CPU time.
 *
 * It is pointless to read MIB counters when the port is disconnected.  The
 * @state provides the connection status so that MIB counters are read only
 * when the port is connected.  The @link_down indicates the port is just
 * disconnected so that all MIB counters are read one last time to update the
 * information.
 */

struct ksz_port_mib {
 u8 cnt_ptr;
 u8 link_down;
 u8 state;
 u8 mib_start;

 u64 counter[TOTAL_PORT_COUNTER_NUM];
 u32 dropped[2];
};

/**
 * struct ksz_port_cfg - Port configuration data structure
 * @vid: VID value.
 * @member: Port membership.
 * @port_prio: Port priority.
 * @rx_rate: Receive priority rate.
 * @tx_rate: Transmit priority rate.
 * @stp_state: Current Spanning Tree Protocol state.
 */

struct ksz_port_cfg {
 u16 vid;
 u8 member;
 u8 port_prio;
 u32 rx_rate[PRIO_QUEUES];
 u32 tx_rate[PRIO_QUEUES];
 int stp_state;
};

/**
 * struct ksz_switch - KSZ8842 switch data structure
 * @mac_table: MAC table entries information.
 * @vlan_table: VLAN table entries information.
 * @port_cfg: Port configuration information.
 * @diffserv: DiffServ priority settings.  Possible values from 6-bit of ToS
 *  (bit7 ~ bit2) field.
 * @p_802_1p: 802.1P priority settings.  Possible values from 3-bit of 802.1p
 *  Tag priority field.
 * @br_addr: Bridge address.  Used for STP.
 * @other_addr: Other MAC address.  Used for multiple network device mode.
 * @broad_per: Broadcast storm percentage.
 * @member: Current port membership.  Used for STP.
 */

struct ksz_switch {
 struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES];
 struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES];
 struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];

 u8 diffserv[DIFFSERV_ENTRIES];
 u8 p_802_1p[PRIO_802_1P_ENTRIES];

 u8 br_addr[ETH_ALEN];
 u8 other_addr[ETH_ALEN];

 u8 broad_per;
 u8 member;
};

#define TX_RATE_UNIT   10000

/**
 * struct ksz_port_info - Port information data structure
 * @state: Connection status of the port.
 * @tx_rate: Transmit rate divided by 10000 to get Mbit.
 * @duplex: Duplex mode.
 * @advertised: Advertised auto-negotiation setting.  Used to determine link.
 * @partner: Auto-negotiation partner setting.  Used to determine link.
 * @port_id: Port index to access actual hardware register.
 * @pdev: Pointer to OS dependent network device.
 */

struct ksz_port_info {
 uint state;
 uint tx_rate;
 u8 duplex;
 u8 advertised;
 u8 partner;
 u8 port_id;
 void *pdev;
};

#define MAX_TX_HELD_SIZE  52000

/* Hardware features and bug fixes. */
#define LINK_INT_WORKING  (1 << 0)
#define SMALL_PACKET_TX_BUG  (1 << 1)
#define HALF_DUPLEX_SIGNAL_BUG  (1 << 2)
#define RX_HUGE_FRAME   (1 << 4)
#define STP_SUPPORT   (1 << 8)

/* Software overrides. */
#define PAUSE_FLOW_CTRL   (1 << 0)
#define FAST_AGING   (1 << 1)

/**
 * struct ksz_hw - KSZ884X hardware data structure
 * @io: Virtual address assigned.
 * @ksz_switch: Pointer to KSZ8842 switch.
 * @port_info: Port information.
 * @port_mib: Port MIB information.
 * @dev_count: Number of network devices this hardware supports.
 * @dst_ports: Destination ports in switch for transmission.
 * @id: Hardware ID.  Used for display only.
 * @mib_cnt: Number of MIB counters this hardware has.
 * @mib_port_cnt: Number of ports with MIB counters.
 * @tx_cfg: Cached transmit control settings.
 * @rx_cfg: Cached receive control settings.
 * @intr_mask: Current interrupt mask.
 * @intr_set: Current interrup set.
 * @intr_blocked: Interrupt blocked.
 * @rx_desc_info: Receive descriptor information.
 * @tx_desc_info: Transmit descriptor information.
 * @tx_int_cnt: Transmit interrupt count.  Used for TX optimization.
 * @tx_int_mask: Transmit interrupt mask.  Used for TX optimization.
 * @tx_size: Transmit data size.  Used for TX optimization.
 *  The maximum is defined by MAX_TX_HELD_SIZE.
 * @perm_addr: Permanent MAC address.
 * @override_addr: Overridden MAC address.
 * @address: Additional MAC address entries.
 * @addr_list_size: Additional MAC address list size.
 * @mac_override: Indication of MAC address overridden.
 * @promiscuous: Counter to keep track of promiscuous mode set.
 * @all_multi: Counter to keep track of all multicast mode set.
 * @multi_list: Multicast address entries.
 * @multi_bits: Cached multicast hash table settings.
 * @multi_list_size: Multicast address list size.
 * @enabled: Indication of hardware enabled.
 * @rx_stop: Indication of receive process stop.
 * @reserved2: none
 * @features: Hardware features to enable.
 * @overrides: Hardware features to override.
 * @parent: Pointer to parent, network device private structure.
 */

struct ksz_hw {
 void __iomem *io;

 struct ksz_switch *ksz_switch;
 struct ksz_port_info port_info[SWITCH_PORT_NUM];
 struct ksz_port_mib port_mib[TOTAL_PORT_NUM];
 int dev_count;
 int dst_ports;
 int id;
 int mib_cnt;
 int mib_port_cnt;

 u32 tx_cfg;
 u32 rx_cfg;
 u32 intr_mask;
 u32 intr_set;
 uint intr_blocked;

 struct ksz_desc_info rx_desc_info;
 struct ksz_desc_info tx_desc_info;

 int tx_int_cnt;
 int tx_int_mask;
 int tx_size;

 u8 perm_addr[ETH_ALEN];
 u8 override_addr[ETH_ALEN];
 u8 address[ADDITIONAL_ENTRIES][ETH_ALEN];
 u8 addr_list_size;
 u8 mac_override;
 u8 promiscuous;
 u8 all_multi;
 u8 multi_list[MAX_MULTICAST_LIST][ETH_ALEN];
 u8 multi_bits[HW_MULTICAST_SIZE];
 u8 multi_list_size;

 u8 enabled;
 u8 rx_stop;
 u8 reserved2[1];

 uint features;
 uint overrides;

 void *parent;
};

enum {
 PHY_NO_FLOW_CTRL,
 PHY_FLOW_CTRL,
 PHY_TX_ONLY,
 PHY_RX_ONLY
};

/**
 * struct ksz_port - Virtual port data structure
 * @duplex: Duplex mode setting.  1 for half duplex, 2 for full
 *  duplex, and 0 for auto, which normally results in full
 *  duplex.
 * @speed: Speed setting.  10 for 10 Mbit, 100 for 100 Mbit, and
 *  0 for auto, which normally results in 100 Mbit.
 * @force_link: Force link setting.  0 for auto-negotiation, and 1 for
 *  force.
 * @flow_ctrl: Flow control setting.  PHY_NO_FLOW_CTRL for no flow
 *  control, and PHY_FLOW_CTRL for flow control.
 *  PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100
 *  Mbit PHY.
 * @first_port: Index of first port this port supports.
 * @mib_port_cnt: Number of ports with MIB counters.
 * @port_cnt: Number of ports this port supports.
 * @counter: Port statistics counter.
 * @hw: Pointer to hardware structure.
 * @linked: Pointer to port information linked to this port.
 */

struct ksz_port {
 u8 duplex;
 u8 speed;
 u8 force_link;
 u8 flow_ctrl;

 int first_port;
 int mib_port_cnt;
 int port_cnt;
 u64 counter[OID_COUNTER_LAST];

 struct ksz_hw *hw;
 struct ksz_port_info *linked;
};

/**
 * struct ksz_timer_info - Timer information data structure
 * @timer: Kernel timer.
 * @cnt: Running timer counter.
 * @max: Number of times to run timer; -1 for infinity.
 * @period: Timer period in jiffies.
 */

struct ksz_timer_info {
 struct timer_list timer;
 int cnt;
 int max;
 int period;
};

/**
 * struct ksz_shared_mem - OS dependent shared memory data structure
 * @dma_addr: Physical DMA address allocated.
 * @alloc_size: Allocation size.
 * @phys: Actual physical address used.
 * @alloc_virt: Virtual address allocated.
 * @virt: Actual virtual address used.
 */

struct ksz_shared_mem {
 dma_addr_t dma_addr;
 uint alloc_size;
 uint phys;
 u8 *alloc_virt;
 u8 *virt;
};

/**
 * struct ksz_counter_info - OS dependent counter information data structure
 * @counter: Wait queue to wakeup after counters are read.
 * @time: Next time in jiffies to read counter.
 * @read: Indication of counters read in full or not.
 */

struct ksz_counter_info {
 wait_queue_head_t counter;
 unsigned long time;
 int read;
};

/**
 * struct dev_info - Network device information data structure
 * @dev: Pointer to network device.
 * @pdev: Pointer to PCI device.
 * @hw: Hardware structure.
 * @desc_pool: Physical memory used for descriptor pool.
 * @hwlock: Spinlock to prevent hardware from accessing.
 * @lock: Mutex lock to prevent device from accessing.
 * @dev_rcv: Receive process function used.
 * @last_skb: Socket buffer allocated for descriptor rx fragments.
 * @skb_index: Buffer index for receiving fragments.
 * @skb_len: Buffer length for receiving fragments.
 * @mib_read: Workqueue to read MIB counters.
 * @mib_timer_info: Timer to read MIB counters.
 * @counter: Used for MIB reading.
 * @mtu: Current MTU used.  The default is REGULAR_RX_BUF_SIZE;
 *  the maximum is MAX_RX_BUF_SIZE.
 * @opened: Counter to keep track of device open.
 * @rx_tasklet: Receive processing tasklet.
 * @tx_tasklet: Transmit processing tasklet.
 * @wol_enable: Wake-on-LAN enable set by ethtool.
 * @wol_support: Wake-on-LAN support used by ethtool.
 * @pme_wait: Used for KSZ8841 power management.
 */

struct dev_info {
 struct net_device *dev;
 struct pci_dev *pdev;

 struct ksz_hw hw;
 struct ksz_shared_mem desc_pool;

 spinlock_t hwlock;
 struct mutex lock;

 int (*dev_rcv)(struct dev_info *);

 struct sk_buff *last_skb;
 int skb_index;
 int skb_len;

 struct work_struct mib_read;
 struct ksz_timer_info mib_timer_info;
 struct ksz_counter_info counter[TOTAL_PORT_NUM];

 int mtu;
 int opened;

 struct tasklet_struct rx_tasklet;
 struct tasklet_struct tx_tasklet;

 int wol_enable;
 int wol_support;
 unsigned long pme_wait;
};

/**
 * struct dev_priv - Network device private data structure
 * @adapter: Adapter device information.
 * @port: Port information.
 * @monitor_timer_info: Timer to monitor ports.
 * @proc_sem: Semaphore for proc accessing.
 * @id: Device ID.
 * @mii_if: MII interface information.
 * @advertising: Temporary variable to store advertised settings.
 * @msg_enable: The message flags controlling driver output.
 * @media_state: The connection status of the device.
 * @multicast: The all multicast state of the device.
 * @promiscuous: The promiscuous state of the device.
 */

struct dev_priv {
 struct dev_info *adapter;
 struct ksz_port port;
 struct ksz_timer_info monitor_timer_info;

 struct semaphore proc_sem;
 int id;

 struct mii_if_info mii_if;
 u32 advertising;

 u32 msg_enable;
 int media_state;
 int multicast;
 int promiscuous;
};

#define DRV_NAME  "KSZ884X PCI"
#define DEVICE_NAME  "KSZ884x PCI"
#define DRV_VERSION  "1.0.0"
#define DRV_RELDATE  "Feb 8, 2010"

static char version[] =
 "Micrel " DEVICE_NAME " " DRV_VERSION " (" DRV_RELDATE ")";

static u8 DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x88, 0x42, 0x01 };

/*
 * Interrupt processing primary routines
 */


static inline void hw_ack_intr(struct ksz_hw *hw, uint interrupt)
{
 writel(interrupt, hw->io + KS884X_INTERRUPTS_STATUS);
}

static inline void hw_dis_intr(struct ksz_hw *hw)
{
 hw->intr_blocked = hw->intr_mask;
 writel(0, hw->io + KS884X_INTERRUPTS_ENABLE);
 hw->intr_set = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
}

static inline void hw_set_intr(struct ksz_hw *hw, uint interrupt)
{
 hw->intr_set = interrupt;
 writel(interrupt, hw->io + KS884X_INTERRUPTS_ENABLE);
}

static inline void hw_ena_intr(struct ksz_hw *hw)
{
 hw->intr_blocked = 0;
 hw_set_intr(hw, hw->intr_mask);
}

static inline void hw_dis_intr_bit(struct ksz_hw *hw, uint bit)
{
 hw->intr_mask &= ~(bit);
}

static inline void hw_turn_off_intr(struct ksz_hw *hw, uint interrupt)
{
 u32 read_intr;

 read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
 hw->intr_set = read_intr & ~interrupt;
 writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
 hw_dis_intr_bit(hw, interrupt);
}

/**
 * hw_turn_on_intr - turn on specified interrupts
 * @hw:  The hardware instance.
 * @bit: The interrupt bits to be on.
 *
 * This routine turns on the specified interrupts in the interrupt mask so that
 * those interrupts will be enabled.
 */

static void hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
{
 hw->intr_mask |= bit;

 if (!hw->intr_blocked)
  hw_set_intr(hw, hw->intr_mask);
}

static inline void hw_read_intr(struct ksz_hw *hw, uint *status)
{
 *status = readl(hw->io + KS884X_INTERRUPTS_STATUS);
 *status = *status & hw->intr_set;
}

static inline void hw_restore_intr(struct ksz_hw *hw, uint interrupt)
{
 if (interrupt)
  hw_ena_intr(hw);
}

/**
 * hw_block_intr - block hardware interrupts
 * @hw: The hardware instance.
 *
 * This function blocks all interrupts of the hardware and returns the current
 * interrupt enable mask so that interrupts can be restored later.
 *
 * Return the current interrupt enable mask.
 */

static uint hw_block_intr(struct ksz_hw *hw)
{
 uint interrupt = 0;

 if (!hw->intr_blocked) {
  hw_dis_intr(hw);
  interrupt = hw->intr_blocked;
 }
 return interrupt;
}

/*
 * Hardware descriptor routines
 */


static inline void reset_desc(struct ksz_desc *desc, union desc_stat status)
{
 status.rx.hw_owned = 0;
 desc->phw->ctrl.data = cpu_to_le32(status.data);
}

static inline void release_desc(struct ksz_desc *desc)
{
 desc->sw.ctrl.tx.hw_owned = 1;
 if (desc->sw.buf_size != desc->sw.buf.data) {
  desc->sw.buf_size = desc->sw.buf.data;
  desc->phw->buf.data = cpu_to_le32(desc->sw.buf.data);
 }
 desc->phw->ctrl.data = cpu_to_le32(desc->sw.ctrl.data);
}

static void get_rx_pkt(struct ksz_desc_info *info, struct ksz_desc **desc)
{
 *desc = &info->ring[info->last];
 info->last++;
 info->last &= info->mask;
 info->avail--;
 (*desc)->sw.buf.data &= ~KS_DESC_RX_MASK;
}

static inline void set_rx_buf(struct ksz_desc *desc, u32 addr)
{
 desc->phw->addr = cpu_to_le32(addr);
}

static inline void set_rx_len(struct ksz_desc *desc, u32 len)
{
 desc->sw.buf.rx.buf_size = len;
}

static inline void get_tx_pkt(struct ksz_desc_info *info,
 struct ksz_desc **desc)
{
 *desc = &info->ring[info->next];
 info->next++;
 info->next &= info->mask;
 info->avail--;
 (*desc)->sw.buf.data &= ~KS_DESC_TX_MASK;
}

static inline void set_tx_buf(struct ksz_desc *desc, u32 addr)
{
 desc->phw->addr = cpu_to_le32(addr);
}

static inline void set_tx_len(struct ksz_desc *desc, u32 len)
{
 desc->sw.buf.tx.buf_size = len;
}

/* Switch functions */

#define TABLE_READ   0x10
#define TABLE_SEL_SHIFT   2

#define HW_DELAY(hw, reg)   \
 do {     \
  readw(hw->io + reg);  \
 } while (0)

/**
 * sw_r_table - read 4 bytes of data from switch table
 * @hw: The hardware instance.
 * @table: The table selector.
 * @addr: The address of the table entry.
 * @data: Buffer to store the read data.
 *
 * This routine reads 4 bytes of data from the table of the switch.
 * Hardware interrupts are disabled to minimize corruption of read data.
 */

static void sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
{
 u16 ctrl_addr;
 uint interrupt;

 ctrl_addr = (((table << TABLE_SEL_SHIFT) | TABLE_READ) << 8) | addr;

 interrupt = hw_block_intr(hw);

 writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
 HW_DELAY(hw, KS884X_IACR_OFFSET);
 *data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);

 hw_restore_intr(hw, interrupt);
}

/**
 * sw_w_table_64 - write 8 bytes of data to the switch table
 * @hw: The hardware instance.
 * @table: The table selector.
 * @addr: The address of the table entry.
 * @data_hi: The high part of data to be written (bit63 ~ bit32).
 * @data_lo: The low part of data to be written (bit31 ~ bit0).
 *
 * This routine writes 8 bytes of data to the table of the switch.
 * Hardware interrupts are disabled to minimize corruption of written data.
 */

static void sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
 u32 data_lo)
{
 u16 ctrl_addr;
 uint interrupt;

 ctrl_addr = ((table << TABLE_SEL_SHIFT) << 8) | addr;

 interrupt = hw_block_intr(hw);

 writel(data_hi, hw->io + KS884X_ACC_DATA_4_OFFSET);
 writel(data_lo, hw->io + KS884X_ACC_DATA_0_OFFSET);

 writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
 HW_DELAY(hw, KS884X_IACR_OFFSET);

 hw_restore_intr(hw, interrupt);
}

/**
 * sw_w_sta_mac_table - write to the static MAC table
 * @hw:  The hardware instance.
 * @addr: The address of the table entry.
 * @mac_addr: The MAC address.
 * @ports: The port members.
 * @override: The flag to override the port receive/transmit settings.
 * @valid: The flag to indicate entry is valid.
 * @use_fid: The flag to indicate the FID is valid.
 * @fid: The FID value.
 *
 * This routine writes an entry of the static MAC table of the switch.  It
 * calls sw_w_table_64() to write the data.
 */

static void sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
 u8 ports, int override, int valid, int use_fid, u8 fid)
{
 u32 data_hi;
 u32 data_lo;

 data_lo = ((u32) mac_addr[2] << 24) |
  ((u32) mac_addr[3] << 16) |
  ((u32) mac_addr[4] << 8) | mac_addr[5];
 data_hi = ((u32) mac_addr[0] << 8) | mac_addr[1];
 data_hi |= (u32) ports << STATIC_MAC_FWD_PORTS_SHIFT;

 if (override)
  data_hi |= STATIC_MAC_TABLE_OVERRIDE;
 if (use_fid) {
  data_hi |= STATIC_MAC_TABLE_USE_FID;
  data_hi |= (u32) fid << STATIC_MAC_FID_SHIFT;
 }
 if (valid)
  data_hi |= STATIC_MAC_TABLE_VALID;

 sw_w_table_64(hw, TABLE_STATIC_MAC, addr, data_hi, data_lo);
}

/**
 * sw_r_vlan_table - read from the VLAN table
 * @hw:  The hardware instance.
 * @addr: The address of the table entry.
 * @vid: Buffer to store the VID.
 * @fid: Buffer to store the VID.
 * @member: Buffer to store the port membership.
 *
 * This function reads an entry of the VLAN table of the switch.  It calls
 * sw_r_table() to get the data.
 *
 * Return 0 if the entry is valid; otherwise -1.
 */

static int sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
 u8 *member)
{
 u32 data;

 sw_r_table(hw, TABLE_VLAN, addr, &data);
 if (data & VLAN_TABLE_VALID) {
  *vid = (u16)(data & VLAN_TABLE_VID);
  *fid = (u8)((data & VLAN_TABLE_FID) >> VLAN_TABLE_FID_SHIFT);
  *member = (u8)((data & VLAN_TABLE_MEMBERSHIP) >>
   VLAN_TABLE_MEMBERSHIP_SHIFT);
  return 0;
 }
 return -1;
}

/**
 * port_r_mib_cnt - read MIB counter
 * @hw:  The hardware instance.
 * @port: The port index.
 * @addr: The address of the counter.
 * @cnt: Buffer to store the counter.
 *
 * This routine reads a MIB counter of the port.
 * Hardware interrupts are disabled to minimize corruption of read data.
 */

static void port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
{
 u32 data;
 u16 ctrl_addr;
 uint interrupt;
 int timeout;

 ctrl_addr = addr + PORT_COUNTER_NUM * port;

 interrupt = hw_block_intr(hw);

 ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ) << 8);
 writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
 HW_DELAY(hw, KS884X_IACR_OFFSET);

 for (timeout = 100; timeout > 0; timeout--) {
  data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);

  if (data & MIB_COUNTER_VALID) {
   if (data & MIB_COUNTER_OVERFLOW)
    *cnt += MIB_COUNTER_VALUE + 1;
   *cnt += data & MIB_COUNTER_VALUE;
   break;
  }
 }

 hw_restore_intr(hw, interrupt);
}

/**
 * port_r_mib_pkt - read dropped packet counts
 * @hw:  The hardware instance.
 * @port: The port index.
 * @last: last one
 * @cnt: Buffer to store the receive and transmit dropped packet counts.
 *
 * This routine reads the dropped packet counts of the port.
 * Hardware interrupts are disabled to minimize corruption of read data.
 */

static void port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
{
 u32 cur;
 u32 data;
 u16 ctrl_addr;
 uint interrupt;
 int index;

 index = KS_MIB_PACKET_DROPPED_RX_0 + port;
 do {
  interrupt = hw_block_intr(hw);

  ctrl_addr = (u16) index;
  ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ)
   << 8);
  writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
  HW_DELAY(hw, KS884X_IACR_OFFSET);
  data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);

  hw_restore_intr(hw, interrupt);

  data &= MIB_PACKET_DROPPED;
  cur = *last;
  if (data != cur) {
   *last = data;
   if (data < cur)
    data += MIB_PACKET_DROPPED + 1;
   data -= cur;
   *cnt += data;
  }
  ++last;
  ++cnt;
  index -= KS_MIB_PACKET_DROPPED_TX -
   KS_MIB_PACKET_DROPPED_TX_0 + 1;
 } while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
}

/**
 * port_r_cnt - read MIB counters periodically
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine is used to read the counters of the port periodically to avoid
 * counter overflow.  The hardware should be acquired first before calling this
 * routine.
 *
 * Return non-zero when not all counters not read.
 */

static int port_r_cnt(struct ksz_hw *hw, int port)
{
 struct ksz_port_mib *mib = &hw->port_mib[port];

 if (mib->mib_start < PORT_COUNTER_NUM)
  while (mib->cnt_ptr < PORT_COUNTER_NUM) {
   port_r_mib_cnt(hw, port, mib->cnt_ptr,
    &mib->counter[mib->cnt_ptr]);
   ++mib->cnt_ptr;
  }
 if (hw->mib_cnt > PORT_COUNTER_NUM)
  port_r_mib_pkt(hw, port, mib->dropped,
   &mib->counter[PORT_COUNTER_NUM]);
 mib->cnt_ptr = 0;
 return 0;
}

/**
 * port_init_cnt - initialize MIB counter values
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine is used to initialize all counters to zero if the hardware
 * cannot do it after reset.
 */

static void port_init_cnt(struct ksz_hw *hw, int port)
{
 struct ksz_port_mib *mib = &hw->port_mib[port];

 mib->cnt_ptr = 0;
 if (mib->mib_start < PORT_COUNTER_NUM)
  do {
   port_r_mib_cnt(hw, port, mib->cnt_ptr,
    &mib->counter[mib->cnt_ptr]);
   ++mib->cnt_ptr;
  } while (mib->cnt_ptr < PORT_COUNTER_NUM);
 if (hw->mib_cnt > PORT_COUNTER_NUM)
  port_r_mib_pkt(hw, port, mib->dropped,
   &mib->counter[PORT_COUNTER_NUM]);
 memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
 mib->cnt_ptr = 0;
}

/*
 * Port functions
 */


/**
 * port_cfg - set port register bits
 * @hw:  The hardware instance.
 * @port: The port index.
 * @offset: The offset of the port register.
 * @bits: The data bits to set.
 * @set: The flag indicating whether the bits are to be set or not.
 *
 * This routine sets or resets the specified bits of the port register.
 */

static void port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits,
 int set)
{
 u32 addr;
 u16 data;

 PORT_CTRL_ADDR(port, addr);
 addr += offset;
 data = readw(hw->io + addr);
 if (set)
  data |= bits;
 else
  data &= ~bits;
 writew(data, hw->io + addr);
}

/**
 * port_r8 - read byte from port register
 * @hw:  The hardware instance.
 * @port: The port index.
 * @offset: The offset of the port register.
 * @data: Buffer to store the data.
 *
 * This routine reads a byte from the port register.
 */

static void port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
{
 u32 addr;

 PORT_CTRL_ADDR(port, addr);
 addr += offset;
 *data = readb(hw->io + addr);
}

/**
 * port_r16 - read word from port register.
 * @hw:  The hardware instance.
 * @port: The port index.
 * @offset: The offset of the port register.
 * @data: Buffer to store the data.
 *
 * This routine reads a word from the port register.
 */

static void port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
{
 u32 addr;

 PORT_CTRL_ADDR(port, addr);
 addr += offset;
 *data = readw(hw->io + addr);
}

/**
 * port_w16 - write word to port register.
 * @hw:  The hardware instance.
 * @port: The port index.
 * @offset: The offset of the port register.
 * @data: Data to write.
 *
 * This routine writes a word to the port register.
 */

static void port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
{
 u32 addr;

 PORT_CTRL_ADDR(port, addr);
 addr += offset;
 writew(data, hw->io + addr);
}

/**
 * sw_chk - check switch register bits
 * @hw:  The hardware instance.
 * @addr: The address of the switch register.
 * @bits: The data bits to check.
 *
 * This function checks whether the specified bits of the switch register are
 * set or not.
 *
 * Return 0 if the bits are not set.
 */

static int sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
{
 u16 data;

 data = readw(hw->io + addr);
 return (data & bits) == bits;
}

/**
 * sw_cfg - set switch register bits
 * @hw:  The hardware instance.
 * @addr: The address of the switch register.
 * @bits: The data bits to set.
 * @set: The flag indicating whether the bits are to be set or not.
 *
 * This function sets or resets the specified bits of the switch register.
 */

static void sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
{
 u16 data;

 data = readw(hw->io + addr);
 if (set)
  data |= bits;
 else
  data &= ~bits;
 writew(data, hw->io + addr);
}

/* Bandwidth */

static inline void port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
}

/* Driver set switch broadcast storm protection at 10% rate. */
#define BROADCAST_STORM_PROTECTION_RATE 10

/* 148,800 frames * 67 ms / 100 */
#define BROADCAST_STORM_VALUE  9969

/**
 * sw_cfg_broad_storm - configure broadcast storm threshold
 * @hw:  The hardware instance.
 * @percent: Broadcast storm threshold in percent of transmit rate.
 *
 * This routine configures the broadcast storm threshold of the switch.
 */

static void sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
{
 u16 data;
 u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);

 if (value > BROADCAST_STORM_RATE)
  value = BROADCAST_STORM_RATE;

 data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
 data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
 data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
 writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
}

/**
 * sw_get_broad_storm - get broadcast storm threshold
 * @hw:  The hardware instance.
 * @percent: Buffer to store the broadcast storm threshold percentage.
 *
 * This routine retrieves the broadcast storm threshold of the switch.
 */

static void sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
{
 int num;
 u16 data;

 data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
 num = (data & BROADCAST_STORM_RATE_HI);
 num <<= 8;
 num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
 num = DIV_ROUND_CLOSEST(num * 100, BROADCAST_STORM_VALUE);
 *percent = (u8) num;
}

/**
 * sw_dis_broad_storm - disable broadstorm
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine disables the broadcast storm limit function of the switch.
 */

static void sw_dis_broad_storm(struct ksz_hw *hw, int port)
{
 port_cfg_broad_storm(hw, port, 0);
}

/**
 * sw_ena_broad_storm - enable broadcast storm
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine enables the broadcast storm limit function of the switch.
 */

static void sw_ena_broad_storm(struct ksz_hw *hw, int port)
{
 sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
 port_cfg_broad_storm(hw, port, 1);
}

/**
 * sw_init_broad_storm - initialize broadcast storm
 * @hw:  The hardware instance.
 *
 * This routine initializes the broadcast storm limit function of the switch.
 */

static void sw_init_broad_storm(struct ksz_hw *hw)
{
 int port;

 hw->ksz_switch->broad_per = 1;
 sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
 for (port = 0; port < TOTAL_PORT_NUM; port++)
  sw_dis_broad_storm(hw, port);
 sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
}

/**
 * hw_cfg_broad_storm - configure broadcast storm
 * @hw:  The hardware instance.
 * @percent: Broadcast storm threshold in percent of transmit rate.
 *
 * This routine configures the broadcast storm threshold of the switch.
 * It is called by user functions.  The hardware should be acquired first.
 */

static void hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
{
 if (percent > 100)
  percent = 100;

 sw_cfg_broad_storm(hw, percent);
 sw_get_broad_storm(hw, &percent);
 hw->ksz_switch->broad_per = percent;
}

/**
 * sw_dis_prio_rate - disable switch priority rate
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine disables the priority rate function of the switch.
 */

static void sw_dis_prio_rate(struct ksz_hw *hw, int port)
{
 u32 addr;

 PORT_CTRL_ADDR(port, addr);
 addr += KS8842_PORT_IN_RATE_OFFSET;
 writel(0, hw->io + addr);
}

/**
 * sw_init_prio_rate - initialize switch prioirty rate
 * @hw:  The hardware instance.
 *
 * This routine initializes the priority rate function of the switch.
 */

static void sw_init_prio_rate(struct ksz_hw *hw)
{
 int port;
 int prio;
 struct ksz_switch *sw = hw->ksz_switch;

 for (port = 0; port < TOTAL_PORT_NUM; port++) {
  for (prio = 0; prio < PRIO_QUEUES; prio++) {
   sw->port_cfg[port].rx_rate[prio] =
   sw->port_cfg[port].tx_rate[prio] = 0;
  }
  sw_dis_prio_rate(hw, port);
 }
}

/* Communication */

static inline void port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
}

/* Mirroring */

static inline void port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
}

static inline void port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
}

static inline void port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
}

static inline void sw_cfg_mirror_rx_tx(struct ksz_hw *hw, int set)
{
 sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, SWITCH_MIRROR_RX_TX, set);
}

static void sw_init_mirror(struct ksz_hw *hw)
{
 int port;

 for (port = 0; port < TOTAL_PORT_NUM; port++) {
  port_cfg_mirror_sniffer(hw, port, 0);
  port_cfg_mirror_rx(hw, port, 0);
  port_cfg_mirror_tx(hw, port, 0);
 }
 sw_cfg_mirror_rx_tx(hw, 0);
}

/* Priority */

static inline void port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
}

static inline void port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
}

static inline void port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
}

static inline void port_cfg_prio(struct ksz_hw *hw, int p, int set)
{
 port_cfg(hw, p,
  KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
}

/**
 * sw_dis_diffserv - disable switch DiffServ priority
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine disables the DiffServ priority function of the switch.
 */

static void sw_dis_diffserv(struct ksz_hw *hw, int port)
{
 port_cfg_diffserv(hw, port, 0);
}

/**
 * sw_dis_802_1p - disable switch 802.1p priority
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine disables the 802.1p priority function of the switch.
 */

static void sw_dis_802_1p(struct ksz_hw *hw, int port)
{
 port_cfg_802_1p(hw, port, 0);
}

/**
 * sw_cfg_replace_null_vid -
 * @hw:  The hardware instance.
 * @set: The flag to disable or enable.
 *
 */

static void sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
{
 sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
}

/**
 * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping
 * @hw:  The hardware instance.
 * @port: The port index.
 * @set: The flag to disable or enable.
 *
 * This routine enables the 802.1p priority re-mapping function of the switch.
 * That allows 802.1p priority field to be replaced with the port's default
 * tag's priority value if the ingress packet's 802.1p priority has a higher
 * priority than port's default tag's priority.
 */

static void sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
{
 port_cfg_replace_vid(hw, port, set);
}

/**
 * sw_cfg_port_based - configure switch port based priority
 * @hw:  The hardware instance.
 * @port: The port index.
 * @prio: The priority to set.
 *
 * This routine configures the port based priority of the switch.
 */

static void sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
{
 u16 data;

 if (prio > PORT_BASED_PRIORITY_BASE)
  prio = PORT_BASED_PRIORITY_BASE;

 hw->ksz_switch->port_cfg[port].port_prio = prio;

 port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
 data &= ~PORT_BASED_PRIORITY_MASK;
 data |= prio << PORT_BASED_PRIORITY_SHIFT;
 port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
}

/**
 * sw_dis_multi_queue - disable transmit multiple queues
 * @hw:  The hardware instance.
 * @port: The port index.
 *
 * This routine disables the transmit multiple queues selection of the switch
 * port.  Only single transmit queue on the port.
 */

static void sw_dis_multi_queue(struct ksz_hw *hw, int port)
{
 port_cfg_prio(hw, port, 0);
}

/**
 * sw_init_prio - initialize switch priority
 * @hw:  The hardware instance.
 *
 * This routine initializes the switch QoS priority functions.
 */

static void sw_init_prio(struct ksz_hw *hw)
{
 int port;
 int tos;
 struct ksz_switch *sw = hw->ksz_switch;

 /*
 * Init all the 802.1p tag priority value to be assigned to different
 * priority queue.
 */

 sw->p_802_1p[0] = 0;
 sw->p_802_1p[1] = 0;
 sw->p_802_1p[2] = 1;
 sw->p_802_1p[3] = 1;
 sw->p_802_1p[4] = 2;
 sw->p_802_1p[5] = 2;
 sw->p_802_1p[6] = 3;
 sw->p_802_1p[7] = 3;

 /*
 * Init all the DiffServ priority value to be assigned to priority
 * queue 0.
 */

 for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
  sw->diffserv[tos] = 0;

 /* All QoS functions disabled. */
 for (port = 0; port < TOTAL_PORT_NUM; port++) {
  sw_dis_multi_queue(hw, port);
  sw_dis_diffserv(hw, port);
  sw_dis_802_1p(hw, port);
  sw_cfg_replace_vid(hw, port, 0);

  sw->port_cfg[port].port_prio = 0;
  sw_cfg_port_based(hw, port, sw->port_cfg[port].port_prio);
 }
 sw_cfg_replace_null_vid(hw, 0);
}

/**
 * port_get_def_vid - get port default VID.
 * @hw:  The hardware instance.
 * @port: The port index.
 * @vid: Buffer to store the VID.
 *
 * This routine retrieves the default VID of the port.
 */

static void port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
{
 u32 addr;

 PORT_CTRL_ADDR(port, addr);
 addr += KS8842_PORT_CTRL_VID_OFFSET;
 *vid = readw(hw->io + addr);
}

/**
 * sw_init_vlan - initialize switch VLAN
 * @hw:  The hardware instance.
 *
 * This routine initializes the VLAN function of the switch.
 */

static void sw_init_vlan(struct ksz_hw *hw)
{
 int port;
 int entry;
 struct ksz_switch *sw = hw->ksz_switch;

 /* Read 16 VLAN entries from device's VLAN table. */
 for (entry = 0; entry < VLAN_TABLE_ENTRIES; entry++) {
  sw_r_vlan_table(hw, entry,
   &sw->vlan_table[entry].vid,
   &sw->vlan_table[entry].fid,
   &sw->vlan_table[entry].member);
 }

 for (port = 0; port < TOTAL_PORT_NUM; port++) {
  port_get_def_vid(hw, port, &sw->port_cfg[port].vid);
  sw->port_cfg[port].member = PORT_MASK;
 }
}

/**
 * sw_cfg_port_base_vlan - configure port-based VLAN membership
 * @hw:  The hardware instance.
 * @port: The port index.
 * @member: The port-based VLAN membership.
 *
 * This routine configures the port-based VLAN membership of the port.
 */

static void sw_cfg_port_base_vlan(struct ksz_hw *hw, int port, u8 member)
{
 u32 addr;
 u8 data;

 PORT_CTRL_ADDR(port, addr);
 addr += KS8842_PORT_CTRL_2_OFFSET;

 data = readb(hw->io + addr);
 data &= ~PORT_VLAN_MEMBERSHIP;
 data |= (member & PORT_MASK);
 writeb(data, hw->io + addr);

 hw->ksz_switch->port_cfg[port].member = member;
}

/**
 * sw_set_addr - configure switch MAC address
 * @hw:  The hardware instance.
 * @mac_addr: The MAC address.
 *
 * This function configures the MAC address of the switch.
 */

static void sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
{
 int i;

 for (i = 0; i < 6; i += 2) {
  writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
  writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
 }
}

/**
 * sw_set_global_ctrl - set switch global control
 * @hw:  The hardware instance.
 *
 * This routine sets the global control of the switch function.
 */

static void sw_set_global_ctrl(struct ksz_hw *hw)
{
 u16 data;

 /* Enable switch MII flow control. */
 data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
 data |= SWITCH_FLOW_CTRL;
 writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);

 data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);

 /* Enable aggressive back off algorithm in half duplex mode. */
 data |= SWITCH_AGGR_BACKOFF;

 /* Enable automatic fast aging when link changed detected. */
 data |= SWITCH_AGING_ENABLE;
 data |= SWITCH_LINK_AUTO_AGING;

 if (hw->overrides & FAST_AGING)
  data |= SWITCH_FAST_AGING;
 else
  data &= ~SWITCH_FAST_AGING;
 writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);

 data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);

 /* Enable no excessive collision drop. */
 data |= NO_EXC_COLLISION_DROP;
 writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
}

enum {
 STP_STATE_DISABLED = 0,
 STP_STATE_LISTENING,
 STP_STATE_LEARNING,
 STP_STATE_FORWARDING,
 STP_STATE_BLOCKED,
 STP_STATE_SIMPLE
};

/**
 * port_set_stp_state - configure port spanning tree state
 * @hw:  The hardware instance.
 * @port: The port index.
 * @state: The spanning tree state.
 *
 * This routine configures the spanning tree state of the port.
 */

static void port_set_stp_state(struct ksz_hw *hw, int port, int state)
{
 u16 data;

 port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data);
 switch (state) {
 case STP_STATE_DISABLED:
  data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
  data |= PORT_LEARN_DISABLE;
  break;
 case STP_STATE_LISTENING:
/*
 * No need to turn on transmit because of port direct mode.
 * Turning on receive is required if static MAC table is not setup.
 */

  data &= ~PORT_TX_ENABLE;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.52 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.