162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Copyright(c) 2006 Tundra Semiconductor Corporation.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci*******************************************************************************/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* This driver is based on the driver code originally developed
1062306a36Sopenharmony_ci * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
1162306a36Sopenharmony_ci * scott.wood@timesys.com  * Copyright (C) 2003 TimeSys Corporation
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Currently changes from original version are:
1462306a36Sopenharmony_ci * - porting to Tsi108-based platform and kernel 2.6 (kong.lai@tundra.com)
1562306a36Sopenharmony_ci * - modifications to handle two ports independently and support for
1662306a36Sopenharmony_ci *   additional PHY devices (alexandre.bounine@tundra.com)
1762306a36Sopenharmony_ci * - Get hardware information from platform device. (tie-fei.zang@freescale.com)
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/types.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/net.h>
2562306a36Sopenharmony_ci#include <linux/netdevice.h>
2662306a36Sopenharmony_ci#include <linux/etherdevice.h>
2762306a36Sopenharmony_ci#include <linux/ethtool.h>
2862306a36Sopenharmony_ci#include <linux/skbuff.h>
2962306a36Sopenharmony_ci#include <linux/spinlock.h>
3062306a36Sopenharmony_ci#include <linux/delay.h>
3162306a36Sopenharmony_ci#include <linux/crc32.h>
3262306a36Sopenharmony_ci#include <linux/mii.h>
3362306a36Sopenharmony_ci#include <linux/device.h>
3462306a36Sopenharmony_ci#include <linux/pci.h>
3562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3662306a36Sopenharmony_ci#include <linux/timer.h>
3762306a36Sopenharmony_ci#include <linux/platform_device.h>
3862306a36Sopenharmony_ci#include <linux/gfp.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <asm/io.h>
4162306a36Sopenharmony_ci#include <asm/tsi108.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "tsi108_eth.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define MII_READ_DELAY 10000	/* max link wait time in msec */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define TSI108_RXRING_LEN     256
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* NOTE: The driver currently does not support receiving packets
5062306a36Sopenharmony_ci * larger than the buffer size, so don't decrease this (unless you
5162306a36Sopenharmony_ci * want to add such support).
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#define TSI108_RXBUF_SIZE     1536
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define TSI108_TXRING_LEN     256
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define TSI108_TX_INT_FREQ    64
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Check the phy status every half a second. */
6062306a36Sopenharmony_ci#define CHECK_PHY_INTERVAL (HZ/2)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct tsi108_prv_data {
6362306a36Sopenharmony_ci	void  __iomem *regs;	/* Base of normal regs */
6462306a36Sopenharmony_ci	void  __iomem *phyregs;	/* Base of register bank used for PHY access */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	struct net_device *dev;
6762306a36Sopenharmony_ci	struct napi_struct napi;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	unsigned int phy;		/* Index of PHY for this interface */
7062306a36Sopenharmony_ci	unsigned int irq_num;
7162306a36Sopenharmony_ci	unsigned int id;
7262306a36Sopenharmony_ci	unsigned int phy_type;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	struct timer_list timer;/* Timer that triggers the check phy function */
7562306a36Sopenharmony_ci	unsigned int rxtail;	/* Next entry in rxring to read */
7662306a36Sopenharmony_ci	unsigned int rxhead;	/* Next entry in rxring to give a new buffer */
7762306a36Sopenharmony_ci	unsigned int rxfree;	/* Number of free, allocated RX buffers */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	unsigned int rxpending;	/* Non-zero if there are still descriptors
8062306a36Sopenharmony_ci				 * to be processed from a previous descriptor
8162306a36Sopenharmony_ci				 * interrupt condition that has been cleared */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	unsigned int txtail;	/* Next TX descriptor to check status on */
8462306a36Sopenharmony_ci	unsigned int txhead;	/* Next TX descriptor to use */
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Number of free TX descriptors.  This could be calculated from
8762306a36Sopenharmony_ci	 * rxhead and rxtail if one descriptor were left unused to disambiguate
8862306a36Sopenharmony_ci	 * full and empty conditions, but it's simpler to just keep track
8962306a36Sopenharmony_ci	 * explicitly. */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	unsigned int txfree;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	unsigned int phy_ok;		/* The PHY is currently powered on. */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* PHY status (duplex is 1 for half, 2 for full,
9662306a36Sopenharmony_ci	 * so that the default 0 indicates that neither has
9762306a36Sopenharmony_ci	 * yet been configured). */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	unsigned int link_up;
10062306a36Sopenharmony_ci	unsigned int speed;
10162306a36Sopenharmony_ci	unsigned int duplex;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	tx_desc *txring;
10462306a36Sopenharmony_ci	rx_desc *rxring;
10562306a36Sopenharmony_ci	struct sk_buff *txskbs[TSI108_TXRING_LEN];
10662306a36Sopenharmony_ci	struct sk_buff *rxskbs[TSI108_RXRING_LEN];
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	dma_addr_t txdma, rxdma;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* txlock nests in misclock and phy_lock */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	spinlock_t txlock, misclock;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* stats is used to hold the upper bits of each hardware counter,
11562306a36Sopenharmony_ci	 * and tmpstats is used to hold the full values for returning
11662306a36Sopenharmony_ci	 * to the caller of get_stats().  They must be separate in case
11762306a36Sopenharmony_ci	 * an overflow interrupt occurs before the stats are consumed.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	struct net_device_stats stats;
12162306a36Sopenharmony_ci	struct net_device_stats tmpstats;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* These stats are kept separate in hardware, thus require individual
12462306a36Sopenharmony_ci	 * fields for handling carry.  They are combined in get_stats.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	unsigned long rx_fcs;	/* Add to rx_frame_errors */
12862306a36Sopenharmony_ci	unsigned long rx_short_fcs;	/* Add to rx_frame_errors */
12962306a36Sopenharmony_ci	unsigned long rx_long_fcs;	/* Add to rx_frame_errors */
13062306a36Sopenharmony_ci	unsigned long rx_underruns;	/* Add to rx_length_errors */
13162306a36Sopenharmony_ci	unsigned long rx_overruns;	/* Add to rx_length_errors */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	unsigned long tx_coll_abort;	/* Add to tx_aborted_errors/collisions */
13462306a36Sopenharmony_ci	unsigned long tx_pause_drop;	/* Add to tx_aborted_errors */
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	unsigned long mc_hash[16];
13762306a36Sopenharmony_ci	u32 msg_enable;			/* debug message level */
13862306a36Sopenharmony_ci	struct mii_if_info mii_if;
13962306a36Sopenharmony_ci	unsigned int init_media;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	struct platform_device *pdev;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void tsi108_timed_checker(struct timer_list *t);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#ifdef DEBUG
14762306a36Sopenharmony_cistatic void dump_eth_one(struct net_device *dev)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	printk("Dumping %s...\n", dev->name);
15262306a36Sopenharmony_ci	printk("intstat %x intmask %x phy_ok %d"
15362306a36Sopenharmony_ci	       " link %d speed %d duplex %d\n",
15462306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_INTSTAT),
15562306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_INTMASK), data->phy_ok,
15662306a36Sopenharmony_ci	       data->link_up, data->speed, data->duplex);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n",
15962306a36Sopenharmony_ci	       data->txhead, data->txtail, data->txfree,
16062306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_TXSTAT),
16162306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_TXESTAT),
16262306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_TXERR));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	printk("RX: head %d, tail %d, free %d, stat %x,"
16562306a36Sopenharmony_ci	       " estat %x, err %x, pending %d\n\n",
16662306a36Sopenharmony_ci	       data->rxhead, data->rxtail, data->rxfree,
16762306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_RXSTAT),
16862306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_RXESTAT),
16962306a36Sopenharmony_ci	       TSI_READ(TSI108_EC_RXERR), data->rxpending);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci#endif
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/* Synchronization is needed between the thread and up/down events.
17462306a36Sopenharmony_ci * Note that the PHY is accessed through the same registers for both
17562306a36Sopenharmony_ci * interfaces, so this can't be made interface-specific.
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(phy_lock);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int tsi108_read_mii(struct tsi108_prv_data *data, int reg)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	unsigned i;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	TSI_WRITE_PHY(TSI108_MAC_MII_ADDR,
18562306a36Sopenharmony_ci				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
18662306a36Sopenharmony_ci				(reg << TSI108_MAC_MII_ADDR_REG));
18762306a36Sopenharmony_ci	TSI_WRITE_PHY(TSI108_MAC_MII_CMD, 0);
18862306a36Sopenharmony_ci	TSI_WRITE_PHY(TSI108_MAC_MII_CMD, TSI108_MAC_MII_CMD_READ);
18962306a36Sopenharmony_ci	for (i = 0; i < 100; i++) {
19062306a36Sopenharmony_ci		if (!(TSI_READ_PHY(TSI108_MAC_MII_IND) &
19162306a36Sopenharmony_ci		      (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		udelay(10);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (i == 100)
19762306a36Sopenharmony_ci		return 0xffff;
19862306a36Sopenharmony_ci	else
19962306a36Sopenharmony_ci		return TSI_READ_PHY(TSI108_MAC_MII_DATAIN);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void tsi108_write_mii(struct tsi108_prv_data *data,
20362306a36Sopenharmony_ci				int reg, u16 val)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	unsigned i = 100;
20662306a36Sopenharmony_ci	TSI_WRITE_PHY(TSI108_MAC_MII_ADDR,
20762306a36Sopenharmony_ci				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
20862306a36Sopenharmony_ci				(reg << TSI108_MAC_MII_ADDR_REG));
20962306a36Sopenharmony_ci	TSI_WRITE_PHY(TSI108_MAC_MII_DATAOUT, val);
21062306a36Sopenharmony_ci	while (i--) {
21162306a36Sopenharmony_ci		if(!(TSI_READ_PHY(TSI108_MAC_MII_IND) &
21262306a36Sopenharmony_ci			TSI108_MAC_MII_IND_BUSY))
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci		udelay(10);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int tsi108_mdio_read(struct net_device *dev, int addr, int reg)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
22162306a36Sopenharmony_ci	return tsi108_read_mii(data, reg);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void tsi108_mdio_write(struct net_device *dev, int addr, int reg, int val)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
22762306a36Sopenharmony_ci	tsi108_write_mii(data, reg, val);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic inline void tsi108_write_tbi(struct tsi108_prv_data *data,
23162306a36Sopenharmony_ci					int reg, u16 val)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	unsigned i = 1000;
23462306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_MII_ADDR,
23562306a36Sopenharmony_ci			     (0x1e << TSI108_MAC_MII_ADDR_PHY)
23662306a36Sopenharmony_ci			     | (reg << TSI108_MAC_MII_ADDR_REG));
23762306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_MII_DATAOUT, val);
23862306a36Sopenharmony_ci	while(i--) {
23962306a36Sopenharmony_ci		if(!(TSI_READ(TSI108_MAC_MII_IND) & TSI108_MAC_MII_IND_BUSY))
24062306a36Sopenharmony_ci			return;
24162306a36Sopenharmony_ci		udelay(10);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	printk(KERN_ERR "%s function time out\n", __func__);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int mii_speed(struct mii_if_info *mii)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	int advert, lpa, val, media;
24962306a36Sopenharmony_ci	int lpa2 = 0;
25062306a36Sopenharmony_ci	int speed;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!mii_link_ok(mii))
25362306a36Sopenharmony_ci		return 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	val = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_BMSR);
25662306a36Sopenharmony_ci	if ((val & BMSR_ANEGCOMPLETE) == 0)
25762306a36Sopenharmony_ci		return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	advert = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_ADVERTISE);
26062306a36Sopenharmony_ci	lpa = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_LPA);
26162306a36Sopenharmony_ci	media = mii_nway_result(advert & lpa);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (mii->supports_gmii)
26462306a36Sopenharmony_ci		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	speed = lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
26762306a36Sopenharmony_ci			(media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 100 : 10);
26862306a36Sopenharmony_ci	return speed;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void tsi108_check_phy(struct net_device *dev)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
27462306a36Sopenharmony_ci	u32 mac_cfg2_reg, portctrl_reg;
27562306a36Sopenharmony_ci	u32 duplex;
27662306a36Sopenharmony_ci	u32 speed;
27762306a36Sopenharmony_ci	unsigned long flags;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	spin_lock_irqsave(&phy_lock, flags);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!data->phy_ok)
28262306a36Sopenharmony_ci		goto out;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	duplex = mii_check_media(&data->mii_if, netif_msg_link(data), data->init_media);
28562306a36Sopenharmony_ci	data->init_media = 0;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (netif_carrier_ok(dev)) {
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		speed = mii_speed(&data->mii_if);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		if ((speed != data->speed) || duplex) {
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci			mac_cfg2_reg = TSI_READ(TSI108_MAC_CFG2);
29462306a36Sopenharmony_ci			portctrl_reg = TSI_READ(TSI108_EC_PORTCTRL);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci			mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			if (speed == 1000) {
29962306a36Sopenharmony_ci				mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
30062306a36Sopenharmony_ci				portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
30162306a36Sopenharmony_ci			} else {
30262306a36Sopenharmony_ci				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
30362306a36Sopenharmony_ci				portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci			data->speed = speed;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci			if (data->mii_if.full_duplex) {
30962306a36Sopenharmony_ci				mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
31062306a36Sopenharmony_ci				portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
31162306a36Sopenharmony_ci				data->duplex = 2;
31262306a36Sopenharmony_ci			} else {
31362306a36Sopenharmony_ci				mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
31462306a36Sopenharmony_ci				portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
31562306a36Sopenharmony_ci				data->duplex = 1;
31662306a36Sopenharmony_ci			}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			TSI_WRITE(TSI108_MAC_CFG2, mac_cfg2_reg);
31962306a36Sopenharmony_ci			TSI_WRITE(TSI108_EC_PORTCTRL, portctrl_reg);
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		if (data->link_up == 0) {
32362306a36Sopenharmony_ci			/* The manual says it can take 3-4 usecs for the speed change
32462306a36Sopenharmony_ci			 * to take effect.
32562306a36Sopenharmony_ci			 */
32662306a36Sopenharmony_ci			udelay(5);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci			spin_lock(&data->txlock);
32962306a36Sopenharmony_ci			if (is_valid_ether_addr(dev->dev_addr) && data->txfree)
33062306a36Sopenharmony_ci				netif_wake_queue(dev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			data->link_up = 1;
33362306a36Sopenharmony_ci			spin_unlock(&data->txlock);
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci	} else {
33662306a36Sopenharmony_ci		if (data->link_up == 1) {
33762306a36Sopenharmony_ci			netif_stop_queue(dev);
33862306a36Sopenharmony_ci			data->link_up = 0;
33962306a36Sopenharmony_ci			printk(KERN_NOTICE "%s : link is down\n", dev->name);
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		goto out;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciout:
34762306a36Sopenharmony_ci	spin_unlock_irqrestore(&phy_lock, flags);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic inline void
35162306a36Sopenharmony_citsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
35262306a36Sopenharmony_ci		      unsigned long *upper)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	if (carry & carry_bit)
35562306a36Sopenharmony_ci		*upper += carry_shift;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void tsi108_stat_carry(struct net_device *dev)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
36162306a36Sopenharmony_ci	unsigned long flags;
36262306a36Sopenharmony_ci	u32 carry1, carry2;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_lock_irqsave(&data->misclock, flags);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	carry1 = TSI_READ(TSI108_STAT_CARRY1);
36762306a36Sopenharmony_ci	carry2 = TSI_READ(TSI108_STAT_CARRY2);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	TSI_WRITE(TSI108_STAT_CARRY1, carry1);
37062306a36Sopenharmony_ci	TSI_WRITE(TSI108_STAT_CARRY2, carry2);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
37362306a36Sopenharmony_ci			      TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
37662306a36Sopenharmony_ci			      TSI108_STAT_RXPKTS_CARRY,
37762306a36Sopenharmony_ci			      &data->stats.rx_packets);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
38062306a36Sopenharmony_ci			      TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
38362306a36Sopenharmony_ci			      TSI108_STAT_RXMCAST_CARRY,
38462306a36Sopenharmony_ci			      &data->stats.multicast);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
38762306a36Sopenharmony_ci			      TSI108_STAT_RXALIGN_CARRY,
38862306a36Sopenharmony_ci			      &data->stats.rx_frame_errors);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
39162306a36Sopenharmony_ci			      TSI108_STAT_RXLENGTH_CARRY,
39262306a36Sopenharmony_ci			      &data->stats.rx_length_errors);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
39562306a36Sopenharmony_ci			      TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
39862306a36Sopenharmony_ci			      TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
40162306a36Sopenharmony_ci			      TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
40462306a36Sopenharmony_ci			      TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
40762306a36Sopenharmony_ci			      TSI108_STAT_RXDROP_CARRY,
40862306a36Sopenharmony_ci			      &data->stats.rx_missed_errors);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
41162306a36Sopenharmony_ci			      TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
41462306a36Sopenharmony_ci			      TSI108_STAT_TXPKTS_CARRY,
41562306a36Sopenharmony_ci			      &data->stats.tx_packets);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
41862306a36Sopenharmony_ci			      TSI108_STAT_TXEXDEF_CARRY,
41962306a36Sopenharmony_ci			      &data->stats.tx_aborted_errors);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
42262306a36Sopenharmony_ci			      TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
42562306a36Sopenharmony_ci			      TSI108_STAT_TXTCOL_CARRY,
42662306a36Sopenharmony_ci			      &data->stats.collisions);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
42962306a36Sopenharmony_ci			      TSI108_STAT_TXPAUSEDROP_CARRY,
43062306a36Sopenharmony_ci			      &data->tx_pause_drop);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	spin_unlock_irqrestore(&data->misclock, flags);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci/* Read a stat counter atomically with respect to carries.
43662306a36Sopenharmony_ci * data->misclock must be held.
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_cistatic inline unsigned long
43962306a36Sopenharmony_citsi108_read_stat(struct tsi108_prv_data * data, int reg, int carry_bit,
44062306a36Sopenharmony_ci		 int carry_shift, unsigned long *upper)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	int carryreg;
44362306a36Sopenharmony_ci	unsigned long val;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (reg < 0xb0)
44662306a36Sopenharmony_ci		carryreg = TSI108_STAT_CARRY1;
44762306a36Sopenharmony_ci	else
44862306a36Sopenharmony_ci		carryreg = TSI108_STAT_CARRY2;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci      again:
45162306a36Sopenharmony_ci	val = TSI_READ(reg) | *upper;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* Check to see if it overflowed, but the interrupt hasn't
45462306a36Sopenharmony_ci	 * been serviced yet.  If so, handle the carry here, and
45562306a36Sopenharmony_ci	 * try again.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (unlikely(TSI_READ(carryreg) & carry_bit)) {
45962306a36Sopenharmony_ci		*upper += carry_shift;
46062306a36Sopenharmony_ci		TSI_WRITE(carryreg, carry_bit);
46162306a36Sopenharmony_ci		goto again;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return val;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic struct net_device_stats *tsi108_get_stats(struct net_device *dev)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	unsigned long excol;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
47262306a36Sopenharmony_ci	spin_lock_irq(&data->misclock);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	data->tmpstats.rx_packets =
47562306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXPKTS,
47662306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXPKTS,
47762306a36Sopenharmony_ci			     TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	data->tmpstats.tx_packets =
48062306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_TXPKTS,
48162306a36Sopenharmony_ci			     TSI108_STAT_CARRY2_TXPKTS,
48262306a36Sopenharmony_ci			     TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	data->tmpstats.rx_bytes =
48562306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXBYTES,
48662306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXBYTES,
48762306a36Sopenharmony_ci			     TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	data->tmpstats.tx_bytes =
49062306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_TXBYTES,
49162306a36Sopenharmony_ci			     TSI108_STAT_CARRY2_TXBYTES,
49262306a36Sopenharmony_ci			     TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	data->tmpstats.multicast =
49562306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXMCAST,
49662306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXMCAST,
49762306a36Sopenharmony_ci			     TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
50062306a36Sopenharmony_ci				 TSI108_STAT_CARRY2_TXEXCOL,
50162306a36Sopenharmony_ci				 TSI108_STAT_TXEXCOL_CARRY,
50262306a36Sopenharmony_ci				 &data->tx_coll_abort);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	data->tmpstats.collisions =
50562306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_TXTCOL,
50662306a36Sopenharmony_ci			     TSI108_STAT_CARRY2_TXTCOL,
50762306a36Sopenharmony_ci			     TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	data->tmpstats.collisions += excol;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	data->tmpstats.rx_length_errors =
51262306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
51362306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXLENGTH,
51462306a36Sopenharmony_ci			     TSI108_STAT_RXLENGTH_CARRY,
51562306a36Sopenharmony_ci			     &data->stats.rx_length_errors);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	data->tmpstats.rx_length_errors +=
51862306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXRUNT,
51962306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXRUNT,
52062306a36Sopenharmony_ci			     TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	data->tmpstats.rx_length_errors +=
52362306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
52462306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXJUMBO,
52562306a36Sopenharmony_ci			     TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	data->tmpstats.rx_frame_errors =
52862306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXALIGN,
52962306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXALIGN,
53062306a36Sopenharmony_ci			     TSI108_STAT_RXALIGN_CARRY,
53162306a36Sopenharmony_ci			     &data->stats.rx_frame_errors);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	data->tmpstats.rx_frame_errors +=
53462306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXFCS,
53562306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
53662306a36Sopenharmony_ci			     &data->rx_fcs);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	data->tmpstats.rx_frame_errors +=
53962306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXFRAG,
54062306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXFRAG,
54162306a36Sopenharmony_ci			     TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	data->tmpstats.rx_missed_errors =
54462306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_RXDROP,
54562306a36Sopenharmony_ci			     TSI108_STAT_CARRY1_RXDROP,
54662306a36Sopenharmony_ci			     TSI108_STAT_RXDROP_CARRY,
54762306a36Sopenharmony_ci			     &data->stats.rx_missed_errors);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* These three are maintained by software. */
55062306a36Sopenharmony_ci	data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
55162306a36Sopenharmony_ci	data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	data->tmpstats.tx_aborted_errors =
55462306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
55562306a36Sopenharmony_ci			     TSI108_STAT_CARRY2_TXEXDEF,
55662306a36Sopenharmony_ci			     TSI108_STAT_TXEXDEF_CARRY,
55762306a36Sopenharmony_ci			     &data->stats.tx_aborted_errors);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	data->tmpstats.tx_aborted_errors +=
56062306a36Sopenharmony_ci	    tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
56162306a36Sopenharmony_ci			     TSI108_STAT_CARRY2_TXPAUSE,
56262306a36Sopenharmony_ci			     TSI108_STAT_TXPAUSEDROP_CARRY,
56362306a36Sopenharmony_ci			     &data->tx_pause_drop);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	data->tmpstats.tx_aborted_errors += excol;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
56862306a36Sopenharmony_ci	data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
56962306a36Sopenharmony_ci	    data->tmpstats.rx_crc_errors +
57062306a36Sopenharmony_ci	    data->tmpstats.rx_frame_errors +
57162306a36Sopenharmony_ci	    data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	spin_unlock_irq(&data->misclock);
57462306a36Sopenharmony_ci	return &data->tmpstats;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void tsi108_restart_rx(struct tsi108_prv_data * data, struct net_device *dev)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXQ_PTRHIGH,
58062306a36Sopenharmony_ci			     TSI108_EC_RXQ_PTRHIGH_VALID);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
58362306a36Sopenharmony_ci			     | TSI108_EC_RXCTRL_QUEUE0);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void tsi108_restart_tx(struct tsi108_prv_data * data)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXQ_PTRHIGH,
58962306a36Sopenharmony_ci			     TSI108_EC_TXQ_PTRHIGH_VALID);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
59262306a36Sopenharmony_ci			     TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/* txlock must be held by caller, with IRQs disabled, and
59662306a36Sopenharmony_ci * with permission to re-enable them when the lock is dropped.
59762306a36Sopenharmony_ci */
59862306a36Sopenharmony_cistatic void tsi108_complete_tx(struct net_device *dev)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
60162306a36Sopenharmony_ci	int tx;
60262306a36Sopenharmony_ci	struct sk_buff *skb;
60362306a36Sopenharmony_ci	int release = 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	while (!data->txfree || data->txhead != data->txtail) {
60662306a36Sopenharmony_ci		tx = data->txtail;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		if (data->txring[tx].misc & TSI108_TX_OWN)
60962306a36Sopenharmony_ci			break;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		skb = data->txskbs[tx];
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		if (!(data->txring[tx].misc & TSI108_TX_OK))
61462306a36Sopenharmony_ci			printk("%s: bad tx packet, misc %x\n",
61562306a36Sopenharmony_ci			       dev->name, data->txring[tx].misc);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
61862306a36Sopenharmony_ci		data->txfree++;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (data->txring[tx].misc & TSI108_TX_EOF) {
62162306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
62262306a36Sopenharmony_ci			release++;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (release) {
62762306a36Sopenharmony_ci		if (is_valid_ether_addr(dev->dev_addr) && data->link_up)
62862306a36Sopenharmony_ci			netif_wake_queue(dev);
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int tsi108_send_packet(struct sk_buff * skb, struct net_device *dev)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
63562306a36Sopenharmony_ci	int frags = skb_shinfo(skb)->nr_frags + 1;
63662306a36Sopenharmony_ci	int i;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (!data->phy_ok && net_ratelimit())
63962306a36Sopenharmony_ci		printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (!data->link_up) {
64262306a36Sopenharmony_ci		printk(KERN_ERR "%s: Transmit while link is down!\n",
64362306a36Sopenharmony_ci		       dev->name);
64462306a36Sopenharmony_ci		netif_stop_queue(dev);
64562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (data->txfree < MAX_SKB_FRAGS + 1) {
64962306a36Sopenharmony_ci		netif_stop_queue(dev);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		if (net_ratelimit())
65262306a36Sopenharmony_ci			printk(KERN_ERR "%s: Transmit with full tx ring!\n",
65362306a36Sopenharmony_ci			       dev->name);
65462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
65862306a36Sopenharmony_ci		netif_stop_queue(dev);
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	spin_lock_irq(&data->txlock);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	for (i = 0; i < frags; i++) {
66462306a36Sopenharmony_ci		int misc = 0;
66562306a36Sopenharmony_ci		int tx = data->txhead;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		/* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
66862306a36Sopenharmony_ci		 * the interrupt bit.  TX descriptor-complete interrupts are
66962306a36Sopenharmony_ci		 * enabled when the queue fills up, and masked when there is
67062306a36Sopenharmony_ci		 * still free space.  This way, when saturating the outbound
67162306a36Sopenharmony_ci		 * link, the tx interrupts are kept to a reasonable level.
67262306a36Sopenharmony_ci		 * When the queue is not full, reclamation of skbs still occurs
67362306a36Sopenharmony_ci		 * as new packets are transmitted, or on a queue-empty
67462306a36Sopenharmony_ci		 * interrupt.
67562306a36Sopenharmony_ci		 */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		if ((tx % TSI108_TX_INT_FREQ == 0) &&
67862306a36Sopenharmony_ci		    ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ))
67962306a36Sopenharmony_ci			misc = TSI108_TX_INT;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		data->txskbs[tx] = skb;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		if (i == 0) {
68462306a36Sopenharmony_ci			data->txring[tx].buf0 = dma_map_single(&data->pdev->dev,
68562306a36Sopenharmony_ci					skb->data, skb_headlen(skb),
68662306a36Sopenharmony_ci					DMA_TO_DEVICE);
68762306a36Sopenharmony_ci			data->txring[tx].len = skb_headlen(skb);
68862306a36Sopenharmony_ci			misc |= TSI108_TX_SOF;
68962306a36Sopenharmony_ci		} else {
69062306a36Sopenharmony_ci			const skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci			data->txring[tx].buf0 =
69362306a36Sopenharmony_ci				skb_frag_dma_map(&data->pdev->dev, frag,
69462306a36Sopenharmony_ci						0, skb_frag_size(frag),
69562306a36Sopenharmony_ci						DMA_TO_DEVICE);
69662306a36Sopenharmony_ci			data->txring[tx].len = skb_frag_size(frag);
69762306a36Sopenharmony_ci		}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		if (i == frags - 1)
70062306a36Sopenharmony_ci			misc |= TSI108_TX_EOF;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (netif_msg_pktdata(data)) {
70362306a36Sopenharmony_ci			int i;
70462306a36Sopenharmony_ci			printk("%s: Tx Frame contents (%d)\n", dev->name,
70562306a36Sopenharmony_ci			       skb->len);
70662306a36Sopenharmony_ci			for (i = 0; i < skb->len; i++)
70762306a36Sopenharmony_ci				printk(" %2.2x", skb->data[i]);
70862306a36Sopenharmony_ci			printk(".\n");
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci		data->txring[tx].misc = misc | TSI108_TX_OWN;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
71362306a36Sopenharmony_ci		data->txfree--;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	tsi108_complete_tx(dev);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* This must be done after the check for completed tx descriptors,
71962306a36Sopenharmony_ci	 * so that the tail pointer is correct.
72062306a36Sopenharmony_ci	 */
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (!(TSI_READ(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_QUEUE0))
72362306a36Sopenharmony_ci		tsi108_restart_tx(data);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	spin_unlock_irq(&data->txlock);
72662306a36Sopenharmony_ci	return NETDEV_TX_OK;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic int tsi108_complete_rx(struct net_device *dev, int budget)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
73262306a36Sopenharmony_ci	int done = 0;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	while (data->rxfree && done != budget) {
73562306a36Sopenharmony_ci		int rx = data->rxtail;
73662306a36Sopenharmony_ci		struct sk_buff *skb;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		if (data->rxring[rx].misc & TSI108_RX_OWN)
73962306a36Sopenharmony_ci			break;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		skb = data->rxskbs[rx];
74262306a36Sopenharmony_ci		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
74362306a36Sopenharmony_ci		data->rxfree--;
74462306a36Sopenharmony_ci		done++;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		if (data->rxring[rx].misc & TSI108_RX_BAD) {
74762306a36Sopenharmony_ci			spin_lock_irq(&data->misclock);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci			if (data->rxring[rx].misc & TSI108_RX_CRC)
75062306a36Sopenharmony_ci				data->stats.rx_crc_errors++;
75162306a36Sopenharmony_ci			if (data->rxring[rx].misc & TSI108_RX_OVER)
75262306a36Sopenharmony_ci				data->stats.rx_fifo_errors++;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci			spin_unlock_irq(&data->misclock);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
75762306a36Sopenharmony_ci			continue;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci		if (netif_msg_pktdata(data)) {
76062306a36Sopenharmony_ci			int i;
76162306a36Sopenharmony_ci			printk("%s: Rx Frame contents (%d)\n",
76262306a36Sopenharmony_ci			       dev->name, data->rxring[rx].len);
76362306a36Sopenharmony_ci			for (i = 0; i < data->rxring[rx].len; i++)
76462306a36Sopenharmony_ci				printk(" %2.2x", skb->data[i]);
76562306a36Sopenharmony_ci			printk(".\n");
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		skb_put(skb, data->rxring[rx].len);
76962306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
77062306a36Sopenharmony_ci		netif_receive_skb(skb);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return done;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int tsi108_refill_rx(struct net_device *dev, int budget)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
77962306a36Sopenharmony_ci	int done = 0;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
78262306a36Sopenharmony_ci		int rx = data->rxhead;
78362306a36Sopenharmony_ci		struct sk_buff *skb;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE);
78662306a36Sopenharmony_ci		data->rxskbs[rx] = skb;
78762306a36Sopenharmony_ci		if (!skb)
78862306a36Sopenharmony_ci			break;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		data->rxring[rx].buf0 = dma_map_single(&data->pdev->dev,
79162306a36Sopenharmony_ci				skb->data, TSI108_RX_SKB_SIZE,
79262306a36Sopenharmony_ci				DMA_FROM_DEVICE);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		/* Sometimes the hardware sets blen to zero after packet
79562306a36Sopenharmony_ci		 * reception, even though the manual says that it's only ever
79662306a36Sopenharmony_ci		 * modified by the driver.
79762306a36Sopenharmony_ci		 */
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		data->rxring[rx].blen = TSI108_RX_SKB_SIZE;
80062306a36Sopenharmony_ci		data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
80362306a36Sopenharmony_ci		data->rxfree++;
80462306a36Sopenharmony_ci		done++;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (done != 0 && !(TSI_READ(TSI108_EC_RXSTAT) &
80862306a36Sopenharmony_ci			   TSI108_EC_RXSTAT_QUEUE0))
80962306a36Sopenharmony_ci		tsi108_restart_rx(data, dev);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return done;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int tsi108_poll(struct napi_struct *napi, int budget)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct tsi108_prv_data *data = container_of(napi, struct tsi108_prv_data, napi);
81762306a36Sopenharmony_ci	struct net_device *dev = data->dev;
81862306a36Sopenharmony_ci	u32 estat = TSI_READ(TSI108_EC_RXESTAT);
81962306a36Sopenharmony_ci	u32 intstat = TSI_READ(TSI108_EC_INTSTAT);
82062306a36Sopenharmony_ci	int num_received = 0, num_filled = 0;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
82362306a36Sopenharmony_ci	    TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXESTAT, estat);
82662306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTSTAT, intstat);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
82962306a36Sopenharmony_ci		num_received = tsi108_complete_rx(dev, budget);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* This should normally fill no more slots than the number of
83262306a36Sopenharmony_ci	 * packets received in tsi108_complete_rx().  The exception
83362306a36Sopenharmony_ci	 * is when we previously ran out of memory for RX SKBs.  In that
83462306a36Sopenharmony_ci	 * case, it's helpful to obey the budget, not only so that the
83562306a36Sopenharmony_ci	 * CPU isn't hogged, but so that memory (which may still be low)
83662306a36Sopenharmony_ci	 * is not hogged by one device.
83762306a36Sopenharmony_ci	 *
83862306a36Sopenharmony_ci	 * A work unit is considered to be two SKBs to allow us to catch
83962306a36Sopenharmony_ci	 * up when the ring has shrunk due to out-of-memory but we're
84062306a36Sopenharmony_ci	 * still removing the full budget's worth of packets each time.
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (data->rxfree < TSI108_RXRING_LEN)
84462306a36Sopenharmony_ci		num_filled = tsi108_refill_rx(dev, budget * 2);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (intstat & TSI108_INT_RXERROR) {
84762306a36Sopenharmony_ci		u32 err = TSI_READ(TSI108_EC_RXERR);
84862306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_RXERR, err);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		if (err) {
85162306a36Sopenharmony_ci			if (net_ratelimit())
85262306a36Sopenharmony_ci				printk(KERN_DEBUG "%s: RX error %x\n",
85362306a36Sopenharmony_ci				       dev->name, err);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci			if (!(TSI_READ(TSI108_EC_RXSTAT) &
85662306a36Sopenharmony_ci			      TSI108_EC_RXSTAT_QUEUE0))
85762306a36Sopenharmony_ci				tsi108_restart_rx(data, dev);
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (intstat & TSI108_INT_RXOVERRUN) {
86262306a36Sopenharmony_ci		spin_lock_irq(&data->misclock);
86362306a36Sopenharmony_ci		data->stats.rx_fifo_errors++;
86462306a36Sopenharmony_ci		spin_unlock_irq(&data->misclock);
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (num_received < budget) {
86862306a36Sopenharmony_ci		data->rxpending = 0;
86962306a36Sopenharmony_ci		napi_complete_done(napi, num_received);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_INTMASK,
87262306a36Sopenharmony_ci				     TSI_READ(TSI108_EC_INTMASK)
87362306a36Sopenharmony_ci				     & ~(TSI108_INT_RXQUEUE0
87462306a36Sopenharmony_ci					 | TSI108_INT_RXTHRESH |
87562306a36Sopenharmony_ci					 TSI108_INT_RXOVERRUN |
87662306a36Sopenharmony_ci					 TSI108_INT_RXERROR |
87762306a36Sopenharmony_ci					 TSI108_INT_RXWAIT));
87862306a36Sopenharmony_ci	} else {
87962306a36Sopenharmony_ci		data->rxpending = 1;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	return num_received;
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic void tsi108_rx_int(struct net_device *dev)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* A race could cause dev to already be scheduled, so it's not an
89062306a36Sopenharmony_ci	 * error if that happens (and interrupts shouldn't be re-masked,
89162306a36Sopenharmony_ci	 * because that can cause harmful races, if poll has already
89262306a36Sopenharmony_ci	 * unmasked them but not cleared LINK_STATE_SCHED).
89362306a36Sopenharmony_ci	 *
89462306a36Sopenharmony_ci	 * This can happen if this code races with tsi108_poll(), which masks
89562306a36Sopenharmony_ci	 * the interrupts after tsi108_irq_one() read the mask, but before
89662306a36Sopenharmony_ci	 * napi_schedule is called.  It could also happen due to calls
89762306a36Sopenharmony_ci	 * from tsi108_check_rxring().
89862306a36Sopenharmony_ci	 */
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (napi_schedule_prep(&data->napi)) {
90162306a36Sopenharmony_ci		/* Mask, rather than ack, the receive interrupts.  The ack
90262306a36Sopenharmony_ci		 * will happen in tsi108_poll().
90362306a36Sopenharmony_ci		 */
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_INTMASK,
90662306a36Sopenharmony_ci				     TSI_READ(TSI108_EC_INTMASK) |
90762306a36Sopenharmony_ci				     TSI108_INT_RXQUEUE0
90862306a36Sopenharmony_ci				     | TSI108_INT_RXTHRESH |
90962306a36Sopenharmony_ci				     TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
91062306a36Sopenharmony_ci				     TSI108_INT_RXWAIT);
91162306a36Sopenharmony_ci		__napi_schedule(&data->napi);
91262306a36Sopenharmony_ci	} else {
91362306a36Sopenharmony_ci		if (!netif_running(dev)) {
91462306a36Sopenharmony_ci			/* This can happen if an interrupt occurs while the
91562306a36Sopenharmony_ci			 * interface is being brought down, as the START
91662306a36Sopenharmony_ci			 * bit is cleared before the stop function is called.
91762306a36Sopenharmony_ci			 *
91862306a36Sopenharmony_ci			 * In this case, the interrupts must be masked, or
91962306a36Sopenharmony_ci			 * they will continue indefinitely.
92062306a36Sopenharmony_ci			 *
92162306a36Sopenharmony_ci			 * There's a race here if the interface is brought down
92262306a36Sopenharmony_ci			 * and then up in rapid succession, as the device could
92362306a36Sopenharmony_ci			 * be made running after the above check and before
92462306a36Sopenharmony_ci			 * the masking below.  This will only happen if the IRQ
92562306a36Sopenharmony_ci			 * thread has a lower priority than the task brining
92662306a36Sopenharmony_ci			 * up the interface.  Fixing this race would likely
92762306a36Sopenharmony_ci			 * require changes in generic code.
92862306a36Sopenharmony_ci			 */
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			TSI_WRITE(TSI108_EC_INTMASK,
93162306a36Sopenharmony_ci					     TSI_READ
93262306a36Sopenharmony_ci					     (TSI108_EC_INTMASK) |
93362306a36Sopenharmony_ci					     TSI108_INT_RXQUEUE0 |
93462306a36Sopenharmony_ci					     TSI108_INT_RXTHRESH |
93562306a36Sopenharmony_ci					     TSI108_INT_RXOVERRUN |
93662306a36Sopenharmony_ci					     TSI108_INT_RXERROR |
93762306a36Sopenharmony_ci					     TSI108_INT_RXWAIT);
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci/* If the RX ring has run out of memory, try periodically
94362306a36Sopenharmony_ci * to allocate some more, as otherwise poll would never
94462306a36Sopenharmony_ci * get called (apart from the initial end-of-queue condition).
94562306a36Sopenharmony_ci *
94662306a36Sopenharmony_ci * This is called once per second (by default) from the thread.
94762306a36Sopenharmony_ci */
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic void tsi108_check_rxring(struct net_device *dev)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* A poll is scheduled, as opposed to caling tsi108_refill_rx
95462306a36Sopenharmony_ci	 * directly, so as to keep the receive path single-threaded
95562306a36Sopenharmony_ci	 * (and thus not needing a lock).
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
95962306a36Sopenharmony_ci		tsi108_rx_int(dev);
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void tsi108_tx_int(struct net_device *dev)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
96562306a36Sopenharmony_ci	u32 estat = TSI_READ(TSI108_EC_TXESTAT);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXESTAT, estat);
96862306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
96962306a36Sopenharmony_ci			     TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
97062306a36Sopenharmony_ci	if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
97162306a36Sopenharmony_ci		u32 err = TSI_READ(TSI108_EC_TXERR);
97262306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_TXERR, err);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		if (err && net_ratelimit())
97562306a36Sopenharmony_ci			printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | TSI108_EC_TXESTAT_Q0_EOQ)) {
97962306a36Sopenharmony_ci		spin_lock(&data->txlock);
98062306a36Sopenharmony_ci		tsi108_complete_tx(dev);
98162306a36Sopenharmony_ci		spin_unlock(&data->txlock);
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic irqreturn_t tsi108_irq(int irq, void *dev_id)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct net_device *dev = dev_id;
98962306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
99062306a36Sopenharmony_ci	u32 stat = TSI_READ(TSI108_EC_INTSTAT);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (!(stat & TSI108_INT_ANY))
99362306a36Sopenharmony_ci		return IRQ_NONE;	/* Not our interrupt */
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	stat &= ~TSI_READ(TSI108_EC_INTMASK);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
99862306a36Sopenharmony_ci		    TSI108_INT_TXERROR))
99962306a36Sopenharmony_ci		tsi108_tx_int(dev);
100062306a36Sopenharmony_ci	if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
100162306a36Sopenharmony_ci		    TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
100262306a36Sopenharmony_ci		    TSI108_INT_RXERROR))
100362306a36Sopenharmony_ci		tsi108_rx_int(dev);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (stat & TSI108_INT_SFN) {
100662306a36Sopenharmony_ci		if (net_ratelimit())
100762306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: SFN error\n", dev->name);
100862306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_SFN);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (stat & TSI108_INT_STATCARRY) {
101262306a36Sopenharmony_ci		tsi108_stat_carry(dev);
101362306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return IRQ_HANDLED;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void tsi108_stop_ethernet(struct net_device *dev)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
102262306a36Sopenharmony_ci	int i = 1000;
102362306a36Sopenharmony_ci	/* Disable all TX and RX queues ... */
102462306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXCTRL, 0);
102562306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCTRL, 0);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/* ...and wait for them to become idle */
102862306a36Sopenharmony_ci	while(i--) {
102962306a36Sopenharmony_ci		if(!(TSI_READ(TSI108_EC_TXSTAT) & TSI108_EC_TXSTAT_ACTIVE))
103062306a36Sopenharmony_ci			break;
103162306a36Sopenharmony_ci		udelay(10);
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	i = 1000;
103462306a36Sopenharmony_ci	while(i--){
103562306a36Sopenharmony_ci		if(!(TSI_READ(TSI108_EC_RXSTAT) & TSI108_EC_RXSTAT_ACTIVE))
103662306a36Sopenharmony_ci			return;
103762306a36Sopenharmony_ci		udelay(10);
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci	printk(KERN_ERR "%s function time out\n", __func__);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void tsi108_reset_ether(struct tsi108_prv_data * data)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
104562306a36Sopenharmony_ci	udelay(100);
104662306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG1, 0);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATRST);
104962306a36Sopenharmony_ci	udelay(100);
105062306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_PORTCTRL,
105162306a36Sopenharmony_ci			     TSI_READ(TSI108_EC_PORTCTRL) &
105262306a36Sopenharmony_ci			     ~TSI108_EC_PORTCTRL_STATRST);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
105562306a36Sopenharmony_ci	udelay(100);
105662306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXCFG,
105762306a36Sopenharmony_ci			     TSI_READ(TSI108_EC_TXCFG) &
105862306a36Sopenharmony_ci			     ~TSI108_EC_TXCFG_RST);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
106162306a36Sopenharmony_ci	udelay(100);
106262306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCFG,
106362306a36Sopenharmony_ci			     TSI_READ(TSI108_EC_RXCFG) &
106462306a36Sopenharmony_ci			     ~TSI108_EC_RXCFG_RST);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_MII_MGMT_CFG,
106762306a36Sopenharmony_ci			     TSI_READ(TSI108_MAC_MII_MGMT_CFG) |
106862306a36Sopenharmony_ci			     TSI108_MAC_MII_MGMT_RST);
106962306a36Sopenharmony_ci	udelay(100);
107062306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_MII_MGMT_CFG,
107162306a36Sopenharmony_ci			     (TSI_READ(TSI108_MAC_MII_MGMT_CFG) &
107262306a36Sopenharmony_ci			     ~(TSI108_MAC_MII_MGMT_RST |
107362306a36Sopenharmony_ci			       TSI108_MAC_MII_MGMT_CLK)) | 0x07);
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic int tsi108_get_mac(struct net_device *dev)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
107962306a36Sopenharmony_ci	u32 word1 = TSI_READ(TSI108_MAC_ADDR1);
108062306a36Sopenharmony_ci	u32 word2 = TSI_READ(TSI108_MAC_ADDR2);
108162306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* Note that the octets are reversed from what the manual says,
108462306a36Sopenharmony_ci	 * producing an even weirder ordering...
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	if (word2 == 0 && word1 == 0) {
108762306a36Sopenharmony_ci		addr[0] = 0x00;
108862306a36Sopenharmony_ci		addr[1] = 0x06;
108962306a36Sopenharmony_ci		addr[2] = 0xd2;
109062306a36Sopenharmony_ci		addr[3] = 0x00;
109162306a36Sopenharmony_ci		addr[4] = 0x00;
109262306a36Sopenharmony_ci		if (0x8 == data->phy)
109362306a36Sopenharmony_ci			addr[5] = 0x01;
109462306a36Sopenharmony_ci		else
109562306a36Sopenharmony_ci			addr[5] = 0x02;
109662306a36Sopenharmony_ci		eth_hw_addr_set(dev, addr);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
110162306a36Sopenharmony_ci		    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		TSI_WRITE(TSI108_MAC_ADDR1, word1);
110462306a36Sopenharmony_ci		TSI_WRITE(TSI108_MAC_ADDR2, word2);
110562306a36Sopenharmony_ci	} else {
110662306a36Sopenharmony_ci		addr[0] = (word2 >> 16) & 0xff;
110762306a36Sopenharmony_ci		addr[1] = (word2 >> 24) & 0xff;
110862306a36Sopenharmony_ci		addr[2] = (word1 >> 0) & 0xff;
110962306a36Sopenharmony_ci		addr[3] = (word1 >> 8) & 0xff;
111062306a36Sopenharmony_ci		addr[4] = (word1 >> 16) & 0xff;
111162306a36Sopenharmony_ci		addr[5] = (word1 >> 24) & 0xff;
111262306a36Sopenharmony_ci		eth_hw_addr_set(dev, addr);
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	if (!is_valid_ether_addr(dev->dev_addr)) {
111662306a36Sopenharmony_ci		printk(KERN_ERR
111762306a36Sopenharmony_ci		       "%s: Invalid MAC address. word1: %08x, word2: %08x\n",
111862306a36Sopenharmony_ci		       dev->name, word1, word2);
111962306a36Sopenharmony_ci		return -EINVAL;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	return 0;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_cistatic int tsi108_set_mac(struct net_device *dev, void *addr)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
112862306a36Sopenharmony_ci	u32 word1, word2;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr))
113162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* +2 is for the offset of the HW addr type */
113462306a36Sopenharmony_ci	eth_hw_addr_set(dev, ((unsigned char *)addr) + 2);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
113962306a36Sopenharmony_ci	    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	spin_lock_irq(&data->misclock);
114262306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_ADDR1, word1);
114362306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_ADDR2, word2);
114462306a36Sopenharmony_ci	spin_lock(&data->txlock);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (data->txfree && data->link_up)
114762306a36Sopenharmony_ci		netif_wake_queue(dev);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	spin_unlock(&data->txlock);
115062306a36Sopenharmony_ci	spin_unlock_irq(&data->misclock);
115162306a36Sopenharmony_ci	return 0;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci/* Protected by dev->xmit_lock. */
115562306a36Sopenharmony_cistatic void tsi108_set_rx_mode(struct net_device *dev)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
115862306a36Sopenharmony_ci	u32 rxcfg = TSI_READ(TSI108_EC_RXCFG);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
116162306a36Sopenharmony_ci		rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
116262306a36Sopenharmony_ci		rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
116362306a36Sopenharmony_ci		goto out;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) {
116962306a36Sopenharmony_ci		int i;
117062306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
117162306a36Sopenharmony_ci		rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		memset(data->mc_hash, 0, sizeof(data->mc_hash));
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
117662306a36Sopenharmony_ci			u32 hash, crc;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci			crc = ether_crc(6, ha->addr);
117962306a36Sopenharmony_ci			hash = crc >> 23;
118062306a36Sopenharmony_ci			__set_bit(hash, &data->mc_hash[0]);
118162306a36Sopenharmony_ci		}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci		TSI_WRITE(TSI108_EC_HASHADDR,
118462306a36Sopenharmony_ci				     TSI108_EC_HASHADDR_AUTOINC |
118562306a36Sopenharmony_ci				     TSI108_EC_HASHADDR_MCAST);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		for (i = 0; i < 16; i++) {
118862306a36Sopenharmony_ci			/* The manual says that the hardware may drop
118962306a36Sopenharmony_ci			 * back-to-back writes to the data register.
119062306a36Sopenharmony_ci			 */
119162306a36Sopenharmony_ci			udelay(1);
119262306a36Sopenharmony_ci			TSI_WRITE(TSI108_EC_HASHDATA,
119362306a36Sopenharmony_ci					     data->mc_hash[i]);
119462306a36Sopenharmony_ci		}
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci      out:
119862306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCFG, rxcfg);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic void tsi108_init_phy(struct net_device *dev)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
120462306a36Sopenharmony_ci	u32 i = 0;
120562306a36Sopenharmony_ci	u16 phyval = 0;
120662306a36Sopenharmony_ci	unsigned long flags;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	spin_lock_irqsave(&phy_lock, flags);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	tsi108_write_mii(data, MII_BMCR, BMCR_RESET);
121162306a36Sopenharmony_ci	while (--i) {
121262306a36Sopenharmony_ci		if(!(tsi108_read_mii(data, MII_BMCR) & BMCR_RESET))
121362306a36Sopenharmony_ci			break;
121462306a36Sopenharmony_ci		udelay(10);
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci	if (i == 0)
121762306a36Sopenharmony_ci		printk(KERN_ERR "%s function time out\n", __func__);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (data->phy_type == TSI108_PHY_BCM54XX) {
122062306a36Sopenharmony_ci		tsi108_write_mii(data, 0x09, 0x0300);
122162306a36Sopenharmony_ci		tsi108_write_mii(data, 0x10, 0x1020);
122262306a36Sopenharmony_ci		tsi108_write_mii(data, 0x1c, 0x8c00);
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	tsi108_write_mii(data,
122662306a36Sopenharmony_ci			 MII_BMCR,
122762306a36Sopenharmony_ci			 BMCR_ANENABLE | BMCR_ANRESTART);
122862306a36Sopenharmony_ci	while (tsi108_read_mii(data, MII_BMCR) & BMCR_ANRESTART)
122962306a36Sopenharmony_ci		cpu_relax();
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	/* Set G/MII mode and receive clock select in TBI control #2.  The
123262306a36Sopenharmony_ci	 * second port won't work if this isn't done, even though we don't
123362306a36Sopenharmony_ci	 * use TBI mode.
123462306a36Sopenharmony_ci	 */
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	tsi108_write_tbi(data, 0x11, 0x30);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* FIXME: It seems to take more than 2 back-to-back reads to the
123962306a36Sopenharmony_ci	 * PHY_STAT register before the link up status bit is set.
124062306a36Sopenharmony_ci	 */
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	data->link_up = 0;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	while (!((phyval = tsi108_read_mii(data, MII_BMSR)) &
124562306a36Sopenharmony_ci		 BMSR_LSTATUS)) {
124662306a36Sopenharmony_ci		if (i++ > (MII_READ_DELAY / 10)) {
124762306a36Sopenharmony_ci			break;
124862306a36Sopenharmony_ci		}
124962306a36Sopenharmony_ci		spin_unlock_irqrestore(&phy_lock, flags);
125062306a36Sopenharmony_ci		msleep(10);
125162306a36Sopenharmony_ci		spin_lock_irqsave(&phy_lock, flags);
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	data->mii_if.supports_gmii = mii_check_gmii_support(&data->mii_if);
125562306a36Sopenharmony_ci	printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyval);
125662306a36Sopenharmony_ci	data->phy_ok = 1;
125762306a36Sopenharmony_ci	data->init_media = 1;
125862306a36Sopenharmony_ci	spin_unlock_irqrestore(&phy_lock, flags);
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic void tsi108_kill_phy(struct net_device *dev)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
126462306a36Sopenharmony_ci	unsigned long flags;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	spin_lock_irqsave(&phy_lock, flags);
126762306a36Sopenharmony_ci	tsi108_write_mii(data, MII_BMCR, BMCR_PDOWN);
126862306a36Sopenharmony_ci	data->phy_ok = 0;
126962306a36Sopenharmony_ci	spin_unlock_irqrestore(&phy_lock, flags);
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic int tsi108_open(struct net_device *dev)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	int i;
127562306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
127662306a36Sopenharmony_ci	unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
127762306a36Sopenharmony_ci	unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
128062306a36Sopenharmony_ci	if (i != 0) {
128162306a36Sopenharmony_ci		printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
128262306a36Sopenharmony_ci		       data->id, data->irq_num);
128362306a36Sopenharmony_ci		return i;
128462306a36Sopenharmony_ci	} else {
128562306a36Sopenharmony_ci		dev->irq = data->irq_num;
128662306a36Sopenharmony_ci		printk(KERN_NOTICE
128762306a36Sopenharmony_ci		       "tsi108_open : Port %d Assigned IRQ %d to %s\n",
128862306a36Sopenharmony_ci		       data->id, dev->irq, dev->name);
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	data->rxring = dma_alloc_coherent(&data->pdev->dev, rxring_size,
129262306a36Sopenharmony_ci					  &data->rxdma, GFP_KERNEL);
129362306a36Sopenharmony_ci	if (!data->rxring) {
129462306a36Sopenharmony_ci		free_irq(data->irq_num, dev);
129562306a36Sopenharmony_ci		return -ENOMEM;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	data->txring = dma_alloc_coherent(&data->pdev->dev, txring_size,
129962306a36Sopenharmony_ci					  &data->txdma, GFP_KERNEL);
130062306a36Sopenharmony_ci	if (!data->txring) {
130162306a36Sopenharmony_ci		free_irq(data->irq_num, dev);
130262306a36Sopenharmony_ci		dma_free_coherent(&data->pdev->dev, rxring_size, data->rxring,
130362306a36Sopenharmony_ci				    data->rxdma);
130462306a36Sopenharmony_ci		return -ENOMEM;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	for (i = 0; i < TSI108_RXRING_LEN; i++) {
130862306a36Sopenharmony_ci		data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc);
130962306a36Sopenharmony_ci		data->rxring[i].blen = TSI108_RXBUF_SIZE;
131062306a36Sopenharmony_ci		data->rxring[i].vlan = 0;
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	data->rxtail = 0;
131662306a36Sopenharmony_ci	data->rxhead = 0;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	for (i = 0; i < TSI108_RXRING_LEN; i++) {
131962306a36Sopenharmony_ci		struct sk_buff *skb;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE);
132262306a36Sopenharmony_ci		if (!skb) {
132362306a36Sopenharmony_ci			/* Bah.  No memory for now, but maybe we'll get
132462306a36Sopenharmony_ci			 * some more later.
132562306a36Sopenharmony_ci			 * For now, we'll live with the smaller ring.
132662306a36Sopenharmony_ci			 */
132762306a36Sopenharmony_ci			printk(KERN_WARNING
132862306a36Sopenharmony_ci			       "%s: Could only allocate %d receive skb(s).\n",
132962306a36Sopenharmony_ci			       dev->name, i);
133062306a36Sopenharmony_ci			data->rxhead = i;
133162306a36Sopenharmony_ci			break;
133262306a36Sopenharmony_ci		}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		data->rxskbs[i] = skb;
133562306a36Sopenharmony_ci		data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
133662306a36Sopenharmony_ci		data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	data->rxfree = i;
134062306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXQ_PTRLOW, data->rxdma);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	for (i = 0; i < TSI108_TXRING_LEN; i++) {
134362306a36Sopenharmony_ci		data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc);
134462306a36Sopenharmony_ci		data->txring[i].misc = 0;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
134862306a36Sopenharmony_ci	data->txtail = 0;
134962306a36Sopenharmony_ci	data->txhead = 0;
135062306a36Sopenharmony_ci	data->txfree = TSI108_TXRING_LEN;
135162306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXQ_PTRLOW, data->txdma);
135262306a36Sopenharmony_ci	tsi108_init_phy(dev);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	napi_enable(&data->napi);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	timer_setup(&data->timer, tsi108_timed_checker, 0);
135762306a36Sopenharmony_ci	mod_timer(&data->timer, jiffies + 1);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	tsi108_restart_rx(data, dev);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTSTAT, ~0);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTMASK,
136462306a36Sopenharmony_ci			     ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
136562306a36Sopenharmony_ci			       TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
136662306a36Sopenharmony_ci			       TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
136762306a36Sopenharmony_ci			       TSI108_INT_SFN | TSI108_INT_STATCARRY));
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG1,
137062306a36Sopenharmony_ci			     TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
137162306a36Sopenharmony_ci	netif_start_queue(dev);
137262306a36Sopenharmony_ci	return 0;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic int tsi108_close(struct net_device *dev)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	netif_stop_queue(dev);
138062306a36Sopenharmony_ci	napi_disable(&data->napi);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	del_timer_sync(&data->timer);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	tsi108_stop_ethernet(dev);
138562306a36Sopenharmony_ci	tsi108_kill_phy(dev);
138662306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTMASK, ~0);
138762306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG1, 0);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	/* Check for any pending TX packets, and drop them. */
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	while (!data->txfree || data->txhead != data->txtail) {
139262306a36Sopenharmony_ci		int tx = data->txtail;
139362306a36Sopenharmony_ci		struct sk_buff *skb;
139462306a36Sopenharmony_ci		skb = data->txskbs[tx];
139562306a36Sopenharmony_ci		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
139662306a36Sopenharmony_ci		data->txfree++;
139762306a36Sopenharmony_ci		dev_kfree_skb(skb);
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	free_irq(data->irq_num, dev);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	/* Discard the RX ring. */
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	while (data->rxfree) {
140562306a36Sopenharmony_ci		int rx = data->rxtail;
140662306a36Sopenharmony_ci		struct sk_buff *skb;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		skb = data->rxskbs[rx];
140962306a36Sopenharmony_ci		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
141062306a36Sopenharmony_ci		data->rxfree--;
141162306a36Sopenharmony_ci		dev_kfree_skb(skb);
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	dma_free_coherent(&data->pdev->dev,
141562306a36Sopenharmony_ci			    TSI108_RXRING_LEN * sizeof(rx_desc),
141662306a36Sopenharmony_ci			    data->rxring, data->rxdma);
141762306a36Sopenharmony_ci	dma_free_coherent(&data->pdev->dev,
141862306a36Sopenharmony_ci			    TSI108_TXRING_LEN * sizeof(tx_desc),
141962306a36Sopenharmony_ci			    data->txring, data->txdma);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	return 0;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic void tsi108_init_mac(struct net_device *dev)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE |
142962306a36Sopenharmony_ci			     TSI108_MAC_CFG2_PADCRC);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXTHRESH,
143262306a36Sopenharmony_ci			     (192 << TSI108_EC_TXTHRESH_STARTFILL) |
143362306a36Sopenharmony_ci			     (192 << TSI108_EC_TXTHRESH_STOPFILL));
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	TSI_WRITE(TSI108_STAT_CARRYMASK1,
143662306a36Sopenharmony_ci			     ~(TSI108_STAT_CARRY1_RXBYTES |
143762306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXPKTS |
143862306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXFCS |
143962306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXMCAST |
144062306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXALIGN |
144162306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXLENGTH |
144262306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXRUNT |
144362306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXJUMBO |
144462306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXFRAG |
144562306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXJABBER |
144662306a36Sopenharmony_ci			       TSI108_STAT_CARRY1_RXDROP));
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	TSI_WRITE(TSI108_STAT_CARRYMASK2,
144962306a36Sopenharmony_ci			     ~(TSI108_STAT_CARRY2_TXBYTES |
145062306a36Sopenharmony_ci			       TSI108_STAT_CARRY2_TXPKTS |
145162306a36Sopenharmony_ci			       TSI108_STAT_CARRY2_TXEXDEF |
145262306a36Sopenharmony_ci			       TSI108_STAT_CARRY2_TXEXCOL |
145362306a36Sopenharmony_ci			       TSI108_STAT_CARRY2_TXTCOL |
145462306a36Sopenharmony_ci			       TSI108_STAT_CARRY2_TXPAUSE));
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN);
145762306a36Sopenharmony_ci	TSI_WRITE(TSI108_MAC_CFG1, 0);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXCFG,
146062306a36Sopenharmony_ci			     TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
146362306a36Sopenharmony_ci			     TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
146462306a36Sopenharmony_ci			     TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
146562306a36Sopenharmony_ci						TSI108_EC_TXQ_CFG_SFNPORT));
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
146862306a36Sopenharmony_ci			     TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
146962306a36Sopenharmony_ci			     TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
147062306a36Sopenharmony_ci						TSI108_EC_RXQ_CFG_SFNPORT));
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_TXQ_BUFCFG,
147362306a36Sopenharmony_ci			     TSI108_EC_TXQ_BUFCFG_BURST256 |
147462306a36Sopenharmony_ci			     TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
147562306a36Sopenharmony_ci						TSI108_EC_TXQ_BUFCFG_SFNPORT));
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_RXQ_BUFCFG,
147862306a36Sopenharmony_ci			     TSI108_EC_RXQ_BUFCFG_BURST256 |
147962306a36Sopenharmony_ci			     TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
148062306a36Sopenharmony_ci						TSI108_EC_RXQ_BUFCFG_SFNPORT));
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	TSI_WRITE(TSI108_EC_INTMASK, ~0);
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic int tsi108_get_link_ksettings(struct net_device *dev,
148662306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
148962306a36Sopenharmony_ci	unsigned long flags;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	spin_lock_irqsave(&data->txlock, flags);
149262306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&data->mii_if, cmd);
149362306a36Sopenharmony_ci	spin_unlock_irqrestore(&data->txlock, flags);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return 0;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic int tsi108_set_link_ksettings(struct net_device *dev,
149962306a36Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
150262306a36Sopenharmony_ci	unsigned long flags;
150362306a36Sopenharmony_ci	int rc;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	spin_lock_irqsave(&data->txlock, flags);
150662306a36Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(&data->mii_if, cmd);
150762306a36Sopenharmony_ci	spin_unlock_irqrestore(&data->txlock, flags);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return rc;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_cistatic int tsi108_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct tsi108_prv_data *data = netdev_priv(dev);
151562306a36Sopenharmony_ci	if (!netif_running(dev))
151662306a36Sopenharmony_ci		return -EINVAL;
151762306a36Sopenharmony_ci	return generic_mii_ioctl(&data->mii_if, if_mii(rq), cmd, NULL);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic const struct ethtool_ops tsi108_ethtool_ops = {
152162306a36Sopenharmony_ci	.get_link 	= ethtool_op_get_link,
152262306a36Sopenharmony_ci	.get_link_ksettings	= tsi108_get_link_ksettings,
152362306a36Sopenharmony_ci	.set_link_ksettings	= tsi108_set_link_ksettings,
152462306a36Sopenharmony_ci};
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic const struct net_device_ops tsi108_netdev_ops = {
152762306a36Sopenharmony_ci	.ndo_open		= tsi108_open,
152862306a36Sopenharmony_ci	.ndo_stop		= tsi108_close,
152962306a36Sopenharmony_ci	.ndo_start_xmit		= tsi108_send_packet,
153062306a36Sopenharmony_ci	.ndo_set_rx_mode	= tsi108_set_rx_mode,
153162306a36Sopenharmony_ci	.ndo_get_stats		= tsi108_get_stats,
153262306a36Sopenharmony_ci	.ndo_eth_ioctl		= tsi108_do_ioctl,
153362306a36Sopenharmony_ci	.ndo_set_mac_address	= tsi108_set_mac,
153462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
153562306a36Sopenharmony_ci};
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic int
153862306a36Sopenharmony_citsi108_init_one(struct platform_device *pdev)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	struct net_device *dev = NULL;
154162306a36Sopenharmony_ci	struct tsi108_prv_data *data = NULL;
154262306a36Sopenharmony_ci	hw_info *einfo;
154362306a36Sopenharmony_ci	int err = 0;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	einfo = dev_get_platdata(&pdev->dev);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	if (NULL == einfo) {
154862306a36Sopenharmony_ci		printk(KERN_ERR "tsi-eth %d: Missing additional data!\n",
154962306a36Sopenharmony_ci		       pdev->id);
155062306a36Sopenharmony_ci		return -ENODEV;
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* Create an ethernet device instance */
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct tsi108_prv_data));
155662306a36Sopenharmony_ci	if (!dev)
155762306a36Sopenharmony_ci		return -ENOMEM;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	printk("tsi108_eth%d: probe...\n", pdev->id);
156062306a36Sopenharmony_ci	data = netdev_priv(dev);
156162306a36Sopenharmony_ci	data->dev = dev;
156262306a36Sopenharmony_ci	data->pdev = pdev;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	pr_debug("tsi108_eth%d:regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n",
156562306a36Sopenharmony_ci			pdev->id, einfo->regs, einfo->phyregs,
156662306a36Sopenharmony_ci			einfo->phy, einfo->irq_num);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	data->regs = ioremap(einfo->regs, 0x400);
156962306a36Sopenharmony_ci	if (NULL == data->regs) {
157062306a36Sopenharmony_ci		err = -ENOMEM;
157162306a36Sopenharmony_ci		goto regs_fail;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	data->phyregs = ioremap(einfo->phyregs, 0x400);
157562306a36Sopenharmony_ci	if (NULL == data->phyregs) {
157662306a36Sopenharmony_ci		err = -ENOMEM;
157762306a36Sopenharmony_ci		goto phyregs_fail;
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci/* MII setup */
158062306a36Sopenharmony_ci	data->mii_if.dev = dev;
158162306a36Sopenharmony_ci	data->mii_if.mdio_read = tsi108_mdio_read;
158262306a36Sopenharmony_ci	data->mii_if.mdio_write = tsi108_mdio_write;
158362306a36Sopenharmony_ci	data->mii_if.phy_id = einfo->phy;
158462306a36Sopenharmony_ci	data->mii_if.phy_id_mask = 0x1f;
158562306a36Sopenharmony_ci	data->mii_if.reg_num_mask = 0x1f;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	data->phy = einfo->phy;
158862306a36Sopenharmony_ci	data->phy_type = einfo->phy_type;
158962306a36Sopenharmony_ci	data->irq_num = einfo->irq_num;
159062306a36Sopenharmony_ci	data->id = pdev->id;
159162306a36Sopenharmony_ci	netif_napi_add(dev, &data->napi, tsi108_poll);
159262306a36Sopenharmony_ci	dev->netdev_ops = &tsi108_netdev_ops;
159362306a36Sopenharmony_ci	dev->ethtool_ops = &tsi108_ethtool_ops;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	/* Apparently, the Linux networking code won't use scatter-gather
159662306a36Sopenharmony_ci	 * if the hardware doesn't do checksums.  However, it's faster
159762306a36Sopenharmony_ci	 * to checksum in place and use SG, as (among other reasons)
159862306a36Sopenharmony_ci	 * the cache won't be dirtied (which then has to be flushed
159962306a36Sopenharmony_ci	 * before DMA).  The checksumming is done by the driver (via
160062306a36Sopenharmony_ci	 * a new function skb_csum_dev() in net/core/skbuff.c).
160162306a36Sopenharmony_ci	 */
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	dev->features = NETIF_F_HIGHDMA;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	spin_lock_init(&data->txlock);
160662306a36Sopenharmony_ci	spin_lock_init(&data->misclock);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	tsi108_reset_ether(data);
160962306a36Sopenharmony_ci	tsi108_kill_phy(dev);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	if ((err = tsi108_get_mac(dev)) != 0) {
161262306a36Sopenharmony_ci		printk(KERN_ERR "%s: Invalid MAC address.  Please correct.\n",
161362306a36Sopenharmony_ci		       dev->name);
161462306a36Sopenharmony_ci		goto register_fail;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	tsi108_init_mac(dev);
161862306a36Sopenharmony_ci	err = register_netdev(dev);
161962306a36Sopenharmony_ci	if (err) {
162062306a36Sopenharmony_ci		printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
162162306a36Sopenharmony_ci				dev->name);
162262306a36Sopenharmony_ci		goto register_fail;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
162662306a36Sopenharmony_ci	printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: %pM\n",
162762306a36Sopenharmony_ci	       dev->name, dev->dev_addr);
162862306a36Sopenharmony_ci#ifdef DEBUG
162962306a36Sopenharmony_ci	data->msg_enable = DEBUG;
163062306a36Sopenharmony_ci	dump_eth_one(dev);
163162306a36Sopenharmony_ci#endif
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return 0;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ciregister_fail:
163662306a36Sopenharmony_ci	iounmap(data->phyregs);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ciphyregs_fail:
163962306a36Sopenharmony_ci	iounmap(data->regs);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ciregs_fail:
164262306a36Sopenharmony_ci	free_netdev(dev);
164362306a36Sopenharmony_ci	return err;
164462306a36Sopenharmony_ci}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci/* There's no way to either get interrupts from the PHY when
164762306a36Sopenharmony_ci * something changes, or to have the Tsi108 automatically communicate
164862306a36Sopenharmony_ci * with the PHY to reconfigure itself.
164962306a36Sopenharmony_ci *
165062306a36Sopenharmony_ci * Thus, we have to do it using a timer.
165162306a36Sopenharmony_ci */
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic void tsi108_timed_checker(struct timer_list *t)
165462306a36Sopenharmony_ci{
165562306a36Sopenharmony_ci	struct tsi108_prv_data *data = from_timer(data, t, timer);
165662306a36Sopenharmony_ci	struct net_device *dev = data->dev;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	tsi108_check_phy(dev);
165962306a36Sopenharmony_ci	tsi108_check_rxring(dev);
166062306a36Sopenharmony_ci	mod_timer(&data->timer, jiffies + CHECK_PHY_INTERVAL);
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic int tsi108_ether_remove(struct platform_device *pdev)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
166662306a36Sopenharmony_ci	struct tsi108_prv_data *priv = netdev_priv(dev);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	unregister_netdev(dev);
166962306a36Sopenharmony_ci	tsi108_stop_ethernet(dev);
167062306a36Sopenharmony_ci	iounmap(priv->regs);
167162306a36Sopenharmony_ci	iounmap(priv->phyregs);
167262306a36Sopenharmony_ci	free_netdev(dev);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	return 0;
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci/* Structure for a device driver */
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic struct platform_driver tsi_eth_driver = {
168062306a36Sopenharmony_ci	.probe = tsi108_init_one,
168162306a36Sopenharmony_ci	.remove = tsi108_ether_remove,
168262306a36Sopenharmony_ci	.driver	= {
168362306a36Sopenharmony_ci		.name = "tsi-ethernet",
168462306a36Sopenharmony_ci	},
168562306a36Sopenharmony_ci};
168662306a36Sopenharmony_cimodule_platform_driver(tsi_eth_driver);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ciMODULE_AUTHOR("Tundra Semiconductor Corporation");
168962306a36Sopenharmony_ciMODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
169062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
169162306a36Sopenharmony_ciMODULE_ALIAS("platform:tsi-ethernet");
1692