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(©_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 *)©_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, ðtool_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