Netdev List
 help / color / mirror / Atom feed
* Re: RDMA will be reverted
From: David Miller @ 2006-07-01 21:45 UTC (permalink / raw)
  To: tom; +Cc: rdreier, netdev, akpm
In-Reply-To: <1151708503.11835.8.camel@trinity.ogc.int>

From: Tom Tucker <tom@opengridcomputing.com>
Date: Fri, 30 Jun 2006 18:01:43 -0500

> On Fri, 2006-06-30 at 14:16 -0700, David Miller wrote:
> 
> > The TOE folks have tried to submit their hooks and drivers
> > on several occaisions, and we've rejected it every time.
> 
> iWARP != TOE

You are taking my comment out of context.  And the fact
that you removed the comment I am respond to, shows
that you really aren't following the conversation.

Roland stated that it has never been the case that we have
rejected adding support for a certain class of devices on the
kinds of merits being discussed in this thread.  And I'm saying
that TOE is such a case where we have emphatically done so.

So I am not saying iWARP or RDMA is equal to TOE, and if you had
actually read this thread you would have understood that.

You're just looking for cannon fodder in my emails.

^ permalink raw reply

* [PATCH] d80211: Take lowlevel driver's channel change time into account during scanning.
From: Gertjan van Wingerde @ 2006-07-01 20:40 UTC (permalink / raw)
  To: netdev; +Cc: Jiri Benc, jkmaline, linville

Make the dscape stack take the driver-supplied channel change time into 
account when actively scanning for networks.
This particularly has been a problem in the rt2x00 driver, where 
configuration changes are done via a work-queue, and
the subsequent channel changes failed due to the dscape stack scheduling 
a channel update while the previous one had not
been performed yet, resulting in failed scans.

Signed-off-by: Gertjan van Wingerde <gwingerde@kpnplanet.nl>

diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index 37d40aa..9f8653d 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -2503,7 +2503,8 @@ #endif
                        break;
                }

-               local->scan_timer.expires = jiffies + IEEE80211_PROBE_DELAY;
+               local->scan_timer.expires = jiffies + IEEE80211_PROBE_DELAY +
+                             usecs_to_jiffies(local->hw->channel_change_time);
                local->scan_state = SCAN_SEND_PROBE;
                break;
        case SCAN_SEND_PROBE:





^ permalink raw reply related

* [PATCH, RFT] bcm43xx: init routine rewrite (bugfix)
From: Michael Buesch @ 2006-07-01 20:14 UTC (permalink / raw)
  To: bcm43xx-dev; +Cc: netdev, Stefano Brivio

Hi,

This patch is a rewrite of the bcm43xx init routine.
It is supposed to fix several issues:
* up-down-up.. sequence stale-data issue
  (This may fix dhclient issues)
* Fix init vs IRQ race.
* Fix init for cards with multiple cores (APHY)
  (untested)
* Fix controller restart breakage.
* Make used wireless core (phymode) selectable at runtime.
* Well, few others. Don't remember :)

--

Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx.h
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx.h	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx.h	2006-07-01 22:08:47.000000000 +0200
@@ -502,6 +502,12 @@
 	 * This lock is only used by bcm43xx_phy_{un}lock()
 	 */
 	spinlock_t lock;
+
+	/* Firmware. */
+	const struct firmware *ucode;
+	const struct firmware *pcm;
+	const struct firmware *initvals0;
+	const struct firmware *initvals1;
 };
 
 
@@ -591,12 +597,14 @@
 	u8 available:1,
 	   enabled:1,
 	   initialized:1;
-	/** core_id ID number */
-	u16 id;
 	/** core_rev revision number */
 	u8 rev;
 	/** Index number for _switch_core() */
 	u8 index;
+	/** core_id ID number */
+	u16 id;
+	/** Core-specific data. */
+	void *priv;
 };
 
 /* Additional information for each 80211 core. */
@@ -645,7 +653,10 @@
 	BCM43xx_STAT_RESTARTING,	/* controller_restart() called. */
 };
 #define bcm43xx_status(bcm)		atomic_read(&(bcm)->init_status)
-#define bcm43xx_set_status(bcm, stat)	atomic_set(&(bcm)->init_status, (stat))
+#define bcm43xx_set_status(bcm, stat)	do {			\
+		atomic_set(&(bcm)->init_status, (stat));	\
+		smp_wmb();					\
+					} while (0)
 
 struct bcm43xx_private {
 	struct ieee80211_device *ieee;
@@ -706,10 +717,6 @@
 	struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];
 	/* Additional information, specific to the 80211 cores. */
 	struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];
-	/* Index of the current 80211 core. If current_core is not
-	 * an 80211 core, this is -1.
-	 */
-	int current_80211_core_idx;
 	/* Number of available 80211 cores. */
 	int nr_80211_available;
 
@@ -744,12 +751,6 @@
 	struct bcm43xx_key key[54];
 	u8 default_key_idx;
 
-	/* Firmware. */
-	const struct firmware *ucode;
-	const struct firmware *pcm;
-	const struct firmware *initvals0;
-	const struct firmware *initvals1;
-
 	/* Debugging stuff follows. */
 #ifdef CONFIG_BCM43XX_DEBUG
 	struct bcm43xx_dfsentry *dfsentry;
@@ -857,34 +858,33 @@
  * any of these functions.
  */
 static inline
+struct bcm43xx_coreinfo_80211 *
+bcm43xx_current_80211_priv(struct bcm43xx_private *bcm)
+{
+	assert(bcm->current_core->id == BCM43xx_COREID_80211);
+	return bcm->current_core->priv;
+}
+static inline
 struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)
 {
 	assert(bcm43xx_using_pio(bcm));
-	assert(bcm->current_80211_core_idx >= 0);
-	assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
-	return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio);
+	return &(bcm43xx_current_80211_priv(bcm)->pio);
 }
 static inline
 struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)
 {
 	assert(!bcm43xx_using_pio(bcm));
-	assert(bcm->current_80211_core_idx >= 0);
-	assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
-	return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma);
+	return &(bcm43xx_current_80211_priv(bcm)->dma);
 }
 static inline
 struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)
 {
-	assert(bcm->current_80211_core_idx >= 0);
-	assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
-	return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy);
+	return &(bcm43xx_current_80211_priv(bcm)->phy);
 }
 static inline
 struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)
 {
-	assert(bcm->current_80211_core_idx >= 0);
-	assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
-	return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio);
+	return &(bcm43xx_current_80211_priv(bcm)->radio);
 }
 
 
Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.c
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_main.c	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.c	2006-07-01 22:08:47.000000000 +0200
@@ -537,7 +537,6 @@
 	u16 manufact;
 	u16 version;
 	u8 revision;
-	s8 i;
 
 	if (bcm->chip_id == 0x4317) {
 		if (bcm->chip_rev == 0x00)
@@ -580,20 +579,11 @@
 	radio->version = version;
 	radio->revision = revision;
 
-	/* Set default attenuation values. */
-	radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);
-	radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);
-	radio->txctl1 = bcm43xx_default_txctl1(bcm);
-	radio->txctl2 = 0xFFFF;
 	if (phy->type == BCM43xx_PHYTYPE_A)
 		radio->txpower_desired = bcm->sprom.maxpower_aphy;
 	else
 		radio->txpower_desired = bcm->sprom.maxpower_bgphy;
 
-	/* Initialize the in-memory nrssi Lookup Table. */
-	for (i = 0; i < 64; i++)
-		radio->nrssi_lt[i] = i;
-
 	return 0;
 
 err_unsupported_radio:
@@ -1250,10 +1240,6 @@
 		goto out;
 
 	bcm->current_core = new_core;
-	bcm->current_80211_core_idx = -1;
-	if (new_core->id == BCM43xx_COREID_80211)
-		bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0]));
-
 out:
 	return err;
 }
@@ -1423,15 +1409,10 @@
 	bcm43xx_core_disable(bcm, 0);
 }
 
-/* Mark the current 80211 core inactive.
- * "active_80211_core" is the other 80211 core, which is used.
- */
-static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm,
-					       struct bcm43xx_coreinfo *active_80211_core)
+/* Mark the current 80211 core inactive. */
+static void bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm)
 {
 	u32 sbtmstatelow;
-	struct bcm43xx_coreinfo *old_core;
-	int err = 0;
 
 	bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
 	bcm43xx_radio_turn_off(bcm);
@@ -1445,21 +1426,6 @@
 	sbtmstatelow |= 0x80000;
 	bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
 	udelay(1);
-
-	if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {
-		old_core = bcm->current_core;
-		err = bcm43xx_switch_core(bcm, active_80211_core);
-		if (err)
-			goto out;
-		sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
-		sbtmstatelow &= ~0x20000000;
-		sbtmstatelow |= 0x20000000;
-		bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
-		err = bcm43xx_switch_core(bcm, old_core);
-	}
-
-out:
-	return err;
 }
 
 static void handle_irq_transmit_status(struct bcm43xx_private *bcm)
@@ -1885,14 +1851,8 @@
 
 	spin_lock(&bcm->irq_lock);
 
-	/* Only accept IRQs, if we are initialized properly.
-	 * This avoids an RX race while initializing.
-	 * We should probably not enable IRQs before we are initialized
-	 * completely, but some careful work is needed to fix this. I think it
-	 * is best to stay with this cheap workaround for now... .
-	 */
-	if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED))
-		goto out;
+	assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
+	assert(bcm->current_core->id == BCM43xx_COREID_80211);
 
 	reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
 	if (reason == 0xffffffff) {
@@ -1930,16 +1890,18 @@
 
 static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force)
 {
+	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
 	if (bcm->firmware_norelease && !force)
 		return; /* Suspending or controller reset. */
-	release_firmware(bcm->ucode);
-	bcm->ucode = NULL;
-	release_firmware(bcm->pcm);
-	bcm->pcm = NULL;
-	release_firmware(bcm->initvals0);
-	bcm->initvals0 = NULL;
-	release_firmware(bcm->initvals1);
-	bcm->initvals1 = NULL;
+	release_firmware(phy->ucode);
+	phy->ucode = NULL;
+	release_firmware(phy->pcm);
+	phy->pcm = NULL;
+	release_firmware(phy->initvals0);
+	phy->initvals0 = NULL;
+	release_firmware(phy->initvals1);
+	phy->initvals1 = NULL;
 }
 
 static int bcm43xx_request_firmware(struct bcm43xx_private *bcm)
@@ -1950,11 +1912,11 @@
 	int nr;
 	char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 };
 
-	if (!bcm->ucode) {
+	if (!phy->ucode) {
 		snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw",
 			 (rev >= 5 ? 5 : rev),
 			 modparam_fwpostfix);
-		err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev);
+		err = request_firmware(&phy->ucode, buf, &bcm->pci_dev->dev);
 		if (err) {
 			printk(KERN_ERR PFX 
 			       "Error: Microcode \"%s\" not available or load failed.\n",
@@ -1963,12 +1925,12 @@
 		}
 	}
 
-	if (!bcm->pcm) {
+	if (!phy->pcm) {
 		snprintf(buf, ARRAY_SIZE(buf),
 			 "bcm43xx_pcm%d%s.fw",
 			 (rev < 5 ? 4 : 5),
 			 modparam_fwpostfix);
-		err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev);
+		err = request_firmware(&phy->pcm, buf, &bcm->pci_dev->dev);
 		if (err) {
 			printk(KERN_ERR PFX
 			       "Error: PCM \"%s\" not available or load failed.\n",
@@ -1977,7 +1939,7 @@
 		}
 	}
 
-	if (!bcm->initvals0) {
+	if (!phy->initvals0) {
 		if (rev == 2 || rev == 4) {
 			switch (phy->type) {
 			case BCM43xx_PHYTYPE_A:
@@ -2008,20 +1970,20 @@
 		snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
 			 nr, modparam_fwpostfix);
 
-		err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev);
+		err = request_firmware(&phy->initvals0, buf, &bcm->pci_dev->dev);
 		if (err) {
 			printk(KERN_ERR PFX 
 			       "Error: InitVals \"%s\" not available or load failed.\n",
 			        buf);
 			goto error;
 		}
-		if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) {
+		if (phy->initvals0->size % sizeof(struct bcm43xx_initval)) {
 			printk(KERN_ERR PFX "InitVals fileformat error.\n");
 			goto error;
 		}
 	}
 
-	if (!bcm->initvals1) {
+	if (!phy->initvals1) {
 		if (rev >= 5) {
 			u32 sbtmstatehigh;
 
@@ -2043,14 +2005,14 @@
 			snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
 				 nr, modparam_fwpostfix);
 
-			err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev);
+			err = request_firmware(&phy->initvals1, buf, &bcm->pci_dev->dev);
 			if (err) {
 				printk(KERN_ERR PFX 
 				       "Error: InitVals \"%s\" not available or load failed.\n",
 			        	buf);
 				goto error;
 			}
-			if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) {
+			if (phy->initvals1->size % sizeof(struct bcm43xx_initval)) {
 				printk(KERN_ERR PFX "InitVals fileformat error.\n");
 				goto error;
 			}
@@ -2070,12 +2032,13 @@
 
 static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm)
 {
+	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
 	const u32 *data;
 	unsigned int i, len;
 
 	/* Upload Microcode. */
-	data = (u32 *)(bcm->ucode->data);
-	len = bcm->ucode->size / sizeof(u32);
+	data = (u32 *)(phy->ucode->data);
+	len = phy->ucode->size / sizeof(u32);
 	bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000);
 	for (i = 0; i < len; i++) {
 		bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,
@@ -2084,8 +2047,8 @@
 	}
 
 	/* Upload PCM data. */
-	data = (u32 *)(bcm->pcm->data);
-	len = bcm->pcm->size / sizeof(u32);
+	data = (u32 *)(phy->pcm->data);
+	len = phy->pcm->size / sizeof(u32);
 	bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea);
 	bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000);
 	bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb);
@@ -2131,15 +2094,16 @@
 
 static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm)
 {
+	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
 	int err;
 
-	err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data,
-				     bcm->initvals0->size / sizeof(struct bcm43xx_initval));
+	err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals0->data,
+				     phy->initvals0->size / sizeof(struct bcm43xx_initval));
 	if (err)
 		goto out;
-	if (bcm->initvals1) {
-		err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data,
-					     bcm->initvals1->size / sizeof(struct bcm43xx_initval));
+	if (phy->initvals1) {
+		err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals1->data,
+					     phy->initvals1->size / sizeof(struct bcm43xx_initval));
 		if (err)
 			goto out;
 	}
@@ -2156,9 +2120,7 @@
 
 static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm)
 {
-	int res;
-	unsigned int i;
-	u32 data;
+	int err;
 
 	bcm->irq = bcm->pci_dev->irq;
 #ifdef CONFIG_BCM947XX
@@ -2175,32 +2137,12 @@
 		}
 	}
 #endif
-	res = request_irq(bcm->irq, bcm43xx_interrupt_handler,
+	err = request_irq(bcm->irq, bcm43xx_interrupt_handler,
 			  SA_SHIRQ, KBUILD_MODNAME, bcm);
-	if (res) {
+	if (err)
 		printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq);
-		return -ENODEV;
-	}
-	bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff);
-	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);
-	i = 0;
-	while (1) {
-		data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
-		if (data == BCM43xx_IRQ_READY)
-			break;
-		i++;
-		if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {
-			printk(KERN_ERR PFX "Card IRQ register not responding. "
-					    "Giving up.\n");
-			free_irq(bcm->irq, bcm);
-			return -ENODEV;
-		}
-		udelay(10);
-	}
-	// dummy read
-	bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
 
-	return 0;
+	return err;
 }
 
 /* Switch to the core used to write the GPIO register.
@@ -2394,7 +2336,6 @@
 	if (!modparam_noleds)
 		bcm43xx_leds_exit(bcm);
 	bcm43xx_gpio_cleanup(bcm);
-	free_irq(bcm->irq, bcm);
 	bcm43xx_release_firmware(bcm, 0);
 }
 
@@ -2406,7 +2347,7 @@
 	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
 	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
 	int err;
-	int tmp;
+	int i, tmp;
 	u32 value32;
 	u16 value16;
 
@@ -2419,13 +2360,26 @@
 		goto out;
 	bcm43xx_upload_microcode(bcm);
 
-	err = bcm43xx_initialize_irq(bcm);
-	if (err)
-		goto err_release_fw;
+	bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xFFFFFFFF);
+	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);
+	i = 0;
+	while (1) {
+		value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+		if (value32 == BCM43xx_IRQ_READY)
+			break;
+		i++;
+		if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {
+			printk(KERN_ERR PFX "IRQ_READY timeout\n");
+			err = -ENODEV;
+			goto err_release_fw;
+		}
+		udelay(10);
+	}
+	bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
 
 	err = bcm43xx_gpio_init(bcm);
 	if (err)
-		goto err_free_irq;
+		goto err_release_fw;
 
 	err = bcm43xx_upload_initvals(bcm);
 	if (err)
@@ -2509,8 +2463,6 @@
 	bcm43xx_radio_turn_off(bcm);
 err_gpio_cleanup:
 	bcm43xx_gpio_cleanup(bcm);
-err_free_irq:
-	free_irq(bcm->irq, bcm);
 err_release_fw:
 	bcm43xx_release_firmware(bcm, 1);
 	goto out;
@@ -2550,11 +2502,9 @@
 {
 	/* Initialize a "phyinfo" structure. The structure is already
 	 * zeroed out.
+	 * This is called on insmod time to initialize members.
 	 */
-	phy->antenna_diversity = 0xFFFF;
 	phy->savedpctlreg = 0xFFFF;
-	phy->minlowsig[0] = 0xFFFF;
-	phy->minlowsig[1] = 0xFFFF;
 	spin_lock_init(&phy->lock);
 }
 
@@ -2562,14 +2512,11 @@
 {
 	/* Initialize a "radioinfo" structure. The structure is already
 	 * zeroed out.
+	 * This is called on insmod time to initialize members.
 	 */
 	radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;
 	radio->channel = 0xFF;
 	radio->initial_channel = 0xFF;
-	radio->lofcal = 0xFFFF;
-	radio->initval = 0xFFFF;
-	radio->nrssi[0] = -1000;
-	radio->nrssi[1] = -1000;
 }
 
 static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
@@ -2587,7 +2534,6 @@
 				    * BCM43xx_MAX_80211_CORES);
 	memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211)
 					* BCM43xx_MAX_80211_CORES);
-	bcm->current_80211_core_idx = -1;
 	bcm->nr_80211_available = 0;
 	bcm->current_core = NULL;
 	bcm->active_80211_core = NULL;
@@ -2757,6 +2703,7 @@
 				goto out;
 			}
 			bcm->nr_80211_available++;
+			core->priv = ext_80211;
 			bcm43xx_init_struct_phyinfo(&ext_80211->phy);
 			bcm43xx_init_struct_radioinfo(&ext_80211->radio);
 			break;
@@ -2857,7 +2804,8 @@
 }
 
 /* http://bcm-specs.sipsolutions.net/80211Init */
-static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm)
+static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm,
+				      int active_wlcore)
 {
 	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
 	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
@@ -2939,19 +2887,26 @@
 	if (bcm->current_core->rev >= 5)
 		bcm43xx_write16(bcm, 0x043C, 0x000C);
 
-	if (bcm43xx_using_pio(bcm))
-		err = bcm43xx_pio_init(bcm);
-	else
-		err = bcm43xx_dma_init(bcm);
-	if (err)
-		goto err_chip_cleanup;
+	if (active_wlcore) {
+		if (bcm43xx_using_pio(bcm))
+			err = bcm43xx_pio_init(bcm);
+		else
+			err = bcm43xx_dma_init(bcm);
+		if (err)
+			goto err_chip_cleanup;
+	}
 	bcm43xx_write16(bcm, 0x0612, 0x0050);
 	bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050);
 	bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4);
 
-	bcm43xx_mac_enable(bcm);
-	bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
+	if (active_wlcore) {
+		if (radio->initial_channel != 0xFF)
+			bcm43xx_radio_selectchannel(bcm, radio->initial_channel, 0);
+	}
 
+	/* Don't enable MAC/IRQ here, as it will race with the IRQ handler.
+	 * We enable it later.
+	 */
 	bcm->current_core->initialized = 1;
 out:
 	return err;
@@ -3066,11 +3021,6 @@
 	return err;
 }
 
-static void bcm43xx_softmac_init(struct bcm43xx_private *bcm)
-{
-	ieee80211softmac_start(bcm->net_dev);
-}
-
 static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm)
 {
 	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
@@ -3238,135 +3188,323 @@
 	bcm43xx_clear_keys(bcm);
 }
 
-/* This is the opposite of bcm43xx_init_board() */
-static void bcm43xx_free_board(struct bcm43xx_private *bcm)
+static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm)
 {
+	int ret = 0;
 	int i, err;
+	struct bcm43xx_coreinfo *core;
 
+	bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
+	for (i = 0; i < bcm->nr_80211_available; i++) {
+		core = &(bcm->core_80211[i]);
+		assert(core->available);
+		if (!core->initialized)
+			continue;
+		err = bcm43xx_switch_core(bcm, core);
+		if (err) {
+			dprintk(KERN_ERR PFX "shutdown_all_wireless_cores "
+					     "switch_core failed (%d)\n", err);
+			ret = err;
+			continue;
+		}
+		bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+		bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
+		bcm43xx_wireless_core_cleanup(bcm);
+		if (core == bcm->active_80211_core)
+			bcm->active_80211_core = NULL;
+	}
+	free_irq(bcm->irq, bcm);
+	bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
+
+	return ret;
+}
+
+/* This is the opposite of bcm43xx_init_board() */
+static void bcm43xx_free_board(struct bcm43xx_private *bcm)
+{
 	bcm43xx_lock_noirq(bcm);
 	bcm43xx_sysfs_unregister(bcm);
 	bcm43xx_periodic_tasks_delete(bcm);
 
-	bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
+	bcm43xx_shutdown_all_wireless_cores(bcm);
+	bcm43xx_pctl_set_crystal(bcm, 0);
+
+	bcm43xx_unlock_noirq(bcm);
+}
 
