18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci sis190.c: Silicon Integrated Systems SiS190 ethernet driver 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright (c) 2003 K.M. Liu <kmliu@sis.com> 58c2ecf20Sopenharmony_ci Copyright (c) 2003, 2004 Jeff Garzik <jgarzik@pobox.com> 68c2ecf20Sopenharmony_ci Copyright (c) 2003, 2004, 2005 Francois Romieu <romieu@fr.zoreil.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Based on r8169.c, tg3.c, 8139cp.c, skge.c, epic100.c and SiS 190/191 98c2ecf20Sopenharmony_ci genuine driver. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms of 128c2ecf20Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 138c2ecf20Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 148c2ecf20Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 158c2ecf20Sopenharmony_ci a complete program and may only be used when the entire operating 168c2ecf20Sopenharmony_ci system is licensed under the GPL. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci See the file COPYING in this distribution for more information. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci*/ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 278c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 288c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 298c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 308c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 318c2ecf20Sopenharmony_ci#include <linux/pci.h> 328c2ecf20Sopenharmony_ci#include <linux/mii.h> 338c2ecf20Sopenharmony_ci#include <linux/delay.h> 348c2ecf20Sopenharmony_ci#include <linux/crc32.h> 358c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci#include <asm/irq.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define PHY_MAX_ADDR 32 408c2ecf20Sopenharmony_ci#define PHY_ID_ANY 0x1f 418c2ecf20Sopenharmony_ci#define MII_REG_ANY 0x1f 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define DRV_VERSION "1.4" 448c2ecf20Sopenharmony_ci#define DRV_NAME "sis190" 458c2ecf20Sopenharmony_ci#define SIS190_DRIVER_NAME DRV_NAME " Gigabit Ethernet driver " DRV_VERSION 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define sis190_rx_skb netif_rx 488c2ecf20Sopenharmony_ci#define sis190_rx_quota(count, quota) count 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define NUM_TX_DESC 64 /* [8..1024] */ 518c2ecf20Sopenharmony_ci#define NUM_RX_DESC 64 /* [8..8192] */ 528c2ecf20Sopenharmony_ci#define TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) 538c2ecf20Sopenharmony_ci#define RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) 548c2ecf20Sopenharmony_ci#define RX_BUF_SIZE 1536 558c2ecf20Sopenharmony_ci#define RX_BUF_MASK 0xfff8 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SIS190_REGS_SIZE 0x80 588c2ecf20Sopenharmony_ci#define SIS190_TX_TIMEOUT (6*HZ) 598c2ecf20Sopenharmony_ci#define SIS190_PHY_TIMEOUT (10*HZ) 608c2ecf20Sopenharmony_ci#define SIS190_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ 618c2ecf20Sopenharmony_ci NETIF_MSG_LINK | NETIF_MSG_IFUP | \ 628c2ecf20Sopenharmony_ci NETIF_MSG_IFDOWN) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Enhanced PHY access register bit definitions */ 658c2ecf20Sopenharmony_ci#define EhnMIIread 0x0000 668c2ecf20Sopenharmony_ci#define EhnMIIwrite 0x0020 678c2ecf20Sopenharmony_ci#define EhnMIIdataShift 16 688c2ecf20Sopenharmony_ci#define EhnMIIpmdShift 6 /* 7016 only */ 698c2ecf20Sopenharmony_ci#define EhnMIIregShift 11 708c2ecf20Sopenharmony_ci#define EhnMIIreq 0x0010 718c2ecf20Sopenharmony_ci#define EhnMIInotDone 0x0010 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Write/read MMIO register */ 748c2ecf20Sopenharmony_ci#define SIS_W8(reg, val) writeb ((val), ioaddr + (reg)) 758c2ecf20Sopenharmony_ci#define SIS_W16(reg, val) writew ((val), ioaddr + (reg)) 768c2ecf20Sopenharmony_ci#define SIS_W32(reg, val) writel ((val), ioaddr + (reg)) 778c2ecf20Sopenharmony_ci#define SIS_R8(reg) readb (ioaddr + (reg)) 788c2ecf20Sopenharmony_ci#define SIS_R16(reg) readw (ioaddr + (reg)) 798c2ecf20Sopenharmony_ci#define SIS_R32(reg) readl (ioaddr + (reg)) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define SIS_PCI_COMMIT() SIS_R32(IntrControl) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cienum sis190_registers { 848c2ecf20Sopenharmony_ci TxControl = 0x00, 858c2ecf20Sopenharmony_ci TxDescStartAddr = 0x04, 868c2ecf20Sopenharmony_ci rsv0 = 0x08, // reserved 878c2ecf20Sopenharmony_ci TxSts = 0x0c, // unused (Control/Status) 888c2ecf20Sopenharmony_ci RxControl = 0x10, 898c2ecf20Sopenharmony_ci RxDescStartAddr = 0x14, 908c2ecf20Sopenharmony_ci rsv1 = 0x18, // reserved 918c2ecf20Sopenharmony_ci RxSts = 0x1c, // unused 928c2ecf20Sopenharmony_ci IntrStatus = 0x20, 938c2ecf20Sopenharmony_ci IntrMask = 0x24, 948c2ecf20Sopenharmony_ci IntrControl = 0x28, 958c2ecf20Sopenharmony_ci IntrTimer = 0x2c, // unused (Interrupt Timer) 968c2ecf20Sopenharmony_ci PMControl = 0x30, // unused (Power Mgmt Control/Status) 978c2ecf20Sopenharmony_ci rsv2 = 0x34, // reserved 988c2ecf20Sopenharmony_ci ROMControl = 0x38, 998c2ecf20Sopenharmony_ci ROMInterface = 0x3c, 1008c2ecf20Sopenharmony_ci StationControl = 0x40, 1018c2ecf20Sopenharmony_ci GMIIControl = 0x44, 1028c2ecf20Sopenharmony_ci GIoCR = 0x48, // unused (GMAC IO Compensation) 1038c2ecf20Sopenharmony_ci GIoCtrl = 0x4c, // unused (GMAC IO Control) 1048c2ecf20Sopenharmony_ci TxMacControl = 0x50, 1058c2ecf20Sopenharmony_ci TxLimit = 0x54, // unused (Tx MAC Timer/TryLimit) 1068c2ecf20Sopenharmony_ci RGDelay = 0x58, // unused (RGMII Tx Internal Delay) 1078c2ecf20Sopenharmony_ci rsv3 = 0x5c, // reserved 1088c2ecf20Sopenharmony_ci RxMacControl = 0x60, 1098c2ecf20Sopenharmony_ci RxMacAddr = 0x62, 1108c2ecf20Sopenharmony_ci RxHashTable = 0x68, 1118c2ecf20Sopenharmony_ci // Undocumented = 0x6c, 1128c2ecf20Sopenharmony_ci RxWolCtrl = 0x70, 1138c2ecf20Sopenharmony_ci RxWolData = 0x74, // unused (Rx WOL Data Access) 1148c2ecf20Sopenharmony_ci RxMPSControl = 0x78, // unused (Rx MPS Control) 1158c2ecf20Sopenharmony_ci rsv4 = 0x7c, // reserved 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cienum sis190_register_content { 1198c2ecf20Sopenharmony_ci /* IntrStatus */ 1208c2ecf20Sopenharmony_ci SoftInt = 0x40000000, // unused 1218c2ecf20Sopenharmony_ci Timeup = 0x20000000, // unused 1228c2ecf20Sopenharmony_ci PauseFrame = 0x00080000, // unused 1238c2ecf20Sopenharmony_ci MagicPacket = 0x00040000, // unused 1248c2ecf20Sopenharmony_ci WakeupFrame = 0x00020000, // unused 1258c2ecf20Sopenharmony_ci LinkChange = 0x00010000, 1268c2ecf20Sopenharmony_ci RxQEmpty = 0x00000080, 1278c2ecf20Sopenharmony_ci RxQInt = 0x00000040, 1288c2ecf20Sopenharmony_ci TxQ1Empty = 0x00000020, // unused 1298c2ecf20Sopenharmony_ci TxQ1Int = 0x00000010, 1308c2ecf20Sopenharmony_ci TxQ0Empty = 0x00000008, // unused 1318c2ecf20Sopenharmony_ci TxQ0Int = 0x00000004, 1328c2ecf20Sopenharmony_ci RxHalt = 0x00000002, 1338c2ecf20Sopenharmony_ci TxHalt = 0x00000001, 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* {Rx/Tx}CmdBits */ 1368c2ecf20Sopenharmony_ci CmdReset = 0x10, 1378c2ecf20Sopenharmony_ci CmdRxEnb = 0x08, // unused 1388c2ecf20Sopenharmony_ci CmdTxEnb = 0x01, 1398c2ecf20Sopenharmony_ci RxBufEmpty = 0x01, // unused 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Cfg9346Bits */ 1428c2ecf20Sopenharmony_ci Cfg9346_Lock = 0x00, // unused 1438c2ecf20Sopenharmony_ci Cfg9346_Unlock = 0xc0, // unused 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* RxMacControl */ 1468c2ecf20Sopenharmony_ci AcceptErr = 0x20, // unused 1478c2ecf20Sopenharmony_ci AcceptRunt = 0x10, // unused 1488c2ecf20Sopenharmony_ci AcceptBroadcast = 0x0800, 1498c2ecf20Sopenharmony_ci AcceptMulticast = 0x0400, 1508c2ecf20Sopenharmony_ci AcceptMyPhys = 0x0200, 1518c2ecf20Sopenharmony_ci AcceptAllPhys = 0x0100, 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* RxConfigBits */ 1548c2ecf20Sopenharmony_ci RxCfgFIFOShift = 13, 1558c2ecf20Sopenharmony_ci RxCfgDMAShift = 8, // 0x1a in RxControl ? 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* TxConfigBits */ 1588c2ecf20Sopenharmony_ci TxInterFrameGapShift = 24, 1598c2ecf20Sopenharmony_ci TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci LinkStatus = 0x02, // unused 1628c2ecf20Sopenharmony_ci FullDup = 0x01, // unused 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* TBICSRBit */ 1658c2ecf20Sopenharmony_ci TBILinkOK = 0x02000000, // unused 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistruct TxDesc { 1698c2ecf20Sopenharmony_ci __le32 PSize; 1708c2ecf20Sopenharmony_ci __le32 status; 1718c2ecf20Sopenharmony_ci __le32 addr; 1728c2ecf20Sopenharmony_ci __le32 size; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistruct RxDesc { 1768c2ecf20Sopenharmony_ci __le32 PSize; 1778c2ecf20Sopenharmony_ci __le32 status; 1788c2ecf20Sopenharmony_ci __le32 addr; 1798c2ecf20Sopenharmony_ci __le32 size; 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cienum _DescStatusBit { 1838c2ecf20Sopenharmony_ci /* _Desc.status */ 1848c2ecf20Sopenharmony_ci OWNbit = 0x80000000, // RXOWN/TXOWN 1858c2ecf20Sopenharmony_ci INTbit = 0x40000000, // RXINT/TXINT 1868c2ecf20Sopenharmony_ci CRCbit = 0x00020000, // CRCOFF/CRCEN 1878c2ecf20Sopenharmony_ci PADbit = 0x00010000, // PREADD/PADEN 1888c2ecf20Sopenharmony_ci /* _Desc.size */ 1898c2ecf20Sopenharmony_ci RingEnd = 0x80000000, 1908c2ecf20Sopenharmony_ci /* TxDesc.status */ 1918c2ecf20Sopenharmony_ci LSEN = 0x08000000, // TSO ? -- FR 1928c2ecf20Sopenharmony_ci IPCS = 0x04000000, 1938c2ecf20Sopenharmony_ci TCPCS = 0x02000000, 1948c2ecf20Sopenharmony_ci UDPCS = 0x01000000, 1958c2ecf20Sopenharmony_ci BSTEN = 0x00800000, 1968c2ecf20Sopenharmony_ci EXTEN = 0x00400000, 1978c2ecf20Sopenharmony_ci DEFEN = 0x00200000, 1988c2ecf20Sopenharmony_ci BKFEN = 0x00100000, 1998c2ecf20Sopenharmony_ci CRSEN = 0x00080000, 2008c2ecf20Sopenharmony_ci COLEN = 0x00040000, 2018c2ecf20Sopenharmony_ci THOL3 = 0x30000000, 2028c2ecf20Sopenharmony_ci THOL2 = 0x20000000, 2038c2ecf20Sopenharmony_ci THOL1 = 0x10000000, 2048c2ecf20Sopenharmony_ci THOL0 = 0x00000000, 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci WND = 0x00080000, 2078c2ecf20Sopenharmony_ci TABRT = 0x00040000, 2088c2ecf20Sopenharmony_ci FIFO = 0x00020000, 2098c2ecf20Sopenharmony_ci LINK = 0x00010000, 2108c2ecf20Sopenharmony_ci ColCountMask = 0x0000ffff, 2118c2ecf20Sopenharmony_ci /* RxDesc.status */ 2128c2ecf20Sopenharmony_ci IPON = 0x20000000, 2138c2ecf20Sopenharmony_ci TCPON = 0x10000000, 2148c2ecf20Sopenharmony_ci UDPON = 0x08000000, 2158c2ecf20Sopenharmony_ci Wakup = 0x00400000, 2168c2ecf20Sopenharmony_ci Magic = 0x00200000, 2178c2ecf20Sopenharmony_ci Pause = 0x00100000, 2188c2ecf20Sopenharmony_ci DEFbit = 0x00200000, 2198c2ecf20Sopenharmony_ci BCAST = 0x000c0000, 2208c2ecf20Sopenharmony_ci MCAST = 0x00080000, 2218c2ecf20Sopenharmony_ci UCAST = 0x00040000, 2228c2ecf20Sopenharmony_ci /* RxDesc.PSize */ 2238c2ecf20Sopenharmony_ci TAGON = 0x80000000, 2248c2ecf20Sopenharmony_ci RxDescCountMask = 0x7f000000, // multi-desc pkt when > 1 ? -- FR 2258c2ecf20Sopenharmony_ci ABORT = 0x00800000, 2268c2ecf20Sopenharmony_ci SHORT = 0x00400000, 2278c2ecf20Sopenharmony_ci LIMIT = 0x00200000, 2288c2ecf20Sopenharmony_ci MIIER = 0x00100000, 2298c2ecf20Sopenharmony_ci OVRUN = 0x00080000, 2308c2ecf20Sopenharmony_ci NIBON = 0x00040000, 2318c2ecf20Sopenharmony_ci COLON = 0x00020000, 2328c2ecf20Sopenharmony_ci CRCOK = 0x00010000, 2338c2ecf20Sopenharmony_ci RxSizeMask = 0x0000ffff 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * The asic could apparently do vlan, TSO, jumbo (sis191 only) and 2368c2ecf20Sopenharmony_ci * provide two (unused with Linux) Tx queues. No publicly 2378c2ecf20Sopenharmony_ci * available documentation alas. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cienum sis190_eeprom_access_register_bits { 2428c2ecf20Sopenharmony_ci EECS = 0x00000001, // unused 2438c2ecf20Sopenharmony_ci EECLK = 0x00000002, // unused 2448c2ecf20Sopenharmony_ci EEDO = 0x00000008, // unused 2458c2ecf20Sopenharmony_ci EEDI = 0x00000004, // unused 2468c2ecf20Sopenharmony_ci EEREQ = 0x00000080, 2478c2ecf20Sopenharmony_ci EEROP = 0x00000200, 2488c2ecf20Sopenharmony_ci EEWOP = 0x00000100 // unused 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* EEPROM Addresses */ 2528c2ecf20Sopenharmony_cienum sis190_eeprom_address { 2538c2ecf20Sopenharmony_ci EEPROMSignature = 0x00, 2548c2ecf20Sopenharmony_ci EEPROMCLK = 0x01, // unused 2558c2ecf20Sopenharmony_ci EEPROMInfo = 0x02, 2568c2ecf20Sopenharmony_ci EEPROMMACAddr = 0x03 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cienum sis190_feature { 2608c2ecf20Sopenharmony_ci F_HAS_RGMII = 1, 2618c2ecf20Sopenharmony_ci F_PHY_88E1111 = 2, 2628c2ecf20Sopenharmony_ci F_PHY_BCM5461 = 4 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistruct sis190_private { 2668c2ecf20Sopenharmony_ci void __iomem *mmio_addr; 2678c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 2688c2ecf20Sopenharmony_ci struct net_device *dev; 2698c2ecf20Sopenharmony_ci spinlock_t lock; 2708c2ecf20Sopenharmony_ci u32 rx_buf_sz; 2718c2ecf20Sopenharmony_ci u32 cur_rx; 2728c2ecf20Sopenharmony_ci u32 cur_tx; 2738c2ecf20Sopenharmony_ci u32 dirty_rx; 2748c2ecf20Sopenharmony_ci u32 dirty_tx; 2758c2ecf20Sopenharmony_ci dma_addr_t rx_dma; 2768c2ecf20Sopenharmony_ci dma_addr_t tx_dma; 2778c2ecf20Sopenharmony_ci struct RxDesc *RxDescRing; 2788c2ecf20Sopenharmony_ci struct TxDesc *TxDescRing; 2798c2ecf20Sopenharmony_ci struct sk_buff *Rx_skbuff[NUM_RX_DESC]; 2808c2ecf20Sopenharmony_ci struct sk_buff *Tx_skbuff[NUM_TX_DESC]; 2818c2ecf20Sopenharmony_ci struct work_struct phy_task; 2828c2ecf20Sopenharmony_ci struct timer_list timer; 2838c2ecf20Sopenharmony_ci u32 msg_enable; 2848c2ecf20Sopenharmony_ci struct mii_if_info mii_if; 2858c2ecf20Sopenharmony_ci struct list_head first_phy; 2868c2ecf20Sopenharmony_ci u32 features; 2878c2ecf20Sopenharmony_ci u32 negotiated_lpa; 2888c2ecf20Sopenharmony_ci enum { 2898c2ecf20Sopenharmony_ci LNK_OFF, 2908c2ecf20Sopenharmony_ci LNK_ON, 2918c2ecf20Sopenharmony_ci LNK_AUTONEG, 2928c2ecf20Sopenharmony_ci } link_status; 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistruct sis190_phy { 2968c2ecf20Sopenharmony_ci struct list_head list; 2978c2ecf20Sopenharmony_ci int phy_id; 2988c2ecf20Sopenharmony_ci u16 id[2]; 2998c2ecf20Sopenharmony_ci u16 status; 3008c2ecf20Sopenharmony_ci u8 type; 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cienum sis190_phy_type { 3048c2ecf20Sopenharmony_ci UNKNOWN = 0x00, 3058c2ecf20Sopenharmony_ci HOME = 0x01, 3068c2ecf20Sopenharmony_ci LAN = 0x02, 3078c2ecf20Sopenharmony_ci MIX = 0x03 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic struct mii_chip_info { 3118c2ecf20Sopenharmony_ci const char *name; 3128c2ecf20Sopenharmony_ci u16 id[2]; 3138c2ecf20Sopenharmony_ci unsigned int type; 3148c2ecf20Sopenharmony_ci u32 feature; 3158c2ecf20Sopenharmony_ci} mii_chip_table[] = { 3168c2ecf20Sopenharmony_ci { "Atheros PHY", { 0x004d, 0xd010 }, LAN, 0 }, 3178c2ecf20Sopenharmony_ci { "Atheros PHY AR8012", { 0x004d, 0xd020 }, LAN, 0 }, 3188c2ecf20Sopenharmony_ci { "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN, F_PHY_BCM5461 }, 3198c2ecf20Sopenharmony_ci { "Broadcom PHY AC131", { 0x0143, 0xbc70 }, LAN, 0 }, 3208c2ecf20Sopenharmony_ci { "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN, 0 }, 3218c2ecf20Sopenharmony_ci { "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN, F_PHY_88E1111 }, 3228c2ecf20Sopenharmony_ci { "Realtek PHY RTL8201", { 0x0000, 0x8200 }, LAN, 0 }, 3238c2ecf20Sopenharmony_ci { NULL, } 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct { 3278c2ecf20Sopenharmony_ci const char *name; 3288c2ecf20Sopenharmony_ci} sis_chip_info[] = { 3298c2ecf20Sopenharmony_ci { "SiS 190 PCI Fast Ethernet adapter" }, 3308c2ecf20Sopenharmony_ci { "SiS 191 PCI Gigabit Ethernet adapter" }, 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic const struct pci_device_id sis190_pci_tbl[] = { 3348c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0190), 0, 0, 0 }, 3358c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0191), 0, 0, 1 }, 3368c2ecf20Sopenharmony_ci { 0, }, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sis190_pci_tbl); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int rx_copybreak = 200; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic struct { 3448c2ecf20Sopenharmony_ci u32 msg_enable; 3458c2ecf20Sopenharmony_ci} debug = { -1 }; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SiS sis190/191 Gigabit Ethernet driver"); 3488c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0); 3498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); 3508c2ecf20Sopenharmony_cimodule_param_named(debug, debug.msg_enable, int, 0); 3518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)"); 3528c2ecf20Sopenharmony_ciMODULE_AUTHOR("K.M. Liu <kmliu@sis.com>, Ueimor <romieu@fr.zoreil.com>"); 3538c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 3548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic const u32 sis190_intr_mask = 3578c2ecf20Sopenharmony_ci RxQEmpty | RxQInt | TxQ1Int | TxQ0Int | RxHalt | TxHalt | LinkChange; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * Maximum number of multicast addresses to filter (vs. Rx-all-multicast). 3618c2ecf20Sopenharmony_ci * The chips use a 64 element hash table based on the Ethernet CRC. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_cistatic const int multicast_filter_limit = 32; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void __mdio_cmd(void __iomem *ioaddr, u32 ctl) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci unsigned int i; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci SIS_W32(GMIIControl, ctl); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci msleep(1); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 3748c2ecf20Sopenharmony_ci if (!(SIS_R32(GMIIControl) & EhnMIInotDone)) 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci msleep(1); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (i > 99) 3808c2ecf20Sopenharmony_ci pr_err("PHY command failed !\n"); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void mdio_write(void __iomem *ioaddr, int phy_id, int reg, int val) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIwrite | 3868c2ecf20Sopenharmony_ci (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift) | 3878c2ecf20Sopenharmony_ci (((u32) val) << EhnMIIdataShift)); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int mdio_read(void __iomem *ioaddr, int phy_id, int reg) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci __mdio_cmd(ioaddr, EhnMIIreq | EhnMIIread | 3938c2ecf20Sopenharmony_ci (((u32) reg) << EhnMIIregShift) | (phy_id << EhnMIIpmdShift)); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return (u16) (SIS_R32(GMIIControl) >> EhnMIIdataShift); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void __mdio_write(struct net_device *dev, int phy_id, int reg, int val) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci mdio_write(tp->mmio_addr, phy_id, reg, val); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int __mdio_read(struct net_device *dev, int phy_id, int reg) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return mdio_read(tp->mmio_addr, phy_id, reg); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic u16 mdio_read_latched(void __iomem *ioaddr, int phy_id, int reg) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci mdio_read(ioaddr, phy_id, reg); 4158c2ecf20Sopenharmony_ci return mdio_read(ioaddr, phy_id, reg); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic u16 sis190_read_eeprom(void __iomem *ioaddr, u32 reg) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci u16 data = 0xffff; 4218c2ecf20Sopenharmony_ci unsigned int i; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!(SIS_R32(ROMControl) & 0x0002)) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci SIS_W32(ROMInterface, EEREQ | EEROP | (reg << 10)); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci for (i = 0; i < 200; i++) { 4298c2ecf20Sopenharmony_ci if (!(SIS_R32(ROMInterface) & EEREQ)) { 4308c2ecf20Sopenharmony_ci data = (SIS_R32(ROMInterface) & 0xffff0000) >> 16; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci msleep(1); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return data; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void sis190_irq_mask_and_ack(void __iomem *ioaddr) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci SIS_W32(IntrMask, 0x00); 4428c2ecf20Sopenharmony_ci SIS_W32(IntrStatus, 0xffffffff); 4438c2ecf20Sopenharmony_ci SIS_PCI_COMMIT(); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void sis190_asic_down(void __iomem *ioaddr) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci /* Stop the chip's Tx and Rx DMA processes. */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci SIS_W32(TxControl, 0x1a00); 4518c2ecf20Sopenharmony_ci SIS_W32(RxControl, 0x1a00); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci sis190_irq_mask_and_ack(ioaddr); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void sis190_mark_as_last_descriptor(struct RxDesc *desc) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci desc->size |= cpu_to_le32(RingEnd); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic inline void sis190_give_to_asic(struct RxDesc *desc, u32 rx_buf_sz) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci u32 eor = le32_to_cpu(desc->size) & RingEnd; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci desc->PSize = 0x0; 4668c2ecf20Sopenharmony_ci desc->size = cpu_to_le32((rx_buf_sz & RX_BUF_MASK) | eor); 4678c2ecf20Sopenharmony_ci wmb(); 4688c2ecf20Sopenharmony_ci desc->status = cpu_to_le32(OWNbit | INTbit); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic inline void sis190_map_to_asic(struct RxDesc *desc, dma_addr_t mapping, 4728c2ecf20Sopenharmony_ci u32 rx_buf_sz) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci desc->addr = cpu_to_le32(mapping); 4758c2ecf20Sopenharmony_ci sis190_give_to_asic(desc, rx_buf_sz); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic inline void sis190_make_unusable_by_asic(struct RxDesc *desc) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci desc->PSize = 0x0; 4818c2ecf20Sopenharmony_ci desc->addr = cpu_to_le32(0xdeadbeef); 4828c2ecf20Sopenharmony_ci desc->size &= cpu_to_le32(RingEnd); 4838c2ecf20Sopenharmony_ci wmb(); 4848c2ecf20Sopenharmony_ci desc->status = 0x0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic struct sk_buff *sis190_alloc_rx_skb(struct sis190_private *tp, 4888c2ecf20Sopenharmony_ci struct RxDesc *desc) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci u32 rx_buf_sz = tp->rx_buf_sz; 4918c2ecf20Sopenharmony_ci struct sk_buff *skb; 4928c2ecf20Sopenharmony_ci dma_addr_t mapping; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(tp->dev, rx_buf_sz); 4958c2ecf20Sopenharmony_ci if (unlikely(!skb)) 4968c2ecf20Sopenharmony_ci goto skb_alloc_failed; 4978c2ecf20Sopenharmony_ci mapping = dma_map_single(&tp->pci_dev->dev, skb->data, tp->rx_buf_sz, 4988c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4998c2ecf20Sopenharmony_ci if (dma_mapping_error(&tp->pci_dev->dev, mapping)) 5008c2ecf20Sopenharmony_ci goto out; 5018c2ecf20Sopenharmony_ci sis190_map_to_asic(desc, mapping, rx_buf_sz); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return skb; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciout: 5068c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5078c2ecf20Sopenharmony_ciskb_alloc_failed: 5088c2ecf20Sopenharmony_ci sis190_make_unusable_by_asic(desc); 5098c2ecf20Sopenharmony_ci return NULL; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic u32 sis190_rx_fill(struct sis190_private *tp, struct net_device *dev, 5138c2ecf20Sopenharmony_ci u32 start, u32 end) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci u32 cur; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci for (cur = start; cur < end; cur++) { 5188c2ecf20Sopenharmony_ci unsigned int i = cur % NUM_RX_DESC; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (tp->Rx_skbuff[i]) 5218c2ecf20Sopenharmony_ci continue; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci tp->Rx_skbuff[i] = sis190_alloc_rx_skb(tp, tp->RxDescRing + i); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (!tp->Rx_skbuff[i]) 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci return cur - start; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic bool sis190_try_rx_copy(struct sis190_private *tp, 5328c2ecf20Sopenharmony_ci struct sk_buff **sk_buff, int pkt_size, 5338c2ecf20Sopenharmony_ci dma_addr_t addr) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct sk_buff *skb; 5368c2ecf20Sopenharmony_ci bool done = false; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (pkt_size >= rx_copybreak) 5398c2ecf20Sopenharmony_ci goto out; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size); 5428c2ecf20Sopenharmony_ci if (!skb) 5438c2ecf20Sopenharmony_ci goto out; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&tp->pci_dev->dev, addr, tp->rx_buf_sz, 5468c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5478c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size); 5488c2ecf20Sopenharmony_ci *sk_buff = skb; 5498c2ecf20Sopenharmony_ci done = true; 5508c2ecf20Sopenharmony_ciout: 5518c2ecf20Sopenharmony_ci return done; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic inline int sis190_rx_pkt_err(u32 status, struct net_device_stats *stats) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci#define ErrMask (OVRUN | SHORT | LIMIT | MIIER | NIBON | COLON | ABORT) 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if ((status & CRCOK) && !(status & ErrMask)) 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (!(status & CRCOK)) 5628c2ecf20Sopenharmony_ci stats->rx_crc_errors++; 5638c2ecf20Sopenharmony_ci else if (status & OVRUN) 5648c2ecf20Sopenharmony_ci stats->rx_over_errors++; 5658c2ecf20Sopenharmony_ci else if (status & (SHORT | LIMIT)) 5668c2ecf20Sopenharmony_ci stats->rx_length_errors++; 5678c2ecf20Sopenharmony_ci else if (status & (MIIER | NIBON | COLON)) 5688c2ecf20Sopenharmony_ci stats->rx_frame_errors++; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci stats->rx_errors++; 5718c2ecf20Sopenharmony_ci return -1; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int sis190_rx_interrupt(struct net_device *dev, 5758c2ecf20Sopenharmony_ci struct sis190_private *tp, void __iomem *ioaddr) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 5788c2ecf20Sopenharmony_ci u32 rx_left, cur_rx = tp->cur_rx; 5798c2ecf20Sopenharmony_ci u32 delta, count; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; 5828c2ecf20Sopenharmony_ci rx_left = sis190_rx_quota(rx_left, (u32) dev->quota); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci for (; rx_left > 0; rx_left--, cur_rx++) { 5858c2ecf20Sopenharmony_ci unsigned int entry = cur_rx % NUM_RX_DESC; 5868c2ecf20Sopenharmony_ci struct RxDesc *desc = tp->RxDescRing + entry; 5878c2ecf20Sopenharmony_ci u32 status; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (le32_to_cpu(desc->status) & OWNbit) 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci status = le32_to_cpu(desc->PSize); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci //netif_info(tp, intr, dev, "Rx PSize = %08x\n", status); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (sis190_rx_pkt_err(status, stats) < 0) 5978c2ecf20Sopenharmony_ci sis190_give_to_asic(desc, tp->rx_buf_sz); 5988c2ecf20Sopenharmony_ci else { 5998c2ecf20Sopenharmony_ci struct sk_buff *skb = tp->Rx_skbuff[entry]; 6008c2ecf20Sopenharmony_ci dma_addr_t addr = le32_to_cpu(desc->addr); 6018c2ecf20Sopenharmony_ci int pkt_size = (status & RxSizeMask) - 4; 6028c2ecf20Sopenharmony_ci struct pci_dev *pdev = tp->pci_dev; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (unlikely(pkt_size > tp->rx_buf_sz)) { 6058c2ecf20Sopenharmony_ci netif_info(tp, intr, dev, 6068c2ecf20Sopenharmony_ci "(frag) status = %08x\n", status); 6078c2ecf20Sopenharmony_ci stats->rx_dropped++; 6088c2ecf20Sopenharmony_ci stats->rx_length_errors++; 6098c2ecf20Sopenharmony_ci sis190_give_to_asic(desc, tp->rx_buf_sz); 6108c2ecf20Sopenharmony_ci continue; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (sis190_try_rx_copy(tp, &skb, pkt_size, addr)) { 6158c2ecf20Sopenharmony_ci dma_sync_single_for_device(&pdev->dev, addr, 6168c2ecf20Sopenharmony_ci tp->rx_buf_sz, 6178c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6188c2ecf20Sopenharmony_ci sis190_give_to_asic(desc, tp->rx_buf_sz); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, addr, 6218c2ecf20Sopenharmony_ci tp->rx_buf_sz, 6228c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6238c2ecf20Sopenharmony_ci tp->Rx_skbuff[entry] = NULL; 6248c2ecf20Sopenharmony_ci sis190_make_unusable_by_asic(desc); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci skb_put(skb, pkt_size); 6288c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci sis190_rx_skb(skb); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci stats->rx_packets++; 6338c2ecf20Sopenharmony_ci stats->rx_bytes += pkt_size; 6348c2ecf20Sopenharmony_ci if ((status & BCAST) == MCAST) 6358c2ecf20Sopenharmony_ci stats->multicast++; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci count = cur_rx - tp->cur_rx; 6398c2ecf20Sopenharmony_ci tp->cur_rx = cur_rx; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci delta = sis190_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); 6428c2ecf20Sopenharmony_ci if (!delta && count) 6438c2ecf20Sopenharmony_ci netif_info(tp, intr, dev, "no Rx buffer allocated\n"); 6448c2ecf20Sopenharmony_ci tp->dirty_rx += delta; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if ((tp->dirty_rx + NUM_RX_DESC) == tp->cur_rx) 6478c2ecf20Sopenharmony_ci netif_emerg(tp, intr, dev, "Rx buffers exhausted\n"); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return count; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void sis190_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff *skb, 6538c2ecf20Sopenharmony_ci struct TxDesc *desc) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci unsigned int len; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, le32_to_cpu(desc->addr), len, 6608c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci memset(desc, 0x00, sizeof(*desc)); 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic inline int sis190_tx_pkt_err(u32 status, struct net_device_stats *stats) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci#define TxErrMask (WND | TABRT | FIFO | LINK) 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (!unlikely(status & TxErrMask)) 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (status & WND) 6738c2ecf20Sopenharmony_ci stats->tx_window_errors++; 6748c2ecf20Sopenharmony_ci if (status & TABRT) 6758c2ecf20Sopenharmony_ci stats->tx_aborted_errors++; 6768c2ecf20Sopenharmony_ci if (status & FIFO) 6778c2ecf20Sopenharmony_ci stats->tx_fifo_errors++; 6788c2ecf20Sopenharmony_ci if (status & LINK) 6798c2ecf20Sopenharmony_ci stats->tx_carrier_errors++; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci stats->tx_errors++; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return -1; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic void sis190_tx_interrupt(struct net_device *dev, 6878c2ecf20Sopenharmony_ci struct sis190_private *tp, void __iomem *ioaddr) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 6908c2ecf20Sopenharmony_ci u32 pending, dirty_tx = tp->dirty_tx; 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * It would not be needed if queueing was allowed to be enabled 6938c2ecf20Sopenharmony_ci * again too early (hint: think preempt and unclocked smp systems). 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci unsigned int queue_stopped; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci smp_rmb(); 6988c2ecf20Sopenharmony_ci pending = tp->cur_tx - dirty_tx; 6998c2ecf20Sopenharmony_ci queue_stopped = (pending == NUM_TX_DESC); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci for (; pending; pending--, dirty_tx++) { 7028c2ecf20Sopenharmony_ci unsigned int entry = dirty_tx % NUM_TX_DESC; 7038c2ecf20Sopenharmony_ci struct TxDesc *txd = tp->TxDescRing + entry; 7048c2ecf20Sopenharmony_ci u32 status = le32_to_cpu(txd->status); 7058c2ecf20Sopenharmony_ci struct sk_buff *skb; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (status & OWNbit) 7088c2ecf20Sopenharmony_ci break; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci skb = tp->Tx_skbuff[entry]; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (likely(sis190_tx_pkt_err(status, stats) == 0)) { 7138c2ecf20Sopenharmony_ci stats->tx_packets++; 7148c2ecf20Sopenharmony_ci stats->tx_bytes += skb->len; 7158c2ecf20Sopenharmony_ci stats->collisions += ((status & ColCountMask) - 1); 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci sis190_unmap_tx_skb(tp->pci_dev, skb, txd); 7198c2ecf20Sopenharmony_ci tp->Tx_skbuff[entry] = NULL; 7208c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (tp->dirty_tx != dirty_tx) { 7248c2ecf20Sopenharmony_ci tp->dirty_tx = dirty_tx; 7258c2ecf20Sopenharmony_ci smp_wmb(); 7268c2ecf20Sopenharmony_ci if (queue_stopped) 7278c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci/* 7328c2ecf20Sopenharmony_ci * The interrupt handler does all of the Rx thread work and cleans up after 7338c2ecf20Sopenharmony_ci * the Tx thread. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_cistatic irqreturn_t sis190_irq(int irq, void *__dev) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct net_device *dev = __dev; 7388c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 7398c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 7408c2ecf20Sopenharmony_ci unsigned int handled = 0; 7418c2ecf20Sopenharmony_ci u32 status; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci status = SIS_R32(IntrStatus); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if ((status == 0xffffffff) || !status) 7468c2ecf20Sopenharmony_ci goto out; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci handled = 1; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (unlikely(!netif_running(dev))) { 7518c2ecf20Sopenharmony_ci sis190_asic_down(ioaddr); 7528c2ecf20Sopenharmony_ci goto out; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci SIS_W32(IntrStatus, status); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci// netif_info(tp, intr, dev, "status = %08x\n", status); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (status & LinkChange) { 7608c2ecf20Sopenharmony_ci netif_info(tp, intr, dev, "link change\n"); 7618c2ecf20Sopenharmony_ci del_timer(&tp->timer); 7628c2ecf20Sopenharmony_ci schedule_work(&tp->phy_task); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (status & RxQInt) 7668c2ecf20Sopenharmony_ci sis190_rx_interrupt(dev, tp, ioaddr); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (status & TxQ0Int) 7698c2ecf20Sopenharmony_ci sis190_tx_interrupt(dev, tp, ioaddr); 7708c2ecf20Sopenharmony_ciout: 7718c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 7758c2ecf20Sopenharmony_cistatic void sis190_netpoll(struct net_device *dev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 7788c2ecf20Sopenharmony_ci const int irq = tp->pci_dev->irq; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci disable_irq(irq); 7818c2ecf20Sopenharmony_ci sis190_irq(irq, dev); 7828c2ecf20Sopenharmony_ci enable_irq(irq); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci#endif 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic void sis190_free_rx_skb(struct sis190_private *tp, 7878c2ecf20Sopenharmony_ci struct sk_buff **sk_buff, struct RxDesc *desc) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct pci_dev *pdev = tp->pci_dev; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, le32_to_cpu(desc->addr), tp->rx_buf_sz, 7928c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7938c2ecf20Sopenharmony_ci dev_kfree_skb(*sk_buff); 7948c2ecf20Sopenharmony_ci *sk_buff = NULL; 7958c2ecf20Sopenharmony_ci sis190_make_unusable_by_asic(desc); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic void sis190_rx_clear(struct sis190_private *tp) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci unsigned int i; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RX_DESC; i++) { 8038c2ecf20Sopenharmony_ci if (!tp->Rx_skbuff[i]) 8048c2ecf20Sopenharmony_ci continue; 8058c2ecf20Sopenharmony_ci sis190_free_rx_skb(tp, tp->Rx_skbuff + i, tp->RxDescRing + i); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistatic void sis190_init_ring_indexes(struct sis190_private *tp) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int sis190_init_ring(struct net_device *dev) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci sis190_init_ring_indexes(tp); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *)); 8218c2ecf20Sopenharmony_ci memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (sis190_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) 8248c2ecf20Sopenharmony_ci goto err_rx_clear; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci sis190_mark_as_last_descriptor(tp->RxDescRing + NUM_RX_DESC - 1); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return 0; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cierr_rx_clear: 8318c2ecf20Sopenharmony_ci sis190_rx_clear(tp); 8328c2ecf20Sopenharmony_ci return -ENOMEM; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic void sis190_set_rx_mode(struct net_device *dev) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 8388c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 8398c2ecf20Sopenharmony_ci unsigned long flags; 8408c2ecf20Sopenharmony_ci u32 mc_filter[2]; /* Multicast hash filter */ 8418c2ecf20Sopenharmony_ci u16 rx_mode; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 8448c2ecf20Sopenharmony_ci rx_mode = 8458c2ecf20Sopenharmony_ci AcceptBroadcast | AcceptMulticast | AcceptMyPhys | 8468c2ecf20Sopenharmony_ci AcceptAllPhys; 8478c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 8488c2ecf20Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 8498c2ecf20Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 8508c2ecf20Sopenharmony_ci /* Too many to filter perfectly -- accept all multicasts. */ 8518c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; 8528c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 8538c2ecf20Sopenharmony_ci } else { 8548c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMyPhys; 8578c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 8588c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 8598c2ecf20Sopenharmony_ci int bit_nr = 8608c2ecf20Sopenharmony_ci ether_crc(ETH_ALEN, ha->addr) & 0x3f; 8618c2ecf20Sopenharmony_ci mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); 8628c2ecf20Sopenharmony_ci rx_mode |= AcceptMulticast; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci spin_lock_irqsave(&tp->lock, flags); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci SIS_W16(RxMacControl, rx_mode | 0x2); 8698c2ecf20Sopenharmony_ci SIS_W32(RxHashTable, mc_filter[0]); 8708c2ecf20Sopenharmony_ci SIS_W32(RxHashTable + 4, mc_filter[1]); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tp->lock, flags); 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic void sis190_soft_reset(void __iomem *ioaddr) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci SIS_W32(IntrControl, 0x8000); 8788c2ecf20Sopenharmony_ci SIS_PCI_COMMIT(); 8798c2ecf20Sopenharmony_ci SIS_W32(IntrControl, 0x0); 8808c2ecf20Sopenharmony_ci sis190_asic_down(ioaddr); 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic void sis190_hw_start(struct net_device *dev) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 8868c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci sis190_soft_reset(ioaddr); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci SIS_W32(TxDescStartAddr, tp->tx_dma); 8918c2ecf20Sopenharmony_ci SIS_W32(RxDescStartAddr, tp->rx_dma); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci SIS_W32(IntrStatus, 0xffffffff); 8948c2ecf20Sopenharmony_ci SIS_W32(IntrMask, 0x0); 8958c2ecf20Sopenharmony_ci SIS_W32(GMIIControl, 0x0); 8968c2ecf20Sopenharmony_ci SIS_W32(TxMacControl, 0x60); 8978c2ecf20Sopenharmony_ci SIS_W16(RxMacControl, 0x02); 8988c2ecf20Sopenharmony_ci SIS_W32(RxHashTable, 0x0); 8998c2ecf20Sopenharmony_ci SIS_W32(0x6c, 0x0); 9008c2ecf20Sopenharmony_ci SIS_W32(RxWolCtrl, 0x0); 9018c2ecf20Sopenharmony_ci SIS_W32(RxWolData, 0x0); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci SIS_PCI_COMMIT(); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci sis190_set_rx_mode(dev); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* Enable all known interrupts by setting the interrupt mask. */ 9088c2ecf20Sopenharmony_ci SIS_W32(IntrMask, sis190_intr_mask); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci SIS_W32(TxControl, 0x1a00 | CmdTxEnb); 9118c2ecf20Sopenharmony_ci SIS_W32(RxControl, 0x1a1d); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci netif_start_queue(dev); 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic void sis190_phy_task(struct work_struct *work) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci struct sis190_private *tp = 9198c2ecf20Sopenharmony_ci container_of(work, struct sis190_private, phy_task); 9208c2ecf20Sopenharmony_ci struct net_device *dev = tp->dev; 9218c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 9228c2ecf20Sopenharmony_ci int phy_id = tp->mii_if.phy_id; 9238c2ecf20Sopenharmony_ci u16 val; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci rtnl_lock(); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (!netif_running(dev)) 9288c2ecf20Sopenharmony_ci goto out_unlock; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci val = mdio_read(ioaddr, phy_id, MII_BMCR); 9318c2ecf20Sopenharmony_ci if (val & BMCR_RESET) { 9328c2ecf20Sopenharmony_ci // FIXME: needlessly high ? -- FR 02/07/2005 9338c2ecf20Sopenharmony_ci mod_timer(&tp->timer, jiffies + HZ/10); 9348c2ecf20Sopenharmony_ci goto out_unlock; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci val = mdio_read_latched(ioaddr, phy_id, MII_BMSR); 9388c2ecf20Sopenharmony_ci if (!(val & BMSR_ANEGCOMPLETE) && tp->link_status != LNK_AUTONEG) { 9398c2ecf20Sopenharmony_ci netif_carrier_off(dev); 9408c2ecf20Sopenharmony_ci netif_warn(tp, link, dev, "auto-negotiating...\n"); 9418c2ecf20Sopenharmony_ci tp->link_status = LNK_AUTONEG; 9428c2ecf20Sopenharmony_ci } else if ((val & BMSR_LSTATUS) && tp->link_status != LNK_ON) { 9438c2ecf20Sopenharmony_ci /* Rejoice ! */ 9448c2ecf20Sopenharmony_ci struct { 9458c2ecf20Sopenharmony_ci int val; 9468c2ecf20Sopenharmony_ci u32 ctl; 9478c2ecf20Sopenharmony_ci const char *msg; 9488c2ecf20Sopenharmony_ci } reg31[] = { 9498c2ecf20Sopenharmony_ci { LPA_1000FULL, 0x07000c00 | 0x00001000, 9508c2ecf20Sopenharmony_ci "1000 Mbps Full Duplex" }, 9518c2ecf20Sopenharmony_ci { LPA_1000HALF, 0x07000c00, 9528c2ecf20Sopenharmony_ci "1000 Mbps Half Duplex" }, 9538c2ecf20Sopenharmony_ci { LPA_100FULL, 0x04000800 | 0x00001000, 9548c2ecf20Sopenharmony_ci "100 Mbps Full Duplex" }, 9558c2ecf20Sopenharmony_ci { LPA_100HALF, 0x04000800, 9568c2ecf20Sopenharmony_ci "100 Mbps Half Duplex" }, 9578c2ecf20Sopenharmony_ci { LPA_10FULL, 0x04000400 | 0x00001000, 9588c2ecf20Sopenharmony_ci "10 Mbps Full Duplex" }, 9598c2ecf20Sopenharmony_ci { LPA_10HALF, 0x04000400, 9608c2ecf20Sopenharmony_ci "10 Mbps Half Duplex" }, 9618c2ecf20Sopenharmony_ci { 0, 0x04000400, "unknown" } 9628c2ecf20Sopenharmony_ci }, *p = NULL; 9638c2ecf20Sopenharmony_ci u16 adv, autoexp, gigadv, gigrec; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci val = mdio_read(ioaddr, phy_id, 0x1f); 9668c2ecf20Sopenharmony_ci netif_info(tp, link, dev, "mii ext = %04x\n", val); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci val = mdio_read(ioaddr, phy_id, MII_LPA); 9698c2ecf20Sopenharmony_ci adv = mdio_read(ioaddr, phy_id, MII_ADVERTISE); 9708c2ecf20Sopenharmony_ci autoexp = mdio_read(ioaddr, phy_id, MII_EXPANSION); 9718c2ecf20Sopenharmony_ci netif_info(tp, link, dev, "mii lpa=%04x adv=%04x exp=%04x\n", 9728c2ecf20Sopenharmony_ci val, adv, autoexp); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (val & LPA_NPAGE && autoexp & EXPANSION_NWAY) { 9758c2ecf20Sopenharmony_ci /* check for gigabit speed */ 9768c2ecf20Sopenharmony_ci gigadv = mdio_read(ioaddr, phy_id, MII_CTRL1000); 9778c2ecf20Sopenharmony_ci gigrec = mdio_read(ioaddr, phy_id, MII_STAT1000); 9788c2ecf20Sopenharmony_ci val = (gigadv & (gigrec >> 2)); 9798c2ecf20Sopenharmony_ci if (val & ADVERTISE_1000FULL) 9808c2ecf20Sopenharmony_ci p = reg31; 9818c2ecf20Sopenharmony_ci else if (val & ADVERTISE_1000HALF) 9828c2ecf20Sopenharmony_ci p = reg31 + 1; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci if (!p) { 9858c2ecf20Sopenharmony_ci val &= adv; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci for (p = reg31; p->val; p++) { 9888c2ecf20Sopenharmony_ci if ((val & p->val) == p->val) 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci p->ctl |= SIS_R32(StationControl) & ~0x0f001c00; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if ((tp->features & F_HAS_RGMII) && 9968c2ecf20Sopenharmony_ci (tp->features & F_PHY_BCM5461)) { 9978c2ecf20Sopenharmony_ci // Set Tx Delay in RGMII mode. 9988c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, 0x18, 0xf1c7); 9998c2ecf20Sopenharmony_ci udelay(200); 10008c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, 0x1c, 0x8c00); 10018c2ecf20Sopenharmony_ci p->ctl |= 0x03000000; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci SIS_W32(StationControl, p->ctl); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (tp->features & F_HAS_RGMII) { 10078c2ecf20Sopenharmony_ci SIS_W32(RGDelay, 0x0441); 10088c2ecf20Sopenharmony_ci SIS_W32(RGDelay, 0x0440); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci tp->negotiated_lpa = p->val; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci netif_info(tp, link, dev, "link on %s mode\n", p->msg); 10148c2ecf20Sopenharmony_ci netif_carrier_on(dev); 10158c2ecf20Sopenharmony_ci tp->link_status = LNK_ON; 10168c2ecf20Sopenharmony_ci } else if (!(val & BMSR_LSTATUS) && tp->link_status != LNK_AUTONEG) 10178c2ecf20Sopenharmony_ci tp->link_status = LNK_OFF; 10188c2ecf20Sopenharmony_ci mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ciout_unlock: 10218c2ecf20Sopenharmony_ci rtnl_unlock(); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cistatic void sis190_phy_timer(struct timer_list *t) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct sis190_private *tp = from_timer(tp, t, timer); 10278c2ecf20Sopenharmony_ci struct net_device *dev = tp->dev; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (likely(netif_running(dev))) 10308c2ecf20Sopenharmony_ci schedule_work(&tp->phy_task); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic inline void sis190_delete_timer(struct net_device *dev) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci del_timer_sync(&tp->timer); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic inline void sis190_request_timer(struct net_device *dev) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 10438c2ecf20Sopenharmony_ci struct timer_list *timer = &tp->timer; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci timer_setup(timer, sis190_phy_timer, 0); 10468c2ecf20Sopenharmony_ci timer->expires = jiffies + SIS190_PHY_TIMEOUT; 10478c2ecf20Sopenharmony_ci add_timer(timer); 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic void sis190_set_rxbufsize(struct sis190_private *tp, 10518c2ecf20Sopenharmony_ci struct net_device *dev) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci unsigned int mtu = dev->mtu; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE; 10568c2ecf20Sopenharmony_ci /* RxDesc->size has a licence to kill the lower bits */ 10578c2ecf20Sopenharmony_ci if (tp->rx_buf_sz & 0x07) { 10588c2ecf20Sopenharmony_ci tp->rx_buf_sz += 8; 10598c2ecf20Sopenharmony_ci tp->rx_buf_sz &= RX_BUF_MASK; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int sis190_open(struct net_device *dev) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 10668c2ecf20Sopenharmony_ci struct pci_dev *pdev = tp->pci_dev; 10678c2ecf20Sopenharmony_ci int rc = -ENOMEM; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci sis190_set_rxbufsize(tp, dev); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* 10728c2ecf20Sopenharmony_ci * Rx and Tx descriptors need 256 bytes alignment. 10738c2ecf20Sopenharmony_ci * pci_alloc_consistent() guarantees a stronger alignment. 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_ci tp->TxDescRing = dma_alloc_coherent(&pdev->dev, TX_RING_BYTES, 10768c2ecf20Sopenharmony_ci &tp->tx_dma, GFP_KERNEL); 10778c2ecf20Sopenharmony_ci if (!tp->TxDescRing) 10788c2ecf20Sopenharmony_ci goto out; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci tp->RxDescRing = dma_alloc_coherent(&pdev->dev, RX_RING_BYTES, 10818c2ecf20Sopenharmony_ci &tp->rx_dma, GFP_KERNEL); 10828c2ecf20Sopenharmony_ci if (!tp->RxDescRing) 10838c2ecf20Sopenharmony_ci goto err_free_tx_0; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci rc = sis190_init_ring(dev); 10868c2ecf20Sopenharmony_ci if (rc < 0) 10878c2ecf20Sopenharmony_ci goto err_free_rx_1; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci sis190_request_timer(dev); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci rc = request_irq(pdev->irq, sis190_irq, IRQF_SHARED, dev->name, dev); 10928c2ecf20Sopenharmony_ci if (rc < 0) 10938c2ecf20Sopenharmony_ci goto err_release_timer_2; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci sis190_hw_start(dev); 10968c2ecf20Sopenharmony_ciout: 10978c2ecf20Sopenharmony_ci return rc; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cierr_release_timer_2: 11008c2ecf20Sopenharmony_ci sis190_delete_timer(dev); 11018c2ecf20Sopenharmony_ci sis190_rx_clear(tp); 11028c2ecf20Sopenharmony_cierr_free_rx_1: 11038c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_RING_BYTES, tp->RxDescRing, 11048c2ecf20Sopenharmony_ci tp->rx_dma); 11058c2ecf20Sopenharmony_cierr_free_tx_0: 11068c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_RING_BYTES, tp->TxDescRing, 11078c2ecf20Sopenharmony_ci tp->tx_dma); 11088c2ecf20Sopenharmony_ci goto out; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic void sis190_tx_clear(struct sis190_private *tp) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci unsigned int i; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci for (i = 0; i < NUM_TX_DESC; i++) { 11168c2ecf20Sopenharmony_ci struct sk_buff *skb = tp->Tx_skbuff[i]; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (!skb) 11198c2ecf20Sopenharmony_ci continue; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci sis190_unmap_tx_skb(tp->pci_dev, skb, tp->TxDescRing + i); 11228c2ecf20Sopenharmony_ci tp->Tx_skbuff[i] = NULL; 11238c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci tp->dev->stats.tx_dropped++; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci tp->cur_tx = tp->dirty_tx = 0; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void sis190_down(struct net_device *dev) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 11338c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 11348c2ecf20Sopenharmony_ci unsigned int poll_locked = 0; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci sis190_delete_timer(dev); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci netif_stop_queue(dev); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci do { 11418c2ecf20Sopenharmony_ci spin_lock_irq(&tp->lock); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci sis190_asic_down(ioaddr); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci spin_unlock_irq(&tp->lock); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci synchronize_irq(tp->pci_dev->irq); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (!poll_locked) 11508c2ecf20Sopenharmony_ci poll_locked++; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci synchronize_rcu(); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci } while (SIS_R32(IntrMask)); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci sis190_tx_clear(tp); 11578c2ecf20Sopenharmony_ci sis190_rx_clear(tp); 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int sis190_close(struct net_device *dev) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 11638c2ecf20Sopenharmony_ci struct pci_dev *pdev = tp->pci_dev; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci sis190_down(dev); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci free_irq(pdev->irq, dev); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_RING_BYTES, tp->TxDescRing, 11708c2ecf20Sopenharmony_ci tp->tx_dma); 11718c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_RING_BYTES, tp->RxDescRing, 11728c2ecf20Sopenharmony_ci tp->rx_dma); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci tp->TxDescRing = NULL; 11758c2ecf20Sopenharmony_ci tp->RxDescRing = NULL; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic netdev_tx_t sis190_start_xmit(struct sk_buff *skb, 11818c2ecf20Sopenharmony_ci struct net_device *dev) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 11848c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 11858c2ecf20Sopenharmony_ci u32 len, entry, dirty_tx; 11868c2ecf20Sopenharmony_ci struct TxDesc *desc; 11878c2ecf20Sopenharmony_ci dma_addr_t mapping; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (unlikely(skb->len < ETH_ZLEN)) { 11908c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 11918c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 11928c2ecf20Sopenharmony_ci goto out; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci len = ETH_ZLEN; 11958c2ecf20Sopenharmony_ci } else { 11968c2ecf20Sopenharmony_ci len = skb->len; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci entry = tp->cur_tx % NUM_TX_DESC; 12008c2ecf20Sopenharmony_ci desc = tp->TxDescRing + entry; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (unlikely(le32_to_cpu(desc->status) & OWNbit)) { 12038c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12048c2ecf20Sopenharmony_ci netif_err(tp, tx_err, dev, 12058c2ecf20Sopenharmony_ci "BUG! Tx Ring full when queue awake!\n"); 12068c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci mapping = dma_map_single(&tp->pci_dev->dev, skb->data, len, 12108c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12118c2ecf20Sopenharmony_ci if (dma_mapping_error(&tp->pci_dev->dev, mapping)) { 12128c2ecf20Sopenharmony_ci netif_err(tp, tx_err, dev, 12138c2ecf20Sopenharmony_ci "PCI mapping failed, dropping packet"); 12148c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci tp->Tx_skbuff[entry] = skb; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci desc->PSize = cpu_to_le32(len); 12208c2ecf20Sopenharmony_ci desc->addr = cpu_to_le32(mapping); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci desc->size = cpu_to_le32(len); 12238c2ecf20Sopenharmony_ci if (entry == (NUM_TX_DESC - 1)) 12248c2ecf20Sopenharmony_ci desc->size |= cpu_to_le32(RingEnd); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci wmb(); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci desc->status = cpu_to_le32(OWNbit | INTbit | DEFbit | CRCbit | PADbit); 12298c2ecf20Sopenharmony_ci if (tp->negotiated_lpa & (LPA_1000HALF | LPA_100HALF | LPA_10HALF)) { 12308c2ecf20Sopenharmony_ci /* Half Duplex */ 12318c2ecf20Sopenharmony_ci desc->status |= cpu_to_le32(COLEN | CRSEN | BKFEN); 12328c2ecf20Sopenharmony_ci if (tp->negotiated_lpa & (LPA_1000HALF | LPA_1000FULL)) 12338c2ecf20Sopenharmony_ci desc->status |= cpu_to_le32(EXTEN | BSTEN); /* gigabit HD */ 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci tp->cur_tx++; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci smp_wmb(); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci SIS_W32(TxControl, 0x1a00 | CmdReset | CmdTxEnb); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci dirty_tx = tp->dirty_tx; 12438c2ecf20Sopenharmony_ci if ((tp->cur_tx - NUM_TX_DESC) == dirty_tx) { 12448c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12458c2ecf20Sopenharmony_ci smp_rmb(); 12468c2ecf20Sopenharmony_ci if (dirty_tx != tp->dirty_tx) 12478c2ecf20Sopenharmony_ci netif_wake_queue(dev); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ciout: 12508c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic void sis190_free_phy(struct list_head *first_phy) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct sis190_phy *cur, *next; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci list_for_each_entry_safe(cur, next, first_phy, list) { 12588c2ecf20Sopenharmony_ci kfree(cur); 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci/** 12638c2ecf20Sopenharmony_ci * sis190_default_phy - Select default PHY for sis190 mac. 12648c2ecf20Sopenharmony_ci * @dev: the net device to probe for 12658c2ecf20Sopenharmony_ci * 12668c2ecf20Sopenharmony_ci * Select first detected PHY with link as default. 12678c2ecf20Sopenharmony_ci * If no one is link on, select PHY whose types is HOME as default. 12688c2ecf20Sopenharmony_ci * If HOME doesn't exist, select LAN. 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_cistatic u16 sis190_default_phy(struct net_device *dev) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct sis190_phy *phy, *phy_home, *phy_default, *phy_lan; 12738c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 12748c2ecf20Sopenharmony_ci struct mii_if_info *mii_if = &tp->mii_if; 12758c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 12768c2ecf20Sopenharmony_ci u16 status; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci phy_home = phy_default = phy_lan = NULL; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci list_for_each_entry(phy, &tp->first_phy, list) { 12818c2ecf20Sopenharmony_ci status = mdio_read_latched(ioaddr, phy->phy_id, MII_BMSR); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci // Link ON & Not select default PHY & not ghost PHY. 12848c2ecf20Sopenharmony_ci if ((status & BMSR_LSTATUS) && 12858c2ecf20Sopenharmony_ci !phy_default && 12868c2ecf20Sopenharmony_ci (phy->type != UNKNOWN)) { 12878c2ecf20Sopenharmony_ci phy_default = phy; 12888c2ecf20Sopenharmony_ci } else { 12898c2ecf20Sopenharmony_ci status = mdio_read(ioaddr, phy->phy_id, MII_BMCR); 12908c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy->phy_id, MII_BMCR, 12918c2ecf20Sopenharmony_ci status | BMCR_ANENABLE | BMCR_ISOLATE); 12928c2ecf20Sopenharmony_ci if (phy->type == HOME) 12938c2ecf20Sopenharmony_ci phy_home = phy; 12948c2ecf20Sopenharmony_ci else if (phy->type == LAN) 12958c2ecf20Sopenharmony_ci phy_lan = phy; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (!phy_default) { 13008c2ecf20Sopenharmony_ci if (phy_home) 13018c2ecf20Sopenharmony_ci phy_default = phy_home; 13028c2ecf20Sopenharmony_ci else if (phy_lan) 13038c2ecf20Sopenharmony_ci phy_default = phy_lan; 13048c2ecf20Sopenharmony_ci else 13058c2ecf20Sopenharmony_ci phy_default = list_first_entry(&tp->first_phy, 13068c2ecf20Sopenharmony_ci struct sis190_phy, list); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (mii_if->phy_id != phy_default->phy_id) { 13108c2ecf20Sopenharmony_ci mii_if->phy_id = phy_default->phy_id; 13118c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 13128c2ecf20Sopenharmony_ci pr_info("%s: Using transceiver at address %d as default\n", 13138c2ecf20Sopenharmony_ci pci_name(tp->pci_dev), mii_if->phy_id); 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR); 13178c2ecf20Sopenharmony_ci status &= (~BMCR_ISOLATE); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status); 13208c2ecf20Sopenharmony_ci status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci return status; 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic void sis190_init_phy(struct net_device *dev, struct sis190_private *tp, 13268c2ecf20Sopenharmony_ci struct sis190_phy *phy, unsigned int phy_id, 13278c2ecf20Sopenharmony_ci u16 mii_status) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 13308c2ecf20Sopenharmony_ci struct mii_chip_info *p; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&phy->list); 13338c2ecf20Sopenharmony_ci phy->status = mii_status; 13348c2ecf20Sopenharmony_ci phy->phy_id = phy_id; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1); 13378c2ecf20Sopenharmony_ci phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci for (p = mii_chip_table; p->type; p++) { 13408c2ecf20Sopenharmony_ci if ((p->id[0] == phy->id[0]) && 13418c2ecf20Sopenharmony_ci (p->id[1] == (phy->id[1] & 0xfff0))) { 13428c2ecf20Sopenharmony_ci break; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci if (p->id[1]) { 13478c2ecf20Sopenharmony_ci phy->type = (p->type == MIX) ? 13488c2ecf20Sopenharmony_ci ((mii_status & (BMSR_100FULL | BMSR_100HALF)) ? 13498c2ecf20Sopenharmony_ci LAN : HOME) : p->type; 13508c2ecf20Sopenharmony_ci tp->features |= p->feature; 13518c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 13528c2ecf20Sopenharmony_ci pr_info("%s: %s transceiver at address %d\n", 13538c2ecf20Sopenharmony_ci pci_name(tp->pci_dev), p->name, phy_id); 13548c2ecf20Sopenharmony_ci } else { 13558c2ecf20Sopenharmony_ci phy->type = UNKNOWN; 13568c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 13578c2ecf20Sopenharmony_ci pr_info("%s: unknown PHY 0x%x:0x%x transceiver at address %d\n", 13588c2ecf20Sopenharmony_ci pci_name(tp->pci_dev), 13598c2ecf20Sopenharmony_ci phy->id[0], (phy->id[1] & 0xfff0), phy_id); 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic void sis190_mii_probe_88e1111_fixup(struct sis190_private *tp) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci if (tp->features & F_PHY_88E1111) { 13668c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 13678c2ecf20Sopenharmony_ci int phy_id = tp->mii_if.phy_id; 13688c2ecf20Sopenharmony_ci u16 reg[2][2] = { 13698c2ecf20Sopenharmony_ci { 0x808b, 0x0ce1 }, 13708c2ecf20Sopenharmony_ci { 0x808f, 0x0c60 } 13718c2ecf20Sopenharmony_ci }, *p; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci p = (tp->features & F_HAS_RGMII) ? reg[0] : reg[1]; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, 0x1b, p[0]); 13768c2ecf20Sopenharmony_ci udelay(200); 13778c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, 0x14, p[1]); 13788c2ecf20Sopenharmony_ci udelay(200); 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci/** 13838c2ecf20Sopenharmony_ci * sis190_mii_probe - Probe MII PHY for sis190 13848c2ecf20Sopenharmony_ci * @dev: the net device to probe for 13858c2ecf20Sopenharmony_ci * 13868c2ecf20Sopenharmony_ci * Search for total of 32 possible mii phy addresses. 13878c2ecf20Sopenharmony_ci * Identify and set current phy if found one, 13888c2ecf20Sopenharmony_ci * return error if it failed to found. 13898c2ecf20Sopenharmony_ci */ 13908c2ecf20Sopenharmony_cistatic int sis190_mii_probe(struct net_device *dev) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 13938c2ecf20Sopenharmony_ci struct mii_if_info *mii_if = &tp->mii_if; 13948c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 13958c2ecf20Sopenharmony_ci int phy_id; 13968c2ecf20Sopenharmony_ci int rc = 0; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tp->first_phy); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { 14018c2ecf20Sopenharmony_ci struct sis190_phy *phy; 14028c2ecf20Sopenharmony_ci u16 status; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci status = mdio_read_latched(ioaddr, phy_id, MII_BMSR); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci // Try next mii if the current one is not accessible. 14078c2ecf20Sopenharmony_ci if (status == 0xffff || status == 0x0000) 14088c2ecf20Sopenharmony_ci continue; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci phy = kmalloc(sizeof(*phy), GFP_KERNEL); 14118c2ecf20Sopenharmony_ci if (!phy) { 14128c2ecf20Sopenharmony_ci sis190_free_phy(&tp->first_phy); 14138c2ecf20Sopenharmony_ci rc = -ENOMEM; 14148c2ecf20Sopenharmony_ci goto out; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci sis190_init_phy(dev, tp, phy, phy_id, status); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci list_add(&tp->first_phy, &phy->list); 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (list_empty(&tp->first_phy)) { 14238c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 14248c2ecf20Sopenharmony_ci pr_info("%s: No MII transceivers found!\n", 14258c2ecf20Sopenharmony_ci pci_name(tp->pci_dev)); 14268c2ecf20Sopenharmony_ci rc = -EIO; 14278c2ecf20Sopenharmony_ci goto out; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* Select default PHY for mac */ 14318c2ecf20Sopenharmony_ci sis190_default_phy(dev); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci sis190_mii_probe_88e1111_fixup(tp); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci mii_if->dev = dev; 14368c2ecf20Sopenharmony_ci mii_if->mdio_read = __mdio_read; 14378c2ecf20Sopenharmony_ci mii_if->mdio_write = __mdio_write; 14388c2ecf20Sopenharmony_ci mii_if->phy_id_mask = PHY_ID_ANY; 14398c2ecf20Sopenharmony_ci mii_if->reg_num_mask = MII_REG_ANY; 14408c2ecf20Sopenharmony_ciout: 14418c2ecf20Sopenharmony_ci return rc; 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic void sis190_mii_remove(struct net_device *dev) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci sis190_free_phy(&tp->first_phy); 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic void sis190_release_board(struct pci_dev *pdev) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 14548c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci iounmap(tp->mmio_addr); 14578c2ecf20Sopenharmony_ci pci_release_regions(pdev); 14588c2ecf20Sopenharmony_ci pci_disable_device(pdev); 14598c2ecf20Sopenharmony_ci free_netdev(dev); 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic struct net_device *sis190_init_board(struct pci_dev *pdev) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct sis190_private *tp; 14658c2ecf20Sopenharmony_ci struct net_device *dev; 14668c2ecf20Sopenharmony_ci void __iomem *ioaddr; 14678c2ecf20Sopenharmony_ci int rc; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*tp)); 14708c2ecf20Sopenharmony_ci if (!dev) { 14718c2ecf20Sopenharmony_ci rc = -ENOMEM; 14728c2ecf20Sopenharmony_ci goto err_out_0; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci tp = netdev_priv(dev); 14788c2ecf20Sopenharmony_ci tp->dev = dev; 14798c2ecf20Sopenharmony_ci tp->msg_enable = netif_msg_init(debug.msg_enable, SIS190_MSG_DEFAULT); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 14828c2ecf20Sopenharmony_ci if (rc < 0) { 14838c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 14848c2ecf20Sopenharmony_ci pr_err("%s: enable failure\n", pci_name(pdev)); 14858c2ecf20Sopenharmony_ci goto err_free_dev_1; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci rc = -ENODEV; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 14918c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 14928c2ecf20Sopenharmony_ci pr_err("%s: region #0 is no MMIO resource\n", 14938c2ecf20Sopenharmony_ci pci_name(pdev)); 14948c2ecf20Sopenharmony_ci goto err_pci_disable_2; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci if (pci_resource_len(pdev, 0) < SIS190_REGS_SIZE) { 14978c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 14988c2ecf20Sopenharmony_ci pr_err("%s: invalid PCI region size(s)\n", 14998c2ecf20Sopenharmony_ci pci_name(pdev)); 15008c2ecf20Sopenharmony_ci goto err_pci_disable_2; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci rc = pci_request_regions(pdev, DRV_NAME); 15048c2ecf20Sopenharmony_ci if (rc < 0) { 15058c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 15068c2ecf20Sopenharmony_ci pr_err("%s: could not request regions\n", 15078c2ecf20Sopenharmony_ci pci_name(pdev)); 15088c2ecf20Sopenharmony_ci goto err_pci_disable_2; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 15128c2ecf20Sopenharmony_ci if (rc < 0) { 15138c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 15148c2ecf20Sopenharmony_ci pr_err("%s: DMA configuration failed\n", 15158c2ecf20Sopenharmony_ci pci_name(pdev)); 15168c2ecf20Sopenharmony_ci goto err_free_res_3; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci pci_set_master(pdev); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci ioaddr = ioremap(pci_resource_start(pdev, 0), SIS190_REGS_SIZE); 15228c2ecf20Sopenharmony_ci if (!ioaddr) { 15238c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 15248c2ecf20Sopenharmony_ci pr_err("%s: cannot remap MMIO, aborting\n", 15258c2ecf20Sopenharmony_ci pci_name(pdev)); 15268c2ecf20Sopenharmony_ci rc = -EIO; 15278c2ecf20Sopenharmony_ci goto err_free_res_3; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci tp->pci_dev = pdev; 15318c2ecf20Sopenharmony_ci tp->mmio_addr = ioaddr; 15328c2ecf20Sopenharmony_ci tp->link_status = LNK_OFF; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci sis190_irq_mask_and_ack(ioaddr); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci sis190_soft_reset(ioaddr); 15378c2ecf20Sopenharmony_ciout: 15388c2ecf20Sopenharmony_ci return dev; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cierr_free_res_3: 15418c2ecf20Sopenharmony_ci pci_release_regions(pdev); 15428c2ecf20Sopenharmony_cierr_pci_disable_2: 15438c2ecf20Sopenharmony_ci pci_disable_device(pdev); 15448c2ecf20Sopenharmony_cierr_free_dev_1: 15458c2ecf20Sopenharmony_ci free_netdev(dev); 15468c2ecf20Sopenharmony_cierr_out_0: 15478c2ecf20Sopenharmony_ci dev = ERR_PTR(rc); 15488c2ecf20Sopenharmony_ci goto out; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic void sis190_tx_timeout(struct net_device *dev, unsigned int txqueue) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 15548c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 15558c2ecf20Sopenharmony_ci u8 tmp8; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci /* Disable Tx, if not already */ 15588c2ecf20Sopenharmony_ci tmp8 = SIS_R8(TxControl); 15598c2ecf20Sopenharmony_ci if (tmp8 & CmdTxEnb) 15608c2ecf20Sopenharmony_ci SIS_W8(TxControl, tmp8 & ~CmdTxEnb); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci netif_info(tp, tx_err, dev, "Transmit timeout, status %08x %08x\n", 15638c2ecf20Sopenharmony_ci SIS_R32(TxControl), SIS_R32(TxSts)); 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* Disable interrupts by clearing the interrupt mask. */ 15668c2ecf20Sopenharmony_ci SIS_W32(IntrMask, 0x0000); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci /* Stop a shared interrupt from scavenging while we are. */ 15698c2ecf20Sopenharmony_ci spin_lock_irq(&tp->lock); 15708c2ecf20Sopenharmony_ci sis190_tx_clear(tp); 15718c2ecf20Sopenharmony_ci spin_unlock_irq(&tp->lock); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* ...and finally, reset everything. */ 15748c2ecf20Sopenharmony_ci sis190_hw_start(dev); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci netif_wake_queue(dev); 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_cistatic void sis190_set_rgmii(struct sis190_private *tp, u8 reg) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci tp->features |= (reg & 0x80) ? F_HAS_RGMII : 0; 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_cistatic int sis190_get_mac_addr_from_eeprom(struct pci_dev *pdev, 15858c2ecf20Sopenharmony_ci struct net_device *dev) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 15888c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 15898c2ecf20Sopenharmony_ci u16 sig; 15908c2ecf20Sopenharmony_ci int i; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 15938c2ecf20Sopenharmony_ci pr_info("%s: Read MAC address from EEPROM\n", pci_name(pdev)); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci /* Check to see if there is a sane EEPROM */ 15968c2ecf20Sopenharmony_ci sig = (u16) sis190_read_eeprom(ioaddr, EEPROMSignature); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if ((sig == 0xffff) || (sig == 0x0000)) { 15998c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 16008c2ecf20Sopenharmony_ci pr_info("%s: Error EEPROM read %x\n", 16018c2ecf20Sopenharmony_ci pci_name(pdev), sig); 16028c2ecf20Sopenharmony_ci return -EIO; 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci /* Get MAC address from EEPROM */ 16068c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN / 2; i++) { 16078c2ecf20Sopenharmony_ci u16 w = sis190_read_eeprom(ioaddr, EEPROMMACAddr + i); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(w); 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci sis190_set_rgmii(tp, sis190_read_eeprom(ioaddr, EEPROMInfo)); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci return 0; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci/** 16188c2ecf20Sopenharmony_ci * sis190_get_mac_addr_from_apc - Get MAC address for SiS96x model 16198c2ecf20Sopenharmony_ci * @pdev: PCI device 16208c2ecf20Sopenharmony_ci * @dev: network device to get address for 16218c2ecf20Sopenharmony_ci * 16228c2ecf20Sopenharmony_ci * SiS96x model, use APC CMOS RAM to store MAC address. 16238c2ecf20Sopenharmony_ci * APC CMOS RAM is accessed through ISA bridge. 16248c2ecf20Sopenharmony_ci * MAC address is read into @net_dev->dev_addr. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_cistatic int sis190_get_mac_addr_from_apc(struct pci_dev *pdev, 16278c2ecf20Sopenharmony_ci struct net_device *dev) 16288c2ecf20Sopenharmony_ci{ 16298c2ecf20Sopenharmony_ci static const u16 ids[] = { 0x0965, 0x0966, 0x0968 }; 16308c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 16318c2ecf20Sopenharmony_ci struct pci_dev *isa_bridge; 16328c2ecf20Sopenharmony_ci u8 reg, tmp8; 16338c2ecf20Sopenharmony_ci unsigned int i; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 16368c2ecf20Sopenharmony_ci pr_info("%s: Read MAC address from APC\n", pci_name(pdev)); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ids); i++) { 16398c2ecf20Sopenharmony_ci isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, ids[i], NULL); 16408c2ecf20Sopenharmony_ci if (isa_bridge) 16418c2ecf20Sopenharmony_ci break; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (!isa_bridge) { 16458c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) 16468c2ecf20Sopenharmony_ci pr_info("%s: Can not find ISA bridge\n", 16478c2ecf20Sopenharmony_ci pci_name(pdev)); 16488c2ecf20Sopenharmony_ci return -EIO; 16498c2ecf20Sopenharmony_ci } 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci /* Enable port 78h & 79h to access APC Registers. */ 16528c2ecf20Sopenharmony_ci pci_read_config_byte(isa_bridge, 0x48, &tmp8); 16538c2ecf20Sopenharmony_ci reg = (tmp8 & ~0x02); 16548c2ecf20Sopenharmony_ci pci_write_config_byte(isa_bridge, 0x48, reg); 16558c2ecf20Sopenharmony_ci udelay(50); 16568c2ecf20Sopenharmony_ci pci_read_config_byte(isa_bridge, 0x48, ®); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) { 16598c2ecf20Sopenharmony_ci outb(0x9 + i, 0x78); 16608c2ecf20Sopenharmony_ci dev->dev_addr[i] = inb(0x79); 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci outb(0x12, 0x78); 16648c2ecf20Sopenharmony_ci reg = inb(0x79); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci sis190_set_rgmii(tp, reg); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* Restore the value to ISA Bridge */ 16698c2ecf20Sopenharmony_ci pci_write_config_byte(isa_bridge, 0x48, tmp8); 16708c2ecf20Sopenharmony_ci pci_dev_put(isa_bridge); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci return 0; 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci/** 16768c2ecf20Sopenharmony_ci * sis190_init_rxfilter - Initialize the Rx filter 16778c2ecf20Sopenharmony_ci * @dev: network device to initialize 16788c2ecf20Sopenharmony_ci * 16798c2ecf20Sopenharmony_ci * Set receive filter address to our MAC address 16808c2ecf20Sopenharmony_ci * and enable packet filtering. 16818c2ecf20Sopenharmony_ci */ 16828c2ecf20Sopenharmony_cistatic inline void sis190_init_rxfilter(struct net_device *dev) 16838c2ecf20Sopenharmony_ci{ 16848c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 16858c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 16868c2ecf20Sopenharmony_ci u16 ctl; 16878c2ecf20Sopenharmony_ci int i; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci ctl = SIS_R16(RxMacControl); 16908c2ecf20Sopenharmony_ci /* 16918c2ecf20Sopenharmony_ci * Disable packet filtering before setting filter. 16928c2ecf20Sopenharmony_ci * Note: SiS's driver writes 32 bits but RxMacControl is 16 bits 16938c2ecf20Sopenharmony_ci * only and followed by RxMacAddr (6 bytes). Strange. -- FR 16948c2ecf20Sopenharmony_ci */ 16958c2ecf20Sopenharmony_ci SIS_W16(RxMacControl, ctl & ~0x0f00); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 16988c2ecf20Sopenharmony_ci SIS_W8(RxMacAddr + i, dev->dev_addr[i]); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci SIS_W16(RxMacControl, ctl); 17018c2ecf20Sopenharmony_ci SIS_PCI_COMMIT(); 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic int sis190_get_mac_addr(struct pci_dev *pdev, struct net_device *dev) 17058c2ecf20Sopenharmony_ci{ 17068c2ecf20Sopenharmony_ci int rc; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci rc = sis190_get_mac_addr_from_eeprom(pdev, dev); 17098c2ecf20Sopenharmony_ci if (rc < 0) { 17108c2ecf20Sopenharmony_ci u8 reg; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x73, ®); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (reg & 0x00000001) 17158c2ecf20Sopenharmony_ci rc = sis190_get_mac_addr_from_apc(pdev, dev); 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci return rc; 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cistatic void sis190_set_speed_auto(struct net_device *dev) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17238c2ecf20Sopenharmony_ci void __iomem *ioaddr = tp->mmio_addr; 17248c2ecf20Sopenharmony_ci int phy_id = tp->mii_if.phy_id; 17258c2ecf20Sopenharmony_ci int val; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci netif_info(tp, link, dev, "Enabling Auto-negotiation\n"); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci val = mdio_read(ioaddr, phy_id, MII_ADVERTISE); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci // Enable 10/100 Full/Half Mode, leave MII_ADVERTISE bit4:0 17328c2ecf20Sopenharmony_ci // unchanged. 17338c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, MII_ADVERTISE, (val & ADVERTISE_SLCT) | 17348c2ecf20Sopenharmony_ci ADVERTISE_100FULL | ADVERTISE_10FULL | 17358c2ecf20Sopenharmony_ci ADVERTISE_100HALF | ADVERTISE_10HALF); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci // Enable 1000 Full Mode. 17388c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, MII_CTRL1000, ADVERTISE_1000FULL); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci // Enable auto-negotiation and restart auto-negotiation. 17418c2ecf20Sopenharmony_ci mdio_write(ioaddr, phy_id, MII_BMCR, 17428c2ecf20Sopenharmony_ci BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET); 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic int sis190_get_link_ksettings(struct net_device *dev, 17468c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&tp->mii_if, cmd); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci return 0; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic int sis190_set_link_ksettings(struct net_device *dev, 17568c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci return mii_ethtool_set_link_ksettings(&tp->mii_if, cmd); 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_cistatic void sis190_get_drvinfo(struct net_device *dev, 17648c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 17698c2ecf20Sopenharmony_ci strlcpy(info->version, DRV_VERSION, sizeof(info->version)); 17708c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(tp->pci_dev), 17718c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic int sis190_get_regs_len(struct net_device *dev) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci return SIS190_REGS_SIZE; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_cistatic void sis190_get_regs(struct net_device *dev, struct ethtool_regs *regs, 17808c2ecf20Sopenharmony_ci void *p) 17818c2ecf20Sopenharmony_ci{ 17828c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17838c2ecf20Sopenharmony_ci unsigned long flags; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci spin_lock_irqsave(&tp->lock, flags); 17868c2ecf20Sopenharmony_ci memcpy_fromio(p, tp->mmio_addr, regs->len); 17878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tp->lock, flags); 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cistatic int sis190_nway_reset(struct net_device *dev) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci return mii_nway_restart(&tp->mii_if); 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic u32 sis190_get_msglevel(struct net_device *dev) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci return tp->msg_enable; 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_cistatic void sis190_set_msglevel(struct net_device *dev, u32 value) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci tp->msg_enable = value; 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistatic const struct ethtool_ops sis190_ethtool_ops = { 18128c2ecf20Sopenharmony_ci .get_drvinfo = sis190_get_drvinfo, 18138c2ecf20Sopenharmony_ci .get_regs_len = sis190_get_regs_len, 18148c2ecf20Sopenharmony_ci .get_regs = sis190_get_regs, 18158c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 18168c2ecf20Sopenharmony_ci .get_msglevel = sis190_get_msglevel, 18178c2ecf20Sopenharmony_ci .set_msglevel = sis190_set_msglevel, 18188c2ecf20Sopenharmony_ci .nway_reset = sis190_nway_reset, 18198c2ecf20Sopenharmony_ci .get_link_ksettings = sis190_get_link_ksettings, 18208c2ecf20Sopenharmony_ci .set_link_ksettings = sis190_set_link_ksettings, 18218c2ecf20Sopenharmony_ci}; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci return !netif_running(dev) ? -EINVAL : 18288c2ecf20Sopenharmony_ci generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_cistatic int sis190_mac_addr(struct net_device *dev, void *p) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci int rc; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci rc = eth_mac_addr(dev, p); 18368c2ecf20Sopenharmony_ci if (!rc) 18378c2ecf20Sopenharmony_ci sis190_init_rxfilter(dev); 18388c2ecf20Sopenharmony_ci return rc; 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic const struct net_device_ops sis190_netdev_ops = { 18428c2ecf20Sopenharmony_ci .ndo_open = sis190_open, 18438c2ecf20Sopenharmony_ci .ndo_stop = sis190_close, 18448c2ecf20Sopenharmony_ci .ndo_do_ioctl = sis190_ioctl, 18458c2ecf20Sopenharmony_ci .ndo_start_xmit = sis190_start_xmit, 18468c2ecf20Sopenharmony_ci .ndo_tx_timeout = sis190_tx_timeout, 18478c2ecf20Sopenharmony_ci .ndo_set_rx_mode = sis190_set_rx_mode, 18488c2ecf20Sopenharmony_ci .ndo_set_mac_address = sis190_mac_addr, 18498c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18508c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 18518c2ecf20Sopenharmony_ci .ndo_poll_controller = sis190_netpoll, 18528c2ecf20Sopenharmony_ci#endif 18538c2ecf20Sopenharmony_ci}; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_cistatic int sis190_init_one(struct pci_dev *pdev, 18568c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci static int printed_version = 0; 18598c2ecf20Sopenharmony_ci struct sis190_private *tp; 18608c2ecf20Sopenharmony_ci struct net_device *dev; 18618c2ecf20Sopenharmony_ci void __iomem *ioaddr; 18628c2ecf20Sopenharmony_ci int rc; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (!printed_version) { 18658c2ecf20Sopenharmony_ci if (netif_msg_drv(&debug)) 18668c2ecf20Sopenharmony_ci pr_info(SIS190_DRIVER_NAME " loaded\n"); 18678c2ecf20Sopenharmony_ci printed_version = 1; 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci dev = sis190_init_board(pdev); 18718c2ecf20Sopenharmony_ci if (IS_ERR(dev)) { 18728c2ecf20Sopenharmony_ci rc = PTR_ERR(dev); 18738c2ecf20Sopenharmony_ci goto out; 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci tp = netdev_priv(dev); 18798c2ecf20Sopenharmony_ci ioaddr = tp->mmio_addr; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci rc = sis190_get_mac_addr(pdev, dev); 18828c2ecf20Sopenharmony_ci if (rc < 0) 18838c2ecf20Sopenharmony_ci goto err_release_board; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci sis190_init_rxfilter(dev); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci INIT_WORK(&tp->phy_task, sis190_phy_task); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci dev->netdev_ops = &sis190_netdev_ops; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci dev->ethtool_ops = &sis190_ethtool_ops; 18928c2ecf20Sopenharmony_ci dev->watchdog_timeo = SIS190_TX_TIMEOUT; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci spin_lock_init(&tp->lock); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci rc = sis190_mii_probe(dev); 18978c2ecf20Sopenharmony_ci if (rc < 0) 18988c2ecf20Sopenharmony_ci goto err_release_board; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci rc = register_netdev(dev); 19018c2ecf20Sopenharmony_ci if (rc < 0) 19028c2ecf20Sopenharmony_ci goto err_remove_mii; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (netif_msg_probe(tp)) { 19058c2ecf20Sopenharmony_ci netdev_info(dev, "%s: %s at %p (IRQ: %d), %pM\n", 19068c2ecf20Sopenharmony_ci pci_name(pdev), 19078c2ecf20Sopenharmony_ci sis_chip_info[ent->driver_data].name, 19088c2ecf20Sopenharmony_ci ioaddr, pdev->irq, dev->dev_addr); 19098c2ecf20Sopenharmony_ci netdev_info(dev, "%s mode.\n", 19108c2ecf20Sopenharmony_ci (tp->features & F_HAS_RGMII) ? "RGMII" : "GMII"); 19118c2ecf20Sopenharmony_ci } 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci netif_carrier_off(dev); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci sis190_set_speed_auto(dev); 19168c2ecf20Sopenharmony_ciout: 19178c2ecf20Sopenharmony_ci return rc; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cierr_remove_mii: 19208c2ecf20Sopenharmony_ci sis190_mii_remove(dev); 19218c2ecf20Sopenharmony_cierr_release_board: 19228c2ecf20Sopenharmony_ci sis190_release_board(pdev); 19238c2ecf20Sopenharmony_ci goto out; 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cistatic void sis190_remove_one(struct pci_dev *pdev) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 19298c2ecf20Sopenharmony_ci struct sis190_private *tp = netdev_priv(dev); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci sis190_mii_remove(dev); 19328c2ecf20Sopenharmony_ci cancel_work_sync(&tp->phy_task); 19338c2ecf20Sopenharmony_ci unregister_netdev(dev); 19348c2ecf20Sopenharmony_ci sis190_release_board(pdev); 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cistatic struct pci_driver sis190_pci_driver = { 19388c2ecf20Sopenharmony_ci .name = DRV_NAME, 19398c2ecf20Sopenharmony_ci .id_table = sis190_pci_tbl, 19408c2ecf20Sopenharmony_ci .probe = sis190_init_one, 19418c2ecf20Sopenharmony_ci .remove = sis190_remove_one, 19428c2ecf20Sopenharmony_ci}; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cimodule_pci_driver(sis190_pci_driver); 1945