Subject: Re: smc91cxx send routine
To: Antti Kantee <pooka@cs.hut.fi>
From: Nathan J. Williams <nathanw@wasabisystems.com>
List: tech-kern
Date: 06/05/2005 16:28:32
Antti Kantee <pooka@cs.hut.fi> writes:
> Is that patch ok to commit, or was there a reason for doing two writes
> in the first place?
I did something similar for an evb with a 16-bit bus (plus a 32-bit
kluge) to one of these chips. I had trouble with byte-wite interrupt
registers, too. Try this:
--- smc91cxx.c 22 Aug 2004 20:43:09 -0000 1.1.1.7
+++ smc91cxx.c 21 Sep 2004 23:51:30 -0000 1.1.1.7.4.1
@@ -540,8 +540,10 @@
/*
* Disable all interrupts.
+ * Writes to both the acknowledge register and the mask register;
+ * writing zero to the acknowledge register is a no-op.
*/
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
/*
* On the 91c111, enable auto-negotiation, and set the LED
@@ -598,12 +600,13 @@
SMC_SELECT_BANK(sc, 2);
if (sc->sc_chipid == CHIP_91C111 && sc->sc_internal_phy) {
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
- IM_EPH_INT | IM_RX_OVRN_INT |
- IM_RCV_INT | IM_TX_INT | IM_MD_INT);
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W,
+ (IM_EPH_INT | IM_RX_OVRN_INT |
+ IM_RCV_INT | IM_TX_INT | IM_MD_INT) << 8);
} else {
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
- IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_TX_INT);
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W,
+ (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_TX_INT)
+ << 8);
}
/* Interface is now running, with no output active. */
@@ -636,7 +639,7 @@
bus_space_handle_t bsh = sc->sc_bsh;
u_int len;
struct mbuf *m;
- u_int16_t length, npages;
+ u_int16_t length, npages, isr;
u_int8_t packetno;
int timo, pad;
@@ -697,7 +700,7 @@
timo = MEMORY_WAIT_TIME;
do {
- if (bus_space_read_1(bst, bsh, INTR_STAT_REG_B) & IM_ALLOC_INT)
+ if (bus_space_read_2(bst, bsh, INTR_STAT_REG_W) & IM_ALLOC_INT)
break;
delay(1);
} while (--timo);
@@ -713,8 +716,9 @@
* no one else attempts to transmit while we're allocating
* memory.
*/
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
- bus_space_read_1(bst, bsh, INTR_MASK_REG_B) | IM_ALLOC_INT);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+ isr |= IM_ALLOC_INT;
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr << 8);
ifp->if_timer = 5;
ifp->if_flags |= IFF_OACTIVE;
@@ -737,8 +741,7 @@
* and the status word (set to zeros).
*/
bus_space_write_2(bst, bsh, DATA_REG_W, 0);
- bus_space_write_1(bst, bsh, DATA_REG_B, (length + 6) & 0xff);
- bus_space_write_1(bst, bsh, DATA_REG_B, ((length + 6) >> 8) & 0xff);
+ bus_space_write_2(bst, bsh, DATA_REG_W, (length + 6) & 0x7ff);
/*
* Get the packet from the kernel. This will include the Ethernet
@@ -774,9 +777,9 @@
* Enable transmit interrupts and let the chip go. Set a watchdog
* in case we miss the interrupt.
*/
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B,
- bus_space_read_1(bst, bsh, INTR_MASK_REG_B) |
- IM_TX_INT | IM_TX_EMPTY_INT);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+ isr |= IM_TX_INT | IM_TX_EMPTY_INT;
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr << 8);
bus_space_write_2(bst, bsh, MMU_CMD_REG_W, MMUCR_ENQUEUE);
@@ -852,9 +855,12 @@
dbuf = *p++;
len--;
leftover = 1;
- } else {
+ } else if ((len < 4)
+ || ((long) p & 2)
+ || ((sc->sc_flags & SMC_FLAGS_32BIT_DATA) == 0))
+ {
/*
- * Aligned data. This is the case we like.
+ * 16-bit aligned data.
*
* Write-region out as much as we can, then
* buffer the remaining byte (if any).
@@ -868,6 +874,15 @@
if (leftover)
dbuf = *p++;
len = 0;
+ } else {
+ /* 32-bit aligned data */
+ leftover = len & 3;
+ len &= ~3;
+ bus_space_write_multi_stream_4(sc->sc_d32t,
+ sc->sc_d32h, 0, (u_int32_t *)p, len >> 2);
+ p += len;
+ len = leftover;
+ leftover = 0;
}
}
if (len < 0)
@@ -893,7 +908,7 @@
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
u_int8_t mask, interrupts, status;
- u_int16_t packetno, tx_status, card_stats;
+ u_int16_t packetno, tx_status, card_stats, isr;
if ((sc->sc_flags & SMC_FLAGS_ENABLED) == 0 ||
(sc->sc_dev.dv_flags & DVF_ACTIVE) == 0)
@@ -902,15 +917,16 @@
SMC_SELECT_BANK(sc, 2);
/*
- * Obtain the current interrupt mask.
+ * Obtain the current interrupt mask and interrupts which occurred.
*/
- mask = bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+ mask = isr >> 8;
/*
* Get the set of interrupt which occurred and eliminate any
* which are not enabled.
*/
- interrupts = bus_space_read_1(bst, bsh, INTR_STAT_REG_B);
+ interrupts = isr & 0xff;
status = interrupts & mask;
/* Ours? */
@@ -920,13 +936,16 @@
/*
* It's ours; disable all interrupts while we process them.
*/
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
/*
* Receive overrun interrupts.
*/
if (status & IM_RX_OVRN_INT) {
- bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_RX_OVRN_INT);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+ isr = (isr & 0xff00) | IM_RX_OVRN_INT;
+ /* Acknowledge */
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
ifp->if_ierrors++;
}
@@ -970,7 +989,10 @@
* mode.
*/
if (status & IM_TX_INT) {
- bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_INT);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+ isr = (isr & 0xff00) | IM_TX_INT;
+ /* Acknowledge */
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
packetno = bus_space_read_2(bst, bsh, FIFO_PORTS_REG_W) &
FIFO_TX_MASK;
@@ -1029,7 +1051,10 @@
* update transmit statistics from the card.
*/
if (status & IM_TX_EMPTY_INT) {
- bus_space_write_1(bst, bsh, INTR_ACK_REG_B, IM_TX_EMPTY_INT);
+ isr = bus_space_read_2(bst, bsh, INTR_STAT_REG_W);
+ isr = (isr & 0xff00) | IM_TX_EMPTY_INT;
+ /* Acknowledge */
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, isr);
/* Disable this interrupt. */
mask &= ~IM_TX_EMPTY_INT;
@@ -1074,8 +1099,8 @@
* Reenable the interrupts we wish to receive now that processing
* is complete.
*/
- mask |= bus_space_read_1(bst, bsh, INTR_MASK_REG_B);
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B, mask);
+ mask |= bus_space_read_2(bst, bsh, INTR_STAT_REG_W) >> 8;
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, mask << 8);
#if NRND > 0
if (status)
@@ -1176,7 +1201,32 @@
* Pull the packet off the interface. Make sure the payload
* is aligned.
*/
- if ((sc->sc_flags & SMC_FLAGS_32BIT_READ) == 0) {
+ if (sc->sc_flags & SMC_FLAGS_32BIT_DATA) {
+ int len = packetlen;
+ m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t) +
+ sizeof(struct ether_header)) - sizeof(struct ether_header);
+ eh = mtod(m, struct ether_header *);
+ data = mtod(m, u_int8_t *);
+
+ *(u_int16_t *)data = bus_space_read_2(bst, bsh, DATA_REG_W);
+ data += 2;
+ len -= 2;
+ /* Now 4-byte aligned */
+
+ if (len > 3) {
+ bus_space_read_multi_stream_4(sc->sc_d32t,
+ sc->sc_d32h, 0, (u_int32_t *)data, len >> 2);
+ data += len & ~3;
+ }
+ if (len & 2) {
+ *(u_int16_t *)data =
+ bus_space_read_2(bst, bsh, DATA_REG_W);
+ data += 2;
+ }
+ if (len & 1) {
+ *data = bus_space_read_1(bst, bsh, DATA_REG_B);
+ }
+ } else if ((sc->sc_flags & SMC_FLAGS_32BIT_READ) == 0) {
m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t) +
sizeof(struct ether_header)) - sizeof(struct ether_header);
@@ -1190,11 +1240,9 @@
*data = bus_space_read_1(bst, bsh, DATA_REG_B);
}
} else {
- u_int8_t *dp;
-
m->m_data = (caddr_t) ALIGN(mtod(m, caddr_t));
eh = mtod(m, struct ether_header *);
- dp = data = mtod(m, u_int8_t *);
+ data = mtod(m, u_int8_t *);
if (packetlen > 3)
bus_space_read_multi_stream_4(bst, bsh, DATA_REG_W,
(u_int32_t *)data, packetlen >> 2);
@@ -1425,7 +1473,7 @@
* Clear interrupt mask; disable all interrupts.
*/
SMC_SELECT_BANK(sc, 2);
- bus_space_write_1(bst, bsh, INTR_MASK_REG_B, 0);
+ bus_space_write_2(bst, bsh, INTR_STAT_REG_W, 0);
/*
* Disable transmitter and receiver.