+static void prepare_phydata_for_init(struct bcm43xx_phyinfo *phy)
+{
+	phy->antenna_diversity = 0xFFFF;
+	memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig));
+	memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos));
+
+	/* Flags */
+	phy->calibrated = 0;
+	phy->is_locked = 0;
+
+	memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain));
+}
+
+static void prepare_radiodata_for_init(struct bcm43xx_private *bcm,
+				       struct bcm43xx_radioinfo *radio)
+{
+	int i;
+
+	/* Set default attenuation values. */
+	radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);
+	radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);
+	radio->txctl1 = bcm43xx_default_txctl1(bcm);
+	radio->txctl2 = 0xFFFF;
+	radio->txpwr_offset = 0;
+
+	/* NRSSI */
+	radio->nrssislope = 0;
+	for (i = 0; i < ARRAY_SIZE(radio->nrssi); i++)
+		radio->nrssi[i] = -1000;
+	for (i = 0; i < ARRAY_SIZE(radio->nrssi_lt); i++)
+		radio->nrssi_lt[i] = i;
+
+	radio->lofcal = 0xFFFF;
+	radio->initval = 0xFFFF;
+
+	radio->aci_enable = 0;
+	radio->aci_wlan_automatic = 0;
+	radio->aci_hw_rssi = 0;
+}
+
+static void prepare_priv_for_init(struct bcm43xx_private *bcm)
+{
+	int i;
+	struct bcm43xx_coreinfo *core;
+	struct bcm43xx_coreinfo_80211 *wlext;
+
+	bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+
+	/* Flags */
+	bcm->was_initialized = 0;
+	bcm->reg124_set_0x4 = 0;
+	bcm->firmware_norelease = 0;
+
+	/* Stats */
+	memset(&bcm->stats, 0, sizeof(bcm->stats));
+
+	/* Wireless core data */
 	for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
-		if (!bcm->core_80211[i].available)
-			continue;
-		if (!bcm->core_80211[i].initialized)
+		core = &(bcm->core_80211[i]);
+		wlext = core->priv;
+
+		if (!core->available)
 			continue;
+		assert(wlext == &(bcm->core_80211_ext[i]));
 
-		err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
-		assert(err == 0);
-		bcm43xx_wireless_core_cleanup(bcm);
+		prepare_phydata_for_init(&wlext->phy);
+		prepare_radiodata_for_init(bcm, &wlext->radio);
 	}
 
-	bcm43xx_pctl_set_crystal(bcm, 0);
+	/* IRQ related flags */
+	bcm->irq_reason = 0;
+	memset(bcm->dma_reason, 0, sizeof(bcm->dma_reason));
+	bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
 
-	bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
-	bcm43xx_unlock_noirq(bcm);
+	/* Noise calculation context */
+	memset(&bcm->noisecalc, 0, sizeof(bcm->noisecalc));
+
+	/* Periodic work context */
+	bcm->periodic_state = 0;
 }
 
-static int bcm43xx_init_board(struct bcm43xx_private *bcm)
+static int wireless_core_up(struct bcm43xx_private *bcm,
+			    int active_wlcore)
+{
+	int err;
+
+	if (!bcm43xx_core_enabled(bcm))
+		bcm43xx_wireless_core_reset(bcm, 1);
+	if (!active_wlcore)
+		bcm43xx_wireless_core_mark_inactive(bcm);
+	err = bcm43xx_wireless_core_init(bcm, active_wlcore);
+	if (err)
+		goto out;
+	if (!active_wlcore)
+		bcm43xx_radio_turn_off(bcm);
+out:
+	return err;
+}
+
+/* Select and enable the "to be used" wireless core.
+ * Locking: bcm->mutex must be aquired before calling this.
+ *          bcm->irq_lock must not be aquired.
+ */
+int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
+				 int phytype)
 {
 	int i, err;
-	int connect_phy;
+	struct bcm43xx_coreinfo *active_core = NULL;
+	struct bcm43xx_coreinfo_80211 *active_wlext = NULL;
+	struct bcm43xx_coreinfo *core;
+	struct bcm43xx_coreinfo_80211 *wlext;
+	int adjust_active_sbtmstatelow = 0;
 
 	might_sleep();
 
-	bcm43xx_lock_noirq(bcm);
-	bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+	if (phytype < 0) {
+		/* If no phytype is requested, select the first core. */
+		assert(bcm->core_80211[0].available);
+		wlext = bcm->core_80211[0].priv;
+		phytype = wlext->phy.type;
+	}
+
+	/* Find the requested core. */
+	for (i = 0; i < bcm->nr_80211_available; i++) {
+		core = &(bcm->core_80211[i]);
+		wlext = core->priv;
+		if (wlext->phy.type == phytype) {
+			active_core = core;
+			active_wlext = wlext;
+			break;
+		}
+	}
+	if (!active_core) {
+		/* No such PHYTYPE on this board. */
+		return -ESRCH;
+	}
+
+	if (bcm->active_80211_core) {
+		/* We already selected a wl core in the past.
+		 * So first clean up everything.
+		 */
+		dprintk(KERN_INFO PFX "select_wireless_core: cleanup\n");
+		ieee80211softmac_stop(bcm->net_dev);
+		bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+		err = bcm43xx_disable_interrupts_sync(bcm, NULL);
+		assert(!err);
+		tasklet_enable(&bcm->isr_tasklet);
+		err = bcm43xx_shutdown_all_wireless_cores(bcm);
+		if (err)
+			goto error;
+		/* Ok, everything down, continue to re-initialize. */
+		bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING);
+	}
+
+	/* Reset all data structures. */
+	prepare_priv_for_init(bcm);
 
-	err = bcm43xx_pctl_set_crystal(bcm, 1);
-	if (err)
-		goto out;
-	err = bcm43xx_pctl_init(bcm);
-	if (err)
-		goto err_crystal_off;
 	err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST);
 	if (err)
-		goto err_crystal_off;
+		goto error;
 
-	tasklet_enable(&bcm->isr_tasklet);
+	/* Mark all unused cores "inactive". */
 	for (i = 0; i < bcm->nr_80211_available; i++) {
-		err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
-		assert(err != -ENODEV);
-		if (err)
-			goto err_80211_unwind;
+		core = &(bcm->core_80211[i]);
+		wlext = core->priv;
 
-		/* Enable the selected wireless core.
-		 * Connect PHY only on the first core.
-		 */
-		if (!bcm43xx_core_enabled(bcm)) {
-			if (bcm->nr_80211_available == 1) {
-				connect_phy = bcm43xx_current_phy(bcm)->connected;
-			} else {
-				if (i == 0)
-					connect_phy = 1;
-				else
-					connect_phy = 0;
-			}
-			bcm43xx_wireless_core_reset(bcm, connect_phy);
+		if (core == active_core)
+			continue;
+		err = bcm43xx_switch_core(bcm, core);
+		if (err) {
+			dprintk(KERN_ERR PFX "Could not switch to inactive "
+					     "802.11 core (%d)\n", err);
+			goto error;
 		}
+		err = wireless_core_up(bcm, 0);
+		if (err) {
+			dprintk(KERN_ERR PFX "core_up for inactive 802.11 core "
+					     "failed (%d)\n", err);
+			goto error;
+		}
+		if (wlext->phy.type == BCM43xx_PHYTYPE_G)
+			adjust_active_sbtmstatelow = 1;
+	}
 
-		if (i != 0)
-			bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]);
-
-		err = bcm43xx_wireless_core_init(bcm);
-		if (err)
-			goto err_80211_unwind;
+	/* Now initialize the active 802.11 core. */
+	err = bcm43xx_switch_core(bcm, active_core);
+	if (err) {
+		dprintk(KERN_ERR PFX "Could not switch to active "
+				     "802.11 core (%d)\n", err);
+		goto error;
+	}
+	if (adjust_active_sbtmstatelow) {
+		u32 sbtmstatelow;
 
-		if (i != 0) {
-			bcm43xx_mac_suspend(bcm);
-			bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-			bcm43xx_radio_turn_off(bcm);
-		}
+		/* Some inactive 80211 core is a GPHY. */
+		sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+		sbtmstatelow &= ~0x20000000;
+		sbtmstatelow |= 0x20000000;
+		bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
 	}
-	bcm->active_80211_core = &bcm->core_80211[0];
-	if (bcm->nr_80211_available >= 2) {
-		bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
-		bcm43xx_mac_enable(bcm);
+	err = wireless_core_up(bcm, 1);
+	if (err) {
+		dprintk(KERN_ERR PFX "core_up for active 802.11 core "
+				     "failed (%d)\n", err);
+		goto error;
 	}
+	err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);
+	if (err)
+		goto error;
+	bcm->active_80211_core = active_core;
+
 	bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
 	bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
-	dprintk(KERN_INFO PFX "80211 cores initialized\n");
 	bcm43xx_security_init(bcm);
-	bcm43xx_softmac_init(bcm);
+	ieee80211softmac_start(bcm->net_dev);
 
-	bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);
+	/* Let's go! Be careful after enabling the IRQs.
+	 * Don't switch cores, for example.
+	 */
+	bcm43xx_mac_enable(bcm);
+	bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+	err = bcm43xx_initialize_irq(bcm);
+	if (err)
+		goto error;
+	bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
 
-	if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) {
-		bcm43xx_mac_suspend(bcm);
-		bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0);
-		bcm43xx_mac_enable(bcm);
-	}
+	dprintk(KERN_INFO PFX "Selected 802.11 core (phytype %d)\n",
+		active_wlext->phy.type);
 
-	/* Initialization of the board is done. Flag it as such. */
-	bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED);
+	return 0;
+
+error:
+	bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
+	bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
+	return err;
+}
+
+static int bcm43xx_init_board(struct bcm43xx_private *bcm)
+{
+	int err;
+
+	bcm43xx_lock_noirq(bcm);
+
+	tasklet_enable(&bcm->isr_tasklet);
+	err = bcm43xx_pctl_set_crystal(bcm, 1);
+	if (err)
+		goto err_tasklet;
+	err = bcm43xx_pctl_init(bcm);
+	if (err)
+		goto err_crystal_off;
+	err = bcm43xx_select_wireless_core(bcm, -1);
+	if (err)
+		goto err_crystal_off;
 
 	bcm43xx_periodic_tasks_setup(bcm);
-	bcm43xx_sysfs_register(bcm);
-	//FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though...
+	err = bcm43xx_sysfs_register(bcm);
+	if (err)
+		goto err_wlshutdown;
 
 	/*FIXME: This should be handled by softmac instead. */
 	schedule_work(&bcm->softmac->associnfo.work);
 
-	assert(err == 0);
 out:
 	bcm43xx_unlock_noirq(bcm);
 
 	return err;
 
-err_80211_unwind:
-	tasklet_disable(&bcm->isr_tasklet);
-	/* unwind all 80211 initialization */
-	for (i = 0; i < bcm->nr_80211_available; i++) {
-		if (!bcm->core_80211[i].initialized)
-			continue;
-		bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-		bcm43xx_wireless_core_cleanup(bcm);
-	}
+err_wlshutdown:
+	bcm43xx_shutdown_all_wireless_cores(bcm);
 err_crystal_off:
 	bcm43xx_pctl_set_crystal(bcm, 0);
+err_tasklet:
+	tasklet_disable(&bcm->isr_tasklet);
 	goto out;
 }
 
@@ -3744,7 +3882,8 @@
 	unsigned long flags;
 
 	local_irq_save(flags);
-	bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
+	if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
+		bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
 	local_irq_restore(flags);
 }
 #endif /* CONFIG_NET_POLL_CONTROLLER */
@@ -3903,7 +4042,6 @@
 	bcm43xx_debugfs_remove_device(bcm);
 	unregister_netdev(net_dev);
 	bcm43xx_detach_board(bcm);
-	assert(bcm->ucode == NULL);
 	free_ieee80211softmac(net_dev);
 }
 
@@ -3913,47 +4051,25 @@
 static void bcm43xx_chip_reset(void *_bcm)
 {
 	struct bcm43xx_private *bcm = _bcm;
-	struct net_device *net_dev = bcm->net_dev;
-	struct pci_dev *pci_dev = bcm->pci_dev;
+	struct bcm43xx_phyinfo *phy;
 	int err;
-	int was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
 
-	netif_stop_queue(bcm->net_dev);
-	tasklet_disable(&bcm->isr_tasklet);
-
-	bcm->firmware_norelease = 1;
-	if (was_initialized)
-		bcm43xx_free_board(bcm);
-	bcm->firmware_norelease = 0;
-	bcm43xx_detach_board(bcm);
-	err = bcm43xx_init_private(bcm, net_dev, pci_dev);
-	if (err)
-		goto failure;
-	err = bcm43xx_attach_board(bcm);
-	if (err)
-		goto failure;
-	if (was_initialized) {
-		err = bcm43xx_init_board(bcm);
-		if (err)
-			goto failure;
-	}
-	netif_wake_queue(bcm->net_dev);
-	printk(KERN_INFO PFX "Controller restarted\n");
+	bcm43xx_lock_noirq(bcm);
+	phy = bcm43xx_current_phy(bcm);
+	err = bcm43xx_select_wireless_core(bcm, phy->type);
+	bcm43xx_unlock_noirq(bcm);
 
-	return;
-failure:
-	printk(KERN_ERR PFX "Controller restart failed\n");
+	printk(KERN_ERR PFX "Controller restart%s\n",
+	       (err == 0) ? "ed" : " failed");
 }
 
 /* Hard-reset the chip.
  * This can be called from interrupt or process context.
- * Make sure to _not_ re-enable device interrupts after this has been called.
-*/
+ */
 void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason)
 {
+	assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
 	bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING);
-	bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-	bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
 	printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason);
 	INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm);
 	schedule_work(&bcm->restart_work);
Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c	2006-07-01 22:08:47.000000000 +0200
@@ -306,6 +306,40 @@
 	return res;
 }
 
+static ssize_t restart_write_file(struct file *file, const char __user *user_buf,
+				  size_t count, loff_t *ppos)
+{
+	struct bcm43xx_private *bcm = file->private_data;
+	char *buf = really_big_buffer;
+	ssize_t buf_size;
+	ssize_t res;
+	unsigned long flags;
+
+	buf_size = min(count, sizeof (really_big_buffer) - 1);
+	down(&big_buffer_sem);
+	if (copy_from_user(buf, user_buf, buf_size)) {
+	        res = -EFAULT;
+		goto out_up;
+	}
+	bcm43xx_lock_irqsafe(bcm, flags);
+	if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) {
+		printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	if (count > 0 && buf[0] == '1') {
+		bcm43xx_controller_restart(bcm, "manually restarted");
+		res = count;
+	} else
+		res = -EINVAL;
+
+out_unlock:
+	bcm43xx_unlock_irqsafe(bcm, flags);
+out_up:
+	up(&big_buffer_sem);
+	return res;
+}
+
 #undef fappend
 
 
@@ -339,6 +373,11 @@
 	.open = open_file_generic,
 };
 
+static struct file_operations restart_fops = {
+	.write = restart_write_file,
+	.open = open_file_generic,
+};
+
 
 void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
 {
@@ -390,6 +429,10 @@
 						bcm, &txstat_fops);
 	if (!e->dentry_txstat)
 		printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);
+	e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir,
+						bcm, &restart_fops);
+	if (!e->dentry_restart)
+		printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir);
 }
 
 void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
@@ -405,6 +448,7 @@
 	debugfs_remove(e->dentry_devinfo);
 	debugfs_remove(e->dentry_tsf);
 	debugfs_remove(e->dentry_txstat);
+	debugfs_remove(e->dentry_restart);
 	debugfs_remove(e->subdir);
 	kfree(e->xmitstatus_buffer);
 	kfree(e->xmitstatus_print_buffer);
Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h	2006-07-01 22:08:47.000000000 +0200
@@ -20,6 +20,7 @@
 	struct dentry *dentry_spromdump;
 	struct dentry *dentry_tsf;
 	struct dentry *dentry_txstat;
+	struct dentry *dentry_restart;
 
 	struct bcm43xx_private *bcm;
 
Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.h
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_main.h	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_main.h	2006-07-01 22:08:47.000000000 +0200
@@ -133,6 +133,9 @@
 
 int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);
 
+int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
+				 int phytype);
+
 void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
 
 void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
Index: wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
===================================================================
--- wireless-2.6.orig/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c	2006-07-01 22:07:00.000000000 +0200
+++ wireless-2.6/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c	2006-07-01 22:08:47.000000000 +0200
@@ -299,6 +299,70 @@
 		   bcm43xx_attr_preamble_show,
 		   bcm43xx_attr_preamble_store);
 
+static ssize_t bcm43xx_attr_phymode_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t count)
+{
+	struct bcm43xx_private *bcm = dev_to_bcm(dev);
+	int phytype;
+	int err = -EINVAL;
+
+	if (count < 1)
+		goto out;
+	switch (buf[0]) {
+	case 'a':  case 'A':
+		phytype = BCM43xx_PHYTYPE_A;
+		break;
+	case 'b':  case 'B':
+		phytype = BCM43xx_PHYTYPE_B;
+		break;
+	case 'g':  case 'G':
+		phytype = BCM43xx_PHYTYPE_G;
+		break;
+	default:
+		goto out;
+	}
+
+	bcm43xx_lock_noirq(bcm);
+	err = bcm43xx_select_wireless_core(bcm, phytype);
+	bcm43xx_unlock_noirq(bcm);
+	if (err == -ESRCH)
+		err = -ENODEV;
+
+out:
+	return err ? err : count;
+}
+
+static ssize_t bcm43xx_attr_phymode_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct bcm43xx_private *bcm = dev_to_bcm(dev);
+	ssize_t count = 0;
+
+	bcm43xx_lock_noirq(bcm);
+	switch (bcm43xx_current_phy(bcm)->type) {
+	case BCM43xx_PHYTYPE_A:
+		snprintf(buf, PAGE_SIZE, "A");
+		break;
+	case BCM43xx_PHYTYPE_B:
+		snprintf(buf, PAGE_SIZE, "B");
+		break;
+	case BCM43xx_PHYTYPE_G:
+		snprintf(buf, PAGE_SIZE, "G");
+		break;
+	default:
+		assert(0);
+	}
+	bcm43xx_unlock_noirq(bcm);
+
+	return count;
+}
+
+static DEVICE_ATTR(phymode, 0644,
+		   bcm43xx_attr_phymode_show,
+		   bcm43xx_attr_phymode_store);
+
 int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
 {
 	struct device *dev = &bcm->pci_dev->dev;
@@ -315,9 +379,14 @@
 	err = device_create_file(dev, &dev_attr_shortpreamble);
 	if (err)
 		goto err_remove_interfmode;
+	err = device_create_file(dev, &dev_attr_phymode);
+	if (err)
+		goto err_remove_shortpreamble;
 
 out:
 	return err;
+err_remove_shortpreamble:
+	device_remove_file(dev, &dev_attr_shortpreamble);
 err_remove_interfmode:
 	device_remove_file(dev, &dev_attr_interference);
 err_remove_sprom:
@@ -329,6 +398,7 @@
 {
 	struct device *dev = &bcm->pci_dev->dev;
 
+	device_remove_file(dev, &dev_attr_phymode);
 	device_remove_file(dev, &dev_attr_shortpreamble);
 	device_remove_file(dev, &dev_attr_interference);
 	device_remove_file(dev, &dev_attr_sprom);


-- 
Greetings Michael.

^ permalink raw reply

* Re: [PATCH 0 of 39] ipath - bug fixes, performanceenhancements,and portability improvements
From: Michael S. Tsirkin @ 2006-07-01 19:43 UTC (permalink / raw)
  To: Bryan O'Sullivan
  Cc: akpm, Roland Dreier, openib-general, linux-kernel, netdev
In-Reply-To: <1151686831.2194.7.camel@localhost.localdomain>

Quoting r. Bryan O'Sullivan <bos@pathscale.com>:
> Subject: Re: [PATCH 0 of 39] ipath - bug fixes, performanceenhancements,and portability improvements
> 
> On Fri, 2006-06-30 at 19:31 +0300, Michael S. Tsirkin wrote:
> 
> > OK, next week I'll put these into my tree, too.
> 
> Thanks.  The first 37 are in -mm; the last two you can drop until I sort
> them out.

What about patch 28?

-- 
MST

^ permalink raw reply

* Re[13]:
From: Henry Bowman @ 2006-07-01 18:27 UTC (permalink / raw)
  To: netdev
In-Reply-To: <29131128143048.B6098@wesley.doit.wisc.edu>

Icrease Your S''exual Desire and S''perm volume by 500%
L'onger o''rgasms - The longest most intense o''rgasms of your life
Multiple o''rgasms - Cum again and again
S'PUR-M is The Newest and The Safest Way
100% N''atural and No Side Effects - in contrast to well-known brands.
Experience three times longer o''rgasms
World Wide shipping within 24 hours
 
 Clisk here http://www.ulln.net



trick foppish frances shopworn norwich denver wring caliph
richter arsenic montclair acute olympic plunder boulder kalamazoo

^ permalink raw reply

* Re: Please pull 'upstream' branch of wireless-2.6 (revised)
From: John W. Linville @ 2006-07-01 15:00 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev
In-Reply-To: <20060630192658.GB4359@tuxdriver.com>

On Fri, Jun 30, 2006 at 03:27:02PM -0400, John W. Linville wrote:
> On Fri, Jun 30, 2006 at 12:31:00PM -0400, Jeff Garzik wrote:
> 
> > >The following changes since commit 
> > >fcc18e83e1f6fd9fa6b333735bf0fcd530655511:
> > >  Malcolm Parsons:
> > >        uclinux: use PER_LINUX_32BIT in binfmt_flat
> > >
> > >are found in the git repository at:
> > >
> > >  git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git 
> > >  upstream
> > 
> > nothing pulled?
> 
> Odd, the changes seem to be there...did you happen to pull zd1211rw
> first?  I based that branch off the HEAD of the upstream branch.

Just to be sure, I re-cloned the netdev-2.6 tree from kernel.org and
did pulls from the 'upstream' branch of wireless-2.6.  Everything
seemed to work as expected.

Do you have this commit?

	70a332b048e4d90635dfa47fc5d91cf87b5cc3a5

That is the latest one in my upstream branch.

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply

*  ïîðíî ôîòî
From:  "Îëåã Áîéêî" @ 2006-07-01 15:48 UTC (permalink / raw)
  To: netdev


На сайте порно фото девочек:

http://vigovo.by.ru

^ permalink raw reply

* Re: RDMA will be reverted
From: Andi Kleen @ 2006-07-01 14:26 UTC (permalink / raw)
  To: Tom Tucker; +Cc: David Miller, rdreier, netdev, akpm
In-Reply-To: <1151708503.11835.8.camel@trinity.ogc.int>

On Saturday 01 July 2006 01:01, Tom Tucker wrote:
> On Fri, 2006-06-30 at 14:16 -0700, David Miller wrote:
> 
> > The TOE folks have tried to submit their hooks and drivers
> > on several occaisions, and we've rejected it every time.
> 
> iWARP != TOE

Perhaps a good start of that discussion David asked for would 
be if you could give us an overview of the differences
and how you avoid the TOE problems.

-Andi

^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: jamal @ 2006-07-01 13:47 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Patrick McHardy, David Miller, netdev
In-Reply-To: <20060701115112.GT14627@postel.suug.ch>

On Sat, 2006-01-07 at 13:51 +0200, Thomas Graf wrote:
> * jamal <hadi@cyberus.ca> 2006-06-30 22:23
> > I am  not certain i understood then: Are we in the mode where the
> > refcount is not needed because chances are small that a device will
> > disappear? It seems to me after all this trouble that it may not be so
> > bad to refcount (I guess i meant refcount the device on input to the ifb
> > and decrement on the output).
> 
> Based on two principles:
> 
>  a) All netdevices a packet is processed through is properly
>     refcounted until the packet is queued for the first time.
>     Given that iif is strictly set to the previous device, a
>     refcnt is certainly taken up to the point where the skb is
>     put into rq. 

yes, but that refcount may be lost by the time it is dequeued and
retransmitted. Am i wrong thinking so?

> There is one exception to this: The fact that
>     you don't set skb->dev = ifb when reinjecting at ingress
>     will not take a refcnt on the ifb. This sounds like a broken
>     architecture to me but it doesn't matter as you want to
>     prohibit ifb -> ifb anyways.

Ok, Thomas: Please stop critiqueing the architecture non-stop. You will
not convince me of anything this way. Lets just focus on this issue and
then you can go back and critique all you want.

>  
>  b) The netdevice used for xmit via dev_queue_xmit() is already
>     protected by the initial xmit attempt.
> 

