18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/******************************************************************************* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright(c) 2006 Tundra Semiconductor Corporation. 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci*******************************************************************************/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* This driver is based on the driver code originally developed 108c2ecf20Sopenharmony_ci * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by 118c2ecf20Sopenharmony_ci * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Currently changes from original version are: 148c2ecf20Sopenharmony_ci * - porting to Tsi108-based platform and kernel 2.6 (kong.lai@tundra.com) 158c2ecf20Sopenharmony_ci * - modifications to handle two ports independently and support for 168c2ecf20Sopenharmony_ci * additional PHY devices (alexandre.bounine@tundra.com) 178c2ecf20Sopenharmony_ci * - Get hardware information from platform device. (tie-fei.zang@freescale.com) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/net.h> 258c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 288c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/crc32.h> 328c2ecf20Sopenharmony_ci#include <linux/mii.h> 338c2ecf20Sopenharmony_ci#include <linux/device.h> 348c2ecf20Sopenharmony_ci#include <linux/pci.h> 358c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 368c2ecf20Sopenharmony_ci#include <linux/timer.h> 378c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 388c2ecf20Sopenharmony_ci#include <linux/gfp.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <asm/io.h> 418c2ecf20Sopenharmony_ci#include <asm/tsi108.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "tsi108_eth.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MII_READ_DELAY 10000 /* max link wait time in msec */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define TSI108_RXRING_LEN 256 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* NOTE: The driver currently does not support receiving packets 508c2ecf20Sopenharmony_ci * larger than the buffer size, so don't decrease this (unless you 518c2ecf20Sopenharmony_ci * want to add such support). 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#define TSI108_RXBUF_SIZE 1536 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define TSI108_TXRING_LEN 256 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define TSI108_TX_INT_FREQ 64 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Check the phy status every half a second. */ 608c2ecf20Sopenharmony_ci#define CHECK_PHY_INTERVAL (HZ/2) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int tsi108_init_one(struct platform_device *pdev); 638c2ecf20Sopenharmony_cistatic int tsi108_ether_remove(struct platform_device *pdev); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct tsi108_prv_data { 668c2ecf20Sopenharmony_ci void __iomem *regs; /* Base of normal regs */ 678c2ecf20Sopenharmony_ci void __iomem *phyregs; /* Base of register bank used for PHY access */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci struct net_device *dev; 708c2ecf20Sopenharmony_ci struct napi_struct napi; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci unsigned int phy; /* Index of PHY for this interface */ 738c2ecf20Sopenharmony_ci unsigned int irq_num; 748c2ecf20Sopenharmony_ci unsigned int id; 758c2ecf20Sopenharmony_ci unsigned int phy_type; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci struct timer_list timer;/* Timer that triggers the check phy function */ 788c2ecf20Sopenharmony_ci unsigned int rxtail; /* Next entry in rxring to read */ 798c2ecf20Sopenharmony_ci unsigned int rxhead; /* Next entry in rxring to give a new buffer */ 808c2ecf20Sopenharmony_ci unsigned int rxfree; /* Number of free, allocated RX buffers */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci unsigned int rxpending; /* Non-zero if there are still descriptors 838c2ecf20Sopenharmony_ci * to be processed from a previous descriptor 848c2ecf20Sopenharmony_ci * interrupt condition that has been cleared */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci unsigned int txtail; /* Next TX descriptor to check status on */ 878c2ecf20Sopenharmony_ci unsigned int txhead; /* Next TX descriptor to use */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Number of free TX descriptors. This could be calculated from 908c2ecf20Sopenharmony_ci * rxhead and rxtail if one descriptor were left unused to disambiguate 918c2ecf20Sopenharmony_ci * full and empty conditions, but it's simpler to just keep track 928c2ecf20Sopenharmony_ci * explicitly. */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci unsigned int txfree; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci unsigned int phy_ok; /* The PHY is currently powered on. */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* PHY status (duplex is 1 for half, 2 for full, 998c2ecf20Sopenharmony_ci * so that the default 0 indicates that neither has 1008c2ecf20Sopenharmony_ci * yet been configured). */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci unsigned int link_up; 1038c2ecf20Sopenharmony_ci unsigned int speed; 1048c2ecf20Sopenharmony_ci unsigned int duplex; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci tx_desc *txring; 1078c2ecf20Sopenharmony_ci rx_desc *rxring; 1088c2ecf20Sopenharmony_ci struct sk_buff *txskbs[TSI108_TXRING_LEN]; 1098c2ecf20Sopenharmony_ci struct sk_buff *rxskbs[TSI108_RXRING_LEN]; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dma_addr_t txdma, rxdma; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* txlock nests in misclock and phy_lock */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci spinlock_t txlock, misclock; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* stats is used to hold the upper bits of each hardware counter, 1188c2ecf20Sopenharmony_ci * and tmpstats is used to hold the full values for returning 1198c2ecf20Sopenharmony_ci * to the caller of get_stats(). They must be separate in case 1208c2ecf20Sopenharmony_ci * an overflow interrupt occurs before the stats are consumed. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci struct net_device_stats stats; 1248c2ecf20Sopenharmony_ci struct net_device_stats tmpstats; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* These stats are kept separate in hardware, thus require individual 1278c2ecf20Sopenharmony_ci * fields for handling carry. They are combined in get_stats. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci unsigned long rx_fcs; /* Add to rx_frame_errors */ 1318c2ecf20Sopenharmony_ci unsigned long rx_short_fcs; /* Add to rx_frame_errors */ 1328c2ecf20Sopenharmony_ci unsigned long rx_long_fcs; /* Add to rx_frame_errors */ 1338c2ecf20Sopenharmony_ci unsigned long rx_underruns; /* Add to rx_length_errors */ 1348c2ecf20Sopenharmony_ci unsigned long rx_overruns; /* Add to rx_length_errors */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci unsigned long tx_coll_abort; /* Add to tx_aborted_errors/collisions */ 1378c2ecf20Sopenharmony_ci unsigned long tx_pause_drop; /* Add to tx_aborted_errors */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci unsigned long mc_hash[16]; 1408c2ecf20Sopenharmony_ci u32 msg_enable; /* debug message level */ 1418c2ecf20Sopenharmony_ci struct mii_if_info mii_if; 1428c2ecf20Sopenharmony_ci unsigned int init_media; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci struct platform_device *pdev; 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Structure for a device driver */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct platform_driver tsi_eth_driver = { 1508c2ecf20Sopenharmony_ci .probe = tsi108_init_one, 1518c2ecf20Sopenharmony_ci .remove = tsi108_ether_remove, 1528c2ecf20Sopenharmony_ci .driver = { 1538c2ecf20Sopenharmony_ci .name = "tsi-ethernet", 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void tsi108_timed_checker(struct timer_list *t); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#ifdef DEBUG 1608c2ecf20Sopenharmony_cistatic void dump_eth_one(struct net_device *dev) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci printk("Dumping %s...\n", dev->name); 1658c2ecf20Sopenharmony_ci printk("intstat %x intmask %x phy_ok %d" 1668c2ecf20Sopenharmony_ci " link %d speed %d duplex %d\n", 1678c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_INTSTAT), 1688c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_INTMASK), data->phy_ok, 1698c2ecf20Sopenharmony_ci data->link_up, data->speed, data->duplex); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n", 1728c2ecf20Sopenharmony_ci data->txhead, data->txtail, data->txfree, 1738c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_TXSTAT), 1748c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_TXESTAT), 1758c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_TXERR)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci printk("RX: head %d, tail %d, free %d, stat %x," 1788c2ecf20Sopenharmony_ci " estat %x, err %x, pending %d\n\n", 1798c2ecf20Sopenharmony_ci data->rxhead, data->rxtail, data->rxfree, 1808c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_RXSTAT), 1818c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_RXESTAT), 1828c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_RXERR), data->rxpending); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* Synchronization is needed between the thread and up/down events. 1878c2ecf20Sopenharmony_ci * Note that the PHY is accessed through the same registers for both 1888c2ecf20Sopenharmony_ci * interfaces, so this can't be made interface-specific. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(phy_lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int tsi108_read_mii(struct tsi108_prv_data *data, int reg) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci TSI_WRITE_PHY(TSI108_MAC_MII_ADDR, 1988c2ecf20Sopenharmony_ci (data->phy << TSI108_MAC_MII_ADDR_PHY) | 1998c2ecf20Sopenharmony_ci (reg << TSI108_MAC_MII_ADDR_REG)); 2008c2ecf20Sopenharmony_ci TSI_WRITE_PHY(TSI108_MAC_MII_CMD, 0); 2018c2ecf20Sopenharmony_ci TSI_WRITE_PHY(TSI108_MAC_MII_CMD, TSI108_MAC_MII_CMD_READ); 2028c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 2038c2ecf20Sopenharmony_ci if (!(TSI_READ_PHY(TSI108_MAC_MII_IND) & 2048c2ecf20Sopenharmony_ci (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY))) 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci udelay(10); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (i == 100) 2108c2ecf20Sopenharmony_ci return 0xffff; 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci return TSI_READ_PHY(TSI108_MAC_MII_DATAIN); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void tsi108_write_mii(struct tsi108_prv_data *data, 2168c2ecf20Sopenharmony_ci int reg, u16 val) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci unsigned i = 100; 2198c2ecf20Sopenharmony_ci TSI_WRITE_PHY(TSI108_MAC_MII_ADDR, 2208c2ecf20Sopenharmony_ci (data->phy << TSI108_MAC_MII_ADDR_PHY) | 2218c2ecf20Sopenharmony_ci (reg << TSI108_MAC_MII_ADDR_REG)); 2228c2ecf20Sopenharmony_ci TSI_WRITE_PHY(TSI108_MAC_MII_DATAOUT, val); 2238c2ecf20Sopenharmony_ci while (i--) { 2248c2ecf20Sopenharmony_ci if(!(TSI_READ_PHY(TSI108_MAC_MII_IND) & 2258c2ecf20Sopenharmony_ci TSI108_MAC_MII_IND_BUSY)) 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci udelay(10); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int tsi108_mdio_read(struct net_device *dev, int addr, int reg) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 2348c2ecf20Sopenharmony_ci return tsi108_read_mii(data, reg); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void tsi108_mdio_write(struct net_device *dev, int addr, int reg, int val) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 2408c2ecf20Sopenharmony_ci tsi108_write_mii(data, reg, val); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline void tsi108_write_tbi(struct tsi108_prv_data *data, 2448c2ecf20Sopenharmony_ci int reg, u16 val) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci unsigned i = 1000; 2478c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_MII_ADDR, 2488c2ecf20Sopenharmony_ci (0x1e << TSI108_MAC_MII_ADDR_PHY) 2498c2ecf20Sopenharmony_ci | (reg << TSI108_MAC_MII_ADDR_REG)); 2508c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_MII_DATAOUT, val); 2518c2ecf20Sopenharmony_ci while(i--) { 2528c2ecf20Sopenharmony_ci if(!(TSI_READ(TSI108_MAC_MII_IND) & TSI108_MAC_MII_IND_BUSY)) 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci udelay(10); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci printk(KERN_ERR "%s function time out\n", __func__); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int mii_speed(struct mii_if_info *mii) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci int advert, lpa, val, media; 2628c2ecf20Sopenharmony_ci int lpa2 = 0; 2638c2ecf20Sopenharmony_ci int speed; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!mii_link_ok(mii)) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci val = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_BMSR); 2698c2ecf20Sopenharmony_ci if ((val & BMSR_ANEGCOMPLETE) == 0) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci advert = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_ADVERTISE); 2738c2ecf20Sopenharmony_ci lpa = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_LPA); 2748c2ecf20Sopenharmony_ci media = mii_nway_result(advert & lpa); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (mii->supports_gmii) 2778c2ecf20Sopenharmony_ci lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci speed = lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 : 2808c2ecf20Sopenharmony_ci (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 100 : 10); 2818c2ecf20Sopenharmony_ci return speed; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void tsi108_check_phy(struct net_device *dev) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 2878c2ecf20Sopenharmony_ci u32 mac_cfg2_reg, portctrl_reg; 2888c2ecf20Sopenharmony_ci u32 duplex; 2898c2ecf20Sopenharmony_ci u32 speed; 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci spin_lock_irqsave(&phy_lock, flags); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!data->phy_ok) 2958c2ecf20Sopenharmony_ci goto out; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci duplex = mii_check_media(&data->mii_if, netif_msg_link(data), data->init_media); 2988c2ecf20Sopenharmony_ci data->init_media = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) { 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci speed = mii_speed(&data->mii_if); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if ((speed != data->speed) || duplex) { 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci mac_cfg2_reg = TSI_READ(TSI108_MAC_CFG2); 3078c2ecf20Sopenharmony_ci portctrl_reg = TSI_READ(TSI108_EC_PORTCTRL); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (speed == 1000) { 3128c2ecf20Sopenharmony_ci mac_cfg2_reg |= TSI108_MAC_CFG2_GIG; 3138c2ecf20Sopenharmony_ci portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG; 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG; 3168c2ecf20Sopenharmony_ci portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci data->speed = speed; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (data->mii_if.full_duplex) { 3228c2ecf20Sopenharmony_ci mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX; 3238c2ecf20Sopenharmony_ci portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX; 3248c2ecf20Sopenharmony_ci data->duplex = 2; 3258c2ecf20Sopenharmony_ci } else { 3268c2ecf20Sopenharmony_ci mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX; 3278c2ecf20Sopenharmony_ci portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX; 3288c2ecf20Sopenharmony_ci data->duplex = 1; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG2, mac_cfg2_reg); 3328c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_PORTCTRL, portctrl_reg); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (data->link_up == 0) { 3368c2ecf20Sopenharmony_ci /* The manual says it can take 3-4 usecs for the speed change 3378c2ecf20Sopenharmony_ci * to take effect. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci udelay(5); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock(&data->txlock); 3428c2ecf20Sopenharmony_ci if (is_valid_ether_addr(dev->dev_addr) && data->txfree) 3438c2ecf20Sopenharmony_ci netif_wake_queue(dev); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci data->link_up = 1; 3468c2ecf20Sopenharmony_ci spin_unlock(&data->txlock); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci if (data->link_up == 1) { 3508c2ecf20Sopenharmony_ci netif_stop_queue(dev); 3518c2ecf20Sopenharmony_ci data->link_up = 0; 3528c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s : link is down\n", dev->name); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci goto out; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciout: 3608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&phy_lock, flags); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic inline void 3648c2ecf20Sopenharmony_citsi108_stat_carry_one(int carry, int carry_bit, int carry_shift, 3658c2ecf20Sopenharmony_ci unsigned long *upper) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci if (carry & carry_bit) 3688c2ecf20Sopenharmony_ci *upper += carry_shift; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void tsi108_stat_carry(struct net_device *dev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 3748c2ecf20Sopenharmony_ci unsigned long flags; 3758c2ecf20Sopenharmony_ci u32 carry1, carry2; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci spin_lock_irqsave(&data->misclock, flags); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci carry1 = TSI_READ(TSI108_STAT_CARRY1); 3808c2ecf20Sopenharmony_ci carry2 = TSI_READ(TSI108_STAT_CARRY2); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_STAT_CARRY1, carry1); 3838c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_STAT_CARRY2, carry2); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES, 3868c2ecf20Sopenharmony_ci TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS, 3898c2ecf20Sopenharmony_ci TSI108_STAT_RXPKTS_CARRY, 3908c2ecf20Sopenharmony_ci &data->stats.rx_packets); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS, 3938c2ecf20Sopenharmony_ci TSI108_STAT_RXFCS_CARRY, &data->rx_fcs); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST, 3968c2ecf20Sopenharmony_ci TSI108_STAT_RXMCAST_CARRY, 3978c2ecf20Sopenharmony_ci &data->stats.multicast); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN, 4008c2ecf20Sopenharmony_ci TSI108_STAT_RXALIGN_CARRY, 4018c2ecf20Sopenharmony_ci &data->stats.rx_frame_errors); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH, 4048c2ecf20Sopenharmony_ci TSI108_STAT_RXLENGTH_CARRY, 4058c2ecf20Sopenharmony_ci &data->stats.rx_length_errors); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT, 4088c2ecf20Sopenharmony_ci TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO, 4118c2ecf20Sopenharmony_ci TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG, 4148c2ecf20Sopenharmony_ci TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER, 4178c2ecf20Sopenharmony_ci TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP, 4208c2ecf20Sopenharmony_ci TSI108_STAT_RXDROP_CARRY, 4218c2ecf20Sopenharmony_ci &data->stats.rx_missed_errors); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES, 4248c2ecf20Sopenharmony_ci TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS, 4278c2ecf20Sopenharmony_ci TSI108_STAT_TXPKTS_CARRY, 4288c2ecf20Sopenharmony_ci &data->stats.tx_packets); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF, 4318c2ecf20Sopenharmony_ci TSI108_STAT_TXEXDEF_CARRY, 4328c2ecf20Sopenharmony_ci &data->stats.tx_aborted_errors); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL, 4358c2ecf20Sopenharmony_ci TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL, 4388c2ecf20Sopenharmony_ci TSI108_STAT_TXTCOL_CARRY, 4398c2ecf20Sopenharmony_ci &data->stats.collisions); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE, 4428c2ecf20Sopenharmony_ci TSI108_STAT_TXPAUSEDROP_CARRY, 4438c2ecf20Sopenharmony_ci &data->tx_pause_drop); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&data->misclock, flags); 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* Read a stat counter atomically with respect to carries. 4498c2ecf20Sopenharmony_ci * data->misclock must be held. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic inline unsigned long 4528c2ecf20Sopenharmony_citsi108_read_stat(struct tsi108_prv_data * data, int reg, int carry_bit, 4538c2ecf20Sopenharmony_ci int carry_shift, unsigned long *upper) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci int carryreg; 4568c2ecf20Sopenharmony_ci unsigned long val; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (reg < 0xb0) 4598c2ecf20Sopenharmony_ci carryreg = TSI108_STAT_CARRY1; 4608c2ecf20Sopenharmony_ci else 4618c2ecf20Sopenharmony_ci carryreg = TSI108_STAT_CARRY2; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci again: 4648c2ecf20Sopenharmony_ci val = TSI_READ(reg) | *upper; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Check to see if it overflowed, but the interrupt hasn't 4678c2ecf20Sopenharmony_ci * been serviced yet. If so, handle the carry here, and 4688c2ecf20Sopenharmony_ci * try again. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (unlikely(TSI_READ(carryreg) & carry_bit)) { 4728c2ecf20Sopenharmony_ci *upper += carry_shift; 4738c2ecf20Sopenharmony_ci TSI_WRITE(carryreg, carry_bit); 4748c2ecf20Sopenharmony_ci goto again; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return val; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic struct net_device_stats *tsi108_get_stats(struct net_device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci unsigned long excol; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 4858c2ecf20Sopenharmony_ci spin_lock_irq(&data->misclock); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci data->tmpstats.rx_packets = 4888c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXPKTS, 4898c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXPKTS, 4908c2ecf20Sopenharmony_ci TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci data->tmpstats.tx_packets = 4938c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_TXPKTS, 4948c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXPKTS, 4958c2ecf20Sopenharmony_ci TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci data->tmpstats.rx_bytes = 4988c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXBYTES, 4998c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXBYTES, 5008c2ecf20Sopenharmony_ci TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci data->tmpstats.tx_bytes = 5038c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_TXBYTES, 5048c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXBYTES, 5058c2ecf20Sopenharmony_ci TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci data->tmpstats.multicast = 5088c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXMCAST, 5098c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXMCAST, 5108c2ecf20Sopenharmony_ci TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL, 5138c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXEXCOL, 5148c2ecf20Sopenharmony_ci TSI108_STAT_TXEXCOL_CARRY, 5158c2ecf20Sopenharmony_ci &data->tx_coll_abort); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci data->tmpstats.collisions = 5188c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_TXTCOL, 5198c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXTCOL, 5208c2ecf20Sopenharmony_ci TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci data->tmpstats.collisions += excol; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci data->tmpstats.rx_length_errors = 5258c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXLENGTH, 5268c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXLENGTH, 5278c2ecf20Sopenharmony_ci TSI108_STAT_RXLENGTH_CARRY, 5288c2ecf20Sopenharmony_ci &data->stats.rx_length_errors); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci data->tmpstats.rx_length_errors += 5318c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXRUNT, 5328c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXRUNT, 5338c2ecf20Sopenharmony_ci TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci data->tmpstats.rx_length_errors += 5368c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXJUMBO, 5378c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXJUMBO, 5388c2ecf20Sopenharmony_ci TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci data->tmpstats.rx_frame_errors = 5418c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXALIGN, 5428c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXALIGN, 5438c2ecf20Sopenharmony_ci TSI108_STAT_RXALIGN_CARRY, 5448c2ecf20Sopenharmony_ci &data->stats.rx_frame_errors); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci data->tmpstats.rx_frame_errors += 5478c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXFCS, 5488c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY, 5498c2ecf20Sopenharmony_ci &data->rx_fcs); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci data->tmpstats.rx_frame_errors += 5528c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXFRAG, 5538c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXFRAG, 5548c2ecf20Sopenharmony_ci TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci data->tmpstats.rx_missed_errors = 5578c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_RXDROP, 5588c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXDROP, 5598c2ecf20Sopenharmony_ci TSI108_STAT_RXDROP_CARRY, 5608c2ecf20Sopenharmony_ci &data->stats.rx_missed_errors); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* These three are maintained by software. */ 5638c2ecf20Sopenharmony_ci data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors; 5648c2ecf20Sopenharmony_ci data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci data->tmpstats.tx_aborted_errors = 5678c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_TXEXDEF, 5688c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXEXDEF, 5698c2ecf20Sopenharmony_ci TSI108_STAT_TXEXDEF_CARRY, 5708c2ecf20Sopenharmony_ci &data->stats.tx_aborted_errors); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci data->tmpstats.tx_aborted_errors += 5738c2ecf20Sopenharmony_ci tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP, 5748c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXPAUSE, 5758c2ecf20Sopenharmony_ci TSI108_STAT_TXPAUSEDROP_CARRY, 5768c2ecf20Sopenharmony_ci &data->tx_pause_drop); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci data->tmpstats.tx_aborted_errors += excol; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors; 5818c2ecf20Sopenharmony_ci data->tmpstats.rx_errors = data->tmpstats.rx_length_errors + 5828c2ecf20Sopenharmony_ci data->tmpstats.rx_crc_errors + 5838c2ecf20Sopenharmony_ci data->tmpstats.rx_frame_errors + 5848c2ecf20Sopenharmony_ci data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci spin_unlock_irq(&data->misclock); 5878c2ecf20Sopenharmony_ci return &data->tmpstats; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic void tsi108_restart_rx(struct tsi108_prv_data * data, struct net_device *dev) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXQ_PTRHIGH, 5938c2ecf20Sopenharmony_ci TSI108_EC_RXQ_PTRHIGH_VALID); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO 5968c2ecf20Sopenharmony_ci | TSI108_EC_RXCTRL_QUEUE0); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void tsi108_restart_tx(struct tsi108_prv_data * data) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXQ_PTRHIGH, 6028c2ecf20Sopenharmony_ci TSI108_EC_TXQ_PTRHIGH_VALID); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT | 6058c2ecf20Sopenharmony_ci TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* txlock must be held by caller, with IRQs disabled, and 6098c2ecf20Sopenharmony_ci * with permission to re-enable them when the lock is dropped. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_cistatic void tsi108_complete_tx(struct net_device *dev) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 6148c2ecf20Sopenharmony_ci int tx; 6158c2ecf20Sopenharmony_ci struct sk_buff *skb; 6168c2ecf20Sopenharmony_ci int release = 0; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci while (!data->txfree || data->txhead != data->txtail) { 6198c2ecf20Sopenharmony_ci tx = data->txtail; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (data->txring[tx].misc & TSI108_TX_OWN) 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci skb = data->txskbs[tx]; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!(data->txring[tx].misc & TSI108_TX_OK)) 6278c2ecf20Sopenharmony_ci printk("%s: bad tx packet, misc %x\n", 6288c2ecf20Sopenharmony_ci dev->name, data->txring[tx].misc); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN; 6318c2ecf20Sopenharmony_ci data->txfree++; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (data->txring[tx].misc & TSI108_TX_EOF) { 6348c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6358c2ecf20Sopenharmony_ci release++; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (release) { 6408c2ecf20Sopenharmony_ci if (is_valid_ether_addr(dev->dev_addr) && data->link_up) 6418c2ecf20Sopenharmony_ci netif_wake_queue(dev); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int tsi108_send_packet(struct sk_buff * skb, struct net_device *dev) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 6488c2ecf20Sopenharmony_ci int frags = skb_shinfo(skb)->nr_frags + 1; 6498c2ecf20Sopenharmony_ci int i; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (!data->phy_ok && net_ratelimit()) 6528c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (!data->link_up) { 6558c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Transmit while link is down!\n", 6568c2ecf20Sopenharmony_ci dev->name); 6578c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6588c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (data->txfree < MAX_SKB_FRAGS + 1) { 6628c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (net_ratelimit()) 6658c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Transmit with full tx ring!\n", 6668c2ecf20Sopenharmony_ci dev->name); 6678c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (data->txfree - frags < MAX_SKB_FRAGS + 1) { 6718c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci spin_lock_irq(&data->txlock); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci for (i = 0; i < frags; i++) { 6778c2ecf20Sopenharmony_ci int misc = 0; 6788c2ecf20Sopenharmony_ci int tx = data->txhead; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with 6818c2ecf20Sopenharmony_ci * the interrupt bit. TX descriptor-complete interrupts are 6828c2ecf20Sopenharmony_ci * enabled when the queue fills up, and masked when there is 6838c2ecf20Sopenharmony_ci * still free space. This way, when saturating the outbound 6848c2ecf20Sopenharmony_ci * link, the tx interrupts are kept to a reasonable level. 6858c2ecf20Sopenharmony_ci * When the queue is not full, reclamation of skbs still occurs 6868c2ecf20Sopenharmony_ci * as new packets are transmitted, or on a queue-empty 6878c2ecf20Sopenharmony_ci * interrupt. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if ((tx % TSI108_TX_INT_FREQ == 0) && 6918c2ecf20Sopenharmony_ci ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)) 6928c2ecf20Sopenharmony_ci misc = TSI108_TX_INT; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci data->txskbs[tx] = skb; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (i == 0) { 6978c2ecf20Sopenharmony_ci data->txring[tx].buf0 = dma_map_single(&data->pdev->dev, 6988c2ecf20Sopenharmony_ci skb->data, skb_headlen(skb), 6998c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7008c2ecf20Sopenharmony_ci data->txring[tx].len = skb_headlen(skb); 7018c2ecf20Sopenharmony_ci misc |= TSI108_TX_SOF; 7028c2ecf20Sopenharmony_ci } else { 7038c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci data->txring[tx].buf0 = 7068c2ecf20Sopenharmony_ci skb_frag_dma_map(&data->pdev->dev, frag, 7078c2ecf20Sopenharmony_ci 0, skb_frag_size(frag), 7088c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7098c2ecf20Sopenharmony_ci data->txring[tx].len = skb_frag_size(frag); 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (i == frags - 1) 7138c2ecf20Sopenharmony_ci misc |= TSI108_TX_EOF; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (netif_msg_pktdata(data)) { 7168c2ecf20Sopenharmony_ci int i; 7178c2ecf20Sopenharmony_ci printk("%s: Tx Frame contents (%d)\n", dev->name, 7188c2ecf20Sopenharmony_ci skb->len); 7198c2ecf20Sopenharmony_ci for (i = 0; i < skb->len; i++) 7208c2ecf20Sopenharmony_ci printk(" %2.2x", skb->data[i]); 7218c2ecf20Sopenharmony_ci printk(".\n"); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci data->txring[tx].misc = misc | TSI108_TX_OWN; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN; 7268c2ecf20Sopenharmony_ci data->txfree--; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci tsi108_complete_tx(dev); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* This must be done after the check for completed tx descriptors, 7328c2ecf20Sopenharmony_ci * so that the tail pointer is correct. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (!(TSI_READ(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_QUEUE0)) 7368c2ecf20Sopenharmony_ci tsi108_restart_tx(data); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci spin_unlock_irq(&data->txlock); 7398c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic int tsi108_complete_rx(struct net_device *dev, int budget) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 7458c2ecf20Sopenharmony_ci int done = 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci while (data->rxfree && done != budget) { 7488c2ecf20Sopenharmony_ci int rx = data->rxtail; 7498c2ecf20Sopenharmony_ci struct sk_buff *skb; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (data->rxring[rx].misc & TSI108_RX_OWN) 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci skb = data->rxskbs[rx]; 7558c2ecf20Sopenharmony_ci data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN; 7568c2ecf20Sopenharmony_ci data->rxfree--; 7578c2ecf20Sopenharmony_ci done++; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (data->rxring[rx].misc & TSI108_RX_BAD) { 7608c2ecf20Sopenharmony_ci spin_lock_irq(&data->misclock); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (data->rxring[rx].misc & TSI108_RX_CRC) 7638c2ecf20Sopenharmony_ci data->stats.rx_crc_errors++; 7648c2ecf20Sopenharmony_ci if (data->rxring[rx].misc & TSI108_RX_OVER) 7658c2ecf20Sopenharmony_ci data->stats.rx_fifo_errors++; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci spin_unlock_irq(&data->misclock); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7708c2ecf20Sopenharmony_ci continue; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci if (netif_msg_pktdata(data)) { 7738c2ecf20Sopenharmony_ci int i; 7748c2ecf20Sopenharmony_ci printk("%s: Rx Frame contents (%d)\n", 7758c2ecf20Sopenharmony_ci dev->name, data->rxring[rx].len); 7768c2ecf20Sopenharmony_ci for (i = 0; i < data->rxring[rx].len; i++) 7778c2ecf20Sopenharmony_ci printk(" %2.2x", skb->data[i]); 7788c2ecf20Sopenharmony_ci printk(".\n"); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci skb_put(skb, data->rxring[rx].len); 7828c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 7838c2ecf20Sopenharmony_ci netif_receive_skb(skb); 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return done; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int tsi108_refill_rx(struct net_device *dev, int budget) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 7928c2ecf20Sopenharmony_ci int done = 0; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci while (data->rxfree != TSI108_RXRING_LEN && done != budget) { 7958c2ecf20Sopenharmony_ci int rx = data->rxhead; 7968c2ecf20Sopenharmony_ci struct sk_buff *skb; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE); 7998c2ecf20Sopenharmony_ci data->rxskbs[rx] = skb; 8008c2ecf20Sopenharmony_ci if (!skb) 8018c2ecf20Sopenharmony_ci break; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci data->rxring[rx].buf0 = dma_map_single(&data->pdev->dev, 8048c2ecf20Sopenharmony_ci skb->data, TSI108_RX_SKB_SIZE, 8058c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* Sometimes the hardware sets blen to zero after packet 8088c2ecf20Sopenharmony_ci * reception, even though the manual says that it's only ever 8098c2ecf20Sopenharmony_ci * modified by the driver. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci data->rxring[rx].blen = TSI108_RX_SKB_SIZE; 8138c2ecf20Sopenharmony_ci data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN; 8168c2ecf20Sopenharmony_ci data->rxfree++; 8178c2ecf20Sopenharmony_ci done++; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (done != 0 && !(TSI_READ(TSI108_EC_RXSTAT) & 8218c2ecf20Sopenharmony_ci TSI108_EC_RXSTAT_QUEUE0)) 8228c2ecf20Sopenharmony_ci tsi108_restart_rx(data, dev); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return done; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic int tsi108_poll(struct napi_struct *napi, int budget) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = container_of(napi, struct tsi108_prv_data, napi); 8308c2ecf20Sopenharmony_ci struct net_device *dev = data->dev; 8318c2ecf20Sopenharmony_ci u32 estat = TSI_READ(TSI108_EC_RXESTAT); 8328c2ecf20Sopenharmony_ci u32 intstat = TSI_READ(TSI108_EC_INTSTAT); 8338c2ecf20Sopenharmony_ci int num_received = 0, num_filled = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH | 8368c2ecf20Sopenharmony_ci TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXESTAT, estat); 8398c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTSTAT, intstat); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT)) 8428c2ecf20Sopenharmony_ci num_received = tsi108_complete_rx(dev, budget); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* This should normally fill no more slots than the number of 8458c2ecf20Sopenharmony_ci * packets received in tsi108_complete_rx(). The exception 8468c2ecf20Sopenharmony_ci * is when we previously ran out of memory for RX SKBs. In that 8478c2ecf20Sopenharmony_ci * case, it's helpful to obey the budget, not only so that the 8488c2ecf20Sopenharmony_ci * CPU isn't hogged, but so that memory (which may still be low) 8498c2ecf20Sopenharmony_ci * is not hogged by one device. 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * A work unit is considered to be two SKBs to allow us to catch 8528c2ecf20Sopenharmony_ci * up when the ring has shrunk due to out-of-memory but we're 8538c2ecf20Sopenharmony_ci * still removing the full budget's worth of packets each time. 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (data->rxfree < TSI108_RXRING_LEN) 8578c2ecf20Sopenharmony_ci num_filled = tsi108_refill_rx(dev, budget * 2); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (intstat & TSI108_INT_RXERROR) { 8608c2ecf20Sopenharmony_ci u32 err = TSI_READ(TSI108_EC_RXERR); 8618c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXERR, err); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (err) { 8648c2ecf20Sopenharmony_ci if (net_ratelimit()) 8658c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: RX error %x\n", 8668c2ecf20Sopenharmony_ci dev->name, err); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (!(TSI_READ(TSI108_EC_RXSTAT) & 8698c2ecf20Sopenharmony_ci TSI108_EC_RXSTAT_QUEUE0)) 8708c2ecf20Sopenharmony_ci tsi108_restart_rx(data, dev); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (intstat & TSI108_INT_RXOVERRUN) { 8758c2ecf20Sopenharmony_ci spin_lock_irq(&data->misclock); 8768c2ecf20Sopenharmony_ci data->stats.rx_fifo_errors++; 8778c2ecf20Sopenharmony_ci spin_unlock_irq(&data->misclock); 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (num_received < budget) { 8818c2ecf20Sopenharmony_ci data->rxpending = 0; 8828c2ecf20Sopenharmony_ci napi_complete_done(napi, num_received); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, 8858c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_INTMASK) 8868c2ecf20Sopenharmony_ci & ~(TSI108_INT_RXQUEUE0 8878c2ecf20Sopenharmony_ci | TSI108_INT_RXTHRESH | 8888c2ecf20Sopenharmony_ci TSI108_INT_RXOVERRUN | 8898c2ecf20Sopenharmony_ci TSI108_INT_RXERROR | 8908c2ecf20Sopenharmony_ci TSI108_INT_RXWAIT)); 8918c2ecf20Sopenharmony_ci } else { 8928c2ecf20Sopenharmony_ci data->rxpending = 1; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return num_received; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void tsi108_rx_int(struct net_device *dev) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* A race could cause dev to already be scheduled, so it's not an 9038c2ecf20Sopenharmony_ci * error if that happens (and interrupts shouldn't be re-masked, 9048c2ecf20Sopenharmony_ci * because that can cause harmful races, if poll has already 9058c2ecf20Sopenharmony_ci * unmasked them but not cleared LINK_STATE_SCHED). 9068c2ecf20Sopenharmony_ci * 9078c2ecf20Sopenharmony_ci * This can happen if this code races with tsi108_poll(), which masks 9088c2ecf20Sopenharmony_ci * the interrupts after tsi108_irq_one() read the mask, but before 9098c2ecf20Sopenharmony_ci * napi_schedule is called. It could also happen due to calls 9108c2ecf20Sopenharmony_ci * from tsi108_check_rxring(). 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (napi_schedule_prep(&data->napi)) { 9148c2ecf20Sopenharmony_ci /* Mask, rather than ack, the receive interrupts. The ack 9158c2ecf20Sopenharmony_ci * will happen in tsi108_poll(). 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, 9198c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_INTMASK) | 9208c2ecf20Sopenharmony_ci TSI108_INT_RXQUEUE0 9218c2ecf20Sopenharmony_ci | TSI108_INT_RXTHRESH | 9228c2ecf20Sopenharmony_ci TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | 9238c2ecf20Sopenharmony_ci TSI108_INT_RXWAIT); 9248c2ecf20Sopenharmony_ci __napi_schedule(&data->napi); 9258c2ecf20Sopenharmony_ci } else { 9268c2ecf20Sopenharmony_ci if (!netif_running(dev)) { 9278c2ecf20Sopenharmony_ci /* This can happen if an interrupt occurs while the 9288c2ecf20Sopenharmony_ci * interface is being brought down, as the START 9298c2ecf20Sopenharmony_ci * bit is cleared before the stop function is called. 9308c2ecf20Sopenharmony_ci * 9318c2ecf20Sopenharmony_ci * In this case, the interrupts must be masked, or 9328c2ecf20Sopenharmony_ci * they will continue indefinitely. 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * There's a race here if the interface is brought down 9358c2ecf20Sopenharmony_ci * and then up in rapid succession, as the device could 9368c2ecf20Sopenharmony_ci * be made running after the above check and before 9378c2ecf20Sopenharmony_ci * the masking below. This will only happen if the IRQ 9388c2ecf20Sopenharmony_ci * thread has a lower priority than the task brining 9398c2ecf20Sopenharmony_ci * up the interface. Fixing this race would likely 9408c2ecf20Sopenharmony_ci * require changes in generic code. 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, 9448c2ecf20Sopenharmony_ci TSI_READ 9458c2ecf20Sopenharmony_ci (TSI108_EC_INTMASK) | 9468c2ecf20Sopenharmony_ci TSI108_INT_RXQUEUE0 | 9478c2ecf20Sopenharmony_ci TSI108_INT_RXTHRESH | 9488c2ecf20Sopenharmony_ci TSI108_INT_RXOVERRUN | 9498c2ecf20Sopenharmony_ci TSI108_INT_RXERROR | 9508c2ecf20Sopenharmony_ci TSI108_INT_RXWAIT); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci/* If the RX ring has run out of memory, try periodically 9568c2ecf20Sopenharmony_ci * to allocate some more, as otherwise poll would never 9578c2ecf20Sopenharmony_ci * get called (apart from the initial end-of-queue condition). 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * This is called once per second (by default) from the thread. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic void tsi108_check_rxring(struct net_device *dev) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* A poll is scheduled, as opposed to caling tsi108_refill_rx 9678c2ecf20Sopenharmony_ci * directly, so as to keep the receive path single-threaded 9688c2ecf20Sopenharmony_ci * (and thus not needing a lock). 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4) 9728c2ecf20Sopenharmony_ci tsi108_rx_int(dev); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic void tsi108_tx_int(struct net_device *dev) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 9788c2ecf20Sopenharmony_ci u32 estat = TSI_READ(TSI108_EC_TXESTAT); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXESTAT, estat); 9818c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 | 9828c2ecf20Sopenharmony_ci TSI108_INT_TXIDLE | TSI108_INT_TXERROR); 9838c2ecf20Sopenharmony_ci if (estat & TSI108_EC_TXESTAT_Q0_ERR) { 9848c2ecf20Sopenharmony_ci u32 err = TSI_READ(TSI108_EC_TXERR); 9858c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXERR, err); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (err && net_ratelimit()) 9888c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: TX error %x\n", dev->name, err); 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | TSI108_EC_TXESTAT_Q0_EOQ)) { 9928c2ecf20Sopenharmony_ci spin_lock(&data->txlock); 9938c2ecf20Sopenharmony_ci tsi108_complete_tx(dev); 9948c2ecf20Sopenharmony_ci spin_unlock(&data->txlock); 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_cistatic irqreturn_t tsi108_irq(int irq, void *dev_id) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 10028c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 10038c2ecf20Sopenharmony_ci u32 stat = TSI_READ(TSI108_EC_INTSTAT); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (!(stat & TSI108_INT_ANY)) 10068c2ecf20Sopenharmony_ci return IRQ_NONE; /* Not our interrupt */ 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci stat &= ~TSI_READ(TSI108_EC_INTMASK); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE | 10118c2ecf20Sopenharmony_ci TSI108_INT_TXERROR)) 10128c2ecf20Sopenharmony_ci tsi108_tx_int(dev); 10138c2ecf20Sopenharmony_ci if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH | 10148c2ecf20Sopenharmony_ci TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN | 10158c2ecf20Sopenharmony_ci TSI108_INT_RXERROR)) 10168c2ecf20Sopenharmony_ci tsi108_rx_int(dev); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (stat & TSI108_INT_SFN) { 10198c2ecf20Sopenharmony_ci if (net_ratelimit()) 10208c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: SFN error\n", dev->name); 10218c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_SFN); 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (stat & TSI108_INT_STATCARRY) { 10258c2ecf20Sopenharmony_ci tsi108_stat_carry(dev); 10268c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY); 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic void tsi108_stop_ethernet(struct net_device *dev) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 10358c2ecf20Sopenharmony_ci int i = 1000; 10368c2ecf20Sopenharmony_ci /* Disable all TX and RX queues ... */ 10378c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXCTRL, 0); 10388c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCTRL, 0); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* ...and wait for them to become idle */ 10418c2ecf20Sopenharmony_ci while(i--) { 10428c2ecf20Sopenharmony_ci if(!(TSI_READ(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_ACTIVE)) 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci udelay(10); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci i = 1000; 10478c2ecf20Sopenharmony_ci while(i--){ 10488c2ecf20Sopenharmony_ci if(!(TSI_READ(TSI108_EC_RXSTAT) & TSI108_EC_RXSTAT_ACTIVE)) 10498c2ecf20Sopenharmony_ci return; 10508c2ecf20Sopenharmony_ci udelay(10); 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci printk(KERN_ERR "%s function time out\n", __func__); 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic void tsi108_reset_ether(struct tsi108_prv_data * data) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST); 10588c2ecf20Sopenharmony_ci udelay(100); 10598c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG1, 0); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATRST); 10628c2ecf20Sopenharmony_ci udelay(100); 10638c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_PORTCTRL, 10648c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_PORTCTRL) & 10658c2ecf20Sopenharmony_ci ~TSI108_EC_PORTCTRL_STATRST); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST); 10688c2ecf20Sopenharmony_ci udelay(100); 10698c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXCFG, 10708c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_TXCFG) & 10718c2ecf20Sopenharmony_ci ~TSI108_EC_TXCFG_RST); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST); 10748c2ecf20Sopenharmony_ci udelay(100); 10758c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCFG, 10768c2ecf20Sopenharmony_ci TSI_READ(TSI108_EC_RXCFG) & 10778c2ecf20Sopenharmony_ci ~TSI108_EC_RXCFG_RST); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_MII_MGMT_CFG, 10808c2ecf20Sopenharmony_ci TSI_READ(TSI108_MAC_MII_MGMT_CFG) | 10818c2ecf20Sopenharmony_ci TSI108_MAC_MII_MGMT_RST); 10828c2ecf20Sopenharmony_ci udelay(100); 10838c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_MII_MGMT_CFG, 10848c2ecf20Sopenharmony_ci (TSI_READ(TSI108_MAC_MII_MGMT_CFG) & 10858c2ecf20Sopenharmony_ci ~(TSI108_MAC_MII_MGMT_RST | 10868c2ecf20Sopenharmony_ci TSI108_MAC_MII_MGMT_CLK)) | 0x07); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic int tsi108_get_mac(struct net_device *dev) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 10928c2ecf20Sopenharmony_ci u32 word1 = TSI_READ(TSI108_MAC_ADDR1); 10938c2ecf20Sopenharmony_ci u32 word2 = TSI_READ(TSI108_MAC_ADDR2); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Note that the octets are reversed from what the manual says, 10968c2ecf20Sopenharmony_ci * producing an even weirder ordering... 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_ci if (word2 == 0 && word1 == 0) { 10998c2ecf20Sopenharmony_ci dev->dev_addr[0] = 0x00; 11008c2ecf20Sopenharmony_ci dev->dev_addr[1] = 0x06; 11018c2ecf20Sopenharmony_ci dev->dev_addr[2] = 0xd2; 11028c2ecf20Sopenharmony_ci dev->dev_addr[3] = 0x00; 11038c2ecf20Sopenharmony_ci dev->dev_addr[4] = 0x00; 11048c2ecf20Sopenharmony_ci if (0x8 == data->phy) 11058c2ecf20Sopenharmony_ci dev->dev_addr[5] = 0x01; 11068c2ecf20Sopenharmony_ci else 11078c2ecf20Sopenharmony_ci dev->dev_addr[5] = 0x02; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) | 11128c2ecf20Sopenharmony_ci (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_ADDR1, word1); 11158c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_ADDR2, word2); 11168c2ecf20Sopenharmony_ci } else { 11178c2ecf20Sopenharmony_ci dev->dev_addr[0] = (word2 >> 16) & 0xff; 11188c2ecf20Sopenharmony_ci dev->dev_addr[1] = (word2 >> 24) & 0xff; 11198c2ecf20Sopenharmony_ci dev->dev_addr[2] = (word1 >> 0) & 0xff; 11208c2ecf20Sopenharmony_ci dev->dev_addr[3] = (word1 >> 8) & 0xff; 11218c2ecf20Sopenharmony_ci dev->dev_addr[4] = (word1 >> 16) & 0xff; 11228c2ecf20Sopenharmony_ci dev->dev_addr[5] = (word1 >> 24) & 0xff; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(dev->dev_addr)) { 11268c2ecf20Sopenharmony_ci printk(KERN_ERR 11278c2ecf20Sopenharmony_ci "%s: Invalid MAC address. word1: %08x, word2: %08x\n", 11288c2ecf20Sopenharmony_ci dev->name, word1, word2); 11298c2ecf20Sopenharmony_ci return -EINVAL; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci return 0; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic int tsi108_set_mac(struct net_device *dev, void *addr) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 11388c2ecf20Sopenharmony_ci u32 word1, word2; 11398c2ecf20Sopenharmony_ci int i; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr)) 11428c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 11458c2ecf20Sopenharmony_ci /* +2 is for the offset of the HW addr type */ 11468c2ecf20Sopenharmony_ci dev->dev_addr[i] = ((unsigned char *)addr)[i + 2]; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) | 11518c2ecf20Sopenharmony_ci (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci spin_lock_irq(&data->misclock); 11548c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_ADDR1, word1); 11558c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_ADDR2, word2); 11568c2ecf20Sopenharmony_ci spin_lock(&data->txlock); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (data->txfree && data->link_up) 11598c2ecf20Sopenharmony_ci netif_wake_queue(dev); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci spin_unlock(&data->txlock); 11628c2ecf20Sopenharmony_ci spin_unlock_irq(&data->misclock); 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci/* Protected by dev->xmit_lock. */ 11678c2ecf20Sopenharmony_cistatic void tsi108_set_rx_mode(struct net_device *dev) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 11708c2ecf20Sopenharmony_ci u32 rxcfg = TSI_READ(TSI108_EC_RXCFG); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 11738c2ecf20Sopenharmony_ci rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH); 11748c2ecf20Sopenharmony_ci rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE; 11758c2ecf20Sopenharmony_ci goto out; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) { 11818c2ecf20Sopenharmony_ci int i; 11828c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 11838c2ecf20Sopenharmony_ci rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci memset(data->mc_hash, 0, sizeof(data->mc_hash)); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 11888c2ecf20Sopenharmony_ci u32 hash, crc; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci crc = ether_crc(6, ha->addr); 11918c2ecf20Sopenharmony_ci hash = crc >> 23; 11928c2ecf20Sopenharmony_ci __set_bit(hash, &data->mc_hash[0]); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_HASHADDR, 11968c2ecf20Sopenharmony_ci TSI108_EC_HASHADDR_AUTOINC | 11978c2ecf20Sopenharmony_ci TSI108_EC_HASHADDR_MCAST); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 12008c2ecf20Sopenharmony_ci /* The manual says that the hardware may drop 12018c2ecf20Sopenharmony_ci * back-to-back writes to the data register. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ci udelay(1); 12048c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_HASHDATA, 12058c2ecf20Sopenharmony_ci data->mc_hash[i]); 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci out: 12108c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCFG, rxcfg); 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic void tsi108_init_phy(struct net_device *dev) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 12168c2ecf20Sopenharmony_ci u32 i = 0; 12178c2ecf20Sopenharmony_ci u16 phyval = 0; 12188c2ecf20Sopenharmony_ci unsigned long flags; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci spin_lock_irqsave(&phy_lock, flags); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci tsi108_write_mii(data, MII_BMCR, BMCR_RESET); 12238c2ecf20Sopenharmony_ci while (--i) { 12248c2ecf20Sopenharmony_ci if(!(tsi108_read_mii(data, MII_BMCR) & BMCR_RESET)) 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci udelay(10); 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci if (i == 0) 12298c2ecf20Sopenharmony_ci printk(KERN_ERR "%s function time out\n", __func__); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (data->phy_type == TSI108_PHY_BCM54XX) { 12328c2ecf20Sopenharmony_ci tsi108_write_mii(data, 0x09, 0x0300); 12338c2ecf20Sopenharmony_ci tsi108_write_mii(data, 0x10, 0x1020); 12348c2ecf20Sopenharmony_ci tsi108_write_mii(data, 0x1c, 0x8c00); 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci tsi108_write_mii(data, 12388c2ecf20Sopenharmony_ci MII_BMCR, 12398c2ecf20Sopenharmony_ci BMCR_ANENABLE | BMCR_ANRESTART); 12408c2ecf20Sopenharmony_ci while (tsi108_read_mii(data, MII_BMCR) & BMCR_ANRESTART) 12418c2ecf20Sopenharmony_ci cpu_relax(); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* Set G/MII mode and receive clock select in TBI control #2. The 12448c2ecf20Sopenharmony_ci * second port won't work if this isn't done, even though we don't 12458c2ecf20Sopenharmony_ci * use TBI mode. 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci tsi108_write_tbi(data, 0x11, 0x30); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* FIXME: It seems to take more than 2 back-to-back reads to the 12518c2ecf20Sopenharmony_ci * PHY_STAT register before the link up status bit is set. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci data->link_up = 0; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci while (!((phyval = tsi108_read_mii(data, MII_BMSR)) & 12578c2ecf20Sopenharmony_ci BMSR_LSTATUS)) { 12588c2ecf20Sopenharmony_ci if (i++ > (MII_READ_DELAY / 10)) { 12598c2ecf20Sopenharmony_ci break; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&phy_lock, flags); 12628c2ecf20Sopenharmony_ci msleep(10); 12638c2ecf20Sopenharmony_ci spin_lock_irqsave(&phy_lock, flags); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci data->mii_if.supports_gmii = mii_check_gmii_support(&data->mii_if); 12678c2ecf20Sopenharmony_ci printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyval); 12688c2ecf20Sopenharmony_ci data->phy_ok = 1; 12698c2ecf20Sopenharmony_ci data->init_media = 1; 12708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&phy_lock, flags); 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void tsi108_kill_phy(struct net_device *dev) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 12768c2ecf20Sopenharmony_ci unsigned long flags; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci spin_lock_irqsave(&phy_lock, flags); 12798c2ecf20Sopenharmony_ci tsi108_write_mii(data, MII_BMCR, BMCR_PDOWN); 12808c2ecf20Sopenharmony_ci data->phy_ok = 0; 12818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&phy_lock, flags); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic int tsi108_open(struct net_device *dev) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci int i; 12878c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 12888c2ecf20Sopenharmony_ci unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc); 12898c2ecf20Sopenharmony_ci unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev); 12928c2ecf20Sopenharmony_ci if (i != 0) { 12938c2ecf20Sopenharmony_ci printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n", 12948c2ecf20Sopenharmony_ci data->id, data->irq_num); 12958c2ecf20Sopenharmony_ci return i; 12968c2ecf20Sopenharmony_ci } else { 12978c2ecf20Sopenharmony_ci dev->irq = data->irq_num; 12988c2ecf20Sopenharmony_ci printk(KERN_NOTICE 12998c2ecf20Sopenharmony_ci "tsi108_open : Port %d Assigned IRQ %d to %s\n", 13008c2ecf20Sopenharmony_ci data->id, dev->irq, dev->name); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci data->rxring = dma_alloc_coherent(&data->pdev->dev, rxring_size, 13048c2ecf20Sopenharmony_ci &data->rxdma, GFP_KERNEL); 13058c2ecf20Sopenharmony_ci if (!data->rxring) { 13068c2ecf20Sopenharmony_ci free_irq(data->irq_num, dev); 13078c2ecf20Sopenharmony_ci return -ENOMEM; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci data->txring = dma_alloc_coherent(&data->pdev->dev, txring_size, 13118c2ecf20Sopenharmony_ci &data->txdma, GFP_KERNEL); 13128c2ecf20Sopenharmony_ci if (!data->txring) { 13138c2ecf20Sopenharmony_ci free_irq(data->irq_num, dev); 13148c2ecf20Sopenharmony_ci dma_free_coherent(&data->pdev->dev, rxring_size, data->rxring, 13158c2ecf20Sopenharmony_ci data->rxdma); 13168c2ecf20Sopenharmony_ci return -ENOMEM; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci for (i = 0; i < TSI108_RXRING_LEN; i++) { 13208c2ecf20Sopenharmony_ci data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc); 13218c2ecf20Sopenharmony_ci data->rxring[i].blen = TSI108_RXBUF_SIZE; 13228c2ecf20Sopenharmony_ci data->rxring[i].vlan = 0; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci data->rxtail = 0; 13288c2ecf20Sopenharmony_ci data->rxhead = 0; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci for (i = 0; i < TSI108_RXRING_LEN; i++) { 13318c2ecf20Sopenharmony_ci struct sk_buff *skb; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE); 13348c2ecf20Sopenharmony_ci if (!skb) { 13358c2ecf20Sopenharmony_ci /* Bah. No memory for now, but maybe we'll get 13368c2ecf20Sopenharmony_ci * some more later. 13378c2ecf20Sopenharmony_ci * For now, we'll live with the smaller ring. 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_ci printk(KERN_WARNING 13408c2ecf20Sopenharmony_ci "%s: Could only allocate %d receive skb(s).\n", 13418c2ecf20Sopenharmony_ci dev->name, i); 13428c2ecf20Sopenharmony_ci data->rxhead = i; 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci data->rxskbs[i] = skb; 13478c2ecf20Sopenharmony_ci data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data); 13488c2ecf20Sopenharmony_ci data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci data->rxfree = i; 13528c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXQ_PTRLOW, data->rxdma); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci for (i = 0; i < TSI108_TXRING_LEN; i++) { 13558c2ecf20Sopenharmony_ci data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc); 13568c2ecf20Sopenharmony_ci data->txring[i].misc = 0; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma; 13608c2ecf20Sopenharmony_ci data->txtail = 0; 13618c2ecf20Sopenharmony_ci data->txhead = 0; 13628c2ecf20Sopenharmony_ci data->txfree = TSI108_TXRING_LEN; 13638c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXQ_PTRLOW, data->txdma); 13648c2ecf20Sopenharmony_ci tsi108_init_phy(dev); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci napi_enable(&data->napi); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci timer_setup(&data->timer, tsi108_timed_checker, 0); 13698c2ecf20Sopenharmony_ci mod_timer(&data->timer, jiffies + 1); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci tsi108_restart_rx(data, dev); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTSTAT, ~0); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, 13768c2ecf20Sopenharmony_ci ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR | 13778c2ecf20Sopenharmony_ci TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 | 13788c2ecf20Sopenharmony_ci TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT | 13798c2ecf20Sopenharmony_ci TSI108_INT_SFN | TSI108_INT_STATCARRY)); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG1, 13828c2ecf20Sopenharmony_ci TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN); 13838c2ecf20Sopenharmony_ci netif_start_queue(dev); 13848c2ecf20Sopenharmony_ci return 0; 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic int tsi108_close(struct net_device *dev) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci netif_stop_queue(dev); 13928c2ecf20Sopenharmony_ci napi_disable(&data->napi); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci del_timer_sync(&data->timer); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci tsi108_stop_ethernet(dev); 13978c2ecf20Sopenharmony_ci tsi108_kill_phy(dev); 13988c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, ~0); 13998c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG1, 0); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* Check for any pending TX packets, and drop them. */ 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci while (!data->txfree || data->txhead != data->txtail) { 14048c2ecf20Sopenharmony_ci int tx = data->txtail; 14058c2ecf20Sopenharmony_ci struct sk_buff *skb; 14068c2ecf20Sopenharmony_ci skb = data->txskbs[tx]; 14078c2ecf20Sopenharmony_ci data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN; 14088c2ecf20Sopenharmony_ci data->txfree++; 14098c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci free_irq(data->irq_num, dev); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* Discard the RX ring. */ 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci while (data->rxfree) { 14178c2ecf20Sopenharmony_ci int rx = data->rxtail; 14188c2ecf20Sopenharmony_ci struct sk_buff *skb; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci skb = data->rxskbs[rx]; 14218c2ecf20Sopenharmony_ci data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN; 14228c2ecf20Sopenharmony_ci data->rxfree--; 14238c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci dma_free_coherent(&data->pdev->dev, 14278c2ecf20Sopenharmony_ci TSI108_RXRING_LEN * sizeof(rx_desc), 14288c2ecf20Sopenharmony_ci data->rxring, data->rxdma); 14298c2ecf20Sopenharmony_ci dma_free_coherent(&data->pdev->dev, 14308c2ecf20Sopenharmony_ci TSI108_TXRING_LEN * sizeof(tx_desc), 14318c2ecf20Sopenharmony_ci data->txring, data->txdma); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return 0; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic void tsi108_init_mac(struct net_device *dev) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE | 14418c2ecf20Sopenharmony_ci TSI108_MAC_CFG2_PADCRC); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXTHRESH, 14448c2ecf20Sopenharmony_ci (192 << TSI108_EC_TXTHRESH_STARTFILL) | 14458c2ecf20Sopenharmony_ci (192 << TSI108_EC_TXTHRESH_STOPFILL)); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_STAT_CARRYMASK1, 14488c2ecf20Sopenharmony_ci ~(TSI108_STAT_CARRY1_RXBYTES | 14498c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXPKTS | 14508c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXFCS | 14518c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXMCAST | 14528c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXALIGN | 14538c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXLENGTH | 14548c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXRUNT | 14558c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXJUMBO | 14568c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXFRAG | 14578c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXJABBER | 14588c2ecf20Sopenharmony_ci TSI108_STAT_CARRY1_RXDROP)); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_STAT_CARRYMASK2, 14618c2ecf20Sopenharmony_ci ~(TSI108_STAT_CARRY2_TXBYTES | 14628c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXPKTS | 14638c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXEXDEF | 14648c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXEXCOL | 14658c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXTCOL | 14668c2ecf20Sopenharmony_ci TSI108_STAT_CARRY2_TXPAUSE)); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN); 14698c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_MAC_CFG1, 0); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXCFG, 14728c2ecf20Sopenharmony_ci TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT | 14758c2ecf20Sopenharmony_ci TSI108_EC_TXQ_CFG_EOQ_OWN_INT | 14768c2ecf20Sopenharmony_ci TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT << 14778c2ecf20Sopenharmony_ci TSI108_EC_TXQ_CFG_SFNPORT)); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT | 14808c2ecf20Sopenharmony_ci TSI108_EC_RXQ_CFG_EOQ_OWN_INT | 14818c2ecf20Sopenharmony_ci TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT << 14828c2ecf20Sopenharmony_ci TSI108_EC_RXQ_CFG_SFNPORT)); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_TXQ_BUFCFG, 14858c2ecf20Sopenharmony_ci TSI108_EC_TXQ_BUFCFG_BURST256 | 14868c2ecf20Sopenharmony_ci TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT << 14878c2ecf20Sopenharmony_ci TSI108_EC_TXQ_BUFCFG_SFNPORT)); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_RXQ_BUFCFG, 14908c2ecf20Sopenharmony_ci TSI108_EC_RXQ_BUFCFG_BURST256 | 14918c2ecf20Sopenharmony_ci TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT << 14928c2ecf20Sopenharmony_ci TSI108_EC_RXQ_BUFCFG_SFNPORT)); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci TSI_WRITE(TSI108_EC_INTMASK, ~0); 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic int tsi108_get_link_ksettings(struct net_device *dev, 14988c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 15018c2ecf20Sopenharmony_ci unsigned long flags; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci spin_lock_irqsave(&data->txlock, flags); 15048c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&data->mii_if, cmd); 15058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&data->txlock, flags); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return 0; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic int tsi108_set_link_ksettings(struct net_device *dev, 15118c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 15148c2ecf20Sopenharmony_ci unsigned long flags; 15158c2ecf20Sopenharmony_ci int rc; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci spin_lock_irqsave(&data->txlock, flags); 15188c2ecf20Sopenharmony_ci rc = mii_ethtool_set_link_ksettings(&data->mii_if, cmd); 15198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&data->txlock, flags); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return rc; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic int tsi108_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = netdev_priv(dev); 15278c2ecf20Sopenharmony_ci if (!netif_running(dev)) 15288c2ecf20Sopenharmony_ci return -EINVAL; 15298c2ecf20Sopenharmony_ci return generic_mii_ioctl(&data->mii_if, if_mii(rq), cmd, NULL); 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const struct ethtool_ops tsi108_ethtool_ops = { 15338c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 15348c2ecf20Sopenharmony_ci .get_link_ksettings = tsi108_get_link_ksettings, 15358c2ecf20Sopenharmony_ci .set_link_ksettings = tsi108_set_link_ksettings, 15368c2ecf20Sopenharmony_ci}; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic const struct net_device_ops tsi108_netdev_ops = { 15398c2ecf20Sopenharmony_ci .ndo_open = tsi108_open, 15408c2ecf20Sopenharmony_ci .ndo_stop = tsi108_close, 15418c2ecf20Sopenharmony_ci .ndo_start_xmit = tsi108_send_packet, 15428c2ecf20Sopenharmony_ci .ndo_set_rx_mode = tsi108_set_rx_mode, 15438c2ecf20Sopenharmony_ci .ndo_get_stats = tsi108_get_stats, 15448c2ecf20Sopenharmony_ci .ndo_do_ioctl = tsi108_do_ioctl, 15458c2ecf20Sopenharmony_ci .ndo_set_mac_address = tsi108_set_mac, 15468c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 15478c2ecf20Sopenharmony_ci}; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic int 15508c2ecf20Sopenharmony_citsi108_init_one(struct platform_device *pdev) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 15538c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = NULL; 15548c2ecf20Sopenharmony_ci hw_info *einfo; 15558c2ecf20Sopenharmony_ci int err = 0; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci einfo = dev_get_platdata(&pdev->dev); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (NULL == einfo) { 15608c2ecf20Sopenharmony_ci printk(KERN_ERR "tsi-eth %d: Missing additional data!\n", 15618c2ecf20Sopenharmony_ci pdev->id); 15628c2ecf20Sopenharmony_ci return -ENODEV; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* Create an ethernet device instance */ 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct tsi108_prv_data)); 15688c2ecf20Sopenharmony_ci if (!dev) 15698c2ecf20Sopenharmony_ci return -ENOMEM; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci printk("tsi108_eth%d: probe...\n", pdev->id); 15728c2ecf20Sopenharmony_ci data = netdev_priv(dev); 15738c2ecf20Sopenharmony_ci data->dev = dev; 15748c2ecf20Sopenharmony_ci data->pdev = pdev; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci pr_debug("tsi108_eth%d:regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n", 15778c2ecf20Sopenharmony_ci pdev->id, einfo->regs, einfo->phyregs, 15788c2ecf20Sopenharmony_ci einfo->phy, einfo->irq_num); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci data->regs = ioremap(einfo->regs, 0x400); 15818c2ecf20Sopenharmony_ci if (NULL == data->regs) { 15828c2ecf20Sopenharmony_ci err = -ENOMEM; 15838c2ecf20Sopenharmony_ci goto regs_fail; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci data->phyregs = ioremap(einfo->phyregs, 0x400); 15878c2ecf20Sopenharmony_ci if (NULL == data->phyregs) { 15888c2ecf20Sopenharmony_ci err = -ENOMEM; 15898c2ecf20Sopenharmony_ci goto phyregs_fail; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci/* MII setup */ 15928c2ecf20Sopenharmony_ci data->mii_if.dev = dev; 15938c2ecf20Sopenharmony_ci data->mii_if.mdio_read = tsi108_mdio_read; 15948c2ecf20Sopenharmony_ci data->mii_if.mdio_write = tsi108_mdio_write; 15958c2ecf20Sopenharmony_ci data->mii_if.phy_id = einfo->phy; 15968c2ecf20Sopenharmony_ci data->mii_if.phy_id_mask = 0x1f; 15978c2ecf20Sopenharmony_ci data->mii_if.reg_num_mask = 0x1f; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci data->phy = einfo->phy; 16008c2ecf20Sopenharmony_ci data->phy_type = einfo->phy_type; 16018c2ecf20Sopenharmony_ci data->irq_num = einfo->irq_num; 16028c2ecf20Sopenharmony_ci data->id = pdev->id; 16038c2ecf20Sopenharmony_ci netif_napi_add(dev, &data->napi, tsi108_poll, 64); 16048c2ecf20Sopenharmony_ci dev->netdev_ops = &tsi108_netdev_ops; 16058c2ecf20Sopenharmony_ci dev->ethtool_ops = &tsi108_ethtool_ops; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci /* Apparently, the Linux networking code won't use scatter-gather 16088c2ecf20Sopenharmony_ci * if the hardware doesn't do checksums. However, it's faster 16098c2ecf20Sopenharmony_ci * to checksum in place and use SG, as (among other reasons) 16108c2ecf20Sopenharmony_ci * the cache won't be dirtied (which then has to be flushed 16118c2ecf20Sopenharmony_ci * before DMA). The checksumming is done by the driver (via 16128c2ecf20Sopenharmony_ci * a new function skb_csum_dev() in net/core/skbuff.c). 16138c2ecf20Sopenharmony_ci */ 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci dev->features = NETIF_F_HIGHDMA; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci spin_lock_init(&data->txlock); 16188c2ecf20Sopenharmony_ci spin_lock_init(&data->misclock); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci tsi108_reset_ether(data); 16218c2ecf20Sopenharmony_ci tsi108_kill_phy(dev); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if ((err = tsi108_get_mac(dev)) != 0) { 16248c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n", 16258c2ecf20Sopenharmony_ci dev->name); 16268c2ecf20Sopenharmony_ci goto register_fail; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci tsi108_init_mac(dev); 16308c2ecf20Sopenharmony_ci err = register_netdev(dev); 16318c2ecf20Sopenharmony_ci if (err) { 16328c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot register net device, aborting.\n", 16338c2ecf20Sopenharmony_ci dev->name); 16348c2ecf20Sopenharmony_ci goto register_fail; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 16388c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: %pM\n", 16398c2ecf20Sopenharmony_ci dev->name, dev->dev_addr); 16408c2ecf20Sopenharmony_ci#ifdef DEBUG 16418c2ecf20Sopenharmony_ci data->msg_enable = DEBUG; 16428c2ecf20Sopenharmony_ci dump_eth_one(dev); 16438c2ecf20Sopenharmony_ci#endif 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci return 0; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ciregister_fail: 16488c2ecf20Sopenharmony_ci iounmap(data->phyregs); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ciphyregs_fail: 16518c2ecf20Sopenharmony_ci iounmap(data->regs); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ciregs_fail: 16548c2ecf20Sopenharmony_ci free_netdev(dev); 16558c2ecf20Sopenharmony_ci return err; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci/* There's no way to either get interrupts from the PHY when 16598c2ecf20Sopenharmony_ci * something changes, or to have the Tsi108 automatically communicate 16608c2ecf20Sopenharmony_ci * with the PHY to reconfigure itself. 16618c2ecf20Sopenharmony_ci * 16628c2ecf20Sopenharmony_ci * Thus, we have to do it using a timer. 16638c2ecf20Sopenharmony_ci */ 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic void tsi108_timed_checker(struct timer_list *t) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci struct tsi108_prv_data *data = from_timer(data, t, timer); 16688c2ecf20Sopenharmony_ci struct net_device *dev = data->dev; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci tsi108_check_phy(dev); 16718c2ecf20Sopenharmony_ci tsi108_check_rxring(dev); 16728c2ecf20Sopenharmony_ci mod_timer(&data->timer, jiffies + CHECK_PHY_INTERVAL); 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_cistatic int tsi108_ether_remove(struct platform_device *pdev) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 16788c2ecf20Sopenharmony_ci struct tsi108_prv_data *priv = netdev_priv(dev); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci unregister_netdev(dev); 16818c2ecf20Sopenharmony_ci tsi108_stop_ethernet(dev); 16828c2ecf20Sopenharmony_ci iounmap(priv->regs); 16838c2ecf20Sopenharmony_ci iounmap(priv->phyregs); 16848c2ecf20Sopenharmony_ci free_netdev(dev); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci return 0; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_cimodule_platform_driver(tsi_eth_driver); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tundra Semiconductor Corporation"); 16918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver"); 16928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 16938c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tsi-ethernet"); 1694