Is it guaranteed? if yes, the patch is perfect.

> I can't see reason why to hurt performance by introducing more
> atomic operations in your fast path.
> 

well, i would be fine with this - with a caveat that nothing really has
changed in ifb then, no? i.e the value of the patch in that case would
be to convert input_dev to iif, correct? If yes, this is fine by me
since as we have discussed the likelihood of this was small.

> If you have more concerns regarding these patches, feel free to
> fix your own architecture or propose to drop the patches, I think
> I've spend enough time on this.

Again, you are not being helpful by throwing in side remarks like these.
I am being very fair to you when you ask questions on how X works after
all assumptions you make - yet i ask questions which i see as valid and
you start throwing your hands off in the air. And then Patrick says i am
creating endless debates; this is why we go into loops.
Just answer the questions: I am trying to understand or you can choose
to ignore the emails all together.

cheers,
jamal


^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: jamal @ 2006-07-01 13:35 UTC (permalink / raw)
  To: Thomas Graf; +Cc: kaber, netdev, David Miller
In-Reply-To: <20060701112833.GS14627@postel.suug.ch>

On Sat, 2006-01-07 at 13:28 +0200, Thomas Graf wrote:
> * jamal <hadi@cyberus.ca> 2006-06-30 22:59

> Please enlighten me, I may be making a wrong assumption again.
> 
> 1) tc_verd is reset to 0 after dq in ri_tasklet()
> 2) TC_AT is set to AT_EGRESS in dev_queue_xmit()
> 3) TC_FROM being derived from TC_AT in tcf_mirred() when redirecting
> 4) TC_FROM has the value AT_EGRESS when entering ifb_xmit()
> 

Let me answer the next bit and it may clear this.

> > different instances of the same device do not deadlock. If however, you
> > have a series of devices in which A->B->C->D->A then you will have a
> > possible deadlock. In the case of the ifb i dissallowed it because it
> > was obvious from the testing. 
> 
> Correct me if I'm wrong. The skb is queued between each device. When
> moving skbs from rq to tq the tx lock is acquired, if not available
> the packet is requeued for later. Due to the queueing the tx lock
> of the previous device is released. Where exactly is the deadlock?

using what i said as looping in the case of A->B->C->D->A for the case
of egress since that is what you are alluding to;
note that ifb0 will be entered twice: First for example the tasklet in
ifb0 will emit the packet, and then it will go all the way back to xmit
on ifb0. This is where the issue is. Does that clear it?

cheers,
jamal



^ permalink raw reply

* Re: PATCH SB1250 NAPI support
From: Tom Rix @ 2006-07-01 13:27 UTC (permalink / raw)
  To: Francois Romieu; +Cc: tbm, jgarzik, netdev, linux-mips, mark.e.mason
In-Reply-To: <20060629200107.GA8122@electric-eye.fr.zoreil.com>

[-- Attachment #1: Type: text/plain, Size: 1394 bytes --]

Here is a revised patch that does the interupt masking earlier.  Since  
resetting the mask is done now in 3 places, I created a macro to handle  
it.  This patch has been tested on the linux-mips kernel from 6/29/06 on a  
sb1250.
Tom

On Thu, 29 Jun 2006 15:01:07 -0500, Francois Romieu <romieu@fr.zoreil.com>  
wrote:

> Tom Rix <trix@specifix.com> :
> [...]
> diff -rup a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c
> --- a/drivers/net/sb1250-mac.c	2006-03-09 04:25:41.000000000 -0600
> +++ b/drivers/net/sb1250-mac.c	2006-03-09 05:30:52.000000000 -0600
> [...]
> @@ -2079,13 +2095,31 @@ static irqreturn_t sbmac_intr(int irq,vo
>  		 * Transmits on channel 0
>  		 */
> +#if defined(CONFIG_SBMAC_NAPI)
>  		if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) {
> -			sbdma_tx_process(sc,&(sc->sbm_txdma));
> +			sbdma_tx_process(sc,&(sc->sbm_txdma), 0);
>  		}
> 		/*
>  		 * Receives on channel 0
>  		 */
> +		if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
> +			if (netif_rx_schedule_prep(dev))
> +			{
>
> An irq could appear here. I am skeptical that it is safe to write
> the irq mask register so late.
>
> One should probably consider a break in the enclosing "for" loop too.
>
>
> +				__raw_writeq(0, sc->sbm_imr);
> +				__netif_rx_schedule(dev);
> +			}
> +			else
> +			{
>
> } else {, please.
>



-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

[-- Attachment #2: mips-sb1250-mac-NAPI-5.patch --]
[-- Type: application/octet-stream, Size: 11138 bytes --]

--- a/drivers/net/Kconfig	2006-07-01 06:29:11.000000000 -0500
+++ b/drivers/net/Kconfig	2006-07-01 06:28:58.000000000 -0500
@@ -2013,10 +2013,6 @@ config R8169_NAPI
 
 	  If in doubt, say N.
 
-config NET_SB1250_MAC
-	tristate "SB1250 Ethernet support"
-	depends on SIBYTE_SB1xxx_SOC
-
 config R8169_VLAN
 	bool "VLAN support"
 	depends on R8169 && VLAN_8021Q
@@ -2026,6 +2022,27 @@ config R8169_VLAN
 	  
 	  If in doubt, say Y.
 
+config NET_SB1250_MAC
+	tristate "SB1250 Ethernet support"
+	depends on SIBYTE_SB1xxx_SOC
+
+config SBMAC_NAPI
+	bool "Use Rx Polling (NAPI) (EXPERIMENTAL)"
+	depends on NET_SB1250_MAC && EXPERIMENTAL
+	help
+	  NAPI is a new driver API designed to reduce CPU and interrupt load
+	  when the driver is receiving lots of packets from the card. It is
+	  still somewhat experimental and thus not yet enabled by default.
+
+	  If your estimated Rx load is 10kpps or more, or if the card will be
+	  deployed on potentially unfriendly networks (e.g. in a firewall),
+	  then say Y here.
+
+	  See <file:Documentation/networking/NAPI_HOWTO.txt> for more
+	  information.
+
+	  If in doubt, say y.
+
 config SIS190
 	tristate "SiS190/SiS191 gigabit ethernet support"
 	depends on PCI
--- a/drivers/net/sb1250-mac.c	2006-07-01 06:29:27.000000000 -0500
+++ b/drivers/net/sb1250-mac.c	2006-07-01 06:29:18.000000000 -0500
@@ -164,6 +164,25 @@ typedef enum { sbmac_state_uninit, sbmac
 #define ENET_PACKET_SIZE	1518
 /*#define ENET_PACKET_SIZE	9216 */
 
+
+#ifdef CONFIG_SBMAC_COALESCE
+/*
+ * Accept any TX interrupt and EOP count/timer RX interrupts on ch 0
+ */
+#define SET_INTR_MASK(S) \
+__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | \
+	     ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), \
+	     (S)->sbm_imr);
+#else
+/*
+ * Accept any kind of interrupt on TX and RX DMA channel 0
+ */
+#define SET_INTR_MASK(S) \
+__raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | \
+             (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), (S)->sbm_imr);
+#endif
+
+
 /**********************************************************************
  *  DMA Descriptor structure
  ********************************************************************* */
@@ -205,6 +224,7 @@ typedef struct sbmacdma_s {
 	 * This stuff is for maintenance of the ring
 	 */
 
+	sbdmadscr_t     *sbdma_dscrtable_unaligned; 
 	sbdmadscr_t     *sbdma_dscrtable;	/* base of descriptor table */
 	sbdmadscr_t     *sbdma_dscrtable_end; /* end of descriptor table */
 
@@ -287,8 +307,8 @@ static int sbdma_add_rcvbuffer(sbmacdma_
 static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m);
 static void sbdma_emptyring(sbmacdma_t *d);
 static void sbdma_fillring(sbmacdma_t *d);
-static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d);
-static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d);
+static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll);
+static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll);
 static int sbmac_initctx(struct sbmac_softc *s);
 static void sbmac_channel_start(struct sbmac_softc *s);
 static void sbmac_channel_stop(struct sbmac_softc *s);
@@ -309,6 +329,10 @@ static struct net_device_stats *sbmac_ge
 static void sbmac_set_rx_mode(struct net_device *dev);
 static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int sbmac_close(struct net_device *dev);
+#if defined (CONFIG_SBMAC_NAPI)
+static int sbmac_poll(struct net_device *poll_dev, int *budget);
+#endif
+
 static int sbmac_mii_poll(struct sbmac_softc *s,int noisy);
 static int sbmac_mii_probe(struct net_device *dev);
 
@@ -736,7 +760,7 @@ static void sbdma_initctx(sbmacdma_t *d,
 
 	d->sbdma_maxdescr = maxdescr;
 
-	d->sbdma_dscrtable = (sbdmadscr_t *)
+	d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = (sbdmadscr_t *)
 		kmalloc((d->sbdma_maxdescr+1)*sizeof(sbdmadscr_t), GFP_KERNEL);
 
 	/*
@@ -778,7 +802,6 @@ static void sbdma_initctx(sbmacdma_t *d,
 		d->sbdma_int_timeout = 0;
 	}
 #endif
-
 }
 
 /**********************************************************************
@@ -1146,13 +1169,14 @@ static void sbdma_fillring(sbmacdma_t *d
  *  	   nothing
  ********************************************************************* */
 
-static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d)
+static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll)
 {
 	int curidx;
 	int hwidx;
 	sbdmadscr_t *dsc;
 	struct sk_buff *sb;
 	int len;
+	int work_done = 0;
 
 	for (;;) {
 		/*
@@ -1230,8 +1254,15 @@ static void sbdma_rx_process(struct sbma
 						sb->ip_summed = CHECKSUM_NONE;
 					}
 				}
-
+				
+#if defined(CONFIG_SBMAC_NAPI)
+				if (0 == poll)
+					netif_rx(sb);
+				else
+					netif_receive_skb(sb);
+#else
 				netif_rx(sb);
+#endif				
 			}
 		} else {
 			/*
@@ -1248,8 +1279,9 @@ static void sbdma_rx_process(struct sbma
 		 */
 
 		d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
-
+		work_done++;
 	}
+	return work_done;
 }
 
 
@@ -1271,15 +1303,22 @@ static void sbdma_rx_process(struct sbma
  *  	   nothing
  ********************************************************************* */
 
-static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d)
+static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll)
 {
 	int curidx;
 	int hwidx;
 	sbdmadscr_t *dsc;
 	struct sk_buff *sb;
 	unsigned long flags;
+	int packets_handled = 0;
 
 	spin_lock_irqsave(&(sc->sbm_lock), flags);
+	
+	if (d->sbdma_remptr == d->sbdma_addptr)
+		goto end_unlock;
+
+	hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) -
+			d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t));
 
 	for (;;) {
 		/*
@@ -1294,15 +1333,12 @@ static void sbdma_tx_process(struct sbma
 		 */
 
 		curidx = d->sbdma_remptr - d->sbdma_dscrtable;
-		hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) -
-				d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t));
 
 		/*
 		 * If they're the same, that means we've processed all
 		 * of the descriptors up to (but not including) the one that
 		 * the hardware is working on right now.
 		 */
-
 		if (curidx == hwidx)
 			break;
 
@@ -1333,6 +1369,7 @@ static void sbdma_tx_process(struct sbma
 
 		d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr);
 
+		packets_handled++;
 	}
 
 	/*
@@ -1340,15 +1377,15 @@ static void sbdma_tx_process(struct sbma
 	 * Other drivers seem to do this when we reach a low
 	 * watermark on the transmit queue.
 	 */
+	
+	if (packets_handled)
+		netif_wake_queue(d->sbdma_eth->sbm_dev);
 
-	netif_wake_queue(d->sbdma_eth->sbm_dev);
-
+ end_unlock:
 	spin_unlock_irqrestore(&(sc->sbm_lock), flags);
 
 }
 
-
-
 /**********************************************************************
  *  SBMAC_INITCTX(s)
  *
@@ -1392,7 +1429,6 @@ static int sbmac_initctx(struct sbmac_so
 	 * Initialize the DMA channels.  Right now, only one per MAC is used
 	 * Note: Only do this _once_, as it allocates memory from the kernel!
 	 */
-
 	sbdma_initctx(&(s->sbm_txdma),s,0,DMA_TX,SBMAC_MAX_TXDESCR);
 	sbdma_initctx(&(s->sbm_rxdma),s,0,DMA_RX,SBMAC_MAX_RXDESCR);
 
@@ -1416,9 +1452,9 @@ static int sbmac_initctx(struct sbmac_so
 
 static void sbdma_uninitctx(struct sbmacdma_s *d)
 {
-	if (d->sbdma_dscrtable) {
-		kfree(d->sbdma_dscrtable);
-		d->sbdma_dscrtable = NULL;
+	if (d->sbdma_dscrtable_unaligned) {
+		kfree(d->sbdma_dscrtable_unaligned);
+		d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL;
 	}
 
 	if (d->sbdma_ctxtable) {
@@ -1605,29 +1641,16 @@ static void sbmac_channel_start(struct s
 
 #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
 	__raw_writeq(M_MAC_RXDMA_EN0 |
-		       M_MAC_TXDMA_EN0, s->sbm_macenable);
+		     M_MAC_TXDMA_EN0, s->sbm_macenable);
 #elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
 	__raw_writeq(M_MAC_RXDMA_EN0 |
-		       M_MAC_TXDMA_EN0 |
-		       M_MAC_RX_ENABLE |
-		       M_MAC_TX_ENABLE, s->sbm_macenable);
+		     M_MAC_TXDMA_EN0 |
+		     M_MAC_RX_ENABLE |
+		     M_MAC_TX_ENABLE, s->sbm_macenable);
 #else
 #error invalid SiByte MAC configuation
 #endif
-
-#ifdef CONFIG_SBMAC_COALESCE
-	/*
-	 * Accept any TX interrupt and EOP count/timer RX interrupts on ch 0
-	 */
-	__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) |
-		       ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), s->sbm_imr);
-#else
-	/*
-	 * Accept any kind of interrupt on TX and RX DMA channel 0
-	 */
-	__raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) |
-		       (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), s->sbm_imr);
-#endif
+	SET_INTR_MASK(s);
 
 	/*
 	 * Enable receiving unicasts and broadcasts
@@ -2063,7 +2086,6 @@ static irqreturn_t sbmac_intr(int irq,vo
 		 * Read the ISR (this clears the bits in the real
 		 * register, except for counter addr)
 		 */
-
 		isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR;
 
 		if (isr == 0)
@@ -2075,13 +2097,33 @@ static irqreturn_t sbmac_intr(int irq,vo
 		 * Transmits on channel 0
 		 */
 
+#if defined(CONFIG_SBMAC_NAPI)
+		if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
+			__raw_writeq(0, sc->sbm_imr);
+		}
+
 		if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) {
-			sbdma_tx_process(sc,&(sc->sbm_txdma));
+			sbdma_tx_process(sc,&(sc->sbm_txdma), 0);
 		}
 
 		/*
 		 * Receives on channel 0
 		 */
+		if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
+			if (netif_rx_schedule_prep(dev))
+			{
+				__netif_rx_schedule(dev);
+			} else {
+				SET_INTR_MASK(sc);
+				sbdma_rx_process(sc,&(sc->sbm_rxdma), 0);
+			}
+		}
+#else
+		/* Non NAPI */
+
+		if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) {
+			sbdma_tx_process(sc,&(sc->sbm_txdma), 0);
+		}
 
 		/*
 		 * It's important to test all the bits (or at least the
@@ -2101,8 +2143,9 @@ static irqreturn_t sbmac_intr(int irq,vo
 
 
 		if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
-			sbdma_rx_process(sc,&(sc->sbm_rxdma));
+			sbdma_rx_process(sc,&(sc->sbm_rxdma), 0);
 		}
+#endif
 	}
 	return IRQ_RETVAL(handled);
 }
@@ -2401,7 +2444,10 @@ static int sbmac_init(struct net_device 
 	dev->do_ioctl           = sbmac_mii_ioctl;
 	dev->tx_timeout         = sbmac_tx_timeout;
 	dev->watchdog_timeo     = TX_TIMEOUT;
-
+#if defined(CONFIG_SBMAC_NAPI)
+	dev->poll               = sbmac_poll;
+	dev->weight             = 16;
+#endif
 	dev->change_mtu         = sb1250_change_mtu;
 
 	/* This is needed for PASS2 for Rx H/W checksum feature */
@@ -2809,7 +2855,29 @@ static int sbmac_close(struct net_device
 	return 0;
 }
 
+#if defined(CONFIG_SBMAC_NAPI)
+static int sbmac_poll(struct net_device *dev, int *budget)
+{
+	int work_to_do;
+	int work_done;
+	struct sbmac_softc *sc = netdev_priv(dev);
+
+	work_to_do = min(*budget, dev->quota);
+	work_done = sbdma_rx_process(sc,&(sc->sbm_rxdma), 1);
 
+	sbdma_tx_process(sc,&(sc->sbm_txdma), 1);
+
+	*budget -= work_done;
+	dev->quota -= work_done;
+
+	if (work_done < work_to_do) {
+		netif_rx_complete(dev);
+		SET_INTR_MASK(sc);
+	}
+
+	return (work_done >= work_to_do);
+}
+#endif
 
 #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR)
 static void

^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: Thomas Graf @ 2006-07-01 11:51 UTC (permalink / raw)
  To: jamal; +Cc: netdev, David Miller, Patrick McHardy
In-Reply-To: <1151720635.5093.15.camel@jzny2>

* jamal <hadi@cyberus.ca> 2006-06-30 22:23
> I am  not certain i understood then: Are we in the mode where the
> refcount is not needed because chances are small that a device will
> disappear? It seems to me after all this trouble that it may not be so
> bad to refcount (I guess i meant refcount the device on input to the ifb
> and decrement on the output).

Based on two principles:

 a) All netdevices a packet is processed through is properly
    refcounted until the packet is queued for the first time.
    Given that iif is strictly set to the previous device, a
    refcnt is certainly taken up to the point where the skb is
    put into rq. There is one exception to this: The fact that
    you don't set skb->dev = ifb when reinjecting at ingress
    will not take a refcnt on the ifb. This sounds like a broken
    architecture to me but it doesn't matter as you want to
    prohibit ifb -> ifb anyways.
 
 b) The netdevice used for xmit via dev_queue_xmit() is already
    protected by the initial xmit attempt.

I can't see reason why to hurt performance by introducing more
atomic operations in your fast path.

If you have more concerns regarding these patches, feel free to
fix your own architecture or propose to drop the patches, I think
I've spend enough time on this.

^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: Thomas Graf @ 2006-07-01 11:28 UTC (permalink / raw)
  To: jamal; +Cc: David Miller, netdev, kaber
In-Reply-To: <1151722785.5093.51.camel@jzny2>

* jamal <hadi@cyberus.ca> 2006-06-30 22:59
> On Sat, 2006-01-07 at 01:45 +0200, Thomas Graf wrote:
> > Fact is that the from verdict is set to a meaningful value again at 
> > dev_queue_xmit() or ing_filter() so ifb_xmit() only sees valid values. 
> 
> Ok, Thomas this is one of those places where we end up colliding for no
> good reason - your statement above is accusatory. And sometimes i say 3
> things and you pick one and use that as context. The ifb does clear the
> field. So if you get redirected again, it will be 0.

Please enlighten me, I may be making a wrong assumption again.

1) tc_verd is reset to 0 after dq in ri_tasklet()
2) TC_AT is set to AT_EGRESS in dev_queue_xmit()
3) TC_FROM being derived from TC_AT in tcf_mirred() when redirecting
4) TC_FROM has the value AT_EGRESS when entering ifb_xmit()

> different instances of the same device do not deadlock. If however, you
> have a series of devices in which A->B->C->D->A then you will have a
> possible deadlock. In the case of the ifb i dissallowed it because it
> was obvious from the testing. 

Correct me if I'm wrong. The skb is queued between each device. When
moving skbs from rq to tq the tx lock is acquired, if not available
the packet is requeued for later. Due to the queueing the tx lock
of the previous device is released. Where exactly is the deadlock?

^ permalink raw reply

* Re: [PATCH] [2.6.17]   Add tsi108 Ethernet device driver support
From: Andrew Morton @ 2006-07-01  9:30 UTC (permalink / raw)
  To: Zang Roy-r61911
  Cc: jgarzik, netdev, linux-kernel, Alexandre.Bounine, tie-fei.zang,
	Xin-Xin.Yang
In-Reply-To: <9FCDBA58F226D911B202000BDBAD467306C0358D@zch01exm40.ap.freescale.net>

On Wed, 21 Jun 2006 12:00:40 +0800
Zang Roy-r61911 <tie-fei.zang@freescale.com> wrote:

> This patch adds a net device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit 
> Ethernet controller

Your patch forgot to include these:

> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>

Why would a net driver have files in include/asm/?




Have some comments-via-diff, mainly trivial:


From: Andrew Morton <akpm@osdl.org>

- Nuke the typedef

- Reduce exorbitant inlining.

- possible bug: tsi108_open() does request_irq() as its first operation. 
  The IRQ handler can run immediately.  Is it ready for that?

- Use setup_irq().

- Unneeded casts of void*

- alloc_etherdev() already zeroes the memory.

- why are tsi108_prv_data.regs and .phyregs volatile?  They shouldn't be.

- What's this?

	data->regs = (u32)ioremap(einfo->regs, 0x400);	/*FIX ME */
	data->phyregs = (u32)ioremap(einfo->phyregs, 0x400); 	/*FIX ME */

- Use DEFINE_SPINLOCK()

- Use free_netdev(), not kfree() (Is this right?)

- Use mod_timer()

- tsi108_init_phy() has a 10-millisecond busywait in it, seemingly
  needlessly.  Convert to msleep().

- tsi108_init_phy() has multiple potentially infinite wait loops.  Add
  timeouts.

- Random coding-style cleanups.





Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 drivers/net/tsi108_eth.c |  295 ++++++++++++++++++-------------------
 1 file changed, 145 insertions(+), 150 deletions(-)

diff -puN drivers/net/tsi108_eth.c~add-tsi108-ethernet-device-driver-support-tidy drivers/net/tsi108_eth.c
--- a/drivers/net/tsi108_eth.c~add-tsi108-ethernet-device-driver-support-tidy
+++ a/drivers/net/tsi108_eth.c
@@ -44,15 +44,16 @@
 #include <linux/crc32.h>
 #include <linux/mii.h>
 #include <linux/device.h>
-#include <asm/system.h>
-#include <asm/io.h>
 #include <linux/pci.h>
 #include <linux/rtnetlink.h>
 #include <linux/timer.h>
 #include <linux/platform_device.h>
 
+#include <asm/system.h>
+#include <asm/io.h>
 #include <asm/tsi108_irq.h>
 #include <asm/tsi108.h>
+
 #include "tsi108_eth.h"
 
 #undef DEBUG
@@ -85,7 +86,7 @@ typedef struct sk_buff sk_buff;
 static int tsi108_init_one(struct platform_device *pdev);
 static int tsi108_ether_remove(struct platform_device *pdev);
 
-typedef struct {
+struct tsi108_prv_data {
 	volatile u32 regs;	/* Base of normal regs */
 	volatile u32 phyregs;	/* Base of register bank used for PHY access */
 	int phy;		/* Index of PHY for this interface */
@@ -154,7 +155,7 @@ typedef struct {
 	unsigned long tx_pause_drop;	/* Add to tx_aborted_errors */
 
 	unsigned long mc_hash[16];
-} tsi108_prv_data;
+};
 
 /* Structure for a device driver */
 
@@ -168,9 +169,9 @@ static struct platform_driver tsi_eth_dr
 
 static void tsi108_timed_checker(unsigned long dev_ptr);
 
-static void dump_eth_one(net_device * dev)
+static void dump_eth_one(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	printk("Dumping %s...\n", dev->name);
 	printk("intstat %x intmask %x phy_ok %d"
@@ -198,9 +199,9 @@ static void dump_eth_one(net_device * de
  * interfaces, so this can't be made interface-specific.
  */
 
-static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(phy_lock);
 
-static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg, int *status)
+static u16 tsi108_read_mii(struct tsi108_prv_data *data, int reg, int *status)
 {
 	int i;
 	u16 ret;
@@ -235,7 +236,7 @@ static inline u16 tsi108_read_mii(tsi108
 	return ret;
 }
 
-static inline void tsi108_write_mii(tsi108_prv_data * data, int reg, u16 val)
+static void tsi108_write_mii(struct tsi108_prv_data *data, int reg, u16 val)
 {
 	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
 				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
@@ -247,7 +248,8 @@ static inline void tsi108_write_mii(tsi1
 	       TSI108_MAC_MII_IND_BUSY) ;
 }
 
-static inline void tsi108_write_tbi(tsi108_prv_data * data, int reg, u16 val)
+static inline void tsi108_write_tbi(struct tsi108_prv_data *data,
+					int reg, u16 val)
 {
 
 	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
@@ -261,9 +263,9 @@ static inline void tsi108_write_tbi(tsi1
 	       TSI108_MAC_MII_IND_BUSY) ;
 }
 
-static void tsi108_check_phy(net_device * dev)
+static void tsi108_check_phy(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u16 sumstat;
 	u32 mac_cfg2_reg, portctrl_reg;
 	u32 fdx_flag = 0, reg_update = 0;
@@ -291,79 +293,76 @@ static void tsi108_check_phy(net_device 
 		goto out;
 	}
 
-	{
-		mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
-		portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
-
-		sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
-
-		switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
-		case PHY_SUM_STAT_1000T_FD:
-			fdx_flag++;
-		case PHY_SUM_STAT_1000T_HD:
-			if (data->speed != 1000) {
-				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
-				mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
-				portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
-				data->speed = 1000;
-				reg_update++;
-			}
-			break;
-		case PHY_SUM_STAT_100TX_FD:
-			fdx_flag++;
-		case PHY_SUM_STAT_100TX_HD:
-			if (data->speed != 100) {
-				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
-				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
-				portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
-				data->speed = 100;
-				reg_update++;
-			}
-			break;
+	mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
+	portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
 
-		case PHY_SUM_STAT_10T_FD:
-			fdx_flag++;
-		case PHY_SUM_STAT_10T_HD:
-			if (data->speed != 10) {
-				mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
-				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
-				portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
-				data->speed = 10;
-				reg_update++;
-			}
-			break;
+	sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
 
-		default:
-			if (net_ratelimit())
-				printk(KERN_ERR "PHY reported invalid speed,"
-				       KERN_ERR " summary status %x\n",
-				       sumstat);
-			goto out;
+	switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
+	case PHY_SUM_STAT_1000T_FD:
+		fdx_flag++;
+	case PHY_SUM_STAT_1000T_HD:
+		if (data->speed != 1000) {
+			mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+			mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
+			portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
+			data->speed = 1000;
+			reg_update++;
 		}
+		break;
+	case PHY_SUM_STAT_100TX_FD:
+		fdx_flag++;
+	case PHY_SUM_STAT_100TX_HD:
+		if (data->speed != 100) {
+			mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+			mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
+			portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
+			data->speed = 100;
+			reg_update++;
+		}
+		break;
 
-		if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
-			if (data->duplex != 2) {
-				mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
-				portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
-				data->duplex = 2;
-				reg_update++;
-			}
-		} else {
-			if (data->duplex != 1) {
-				mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
-				portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
-				data->duplex = 1;
-				reg_update++;
-			}
+	case PHY_SUM_STAT_10T_FD:
+		fdx_flag++;
+	case PHY_SUM_STAT_10T_HD:
+		if (data->speed != 10) {
+			mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
+			mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
+			portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
+			data->speed = 10;
+			reg_update++;
 		}
+		break;
 
-		if (reg_update) {
-			TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
-			mb();
-			TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
-			mb();
+	default:
+		if (net_ratelimit())
+			printk(KERN_ERR "PHY reported invalid speed,"
+			       KERN_ERR " summary status %x\n",
+			       sumstat);
+		goto out;
+	}
+
+	if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
+		if (data->duplex != 2) {
+			mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
+			portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
+			data->duplex = 2;
+			reg_update++;
 		}
+	} else {
+		if (data->duplex != 1) {
+			mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
+			portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
+			data->duplex = 1;
+			reg_update++;
+		}
+	}
 
+	if (reg_update) {
+		TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
+		mb();
+		TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
+		mb();
 	}
 
 	if (data->link_up == 0) {
@@ -397,9 +396,9 @@ tsi108_stat_carry_one(int carry, int car
 		*upper += carry_shift;
 }
 
-static void tsi108_stat_carry(net_device * dev)
+static void tsi108_stat_carry(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 carry1, carry2;
 
 	spin_lock_irq(&data->misclock);
@@ -477,7 +476,7 @@ static void tsi108_stat_carry(net_device
  * data->misclock must be held.
  */
 static inline unsigned long
-tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
+tsi108_read_stat(struct tsi108_prv_data * data, int reg, int carry_bit,
 		 int carry_shift, unsigned long *upper)
 {
 	int carryreg;
@@ -509,11 +508,11 @@ tsi108_read_stat(tsi108_prv_data * data,
 	return val;
 }
 
-static struct net_device_stats *tsi108_get_stats(net_device * dev)
+static struct net_device_stats *tsi108_get_stats(net_device *dev)
 {
 	unsigned long excol;
 
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	spin_lock_irq(&data->misclock);
 
 	data->tmpstats.rx_packets =
@@ -619,7 +618,7 @@ static struct net_device_stats *tsi108_g
 	return &data->tmpstats;
 }
 
-static void tsi108_restart_rx(tsi108_prv_data * data, net_device * dev)
+static void tsi108_restart_rx(struct tsi108_prv_data * data, net_device *dev)
 {
 	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
 			     TSI108_EC_RXQ_PTRHIGH_VALID);
@@ -629,7 +628,7 @@ static void tsi108_restart_rx(tsi108_prv
 			     | TSI108_EC_RXCTRL_QUEUE0);
 }
 
-static void tsi108_restart_tx(tsi108_prv_data * data)
+static void tsi108_restart_tx(struct tsi108_prv_data * data)
 {
 	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
 			     TSI108_EC_TXQ_PTRHIGH_VALID);
@@ -642,9 +641,9 @@ static void tsi108_restart_tx(tsi108_prv
 /* txlock must be held by caller, with IRQs disabled, and
  * with permission to re-enable them when the lock is dropped.
  */
-static void tsi108_check_for_completed_tx(net_device * dev)
+static void tsi108_check_for_completed_tx(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	int tx;
 	struct sk_buff *skb;
 	int release = 0;
@@ -671,16 +670,15 @@ static void tsi108_check_for_completed_t
 	}
 
 	if (release) {
-
 		if (netif_queue_stopped(dev)
 		    && is_valid_ether_addr(dev->dev_addr) && data->link_up)
 			netif_wake_queue(dev);
 	}
 }
 
-static int tsi108_send_packet(sk_buff * skb, net_device * dev)
+static int tsi108_send_packet(sk_buff * skb, net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	int frags = skb_shinfo(skb)->nr_frags + 1;
 	int i;
 #ifdef FIXME_SG_CSUM_NOT_TESTED	/* FIXME: Not supported now. */
@@ -735,8 +733,7 @@ static int tsi108_send_packet(sk_buff * 
 		 */
 
 		if ((tx % TSI108_TX_INT_FREQ == 0) &&
-		    ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)
-		    )
+		    ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ))
 			misc = TSI108_TX_INT;
 
 		data->txskbs[tx] = skb;
@@ -787,9 +784,9 @@ static int tsi108_send_packet(sk_buff * 
 	return 0;
 }
 
-static int tsi108_check_for_completed_rx(net_device * dev, int budget)
+static int tsi108_check_for_completed_rx(net_device *dev, int budget)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	int done = 0;
 
 	while (data->rxfree && done != budget) {
@@ -838,9 +835,9 @@ static int tsi108_check_for_completed_rx
 	return done;
 }
 
-static int tsi108_refill_rx(net_device * dev, int budget)
+static int tsi108_refill_rx(net_device *dev, int budget)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	int done = 0;
 
 	while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
@@ -851,7 +848,7 @@ static int tsi108_refill_rx(net_device *
 		if (!skb)
 			break;
 
-		skb_reserve(skb, 2);	/* Align the data on a 4-byte boundary. */
+		skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
 
 		data->rxring[rx].buf0 = virt_to_phys(skb->data);
 
@@ -878,9 +875,9 @@ static int tsi108_refill_rx(net_device *
 	return done;
 }
 
-static int tsi108_poll(net_device * dev, int *budget)
+static int tsi108_poll(net_device *dev, int *budget)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
 	u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
 	int total_budget = min(*budget, dev->quota);
@@ -959,9 +956,9 @@ static int tsi108_poll(net_device * dev,
 	return 1;
 }
 
-static void tsi108_rx_int(net_device * dev)
+static void tsi108_rx_int(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	/* A race could cause dev to already be scheduled, so it's not an
 	 * error if that happens (and interrupts shouldn't be re-masked,
@@ -1025,9 +1022,9 @@ static void tsi108_rx_int(net_device * d
  * This is called once per second (by default) from the thread.
  */
 
-static void tsi108_check_rxring(net_device * dev)
+static void tsi108_check_rxring(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	/* A poll is scheduled, as opposed to caling tsi108_refill_rx
 	 * directly, so as to keep the receive path single-threaded
@@ -1038,9 +1035,9 @@ static void tsi108_check_rxring(net_devi
 		tsi108_rx_int(dev);
 }
 
-static void tsi108_tx_int(net_device * dev)
+static void tsi108_tx_int(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
 
 	mb();
@@ -1064,9 +1061,9 @@ static void tsi108_tx_int(net_device * d
 	}
 }
 
-static irqreturn_t tsi108_irq_one(net_device * dev)
+static irqreturn_t tsi108_irq_one(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
 
 	if (!(stat & TSI108_INT_ANY))
@@ -1101,12 +1098,12 @@ static irqreturn_t tsi108_irq(int irq, v
 	if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
 		return IRQ_NONE;	/* Not our interrupt */
 
-	return tsi108_irq_one((struct net_device *)dev_id);
+	return tsi108_irq_one(dev_id);
 }
 
-static void tsi108_stop_ethernet(net_device * dev)
+static void tsi108_stop_ethernet(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	/* Disable all TX and RX queues ... */
 	TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
@@ -1120,7 +1117,7 @@ static void tsi108_stop_ethernet(net_dev
 	       TSI108_EC_RXSTAT_ACTIVE) ;
 }
 
-static void tsi108_reset_ether(tsi108_prv_data * data)
+static void tsi108_reset_ether(struct tsi108_prv_data * data)
 {
 	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
 	udelay(100);
@@ -1158,10 +1155,9 @@ static void tsi108_reset_ether(tsi108_pr
 			     TSI108_MAC_MII_MGMT_CLK);
 }
 
-static int tsi108_get_mac(net_device * dev)
+static int tsi108_get_mac(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
-
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
 	u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
 
@@ -1203,9 +1199,9 @@ static int tsi108_get_mac(net_device * d
 	return 0;
 }
 
-static int tsi108_set_mac(net_device * dev, void *addr)
+static int tsi108_set_mac(net_device *dev, void *addr)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 word1, word2;
 	int i;
 
@@ -1235,9 +1231,9 @@ static int tsi108_set_mac(net_device * d
 }
 
 /* Protected by dev->xmit_lock. */
-static void tsi108_set_rx_mode(net_device * dev)
+static void tsi108_set_rx_mode(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
 
 	if (dev->flags & IFF_PROMISC) {
@@ -1294,9 +1290,9 @@ static void tsi108_set_rx_mode(net_devic
 	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
 }
 
-static void tsi108_init_phy(net_device * dev)
+static void tsi108_init_phy(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	u32 i = 0;
 	u16 phyVal = 0;
 
@@ -1304,7 +1300,8 @@ static void tsi108_init_phy(net_device *
 
 	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
 	mb();
-	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
+	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET)
+		;
 
 #if (TSI108_PHY_TYPE == PHY_BCM54XX)	/* Broadcom BCM54xx PHY */
 	tsi108_write_mii(data, 0x09, 0x0300);
@@ -1317,7 +1314,8 @@ static void tsi108_init_phy(net_device *
 			 PHY_CTRL,
 			 PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
 	mb();
-	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_AUTONEG_START) ;
+	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_AUTONEG_START)
+		;
 
 	/* Set G/MII mode and receive clock select in TBI control #2.  The
 	 * second port won't work if this isn't done, even though we don't
@@ -1338,7 +1336,9 @@ static void tsi108_init_phy(net_device *
 			data->link_up = 0;
 			break;
 		}
-		mdelay(10);
+		spin_unlock_irq(&phy_lock);
+		msleep(10);
+		spin_lock_irq(&phy_lock);
 	}
 
 	printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
@@ -1348,7 +1348,7 @@ static void tsi108_init_phy(net_device *
 
 static void tsi108_kill_phy(struct net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	spin_lock_irq(&phy_lock);
 	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
@@ -1359,7 +1359,7 @@ static void tsi108_kill_phy(struct net_d
 static int tsi108_open(struct net_device *dev)
 {
 	int i;
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
 	unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
 
@@ -1447,11 +1447,8 @@ static int tsi108_open(struct net_device
 	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
 	tsi108_init_phy(dev);
 
-	init_timer(&data->timer);
-	data->timer.expires = jiffies + 1;
-	data->timer.data = (unsigned long)dev;
-	data->timer.function = &tsi108_timed_checker;	/* timer handler */
-	add_timer(&data->timer);
+	setup_timer(&data->timer, tsi108_timed_checker, (unsigned long)dev);
+	mod_timer(&data->timer, jiffies + 1);
 
 	tsi108_restart_rx(data, dev);
 
@@ -1470,9 +1467,9 @@ static int tsi108_open(struct net_device
 	return 0;
 }
 
-static int tsi108_close(net_device * dev)
+static int tsi108_close(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	netif_stop_queue(dev);
 
@@ -1521,9 +1518,9 @@ static int tsi108_close(net_device * dev
 	return 0;
 }
 
-static void tsi108_init_mac(net_device * dev)
+static void tsi108_init_mac(net_device *dev)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE |
 			     TSI108_MAC_CFG2_PADCRC);
@@ -1562,31 +1559,31 @@ static void tsi108_init_mac(net_device *
 	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
 			     TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
 			     TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
-						       TSI108_EC_TXQ_CFG_SFNPORT));
+						TSI108_EC_TXQ_CFG_SFNPORT));
 
 	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
 			     TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
 			     TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
-						       TSI108_EC_RXQ_CFG_SFNPORT));
+						TSI108_EC_RXQ_CFG_SFNPORT));
 
 	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
 			     TSI108_EC_TXQ_BUFCFG_BURST256 |
 			     TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
-							  TSI108_EC_TXQ_BUFCFG_SFNPORT));
+						TSI108_EC_TXQ_BUFCFG_SFNPORT));
 
 	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
 			     TSI108_EC_RXQ_BUFCFG_BURST256 |
 			     TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
-							  TSI108_EC_RXQ_BUFCFG_SFNPORT));
+						TSI108_EC_RXQ_BUFCFG_SFNPORT));
 
 	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
 }
 
-static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
+static int tsi108_ioctl(net_device *dev, struct ifreq *rq, int cmd)
 {
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 	struct mii_ioctl_data *mii_data =
-	    (struct mii_ioctl_data *)&rq->ifr_data;
+			(struct mii_ioctl_data *)&rq->ifr_data;
 	int ret;
 
 	switch (cmd) {
@@ -1613,11 +1610,11 @@ static int
 tsi108_init_one(struct platform_device *pdev)
 {
 	struct net_device *dev = NULL;
-	tsi108_prv_data *data = NULL;
+	struct tsi108_prv_data *data = NULL;
 	hw_info *einfo;
 	int ret;
 
-	einfo = ( hw_info *) pdev->dev.platform_data;
+	einfo = pdev->dev.platform_data;
 
 	if (NULL == einfo) {
 		printk(KERN_ERR "tsi-eth %d: Missing additional data!\n",
@@ -1627,7 +1624,7 @@ tsi108_init_one(struct platform_device *
 
 	/* Create an ethernet device instance */
 
-	dev = alloc_etherdev(sizeof(tsi108_prv_data));
+	dev = alloc_etherdev(sizeof(struct tsi108_prv_data));
 	if (!dev) {
 		printk("tsi108_eth: Could not allocate a device structure\n");
 		return -ENOMEM;
@@ -1635,7 +1632,6 @@ tsi108_init_one(struct platform_device *
 
 	printk("tsi108_eth%d: probe...\n", pdev->id);
 	data = netdev_priv(dev);
-	memset(data, 0, sizeof(tsi108_prv_data));
 
 	DBG("tsi108_eth%d: regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n",
 			pdev->id, einfo->regs, einfo->phyregs,
@@ -1653,7 +1649,7 @@ tsi108_init_one(struct platform_device *
 	dev->get_stats = tsi108_get_stats;
 	dev->poll = tsi108_poll;
 	dev->do_ioctl = tsi108_ioctl;
-	dev->weight = 64;	/* 64 is more suitable for GigE interface - klai */
+	dev->weight = 64;  /* 64 is more suitable for GigE interface - klai */
 
 	/* Apparently, the Linux networking code won't use scatter-gather
 	 * if the hardware doesn't do checksums.  However, it's faster
@@ -1683,7 +1679,7 @@ tsi108_init_one(struct platform_device *
 	tsi108_init_mac(dev);
 	ret = register_netdev(dev);
 	if (ret < 0) {
-		kfree(dev);
+		free_netdev(dev);
 		return ret;
 	}
 
@@ -1708,12 +1704,11 @@ tsi108_init_one(struct platform_device *
 static void tsi108_timed_checker(unsigned long dev_ptr)
 {
 	struct net_device *dev = (struct net_device *)dev_ptr;
-	tsi108_prv_data *data = netdev_priv(dev);
+	struct tsi108_prv_data *data = netdev_priv(dev);
 
 	tsi108_check_phy(dev);
 	tsi108_check_rxring(dev);
-	data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
-	add_timer(&data->timer);
+	mod_timer(&data->timer, jiffies + CHECK_PHY_INTERVAL);
 }
 
 static int tsi108_ether_init(void)
@@ -1731,7 +1726,7 @@ static int tsi108_ether_init(void)
 static int tsi108_ether_remove(struct platform_device *pdev)
 {
 	struct net_device *dev = platform_get_drvdata(pdev);
-	tsi108_prv_data *priv = netdev_priv(dev);
+	struct tsi108_prv_data *priv = netdev_priv(dev);
 
 	unregister_netdev(dev);
 	tsi108_stop_ethernet(dev);
_


^ permalink raw reply

* RE:  [PATCH] [2.6.17]   Add tsi108 Ethernet device driver support
From: Zang Roy-r61911 @ 2006-07-01  8:32 UTC (permalink / raw)
  To: 'jgarzik@pobox.com'
  Cc: 'netdev@vger.kernel.org', 'linux-kernel',
	'Alexandre.Bounine@tundra.com', Yang Xin-Xin-r48390,
	Zang Roy-r61911

Hi, Jeff
	How about the status of the tsi108 Ethernet device driver ?
Roy 

> 
> This patch adds a net device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit 
> Ethernet controller
>   
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang	<tie-fei.zang@freescale.com>
>   
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index 0c6b45a..360b85f 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2196,6 +2196,14 @@ config SPIDER_NET
>  	  This driver supports the Gigabit Ethernet chips present on the
>  	  Cell Processor-Based Blades from IBM.
>  
> +config TSI108_ETH
> +	   tristate "Tundra TSI108 gigabit Ethernet support"
> +	   depends on TSI108_BRIDGE
> +	   help
> +	     This driver supports Tundra TSI108 gigabit Ethernet ports.
> +	     To compile this driver as a module, choose M here: 
> the module
> +	     will be called tsi108_eth.
> +
>  config GIANFAR
>  	tristate "Gianfar Ethernet"
>  	depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 1eced32..6bf8186 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) += b44.o
>  obj-$(CONFIG_FORCEDETH) += forcedeth.o
>  obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
>  
> +obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
>  obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>  
>  obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..77d5a11
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1755 @@
> +/************************************************************
> *******************
> +  
> +  Copyright(c) 2005 Tundra Semiconductor Corporation.
> +  
> +  This program is free software; you can redistribute it 
> and/or modify it 
> +  under the terms of the GNU General Public License as 
> published by the Free 
> +  Software Foundation; either version 2 of the License, or 
> (at your option) 
> +  any later version.
> +  
> +  This program is distributed in the hope that it will be 
> useful, but WITHOUT 
> +  ANY WARRANTY; without even the implied warranty of 
> MERCHANTABILITY or 
> +  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
> Public License for 
> +  more details.
> +  
> +  You should have received a copy of the GNU General Public 
> License along with
> +  this program; if not, write to the Free Software 
> Foundation, Inc., 59 
> +  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
> +
> +*************************************************************
> ******************/
> +
> +/* This driver is based on the driver code originally developed	
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com  * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6 
> (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + *   additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +#include <linux/platform_device.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +#undef DEBUG
> +#ifdef DEBUG
> +#define DBG(fmt...) do { printk(fmt); } while(0)
> +#else
> +#define DBG(fmt...) do { } while(0)
> +#endif
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000	/* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN     256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE     1536
> +
> +#define TSI108_TXRING_LEN     256
> +
> +#define TSI108_TX_INT_FREQ    64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +static int tsi108_init_one(struct platform_device *pdev);
> +static int tsi108_ether_remove(struct platform_device *pdev);
> +
> +typedef struct {
> +	volatile u32 regs;	/* Base of normal regs */
> +	volatile u32 phyregs;	/* Base of register bank used 
> for PHY access */
> +	int phy;		/* Index of PHY for this interface */
> +	int irq_num;
> +
> +	struct timer_list timer;/* Timer that triggers the 
> check phy function */
> +	int rxtail;		/* Next entry in rxring to read */
> +	int rxhead;		/* Next entry in rxring to give 
> a new buffer */
> +	int rxfree;		/* Number of free, allocated RX 
> buffers */
> +
> +	int rxpending;		/* Non-zero if there are still 
> descriptors
> +				 * to be processed from a 
> previous descriptor
> +				 * interrupt condition that has 
> been cleared */
> +
> +	int txtail;		/* Next TX descriptor to check 
> status on */
> +	int txhead;		/* Next TX descriptor to use */
> +
> +	/* Number of free TX descriptors.  This could be calculated from
> +	 * rxhead and rxtail if one descriptor were left unused 
> to disambiguate
> +	 * full and empty conditions, but it's simpler to just 
> keep track
> +	 * explicitly. */
> +
> +	int txfree;
> +
> +	int phy_ok;		/* The PHY is currently powered on. */
> +
> +	/* PHY status (duplex is 1 for half, 2 for full,
> +	 * so that the default 0 indicates that neither has
> +	 * yet been configured). */
> +
> +	int link_up;
> +	int speed;
> +	int duplex;
> +
> +	tx_desc *txring;
> +	rx_desc *rxring;
> +	sk_buff *txskbs[TSI108_TXRING_LEN];
> +	sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> +	dma_addr_t txdma, rxdma;
> +
> +	/* txlock nests in misclock and phy_lock */
> +
> +	spinlock_t txlock, misclock;
> +
> +	/* stats is used to hold the upper bits of each 
> hardware counter,
> +	 * and tmpstats is used to hold the full values for returning
> +	 * to the caller of get_stats().  They must be separate in case
> +	 * an overflow interrupt occurs before the stats are consumed.
> +	 */
> +
> +	struct net_device_stats stats;
> +	struct net_device_stats tmpstats;
> +
> +	/* These stats are kept separate in hardware, thus 
> require individual
> +	 * fields for handling carry.  They are combined in get_stats.
> +	 */
> +
> +	unsigned long rx_fcs;	/* Add to rx_frame_errors */
> +	unsigned long rx_short_fcs;	/* Add to rx_frame_errors */
> +	unsigned long rx_long_fcs;	/* Add to rx_frame_errors */
> +	unsigned long rx_underruns;	/* Add to rx_length_errors */
> +	unsigned long rx_overruns;	/* Add to rx_length_errors */
> +
> +	unsigned long tx_coll_abort;	/* Add to 
> tx_aborted_errors/collisions */
> +	unsigned long tx_pause_drop;	/* Add to tx_aborted_errors */
> +
> +	unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +
> +/* Structure for a device driver */
> +
> +static struct platform_driver tsi_eth_driver = {
> +	.probe = tsi108_init_one,
> +	.remove = tsi108_ether_remove,
> +	.driver	= {
> +		.name = "tsi-ethernet",
> +	},
> +};
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static void dump_eth_one(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	printk("Dumping %s...\n", dev->name);
> +	printk("intstat %x intmask %x phy_ok %d"
> +	       " link %d speed %d duplex %d\n",
> +	       TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> +	       TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> +	       data->link_up, data->speed, data->duplex);
> +
> +	printk("TX: head %d, tail %d, free %d, stat %x, estat 
> %x, err %x\n",
> +	       data->txhead, data->txtail, data->txfree,
> +	       TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> +	       TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> +	       TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> +	printk("RX: head %d, tail %d, free %d, stat %x,"
> +	       " estat %x, err %x, pending %d\n\n",
> +	       data->rxhead, data->rxtail, data->rxfree,
> +	       TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> +	       TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> +	       TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, 
> int reg, int *status)
> +{
> +	int i;
> +	u16 ret;
> +
> +	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> +				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
> +				(reg << TSI108_MAC_MII_ADDR_REG));
> +	mb();
> +	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> +	mb();
> +	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 
> TSI108_MAC_MII_CMD_READ);
> +	mb();
> +	for (i = 0; i < 100; i++) {
> +		if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> +		      (TSI108_MAC_MII_IND_NOTVALID | 
> TSI108_MAC_MII_IND_BUSY)))
> +			break;
> +		udelay(10);
> +	}
> +
> +	if (i == 100) {
> +		if (status)
> +			*status = -EBUSY;
> +
> +		ret = 0xffff;
> +	} else {
> +		if (status)
> +			*status = 0;
> +
> +		ret = TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> +	}
> +
> +	return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, 
> int reg, u16 val)
> +{
> +	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> +				(data->phy << TSI108_MAC_MII_ADDR_PHY) |
> +				(reg << TSI108_MAC_MII_ADDR_REG));
> +	mb();
> +	TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> +	mb();
> +	while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> +	       TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, 
> int reg, u16 val)
> +{
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> +			     (0x1e << TSI108_MAC_MII_ADDR_PHY)
> +			     | (reg << TSI108_MAC_MII_ADDR_REG));
> +	mb();
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> +	mb();
> +	while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> +	       TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u16 sumstat;
> +	u32 mac_cfg2_reg, portctrl_reg;
> +	u32 fdx_flag = 0, reg_update = 0;
> +
> +	/* Do a dummy read, as for some reason the first read
> +	 * after a link becomes up returns link down, even if
> +	 * it's been a while since the link came up.
> +	 */
> +
> +	spin_lock(&phy_lock);
> +
> +	if (!data->phy_ok)
> +		goto out;
> +
> +	tsi108_read_mii(data, PHY_STAT, NULL);
> +
> +	if (!(tsi108_read_mii(data, PHY_STAT, NULL) & 
> PHY_STAT_LINKUP)) {
> +		if (data->link_up == 1) {
> +			netif_stop_queue(dev);
> +			data->link_up = 0;
> +			printk(KERN_NOTICE "%s : link is 
> down\n", dev->name);
> +			netif_carrier_off(dev);
> +		}
> +
> +		goto out;
> +	}
> +
> +	{
> +		mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> +		portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> +		sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> +		switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> +		case PHY_SUM_STAT_1000T_FD:
> +			fdx_flag++;
> +		case PHY_SUM_STAT_1000T_HD:
> +			if (data->speed != 1000) {
> +				mac_cfg2_reg &= 
> ~TSI108_MAC_CFG2_IFACE_MASK;
> +				mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
> +				portctrl_reg &= 
> ~TSI108_EC_PORTCTRL_NOGIG;
> +				data->speed = 1000;
> +				reg_update++;
> +			}
> +			break;
> +		case PHY_SUM_STAT_100TX_FD:
> +			fdx_flag++;
> +		case PHY_SUM_STAT_100TX_HD:
> +			if (data->speed != 100) {
> +				mac_cfg2_reg &= 
> ~TSI108_MAC_CFG2_IFACE_MASK;
> +				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> +				portctrl_reg |= 
> TSI108_EC_PORTCTRL_NOGIG;
> +				data->speed = 100;
> +				reg_update++;
> +			}
> +			break;
> +
> +		case PHY_SUM_STAT_10T_FD:
> +			fdx_flag++;
> +		case PHY_SUM_STAT_10T_HD:
> +			if (data->speed != 10) {
> +				mac_cfg2_reg &= 
> ~TSI108_MAC_CFG2_IFACE_MASK;
> +				mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> +				portctrl_reg |= 
> TSI108_EC_PORTCTRL_NOGIG;
> +				data->speed = 10;
> +				reg_update++;
> +			}
> +			break;
> +
> +		default:
> +			if (net_ratelimit())
> +				printk(KERN_ERR "PHY reported 
> invalid speed,"
> +				       KERN_ERR " summary status %x\n",
> +				       sumstat);
> +			goto out;
> +		}
> +
> +		if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> +			if (data->duplex != 2) {
> +				mac_cfg2_reg |= 
> TSI108_MAC_CFG2_FULLDUPLEX;
> +				portctrl_reg &= 
> ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> +				data->duplex = 2;
> +				reg_update++;
> +			}
> +		} else {
> +			if (data->duplex != 1) {
> +				mac_cfg2_reg &= 
> ~TSI108_MAC_CFG2_FULLDUPLEX;
> +				portctrl_reg |= 
> TSI108_EC_PORTCTRL_HALFDUPLEX;
> +				data->duplex = 1;
> +				reg_update++;
> +			}
> +		}
> +
> +		if (reg_update) {
> +			TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, 
> mac_cfg2_reg);
> +			mb();
> +			
> TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> +			mb();
> +		}
> +
> +	}
> +
> +	if (data->link_up == 0) {
> +		/* The manual says it can take 3-4 usecs for 
> the speed change
> +		 * to take effect.
> +		 */
> +		udelay(5);
> +
> +		spin_lock(&data->txlock);
> +		if (netif_queue_stopped(dev)
> +		    && is_valid_ether_addr(dev->dev_addr) && 
> data->txfree)
> +			netif_wake_queue(dev);
> +
> +		data->link_up = 1;
> +		spin_unlock(&data->txlock);
> +		printk("%s : link is up: %dMb %s-duplex\n",
> +		       dev->name, data->speed,
> +		       (data->duplex == 2) ? "full" : "half");
> +		netif_carrier_on(dev);
> +	}
> +
> +      out:
> +	spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> +		      unsigned long *upper)
> +{
> +	if (carry & carry_bit)
> +		*upper += carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 carry1, carry2;
> +
> +	spin_lock_irq(&data->misclock);
> +
> +	carry1 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> +	carry2 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> +	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> +			      TSI108_STAT_RXBYTES_CARRY, 
> &data->stats.rx_bytes);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> +			      TSI108_STAT_RXPKTS_CARRY,
> +			      &data->stats.rx_packets);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> +			      TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> +			      TSI108_STAT_RXMCAST_CARRY,
> +			      &data->stats.multicast);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> +			      TSI108_STAT_RXALIGN_CARRY,
> +			      &data->stats.rx_frame_errors);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> +			      TSI108_STAT_RXLENGTH_CARRY,
> +			      &data->stats.rx_length_errors);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> +			      TSI108_STAT_RXRUNT_CARRY, 
> &data->rx_underruns);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> +			      TSI108_STAT_RXJUMBO_CARRY, 
> &data->rx_overruns);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> +			      TSI108_STAT_RXFRAG_CARRY, 
> &data->rx_short_fcs);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> +			      TSI108_STAT_RXJABBER_CARRY, 
> &data->rx_long_fcs);
> +
> +	tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> +			      TSI108_STAT_RXDROP_CARRY,
> +			      &data->stats.rx_missed_errors);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> +			      TSI108_STAT_TXBYTES_CARRY, 
> &data->stats.tx_bytes);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> +			      TSI108_STAT_TXPKTS_CARRY,
> +			      &data->stats.tx_packets);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> +			      TSI108_STAT_TXEXDEF_CARRY,
> +			      &data->stats.tx_aborted_errors);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> +			      TSI108_STAT_TXEXCOL_CARRY, 
> &data->tx_coll_abort);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> +			      TSI108_STAT_TXTCOL_CARRY,
> +			      &data->stats.collisions);
> +
> +	tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> +			      TSI108_STAT_TXPAUSEDROP_CARRY,
> +			      &data->tx_pause_drop);
> +
> +	spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> +		 int carry_shift, unsigned long *upper)
> +{
> +	int carryreg;
> +	unsigned long val;
> +
> +	if (reg < 0xb0)
> +		carryreg = TSI108_STAT_CARRY1;
> +	else
> +		carryreg = TSI108_STAT_CARRY2;
> +
> +      again:
> +	val = TSI108_ETH_READ_REG(reg) | *upper;
> +
> +	rmb();
> +
> +	/* Check to see if it overflowed, but the interrupt hasn't
> +	 * been serviced yet.  If so, handle the carry here, and
> +	 * try again.
> +	 */
> +
> +	if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> +		*upper += carry_shift;
> +		TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> +		mb();
> +
> +		goto again;
> +	}
> +
> +	return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> +	unsigned long excol;
> +
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	spin_lock_irq(&data->misclock);
> +
> +	data->tmpstats.rx_packets =
> +	    tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> +			     TSI108_STAT_CARRY1_RXPKTS,
> +			     TSI108_STAT_RXPKTS_CARRY, 
> &data->stats.rx_packets);
> +
> +	data->tmpstats.tx_packets =
> +	    tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> +			     TSI108_STAT_CARRY2_TXPKTS,
> +			     TSI108_STAT_TXPKTS_CARRY, 
> &data->stats.tx_packets);
> +
> +	data->tmpstats.rx_bytes =
> +	    tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> +			     TSI108_STAT_CARRY1_RXBYTES,
> +			     TSI108_STAT_RXBYTES_CARRY, 
> &data->stats.rx_bytes);
> +
> +	data->tmpstats.tx_bytes =
> +	    tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> +			     TSI108_STAT_CARRY2_TXBYTES,
> +			     TSI108_STAT_TXBYTES_CARRY, 
> &data->stats.tx_bytes);
> +
> +	data->tmpstats.multicast =
> +	    tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> +			     TSI108_STAT_CARRY1_RXMCAST,
> +			     TSI108_STAT_RXMCAST_CARRY, 
> &data->stats.multicast);
> +
> +	excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> +				 TSI108_STAT_CARRY2_TXEXCOL,
> +				 TSI108_STAT_TXEXCOL_CARRY,
> +				 &data->tx_coll_abort);
> +
> +	data->tmpstats.collisions =
> +	    tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> +			     TSI108_STAT_CARRY2_TXTCOL,
> +			     TSI108_STAT_TXTCOL_CARRY, 
> &data->stats.collisions);
> +
> +	data->tmpstats.collisions += excol;
> +
> +	data->tmpstats.rx_length_errors =
> +	    tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> +			     TSI108_STAT_CARRY1_RXLENGTH,
> +			     TSI108_STAT_RXLENGTH_CARRY,
> +			     &data->stats.rx_length_errors);
> +
> +	data->tmpstats.rx_length_errors +=
> +	    tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> +			     TSI108_STAT_CARRY1_RXRUNT,
> +			     TSI108_STAT_RXRUNT_CARRY, 
> &data->rx_underruns);
> +
> +	data->tmpstats.rx_length_errors +=
> +	    tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> +			     TSI108_STAT_CARRY1_RXJUMBO,
> +			     TSI108_STAT_RXJUMBO_CARRY, 
> &data->rx_overruns);
> +
> +	data->tmpstats.rx_frame_errors =
> +	    tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> +			     TSI108_STAT_CARRY1_RXALIGN,
> +			     TSI108_STAT_RXALIGN_CARRY,
> +			     &data->stats.rx_frame_errors);
> +
> +	data->tmpstats.rx_frame_errors +=
> +	    tsi108_read_stat(data, TSI108_STAT_RXFCS,
> +			     TSI108_STAT_CARRY1_RXFCS, 
> TSI108_STAT_RXFCS_CARRY,
> +			     &data->rx_fcs);
> +
> +	data->tmpstats.rx_frame_errors +=
> +	    tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> +			     TSI108_STAT_CARRY1_RXFRAG,
> +			     TSI108_STAT_RXFRAG_CARRY, 
> &data->rx_short_fcs);
> +
> +	data->tmpstats.rx_missed_errors =
> +	    tsi108_read_stat(data, TSI108_STAT_RXDROP,
> +			     TSI108_STAT_CARRY1_RXDROP,
> +			     TSI108_STAT_RXDROP_CARRY,
> +			     &data->stats.rx_missed_errors);
> +
> +	/* These three are maintained by software. */
> +	data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
> +	data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
> +
> +	data->tmpstats.tx_aborted_errors =
> +	    tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> +			     TSI108_STAT_CARRY2_TXEXDEF,
> +			     TSI108_STAT_TXEXDEF_CARRY,
> +			     &data->stats.tx_aborted_errors);
> +
> +	data->tmpstats.tx_aborted_errors +=
> +	    tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> +			     TSI108_STAT_CARRY2_TXPAUSE,
> +			     TSI108_STAT_TXPAUSEDROP_CARRY,
> +			     &data->tx_pause_drop);
> +
> +	data->tmpstats.tx_aborted_errors += excol;
> +
> +	data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
> +	data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
> +	    data->tmpstats.rx_crc_errors +
> +	    data->tmpstats.rx_frame_errors +
> +	    data->tmpstats.rx_fifo_errors + 
> data->tmpstats.rx_missed_errors;
> +
> +	spin_unlock_irq(&data->misclock);
> +	return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, 
> net_device * dev)
> +{
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> +			     TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> +	wmb();
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> +			     | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> +			     TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> +	wmb();
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 
> TSI108_EC_TXCTRL_IDLEINT |
> +			     TSI108_EC_TXCTRL_GO | 
> TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	int tx;
> +	struct sk_buff *skb;
> +	int release = 0;
> +
> +	while (!data->txfree || data->txhead != data->txtail) {
> +		tx = data->txtail;
> +
> +		if (data->txring[tx].misc & TSI108_TX_OWN)
> +			break;
> +
> +		skb = data->txskbs[tx];
> +
> +		if (!(data->txring[tx].misc & TSI108_TX_OK))
> +			printk("%s: bad tx packet, misc %x\n",
> +			       dev->name, data->txring[tx].misc);
> +
> +		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> +		data->txfree++;
> +
> +		if (data->txring[tx].misc & TSI108_TX_EOF) {
> +			dev_kfree_skb_any(skb);
> +			release++;
> +		}
> +	}
> +
> +	if (release) {
> +
> +		if (netif_queue_stopped(dev)
> +		    && is_valid_ether_addr(dev->dev_addr) && 
> data->link_up)
> +			netif_wake_queue(dev);
> +	}
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	int frags = skb_shinfo(skb)->nr_frags + 1;
> +	int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED	/* FIXME: Not supported now. */
> +	long csstart;
> +	long csum;
> +
> +	csstart = skb->len - skb->data_len;
> +	if (csstart > skb->len - skb->data_len)
> +		BUG();
> +	csum = 0;
> +	if (csstart != skb->len)
> +		csum = skb_checksum(skb, csstart, skb->len - 
> csstart, 0);
> +#endif
> +
> +	if (!data->phy_ok && net_ratelimit())
> +		printk(KERN_ERR "%s: Transmit while PHY is 
> down!\n", dev->name);
> +
> +	if (!data->link_up) {
> +		printk(KERN_ERR "%s: Transmit while link is down!\n",
> +		       dev->name);
> +		netif_stop_queue(dev);
> +		return 1;
> +	}
> +
> +	if (data->txfree < MAX_SKB_FRAGS + 1) {
> +		netif_stop_queue(dev);
> +
> +		if (net_ratelimit())
> +			printk(KERN_ERR "%s: Transmit with full 
> tx ring!\n",
> +			       dev->name);
> +		return 1;
> +	}
> +
> +	if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> +		netif_stop_queue(dev);
> +	}
> +
> +	spin_lock_irq(&data->txlock);
> +
> +	for (i = 0; i < frags; i++) {
> +		int misc = 0;
> +		int tx = data->txhead;
> +
> +		/* This is done to mark every 
> TSI108_TX_INT_FREQ tx buffers with
> +		 * the interrupt bit.  TX descriptor-complete 
> interrupts are
> +		 * enabled when the queue fills up, and masked 
> when there is
> +		 * still free space.  This way, when saturating 
> the outbound
> +		 * link, the tx interrupts are kept to a 
> reasonable level. 
> +		 * When the queue is not full, reclamation of 
> skbs still occurs
> +		 * as new packets are transmitted, or on a queue-empty
> +		 * interrupt.
> +		 */
> +
> +		if ((tx % TSI108_TX_INT_FREQ == 0) &&
> +		    ((TSI108_TXRING_LEN - data->txfree) >= 
> TSI108_TX_INT_FREQ)
> +		    )
> +			misc = TSI108_TX_INT;
> +
> +		data->txskbs[tx] = skb;
> +
> +		if (i == 0) {
> +			data->txring[tx].buf0 = virt_to_phys(skb->data);
> +			data->txring[tx].len = skb->len - skb->data_len;
> +			misc |= TSI108_TX_SOF;
> +		} else {
> +			skb_frag_t *frag = 
> &skb_shinfo(skb)->frags[i - 1];
> +
> +			data->txring[tx].buf0 =
> +			    page_to_phys(frag->page) + 
> frag->page_offset;
> +			data->txring[tx].len = frag->size;
> +		}
> +
> +		if (i == frags - 1)
> +			misc |= TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> +		{
> +			int i;
> +			printk("%s: Tx Frame contents (%d)\n", 
> dev->name,
> +			       skb->len);
> +			for (i = 0; i < skb->len; i++)
> +				printk(" %2.2x", skb->data[i]);
> +			printk(".\n");
> +		}
> +#endif				/* TSI108_PRINT_TX_FRAME */
> +
> +		mb();
> +		data->txring[tx].misc = misc | TSI108_TX_OWN;
> +
> +		data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
> +		data->txfree--;
> +	}
> +
> +	tsi108_check_for_completed_tx(dev);
> +
> +	/* This must be done after the check for completed tx 
> descriptors,
> +	 * so that the tail pointer is correct.
> +	 */
> +
> +	if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) & 
> TSI108_EC_TXSTAT_QUEUE0))
> +		tsi108_restart_tx(data);
> +
> +	spin_unlock_irq(&data->txlock);
> +	return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, 
> int budget)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	int done = 0;
> +
> +	while (data->rxfree && done != budget) {
> +		int rx = data->rxtail;
> +		struct sk_buff *skb;
> +
> +		if (data->rxring[rx].misc & TSI108_RX_OWN)
> +			break;
> +
> +		skb = data->rxskbs[rx];
> +		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> +		data->rxfree--;
> +		done++;
> +
> +		if (data->rxring[rx].misc & TSI108_RX_BAD) {
> +			spin_lock_irq(&data->misclock);
> +
> +			if (data->rxring[rx].misc & TSI108_RX_CRC)
> +				data->stats.rx_crc_errors++;
> +			if (data->rxring[rx].misc & TSI108_RX_OVER)
> +				data->stats.rx_fifo_errors++;
> +
> +			spin_unlock_irq(&data->misclock);
> +
> +			dev_kfree_skb_any(skb);
> +			continue;
> +		}
> +#ifdef TSI108_PRINT_RX_FRAME
> +		{
> +			int i;
> +			printk("%s: Rx Frame contents (%d)\n",
> +			       dev->name, data->rxring[rx].len);
> +			for (i = 0; i < data->rxring[rx].len; i++)
> +				printk(" %2.2x", skb->data[i]);
> +			printk(".\n");
> +		}
> +#endif				/* TSI108_PRINT_RX_FRAME */
> +
> +		skb->dev = dev;
> +		skb_put(skb, data->rxring[rx].len);
> +		skb->protocol = eth_type_trans(skb, dev);
> +		netif_receive_skb(skb);
> +		dev->last_rx = jiffies;
> +	}
> +
> +	return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	int done = 0;
> +
> +	while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
> +		int rx = data->rxhead;
> +		sk_buff *skb;
> +
> +		data->rxskbs[rx] = skb = 
> dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> +		if (!skb)
> +			break;
> +
> +		skb_reserve(skb, 2);	/* Align the data on a 
> 4-byte boundary. */
> +
> +		data->rxring[rx].buf0 = virt_to_phys(skb->data);
> +
> +		/* Sometimes the hardware sets blen to zero after packet
> +		 * reception, even though the manual says that 
> it's only ever
> +		 * modified by the driver.
> +		 */
> +
> +		data->rxring[rx].blen = 1536;
> +		mb();
> +		data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
> +
> +		data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
> +		data->rxfree++;
> +		done++;
> +	}
> +
> +	mb();
> +
> +	if (done != 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> +			   TSI108_EC_RXSTAT_QUEUE0))
> +		tsi108_restart_rx(data, dev);
> +
> +	return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> +	u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +	int total_budget = min(*budget, dev->quota);
> +	int num_received = 0, num_filled = 0, budget_used;
> +
> +	intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> +	    TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | 
> TSI108_INT_RXWAIT;
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> +	if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> +		num_received = 
> tsi108_check_for_completed_rx(dev, total_budget);
> +
> +	/* This should normally fill no more slots than the number of
> +	 * packets received in tsi108_check_for_completed_rx(). 
>  The exception
> +	 * is when we previously ran out of memory for RX SKBs.  In that
> +	 * case, it's helpful to obey the budget, not only so that the
> +	 * CPU isn't hogged, but so that memory (which may still be low)
> +	 * is not hogged by one device.
> +	 *
> +	 * A work unit is considered to be two SKBs to allow us to catch
> +	 * up when the ring has shrunk due to out-of-memory but we're
> +	 * still removing the full budget's worth of packets each time.
> +	 */
> +
> +	if (data->rxfree < TSI108_RXRING_LEN)
> +		num_filled = tsi108_refill_rx(dev, total_budget * 2);
> +
> +	if (intstat & TSI108_INT_RXERROR) {
> +		u32 err = TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> +		TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> +		if (err) {
> +			if (net_ratelimit())
> +				printk(KERN_DEBUG "%s: RX error %x\n",
> +				       dev->name, err);
> +
> +			if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> +			      TSI108_EC_RXSTAT_QUEUE0))
> +				tsi108_restart_rx(data, dev);
> +		}
> +	}
> +
> +	if (intstat & TSI108_INT_RXOVERRUN) {
> +		spin_lock_irq(&data->misclock);
> +		data->stats.rx_fifo_errors++;
> +		spin_unlock_irq(&data->misclock);
> +	}
> +
> +	budget_used = max(num_received, num_filled / 2);
> +
> +	*budget -= budget_used;
> +	dev->quota -= budget_used;
> +
> +	if (budget_used != total_budget) {
> +		data->rxpending = 0;
> +		netif_rx_complete(dev);
> +
> +		TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> +				     
> TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> +				     & ~(TSI108_INT_RXQUEUE0
> +					 | TSI108_INT_RXTHRESH |
> +					 TSI108_INT_RXOVERRUN |
> +					 TSI108_INT_RXERROR |
> +					 TSI108_INT_RXWAIT));
> +
> +		mb();
> +
> +		/* IRQs are level-triggered, so no need to re-check */
> +		return 0;
> +	} else {
> +		data->rxpending = 1;
> +	}
> +
> +	return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	/* A race could cause dev to already be scheduled, so 
> it's not an
> +	 * error if that happens (and interrupts shouldn't be re-masked,
> +	 * because that can cause harmful races, if poll has already
> +	 * unmasked them but not cleared LINK_STATE_SCHED).  
> +	 *
> +	 * This can happen if this code races with 
> tsi108_poll(), which masks
> +	 * the interrupts after tsi108_irq_one() read the mask, 
> but before
> +	 * netif_rx_schedule is called.  It could also happen 
> due to calls
> +	 * from tsi108_check_rxring().
> +	 */
> +
> +	if (netif_rx_schedule_prep(dev)) {
> +		/* Mask, rather than ack, the receive 
> interrupts.  The ack
> +		 * will happen in tsi108_poll().
> +		 */
> +
> +		TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> +				     
> TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> +				     TSI108_INT_RXQUEUE0
> +				     | TSI108_INT_RXTHRESH |
> +				     TSI108_INT_RXOVERRUN | 
> TSI108_INT_RXERROR |
> +				     TSI108_INT_RXWAIT);
> +		mb();
> +		__netif_rx_schedule(dev);
> +	} else {
> +		if (!netif_running(dev)) {
> +			/* This can happen if an interrupt 
> occurs while the
> +			 * interface is being brought down, as the START
> +			 * bit is cleared before the stop 
> function is called.
> +			 *
> +			 * In this case, the interrupts must be 
> masked, or
> +			 * they will continue indefinitely.
> +			 *
> +			 * There's a race here if the interface 
> is brought down
> +			 * and then up in rapid succession, as 
> the device could
> +			 * be made running after the above 
> check and before
> +			 * the masking below.  This will only 
> happen if the IRQ
> +			 * thread has a lower priority than the 
> task brining
> +			 * up the interface.  Fixing this race 
> would likely
> +			 * require changes in generic code.
> +			 */
> +
> +			TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> +					     TSI108_ETH_READ_REG
> +					     (TSI108_EC_INTMASK) |
> +					     TSI108_INT_RXQUEUE0 |
> +					     TSI108_INT_RXTHRESH |
> +					     TSI108_INT_RXOVERRUN |
> +					     TSI108_INT_RXERROR |
> +					     TSI108_INT_RXWAIT);
> +			mb();
> +		}
> +	}
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	/* A poll is scheduled, as opposed to caling tsi108_refill_rx 
> +	 * directly, so as to keep the receive path single-threaded
> +	 * (and thus not needing a lock).
> +	 */
> +
> +	if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> +		tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> +	mb();
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> +	mb();
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> +			     TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> +	mb();
> +	if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> +		u32 err = TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> +		TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> +		if (err && net_ratelimit())
> +			printk(KERN_ERR "%s: TX error %x\n", 
> dev->name, err);
> +	}
> +
> +	if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT | 
> TSI108_EC_TXESTAT_Q0_EOQ)) {
> +		spin_lock(&data->txlock);
> +		tsi108_check_for_completed_tx(dev);
> +		spin_unlock(&data->txlock);
> +	}
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> +	if (!(stat & TSI108_INT_ANY))
> +		return IRQ_NONE;	/* Not our interrupt */
> +
> +	stat &= ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> +	if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> +		    TSI108_INT_TXERROR))
> +		tsi108_tx_int(dev);
> +	if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> +		    TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> +		    TSI108_INT_RXERROR))
> +		tsi108_rx_int(dev);
> +
> +	if (stat & TSI108_INT_SFN) {
> +		if (net_ratelimit())
> +			printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> +		TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> +	}
> +
> +	if (stat & TSI108_INT_STATCARRY) {
> +		tsi108_stat_carry(dev);
> +		TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, 
> TSI108_INT_STATCARRY);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct 
> pt_regs *regs)
> +{
> +	if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
> +		return IRQ_NONE;	/* Not our interrupt */
> +
> +	return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	/* Disable all TX and RX queues ... */
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> +	/* ...and wait for them to become idle */
> +	mb();
> +	while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> +	       TSI108_EC_TXSTAT_ACTIVE) ;
> +	while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> +	       TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> +	udelay(100);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, 
> TSI108_EC_PORTCTRL_STATRST);
> +	udelay(100);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> +			     TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> +			     ~TSI108_EC_PORTCTRL_STATRST);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> +	udelay(100);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> +			     TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> +			     ~TSI108_EC_TXCFG_RST);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> +	udelay(100);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> +			     TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> +			     ~TSI108_EC_RXCFG_RST);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> +			     
> TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> +			     TSI108_MAC_MII_MGMT_RST);
> +	udelay(100);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> +			     
> TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> +			     ~(TSI108_MAC_MII_MGMT_RST |
> +			       TSI108_MAC_MII_MGMT_CLK));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> +			     
> TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> +			     TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> +	u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> +	/* Note that the octets are reversed from what the manual says,
> +	 * producing an even weirder ordering...
> +	 */
> +	if (word2 == 0 && word1 == 0) {
> +		dev->dev_addr[0] = 0x00;
> +		dev->dev_addr[1] = 0x06;
> +		dev->dev_addr[2] = 0xd2;
> +		dev->dev_addr[3] = 0x00;
> +		dev->dev_addr[4] = 0x00;
> +		if (0x8 == data->phy)
> +			dev->dev_addr[5] = 0x01;
> +		else
> +			dev->dev_addr[5] = 0x02;
> +
> +		word2 = (dev->dev_addr[0] << 16) | 
> (dev->dev_addr[1] << 24);
> +
> +		word1 = (dev->dev_addr[2] << 0) | 
> (dev->dev_addr[3] << 8) |
> +		    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> +		TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> +		TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> +	} else {
> +		dev->dev_addr[0] = (word2 >> 16) & 0xff;
> +		dev->dev_addr[1] = (word2 >> 24) & 0xff;
> +		dev->dev_addr[2] = (word1 >> 0) & 0xff;
> +		dev->dev_addr[3] = (word1 >> 8) & 0xff;
> +		dev->dev_addr[4] = (word1 >> 16) & 0xff;
> +		dev->dev_addr[5] = (word1 >> 24) & 0xff;
> +	}
> +
> +	if (!is_valid_ether_addr(dev->dev_addr)) {
> +		printk("KERN_ERR: word1: %08x, word2: %08x\n", 
> word1, word2);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 word1, word2;
> +	int i;
> +
> +	if (!is_valid_ether_addr(addr))
> +		return -EINVAL;
> +
> +	for (i = 0; i < 6; i++)
> +		/* +2 is for the offset of the HW addr type */
> +		dev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
> +
> +	word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> +	word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> +	    (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> +	spin_lock_irq(&data->misclock);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> +	spin_lock(&data->txlock);
> +
> +	if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> +		netif_wake_queue(dev);
> +
> +	spin_unlock(&data->txlock);
> +	spin_unlock_irq(&data->misclock);
> +	return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | 
> TSI108_EC_RXCFG_MC_HASH);
> +		rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> +		goto out;
> +	}
> +
> +	rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> +	if (dev->mc_count) {
> +		int i;
> +		struct dev_mc_list *mc = dev->mc_list;
> +		rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> +		memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> +		while (mc) {
> +			u32 hash, crc;
> +
> +			if (mc->dmi_addrlen == 6) {
> +				crc = ether_crc(6, mc->dmi_addr);
> +				hash = crc >> 23;
> +
> +				__set_bit(hash, &data->mc_hash[0]);
> +			} else {
> +				printk(KERN_ERR
> +				       "%s: got multicast 
> address of length %d "
> +				       "instead of 6.\n", dev->name,
> +				       mc->dmi_addrlen);
> +			}
> +
> +			mc = mc->next;
> +		}
> +
> +		TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> +				     TSI108_EC_HASHADDR_AUTOINC |
> +				     TSI108_EC_HASHADDR_MCAST);
> +
> +		for (i = 0; i < 16; i++) {
> +			/* The manual says that the hardware may drop
> +			 * back-to-back writes to the data register.
> +			 */
> +			udelay(1);
> +			mb();
> +			TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> +					     data->mc_hash[i]);
> +			mb();
> +		}
> +	}
> +
> +      out:
> +	mb();
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	u32 i = 0;
> +	u16 phyVal = 0;
> +
> +	spin_lock_irq(&phy_lock);
> +
> +	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> +	mb();
> +	while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE == PHY_BCM54XX)	/* Broadcom BCM54xx PHY */
> +	tsi108_write_mii(data, 0x09, 0x0300);
> +	tsi108_write_mii(data, 0x10, 0x1020);
> +	tsi108_write_mii(data, 0x1c, 0x8c00);
> +	mb();
> +#endif
> +
> +	tsi108_write_mii(data,
> +			 PHY_CTRL,
> +			 PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> +	mb();
> +	while (tsi108_read_mii(data, PHY_CTRL, NULL) & 
> PHY_CTRL_AUTONEG_START) ;
> +
> +	/* Set G/MII mode and receive clock select in TBI 
> control #2.  The
> +	 * second port won't work if this isn't done, even 
> though we don't
> +	 * use TBI mode.
> +	 */
> +
> +	tsi108_write_tbi(data, 0x11, 0x30);
> +
> +	/* FIXME: It seems to take more than 2 back-to-back reads to the
> +	 * PHY_STAT register before the link up status bit is set.
> +	 */
> +
> +	data->link_up = 1;
> +
> +	while (!((phyVal = tsi108_read_mii(data, PHY_STAT, NULL)) &
> +		 PHY_STAT_LINKUP)) {
> +		if (i++ > (MII_READ_DELAY / 10)) {
> +			data->link_up = 0;
> +			break;
> +		}
> +		mdelay(10);
> +	}
> +
> +	printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> +	data->phy_ok = 1;
> +	spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	spin_lock_irq(&phy_lock);
> +	tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> +	data->phy_ok = 0;
> +	spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> +	int i;
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
> +	unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> +	printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> +	i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> +	if (i != 0) {
> +		printk(KERN_ERR "tsi108_eth%d: Could not 
> allocate IRQ%d.\n",
> +		       data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> +		return i;
> +	} else {
> +		dev->irq = data->irq_num;
> +		printk(KERN_NOTICE
> +		       "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> +		       data->irq_num % IRQ_TSI108_GIGE0, 
> dev->irq, dev->name);
> +	}
> +
> +	data->rxring = pci_alloc_consistent(NULL, rxring_size, 
> &data->rxdma);
> +
> +	if (!data->rxring) {
> +		printk(KERN_DEBUG
> +		       "TSI108_ETH: failed to allocate memory 
> for rxring!\n");
> +		return -ENOMEM;
> +	} else {
> +		memset(data->rxring, 0, rxring_size);
> +	}
> +
> +	data->txring = pci_alloc_consistent(NULL, txring_size, 
> &data->txdma);
> +
> +	if (!data->txring) {
> +		printk(KERN_DEBUG
> +		       "TSI108_ETH: failed to allocate memory 
> for txring!\n");
> +		pci_free_consistent(0, rxring_size, 
> data->rxring, data->rxdma);
> +		return -ENOMEM;
> +	} else {
> +		memset(data->txring, 0, txring_size);
> +	}
> +
> +	for (i = 0; i < TSI108_RXRING_LEN; i++) {
> +		data->rxring[i].next0 = data->rxdma + (i + 1) * 
> sizeof(rx_desc);
> +		data->rxring[i].blen = TSI108_RXBUF_SIZE;
> +		data->rxring[i].vlan = 0;
> +	}
> +
> +	data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
> +
> +	data->rxtail = 0;
> +	data->rxhead = 0;
> +
> +	for (i = 0; i < TSI108_RXRING_LEN; i++) {
> +		sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE 
> + NET_IP_ALIGN);
> +
> +		if (!skb) {
> +			/* Bah.  No memory for now, but maybe we'll get
> +			 * some more later.
> +			 * For now, we'll live with the smaller ring.
> +			 */
> +			printk(KERN_WARNING
> +			       "%s: Could only allocate %d 
> receive skb(s).\n",
> +			       dev->name, i);
> +			data->rxhead = i;
> +			break;
> +		}
> +
> +		data->rxskbs[i] = skb;
> +		/* Align the payload on a 4-byte boundary */
> +		skb_reserve(skb, 2);
> +		data->rxskbs[i] = skb;
> +		data->rxring[i].buf0 = 
> virt_to_phys(data->rxskbs[i]->data);
> +		data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
> +	}
> +
> +	data->rxfree = i;
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> +	for (i = 0; i < TSI108_TXRING_LEN; i++) {
> +		data->txring[i].next0 = data->txdma + (i + 1) * 
> sizeof(tx_desc);
> +		data->txring[i].misc = 0;
> +	}
> +
> +	data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
> +	data->txtail = 0;
> +	data->txhead = 0;
> +	data->txfree = TSI108_TXRING_LEN;
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> +	tsi108_init_phy(dev);
> +
> +	init_timer(&data->timer);
> +	data->timer.expires = jiffies + 1;
> +	data->timer.data = (unsigned long)dev;
> +	data->timer.function = &tsi108_timed_checker;	/* 
> timer handler */
> +	add_timer(&data->timer);
> +
> +	tsi108_restart_rx(data, dev);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> +	mb();
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> +			     ~(TSI108_INT_TXQUEUE0 | 
> TSI108_INT_RXERROR |
> +			       TSI108_INT_RXTHRESH | 
> TSI108_INT_RXQUEUE0 |
> +			       TSI108_INT_RXOVERRUN | 
> TSI108_INT_RXWAIT |
> +			       TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> +			     TSI108_MAC_CFG1_RXEN | 
> TSI108_MAC_CFG1_TXEN);
> +	netif_start_queue(dev);
> +	return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	netif_stop_queue(dev);
> +
> +	del_timer_sync(&data->timer);
> +
> +	printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> +	tsi108_stop_ethernet(dev);
> +	tsi108_kill_phy(dev);
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> +	/* Check for any pending TX packets, and drop them. */
> +
> +	while (!data->txfree || data->txhead != data->txtail) {
> +		int tx = data->txtail;
> +		struct sk_buff *skb;
> +		skb = data->txskbs[tx];
> +		data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> +		data->txfree++;
> +		dev_kfree_skb(skb);
> +	}
> +
> +	synchronize_irq(data->irq_num);
> +	free_irq(data->irq_num, dev);
> +
> +	/* Discard the RX ring. */
> +
> +	while (data->rxfree) {
> +		int rx = data->rxtail;
> +		struct sk_buff *skb;
> +
> +		skb = data->rxskbs[rx];
> +		data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> +		data->rxfree--;
> +		dev_kfree_skb(skb);
> +	}
> +
> +	pci_free_consistent(0,
> +			    TSI108_RXRING_LEN * sizeof(rx_desc),
> +			    data->rxring, data->rxdma);
> +	pci_free_consistent(0,
> +			    TSI108_TXRING_LEN * sizeof(tx_desc),
> +			    data->txring, data->txdma);
> +
> +	return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, 
> TSI108_MAC_CFG2_DFLT_PREAMBLE |
> +			     TSI108_MAC_CFG2_PADCRC);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> +			     (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> +			     (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> +			     ~(TSI108_STAT_CARRY1_RXBYTES |
> +			       TSI108_STAT_CARRY1_RXPKTS |
> +			       TSI108_STAT_CARRY1_RXFCS |
> +			       TSI108_STAT_CARRY1_RXMCAST |
> +			       TSI108_STAT_CARRY1_RXALIGN |
> +			       TSI108_STAT_CARRY1_RXLENGTH |
> +			       TSI108_STAT_CARRY1_RXRUNT |
> +			       TSI108_STAT_CARRY1_RXJUMBO |
> +			       TSI108_STAT_CARRY1_RXFRAG |
> +			       TSI108_STAT_CARRY1_RXJABBER |
> +			       TSI108_STAT_CARRY1_RXDROP));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> +			     ~(TSI108_STAT_CARRY2_TXBYTES |
> +			       TSI108_STAT_CARRY2_TXPKTS |
> +			       TSI108_STAT_CARRY2_TXEXDEF |
> +			       TSI108_STAT_CARRY2_TXEXCOL |
> +			       TSI108_STAT_CARRY2_TXTCOL |
> +			       TSI108_STAT_CARRY2_TXPAUSE));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, 
> TSI108_EC_PORTCTRL_STATEN);
> +	TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> +			     TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, 
> TSI108_EC_TXQ_CFG_DESC_INT |
> +			     TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> +			     TSI108_EC_TXQ_CFG_WSWP | 
> (TSI108_PBM_PORT <<
> +						       
> TSI108_EC_TXQ_CFG_SFNPORT));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, 
> TSI108_EC_RXQ_CFG_DESC_INT |
> +			     TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> +			     TSI108_EC_RXQ_CFG_WSWP | 
> (TSI108_PBM_PORT <<
> +						       
> TSI108_EC_RXQ_CFG_SFNPORT));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> +			     TSI108_EC_TXQ_BUFCFG_BURST256 |
> +			     TSI108_EC_TXQ_BUFCFG_BSWP | 
> (TSI108_PBM_PORT <<
> +							  
> TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> +			     TSI108_EC_RXQ_BUFCFG_BURST256 |
> +			     TSI108_EC_RXQ_BUFCFG_BSWP | 
> (TSI108_PBM_PORT <<
> +							  
> TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> +	TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> +	tsi108_prv_data *data = netdev_priv(dev);
> +	struct mii_ioctl_data *mii_data =
> +	    (struct mii_ioctl_data *)&rq->ifr_data;
> +	int ret;
> +
> +	switch (cmd) {
> +	case SIOCGMIIPHY:
> +		mii_data->phy_id = data->phy;
> +		ret = 0;
> +		break;
> +
> +	case SIOCGMIIREG:
> +		spin_lock_irq(&phy_lock);
> +		mii_data->val_out =
> +		    tsi108_read_mii(data, mii_data->reg_num, &ret);
> +		spin_unlock_irq(&phy_lock);
> +		break;
> +
> +	default:
> +		ret = -EOPNOTSUPP;
> +	}
> +
> +	return ret;
> +}
> +
> +static int
> +tsi108_init_one(struct platform_device *pdev)
> +{
> +	struct net_device *dev = NULL;
> +	tsi108_prv_data *data = NULL;
> +	hw_info *einfo;
> +	int ret;
> +	
> +	einfo = ( hw_info *) pdev->dev.platform_data;
> +	
> +	if (NULL == einfo) {
> +		printk(KERN_ERR "tsi-eth %d: Missing additional 
> data!\n",
> +		       pdev->id);
> +		return -ENODEV;
> +	}
> +
> +	/* Create an ethernet device instance */
> +
> +	dev = alloc_etherdev(sizeof(tsi108_prv_data));
> +	if (!dev) {
> +		printk("tsi108_eth: Could not allocate a device 
> structure\n");
> +		return -ENOMEM;
> +	}
> +
> +	printk("tsi108_eth%d: probe...\n", pdev->id);
> +	data = netdev_priv(dev);
> +	memset(data, 0, sizeof(tsi108_prv_data));
> +
> +	DBG("tsi108_eth%d: 
> regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n",
> +			pdev->id, einfo->regs, einfo->phyregs,
> +			einfo->phy, einfo->irq_num);
> +
> +	data->regs = (u32)ioremap(einfo->regs, 0x400);	/*FIX ME */
> +	data->phyregs = (u32)ioremap(einfo->phyregs, 0x400); 	
> /*FIX ME */
> +	data->phy = einfo->phy;
> +	data->irq_num = einfo->irq_num;
> +	dev->open = tsi108_open;
> +	dev->stop = tsi108_close;
> +	dev->hard_start_xmit = tsi108_send_packet;
> +	dev->set_mac_address = tsi108_set_mac;
> +	dev->set_multicast_list = tsi108_set_rx_mode;
> +	dev->get_stats = tsi108_get_stats;
> +	dev->poll = tsi108_poll;
> +	dev->do_ioctl = tsi108_ioctl;
> +	dev->weight = 64;	/* 64 is more suitable for GigE 
> interface - klai */
> +
> +	/* Apparently, the Linux networking code won't use 
> scatter-gather
> +	 * if the hardware doesn't do checksums.  However, it's faster
> +	 * to checksum in place and use SG, as (among other reasons)
> +	 * the cache won't be dirtied (which then has to be flushed
> +	 * before DMA).  The checksumming is done by the driver (via
> +	 * a new function skb_csum_dev() in net/core/skbuff.c).
> +	 */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED	/* FIXME: Not supported now. */
> +	dev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> +	dev->features = NETIF_F_HIGHDMA;
> +#endif
> +	SET_MODULE_OWNER(dev);
> +
> +	spin_lock_init(&data->txlock);
> +	spin_lock_init(&data->misclock);
> +
> +	tsi108_reset_ether(data);
> +	tsi108_kill_phy(dev);
> +
> +	if (tsi108_get_mac(dev) != 0)
> +		printk(KERN_ERR "%s: Invalid MAC address.  
> Please correct.\n",
> +		       dev->name);
> +
> +	tsi108_init_mac(dev);
> +	ret = register_netdev(dev);
> +	if (ret < 0) {
> +		kfree(dev);
> +		return ret;
> +	}
> +
> +	printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> +	       "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> +	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> +	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +#ifdef DEBUG
> +	dump_eth_one(dev);
> +#endif
> +
> +	return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer. 
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> +	struct net_device *dev = (struct net_device *)dev_ptr;
> +	tsi108_prv_data *data = netdev_priv(dev);
> +
> +	tsi108_check_phy(dev);
> +	tsi108_check_rxring(dev);
> +	data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
> +	add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> +	int ret;
> +	ret = platform_driver_register (&tsi_eth_driver);
> +	if (ret < 0){
> +		printk("tsi108_ether_init: error initializing ethernet "
> +		       "device\n");
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +static int tsi108_ether_remove(struct platform_device *pdev)
> +{
> +	struct net_device *dev = platform_get_drvdata(pdev);
> +	tsi108_prv_data *priv = netdev_priv(dev);
> +	
> +	unregister_netdev(dev);
> +	tsi108_stop_ethernet(dev);
> +	platform_set_drvdata(pdev, NULL);
> +	iounmap((void __iomem *)priv->regs);
> +	iounmap((void __iomem *)priv->phyregs);
> +	free_netdev(dev);
> +	
> +	return 0;
> +}
> +static void tsi108_ether_exit(void)
> +{
> +	platform_driver_unregister(&tsi_eth_driver);
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..e491056
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,401 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> +	out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> +	in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> +	out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> +	in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E	1	/* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX	2	/* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE	PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM		2
> +#define TSI108_PBM_PORT			2
> +#define TSI108_SDRAM_PORT		4
> +
> +#define TSI108_MAC_CFG1			(0x000)
> +#define TSI108_MAC_CFG1_SOFTRST		(1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK	(1 << 8)
> +#define TSI108_MAC_CFG1_RXEN		(1 << 2)
> +#define TSI108_MAC_CFG1_TXEN		(1 << 0)
> +
> +#define TSI108_MAC_CFG2			(0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE	(7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK	(3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG		(1 << 8)
> +#define TSI108_MAC_CFG2_GIG		(2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC		(1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX	(1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG		(0x020)
> +#define TSI108_MAC_MII_MGMT_CLK		(7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST		(1 << 31)
> +
> +#define TSI108_MAC_MII_CMD		(0x024)
> +#define TSI108_MAC_MII_CMD_READ		(1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR		(0x028)
> +#define TSI108_MAC_MII_ADDR_REG		0
> +#define TSI108_MAC_MII_ADDR_PHY		8
> +
> +#define TSI108_MAC_MII_DATAOUT		(0x02c)
> +#define TSI108_MAC_MII_DATAIN		(0x030)
> +
> +#define TSI108_MAC_MII_IND		(0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID	(1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING	(1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY		(1 << 0)
> +
> +#define TSI108_MAC_IFCTRL		(0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE	(1 << 24)
> +
> +#define TSI108_MAC_ADDR1		(0x040)
> +#define TSI108_MAC_ADDR2		(0x044)
> +
> +#define TSI108_STAT_RXBYTES		(0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY	(1 << 24)
> +
> +#define TSI108_STAT_RXPKTS		(0x070)
> +#define TSI108_STAT_RXPKTS_CARRY	(1 << 18)
> +
> +#define TSI108_STAT_RXFCS		(0x074)
> +#define TSI108_STAT_RXFCS_CARRY		(1 << 12)
> +
> +#define TSI108_STAT_RXMCAST		(0x078)
> +#define TSI108_STAT_RXMCAST_CARRY	(1 << 18)
> +
> +#define TSI108_STAT_RXALIGN		(0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH		(0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXRUNT		(0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO		(0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXFRAG		(0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXJABBER		(0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_RXDROP		(0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_TXBYTES		(0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY	(1 << 24)
> +
> +#define TSI108_STAT_TXPKTS		(0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY	(1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF		(0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL		(0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_TXTCOL		(0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY	(1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP		(0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY	(1 << 12)
> +
> +#define TSI108_STAT_CARRY1		(0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES	(1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS	(1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS	(1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST	(1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN	(1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH	(1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT	(1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO	(1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG	(1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER	(1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP	(1 << 0)
> +
> +#define TSI108_STAT_CARRY2		(0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES	(1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS	(1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF	(1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL	(1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL	(1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE	(1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1		(0x108)
> +#define TSI108_STAT_CARRYMASK2		(0x10c)
> +
> +#define TSI108_EC_PORTCTRL		(0x200)
> +#define TSI108_EC_PORTCTRL_STATRST	(1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN	(1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG	(1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX	(1 << 16)
> +
> +#define TSI108_EC_INTSTAT		(0x204)
> +#define TSI108_EC_INTMASK		(0x208)
> +
> +#define TSI108_INT_ANY			(1 << 31)
> +#define TSI108_INT_SFN			(1 << 30)
> +#define TSI108_INT_RXIDLE		(1 << 29)
> +#define TSI108_INT_RXABORT		(1 << 28)
> +#define TSI108_INT_RXERROR		(1 << 27)
> +#define TSI108_INT_RXOVERRUN		(1 << 26)
> +#define TSI108_INT_RXTHRESH		(1 << 25)
> +#define TSI108_INT_RXWAIT		(1 << 24)
> +#define TSI108_INT_RXQUEUE0		(1 << 16)
> +#define TSI108_INT_STATCARRY		(1 << 15)
> +#define TSI108_INT_TXIDLE		(1 << 13)
> +#define TSI108_INT_TXABORT		(1 << 12)
> +#define TSI108_INT_TXERROR		(1 << 11)
> +#define TSI108_INT_TXUNDERRUN		(1 << 10)
> +#define TSI108_INT_TXTHRESH		(1 <<  9)
> +#define TSI108_INT_TXWAIT		(1 <<  8)
> +#define TSI108_INT_TXQUEUE0		(1 <<  0)
> +
> +#define TSI108_EC_TXCFG			(0x220)
> +#define TSI108_EC_TXCFG_RST		(1 << 31)
> +
> +#define TSI108_EC_TXCTRL		(0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT	(1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT		(1 << 30)
> +#define TSI108_EC_TXCTRL_GO		(1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0		(1 <<  0)
> +
> +#define TSI108_EC_TXSTAT		(0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE		(1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0		(1 << 0)
> +
> +#define TSI108_EC_TXESTAT		(0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR	(1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT	(1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF	(1 <<  8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ	(1 <<  0)
> +
> +#define TSI108_EC_TXERR			(0x278)
> +
> +#define TSI108_EC_TXQ_CFG		(0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT	(1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT	(1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP		(1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP		(1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT	0
> +
> +#define TSI108_EC_TXQ_BUFCFG		(0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8	(0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32	(1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128	(2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256	(3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP	(1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP	(1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT	0
> +
> +#define TSI108_EC_TXQ_PTRLOW		(0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH		(0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID	(1 << 31)
> +
> +#define TSI108_EC_TXTHRESH		(0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL	0
> +#define TSI108_EC_TXTHRESH_STOPFILL	16
> +
> +#define TSI108_EC_RXCFG			(0x320)
> +#define TSI108_EC_RXCFG_RST		(1 << 31)
> +
> +#define TSI108_EC_RXSTAT		(0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE		(1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0		(1 << 0)
> +
> +#define TSI108_EC_RXESTAT		(0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR	(1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT	(1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF	(1 <<  8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ	(1 <<  0)
> +
> +#define TSI108_EC_HASHADDR		(0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC	(1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD	(1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST	(0 <<  4)
> +#define TSI108_EC_HASHADDR_MCAST	(1 <<  4)
> +
> +#define TSI108_EC_HASHDATA		(0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW		(0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH		(0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID	(1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE		(1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE		(1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE		(1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE		(1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH		(1 <<  9)
> +#define TSI108_EC_RXCFG_MC_HASH		(1 <<  8)
> +
> +#define TSI108_EC_RXQ_CFG		(0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT	(1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT	(1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP		(1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP		(1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT	0
> +
> +#define TSI108_EC_RXQ_BUFCFG		(0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8	(0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32	(1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128	(2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256	(3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP	(1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP	(1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT	0
> +
> +#define TSI108_EC_RXCTRL		(0x324)
> +#define TSI108_EC_RXCTRL_ABORT		(1 << 30)
> +#define TSI108_EC_RXCTRL_GO		(1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0		(1 << 0)
> +
> +#define TSI108_EC_RXERR			(0x378)
> +
> +#define PHY_CTRL			0
> +#define PHY_CTRL_RESET			(1 << 15)
> +#define PHY_CTRL_AUTONEG_EN		(1 << 12)
> +#define PHY_CTRL_POWERDOWN		(1 << 11)
> +#define PHY_CTRL_AUTONEG_START		(1 << 9)
> +
> +#define PHY_STAT			1
> +#define PHY_STAT_LINKUP			(1 << 2)
> +
> +#if (TSI108_PHY_TYPE == PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT			0x11
> +#define PHY_SUM_STAT_SPEED_MASK		(3 << 14)
> +#define PHY_SUM_STAT_SPEED_10		(0 << 14)
> +#define PHY_SUM_STAT_SPEED_100		(1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000		(2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX		(1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD    (PHY_SUM_STAT_SPEED_1000 | 
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD    (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD    (PHY_SUM_STAT_SPEED_100 | 
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD    (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD	    (PHY_SUM_STAT_SPEED_10 | 
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD	    (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE == PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT			0x19
> +#define PHY_SUM_STAT_SPEED_MASK		(7 << 8)	
> /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD		(7 << 8)	
> /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD		(6 << 8)	
> /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD		(5 << 8)	
> /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4		(4 << 8)	/* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD		(3 << 8)	
> /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD		(2 << 8)	/* 
> 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD		(1 << 8)	/* 
> 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL		(0 << 8)	/* 
> 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif				/* MV88_PHY */
> +
> +#define TSI108_TX_EOF	(1 << 0)	/* End of 
> frame; last fragment of packet */
> +#define TSI108_TX_SOF	(1 << 1)	/* Start of 
> frame; first frag. of packet */
> +#define TSI108_TX_VLAN	(1 << 2)	/* Per-frame 
> VLAN: enables VLAN override */
> +#define TSI108_TX_HUGE	(1 << 3)	/* Huge frame enable */
> +#define TSI108_TX_PAD	(1 << 4)	/* Pad the 
> packet if too short */
> +#define TSI108_TX_CRC	(1 << 5)	/* Generate CRC 
> for this packet */
> +#define TSI108_TX_INT	(1 << 14)	/* Generate an 
> IRQ after frag. processed */
> +#define TSI108_TX_RETRY	16	/* 4 bit field 
> indicating num. of retries */
> +#define TSI108_TX_COL	(1 << 20)	/* Set if a 
> collision occured */
> +#define TSI108_TX_LCOL	(1 << 24)	/* Set if a 
> late collision occured */
> +#define TSI108_TX_UNDER	(1 << 25)	/* Set if a 
> FIFO underrun occured */
> +#define TSI108_TX_RLIM	(1 << 26)	/* Set if the 
> retry limit was reached */
> +#define TSI108_TX_OK	(1 << 30)	/* Set if the frame TX 
> was successful */
> +#define TSI108_TX_OWN	(1 << 31)	/* Set if the 
> device owns the descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> +	u32 buf0;
> +	u32 buf1;		/* Base address of buffer */
> +	u32 next0;		/* Address of next descriptor, if any */
> +	u32 next1;
> +	u16 vlan;		/* VLAN, if override enabled 
> for this packet */
> +	u16 len;		/* Length of buffer in bytes */
> +	u32 misc;		/* See TSI108_TX_* above */
> +	u32 reserved0;		/*reserved0 and reserved1 are 
> added to make the desc */
> +	u32 reserved1;		/* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF	(1 << 0)	/* End of 
> frame; last fragment of packet */
> +#define TSI108_RX_SOF	(1 << 1)	/* Start of 
> frame; first frag. of packet */
> +#define TSI108_RX_VLAN	(1 << 2)	/* Set on SOF 
> if packet has a VLAN */
> +#define TSI108_RX_FTYPE	(1 << 3)	/* Length/Type 
> field is type, not length */
> +#define TSI108_RX_RUNT	(1 << 4)/* Packet is less than 
> minimum size */
> +#define TSI108_RX_HASH	(1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD	(1 << 8)	/* Bad frame */
> +#define TSI108_RX_OVER	(1 << 9)	/* FIFO overrun 
> occured */
> +#define TSI108_RX_TRUNC	(1 << 11)	/* Packet 
> truncated due to excess length */
> +#define TSI108_RX_CRC	(1 << 12)	/* Packet had a 
> CRC error */
> +#define TSI108_RX_INT	(1 << 13)	/* Generate an 
> IRQ after frag. processed */
> +#define TSI108_RX_OWN	(1 << 15)	/* Set if the 
> device owns the descriptor */
> +
> +typedef struct {
> +	u32 buf0;		/* Base address of buffer */
> +	u32 buf1;		/* Base address of buffer */
> +	u32 next0;		/* Address of next descriptor, if any */
> +	u32 next1;		/* Address of next descriptor, if any */
> +	u16 vlan;		/* VLAN of received packet, 
> first frag only */
> +	u16 len;		/* Length of received fragment 
> in bytes */
> +	u16 blen;		/* Length of buffer in bytes */
> +	u16 misc;		/* See TSI108_RX_* above */
> +	u32 reserved0;		/* reserved0 and reserved1 are 
> added to make the desc */
> +	u32 reserved1;		/* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif				/* __TSI108_ETH_H */
> 

^ permalink raw reply

* Re: [PATCH] pcmcia: hostap_cs.c - 0xc00f,0x0000 conflicts with pcnet_cs
From: John W. Linville @ 2006-07-01  5:23 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Linux Kernel Mailing List, Andrew Morton, linux, Netdev List
In-Reply-To: <44A5C2FC.9000908@garzik.org>

On Fri, Jun 30, 2006 at 08:34:04PM -0400, Jeff Garzik wrote:
> Linux Kernel Mailing List wrote:
> >commit fd99ddd0701385344eadaf2daa6abbc5fb086750
> >tree 013d75048f086edfa7a89ac3f3301dde13017817
> >parent 0db6095d4ff8918350797dfe299d572980e82fa0
> >author Komuro <komurojun-mbn@nifty.com> Mon, 17 Apr 2006 21:41:21 +0900
> >committer Dominik Brodowski <linux@dominikbrodowski.net> Fri, 30 Jun 2006 
> >22:09:12 +0200
> >
> >[PATCH] pcmcia: hostap_cs.c - 0xc00f,0x0000 conflicts with pcnet_cs
> >
> >Comment out the ID 0xc00f,0x0000 in hostap_cs.c, as it conflicts with the
> >pcnet_cs driver.
> >
> >Signed-off-by: komurojun-mbn@nifty.com
> >Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
> >
> > drivers/net/wireless/hostap/hostap_cs.c |    2 +-
> > 1 files changed, 1 insertion(+), 1 deletion(-)
> 
> drivers/net/wireless stuff should go through netdev and John Linville.
> 
> I'm going to go out on a limb, and guess that Linville never saw this patch?

Actually, I did get a copy.  I also saw a comment from Pavel
Roskin, but it wasn't sure if it was an ACK, a NACK, or something
in between...?

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply

* RE: [PATCH 2.6.17] support for TSO over IPv6
From: Leonid Grossman @ 2006-07-01  4:01 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Ananda Raju, netdev, jgarzik, shemminger, Ravinandan Arakali,
	sivakumar.subramani, Sriram Rapuru, Michael Chan

Thanks Herbert!
We'll fix this.

Leonid 

> -----Original Message-----
> From: Herbert Xu [mailto:herbert@gondor.apana.org.au] 
> Sent: Friday, June 30, 2006 8:38 PM
> To: Leonid Grossman
> Cc: Ananda Raju; netdev@vger.kernel.org; jgarzik@pobox.com; 
> shemminger@osdl.org; Ravinandan Arakali; 
> sivakumar.subramani@neterion.com; Sriram Rapuru; Michael Chan
> Subject: Re: [PATCH 2.6.17] support for TSO over IPv6
> 
> Hi Leonid:
> 
> On Fri, Jun 30, 2006 at 04:46:56PM -0400, Leonid Grossman wrote:
> > 
> > If ECE == 1, then set it to one for all datagrams.
> > If CWR == 1, then set it to one for the first datagram, and 
> set it to 
> > zero for the rest?
> 
> Exactly.
> 
> Cheers,
> --
> Visit Openswan at http://www.openswan.org/
> Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au> 
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
> 


^ permalink raw reply

* Re: [Patch][RFC] Disabling per-tgid stats on task exit in taskstats
From: Andrew Morton @ 2006-07-01  3:51 UTC (permalink / raw)
  To: Shailabh Nagar
  Cc: pj, Valdis.Kletnieks, jlan, balbir, csturtiv, linux-kernel, hadi,
	netdev
In-Reply-To: <44A5EDE6.3010605@watson.ibm.com>

On Fri, 30 Jun 2006 23:37:10 -0400
Shailabh Nagar <nagar@watson.ibm.com> wrote:

> >Set aside the implementation details and ask "what is a good design"?
> >
> >A kernel-wide constant, whether determined at build-time or by a /proc poke
> >isn't a nice design.
> >
> >Can we permit userspace to send in a netlink message describing a cpumask? 
> >That's back-compatible.
> >  
> >
> Yes, that should be doable. And passing in a cpumask is much better 
> since we no longer
> have to maintain mappings.
> 
> So the strawman is:
> Listener bind()s to genetlink using its real pid.
> Sends a separate "registration" message with cpumask to listen to. 
> Kernel stores (real) pid and cpumask.
> During task exit, kernel goes through each registered listener (small 
> list) and decides which
> one needs to get this exit data and calls a genetlink_unicast to each 
> one that does need it.
> 
> If number of listeners is small, the lookups should be swift enough. If 
> it grows large, we
> can consider a fancier lookup (but there I go again, delving into 
> implementation too early :-)

We'll need a map.

1024 CPUs, 1024 listeners, 1000 exits/sec/CPU and we're up to a million
operations per second per CPU.  Meltdown.

But it's a pretty simple map.  A per-cpu array of pointers to the head of a
linked list.  One lock for each CPU's list.

^ permalink raw reply

* Re: [PATCH] NET: Fix ipv6 GSO payload length
From: Herbert Xu @ 2006-07-01  3:44 UTC (permalink / raw)
  To: Michael Chan; +Cc: davem, netdev
In-Reply-To: <1151708208.3645.2.camel@rh4>

On Fri, Jun 30, 2006 at 03:56:47PM -0700, Michael Chan wrote:
> Fix ipv6 GSO payload length calculation.
> 
> The ipv6 payload length excludes the ipv6 base header length and so
> must be subtracted.
> 
> Signed-off-by: Michael Chan <mchan@broadcom.com>

Looks good to me.  Thanks for cathcing this!
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [1/4] [IPV6]: Remove redundant length check on input
From: Herbert Xu @ 2006-07-01  3:39 UTC (permalink / raw)
  To: Ananda Raju; +Cc: David Miller, netdev
In-Reply-To: <78C9135A3D2ECE4B8162EBDCE82CAD7793A4C8@nekter>

On Fri, Jun 30, 2006 at 07:44:49PM -0400, Ananda Raju wrote:
> 
> I tested the patch, and TSO over ipv6 is working fine. But TSO disable
> not working for IPv6. 
> 
> I tried the from tree /pub/scm/linux/kernel/git/davem/net-2.6

I think we need some new ethtool helper functions that sets/clears both
TSO/TSO6.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PATCH 2.6.17] support for TSO over IPv6
From: Herbert Xu @ 2006-07-01  3:38 UTC (permalink / raw)
  To: Leonid Grossman
  Cc: Ananda Raju, netdev, jgarzik, shemminger, Ravinandan Arakali,
	sivakumar.subramani, Sriram Rapuru, Michael Chan
In-Reply-To: <78C9135A3D2ECE4B8162EBDCE82CAD778C6B07@nekter>

Hi Leonid:

On Fri, Jun 30, 2006 at 04:46:56PM -0400, Leonid Grossman wrote:
> 
> If ECE == 1, then set it to one for all datagrams.
> If CWR == 1, then set it to one for the first datagram, and set it to
> zero for the rest?

Exactly.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [Patch][RFC] Disabling per-tgid stats on task exit in taskstats
From: Shailabh Nagar @ 2006-07-01  3:37 UTC (permalink / raw)
  To: Andrew Morton
  Cc: pj, Valdis.Kletnieks, jlan, balbir, csturtiv, linux-kernel, hadi,
	netdev
In-Reply-To: <20060630194353.1cc96ce4.akpm@osdl.org>

Andrew Morton wrote:

>On Fri, 30 Jun 2006 22:20:23 -0400
>Shailabh Nagar <nagar@watson.ibm.com> wrote:
>
>  
>
>>>If we're going to abuse nl_pid then how about we design things so that
>>>nl_pid is treated as two 16-bit words - one word is the start CPU and the
>>>other word is the end cpu?
>>>
>>>Or, if a 65536-CPU limit is too scary, make the bottom 8 bits of nl_pid be
>>>the number of CPUS (ie: TASKSTATS_CPUS_PER_SET) and the top 24 bits is the
>>>starting CPU.  
>>>
>>><avoids mentioning nl_pad>
>>>
>>>It'd be better to use a cpumask, of course..
>>> 
>>>
>>>      
>>>
>>All these options mean each listener gets to pick a "custom" range of 
>>cpus to listen on, 
>>rather than choose one of pre-defined ranges (even if the pre-defined 
>>ranges can change
>>by a configurable TASKSTATS_CPUS_PER_SET). Which means the kernel side 
>>has to
>>figure out which of the listeners cpu range includes the currently 
>>exiting task's cpu. To do
>>this, we'll need a callback from the binding of the netlink socket (so 
>>taskstats can maintain
>>the cpu -> nl_pid mappings at any exit).
>>The current genetlink interface doesn't have that kind of flexibility 
>>(though it can be added
>>I'm sure).
>>
>>Seems a bit involved if the primary aim is to restrict the number of 
>>cpus that one listener
>> wants to listen, rather than be able to pick which ones.
>>
>>A configurable range won't suffice ?
>>
>>    
>>
>
>Set aside the implementation details and ask "what is a good design"?
>
>A kernel-wide constant, whether determined at build-time or by a /proc poke
>isn't a nice design.
>
>Can we permit userspace to send in a netlink message describing a cpumask? 
>That's back-compatible.
>  
>
Yes, that should be doable. And passing in a cpumask is much better 
since we no longer
have to maintain mappings.

So the strawman is:
Listener bind()s to genetlink using its real pid.
Sends a separate "registration" message with cpumask to listen to. 
Kernel stores (real) pid and cpumask.
During task exit, kernel goes through each registered listener (small 
list) and decides which
one needs to get this exit data and calls a genetlink_unicast to each 
one that does need it.

If number of listeners is small, the lookups should be swift enough. If 
it grows large, we
can consider a fancier lookup (but there I go again, delving into 
implementation too early :-)


Sounds good to me !

--Shailabh








^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: jamal @ 2006-07-01  2:59 UTC (permalink / raw)
  To: Thomas Graf; +Cc: David Miller, netdev, kaber
In-Reply-To: <20060630234512.GR14627@postel.suug.ch>

On Sat, 2006-01-07 at 01:45 +0200, Thomas Graf wrote:
> * jamal <hadi@cyberus.ca> 2006-06-30 17:31
> > Better to explain the reason for ifb first:
> > ifb exists initially as a replacement for IMQ. 
> > 1) qdiscs/policies that are per device as opposed to system wide.
> > This now allows for sharing.
> > 
> > 2) Allows for queueing incoming traffic for shaping instead of
> > dropping.
> > 
> > In other wise, the main use is for multiple devices to redirect to it.
> > Main desire is not for it to redirect to any other ifb device or eth
> > devices. I actually tried to get it to do that, but run into issues
> > of complexity and and came up with decision to drop instead of killing
> > the machine.
> > Other than that, it can redirect to any other devices - but may still
> > not be meaningful.
> 
> Last time I'm asking.

Then please listen carefully - because if you read my message with a
reasonable openness the answer is there. 

>  Why are packets dropped? 

When looping happens the only sane thing to do is to drop packets.

I mentioned this was a very tricky thing to achieve in all cases since
there are many behaviors and netdevices that have to be considered. As
an example i ended not submitting the code for looping from egress to
ingress because i felt it needed more testing. And i am certain i have
missed some other device combinations.
Loops could happen within ingress or egress. They could mostly happen
because of mirred or ifb. And some i have discovered via testing.

> You mentioned tc_verd is set to 0 leading to a invalid from verdict. 

yes, for ifb.

> Fact is that the from verdict is set to a meaningful value again at 
> dev_queue_xmit() or ing_filter() so ifb_xmit() only sees valid values. 

Ok, Thomas this is one of those places where we end up colliding for no
good reason - your statement above is accusatory. And sometimes i say 3
things and you pick one and use that as context. The ifb does clear the
field. So if you get redirected again, it will be 0.

> tx locks of individual ifb devices are independant, why would it deadlock? 

different instances of the same device do not deadlock. If however, you
have a series of devices in which A->B->C->D->A then you will have a
possible deadlock. In the case of the ifb i dissallowed it because it
was obvious from the testing. 

> Where is the packet exactly dropped?
> 

For the above in the ifb when they come in with from=0.

> > I have been thinking of Herberts change of qdisc_is_running and this may
> > help actually.
> 
> Help on what?

In the case of deadlocks. Now that it is impossible to contend for the
txlock twice (with that patch), a second redirect will end up just
enqueueing.

cheers,
jamal


^ permalink raw reply

* Re: [Patch][RFC] Disabling per-tgid stats on task exit in taskstats
From: Andrew Morton @ 2006-07-01  2:43 UTC (permalink / raw)
  To: Shailabh Nagar
  Cc: pj, Valdis.Kletnieks, jlan, balbir, csturtiv, linux-kernel, hadi,
	netdev
In-Reply-To: <44A5DBE7.2020704@watson.ibm.com>

On Fri, 30 Jun 2006 22:20:23 -0400
Shailabh Nagar <nagar@watson.ibm.com> wrote:

> >If we're going to abuse nl_pid then how about we design things so that
> >nl_pid is treated as two 16-bit words - one word is the start CPU and the
> >other word is the end cpu?
> >
> >Or, if a 65536-CPU limit is too scary, make the bottom 8 bits of nl_pid be
> >the number of CPUS (ie: TASKSTATS_CPUS_PER_SET) and the top 24 bits is the
> >starting CPU.  
> >
> ><avoids mentioning nl_pad>
> >
> >It'd be better to use a cpumask, of course..
> >  
> >
> All these options mean each listener gets to pick a "custom" range of 
> cpus to listen on, 
> rather than choose one of pre-defined ranges (even if the pre-defined 
> ranges can change
> by a configurable TASKSTATS_CPUS_PER_SET). Which means the kernel side 
> has to
> figure out which of the listeners cpu range includes the currently 
> exiting task's cpu. To do
> this, we'll need a callback from the binding of the netlink socket (so 
> taskstats can maintain
> the cpu -> nl_pid mappings at any exit).
> The current genetlink interface doesn't have that kind of flexibility 
> (though it can be added
> I'm sure).
> 
> Seems a bit involved if the primary aim is to restrict the number of 
> cpus that one listener
>  wants to listen, rather than be able to pick which ones.
> 
> A configurable range won't suffice ?
> 

Set aside the implementation details and ask "what is a good design"?

A kernel-wide constant, whether determined at build-time or by a /proc poke
isn't a nice design.

Can we permit userspace to send in a netlink message describing a cpumask? 
That's back-compatible.


^ permalink raw reply

* Re: [PATCH 2/3] [VLAN]: Update iif when receiving via VLAN device
From: Patrick McHardy @ 2006-07-01  2:47 UTC (permalink / raw)
  To: hadi; +Cc: David Miller, netdev, Thomas Graf
In-Reply-To: <1151697554.5270.241.camel@jzny2>

jamal wrote:
> On Fri, 2006-30-06 at 19:33 +0200, Patrick McHardy wrote:
> 
>>Well, I thought I stay out of this, but since you mention me ..
>>
> I think you will add value to the discussion ;->
> Regardless, we need to settle these kind of issues so we can work better
> together. A while back i said was going to bring some of these issues at
> the next netconf - but since the discussion is happening already, lets
> do it here. Maybe we should take this discussion privately?

Whatever helps resolving this, feel free to take this private if you
feel like it. I think I already managed to get along with our
differences quite well, but it was a hard path. Having friends that
use the same discussion tactics may have helped :) Just to clarify what
I'm saying below, I like you a lot personally, this is purely
"a work thing".

>>I also had the feeling it has gotten easier working with you lately,
> 
> 
> The feeling is mutual. 
> 
> 
>>but I can understand Thomas's pain, I had the same thoughts more than
>>once. Your code often does have an enormous amount of bugs and
>>whitespace and other stylistic problems and working with you can be
>>challenging for multiple reasons, for example having to go to endless,
> 
> 
> One thing is clear in my mind at least (and i have said it several
> times): I am not as good at the semantics as either yourself or Thomas
> or Dave or Acme etc but i have tons of other things that compensate for.
> Probably "not as good" is not the best  description  - rather my brain
> cells put less respect on the stylistic commas than they do on the
> message. Perhaps it's dylexsia or me trying to subconsciouly maximize my
> time. If you push me hard, however, i will get close to do just as a
> good job as you;->  Still, cant live without you guys! 

I think the things you did and which I struggeled with were right
to do, but what I don't understand is why you can't do it right
yourself. Software development has a lot of unpleasant parts,
but ignoring them can't be the right answer. In a shared project,
you should always remember that if you're not doing the work, some
else will have to.

> I would describe the majority of the fixes you have sent against me to
> fall into the stylistic category and into fixing TheLinuxWay (i.e same
> bug in multiple places and files). This is not to say i am not at fault,
> or there havent been issues which made me wonder, but we may have
> different metrics of what bugs i would call my mom and say "Mom, I just
> fixed enormous amount of bugs". 
> [As an example if you went hunting around the kernel, you will find a
> lot of things that dont totally conform to lindent rules. You could
> literally send 100s of patches].
> Perhaps it is the language usage that puts us at odds at times.

Sorry, absolutely not. The bugs I fixed were not just stylistic
things, although I still tried to take care of them where possible.
It was a huge number of bad bugs, even affecting kernels that didn't
even used the feature which introduced the bugs. As for "TheLinuxWay"
(I think I made my opinion on this clear already), cut-and-paste
of crappy code is not it. I'm always amazed how people can copy
crappy code without even thinking about it, when I copy code I usually
fix bugs in the code I copy _before_ doing so.

Language might of course still contribute to our misunderstandings.

>>often entirely pointless discussions for obvious fixes, 
> 
> 
> You may see it that way - I dont and so when that happens it is not by
> any means to engage in a meaningless debate. 
> The discussions are typically of the same nature as i just had with
> Thomas (just a simple one line change in ifb which looked very
> "obvious").

Sometimes you may be right, but despite all the lengthy discussions
we went through, I can't recall a single patch of mine that didn't
went in eventually. But OK, people are complaining about missing
clarification of intent, we're all sometimes guilty of that.
Still, starting a huge discussion about patches that clearly
move in the right direction like Thomas' one which started this
thread is frustrating to the submitter. Discussions about technical
matters should use technical, comprehensible arguments, and nothing
else. This basically means on of three things:

- Bad because it does ..
- Bad because this patch is better
- Applied

Vague expressions about "not good", "might be able to do better" or
"might create problems" without specification are not helpful as long
as they are vague. The point is: _show_ that the submitters argument
is wrong, and if it really is, that shouldn't be hard. This is
especially true if its not some random guy but someone familiar with
what he is changing. This thread is one of many examples.

> The way it goes is that there is a certain assumption made, and a
> solution is suggested in the form of a patch. Accepting such a patch
> (which i once in a while dont even get to see until it is in) means a
> lot of the other assumptions get invalidated and is followed by tons of
> "bug fixes". This has happened a few times to me. And once or twice i
> have bitched because i ended having to support some user for days with
> the wrong view.

Then the patch wasn't reviewed carefully enough. If assumptions get
invalidated, they need to be taken care of at the same time. It can
happen, and sometimes it even is intended (in case of not so well
understood code). But usually it is not and it is simply a bug,
and the submitter is guilty of not beeing careful enough.

>>especially if you're already pissed by noticing 50 bugs at once. 
> 
> 
> I dont get agitated by 100 patches which fix stylistic issues or real
> bugs - although i would have preferred to see the non-obvious first
> before they go in just in case or at least CCed on submission.
> Where i get irritated, depending on how my day is going: when i see even
> a single patch laded with implicit insults. It comes out to me as
> unnecessary arrogance and immaturity. It may not be intentional use of
> language, but thats how it comes out. 

Well, I admit that the insults (in the form of a description of all
the possible bugs, which is not really an insult) are sometimes
intentional, but usually only after I'm already really pissed by
having to take care of an enormous amount of somebody elses crap.
I think that is understandable human behaviour.

>>The reason why you
>>might be able to work better with Herbert is IMO that he usually
>>doesn't touch what you seem to feel is "your area". 
> 
> 
> I think it's the maturity approach perhaps. With Herbert, it ends up
> being about solving the problem; there are always moments of tension and
> what may appear as endless discussion (look at the thread on
> qdisc_is_running), but in the end it doesnt turn out into a high school
> debate to prove one is better than the other. So maybe it is the mutual
> respect and maturity in the discussion. 

Actually that thread was cleared up by Herbert nice and fast :) I too
enjoy working with him, but I think Herbert is a special case as his
arguments are in my opinion always perfectly clear, usually correct,
understandable and nicely formulated. He doesn't leave you much
choice :)

Probably also a bit of a language issue, I'm not able to express myself
as well as he is (especially not as friendly).

> In regards to "areas", you may be right; i cross more into Herberts path
> than he does mine.
> My reaction in this may be related to the way i treat other people and
> my expectations:
> When i submit patches to i would make sure i cc people who i think have
> stakes or better insight in that area even if they seem irrelevant.
> In-fact i may have even private conversations with them first if they
> are large patches.
> As an example i would make sure i cc you if i had some netfilter issues
> or Herbert when i submit ipsec patches and we would have a gazillion
> discussions which would sometimes result in the patches being totally
> re-written. It doesnt make me happy all the time (especially when i have
> to drop patches), but in the end it made me work better with that person
> and removes doubt in my mind that i have missed something. Unfortunately
> this approach (even in non-linux areas) is a lot of times
> taken for a weakness.

I always had the feeling you're beeing "protective", in the form of
pulling people in discussions not really related to the subject.

That was about it with my opinion on the matter, I think we will be
able to resolve this in a better way personally.


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox