Index: sys/dev/bge/if_bgereg.h =================================================================== --- sys/dev/bge/if_bgereg.h (revision 206998) +++ sys/dev/bge/if_bgereg.h (working copy) @@ -229,6 +229,7 @@ #define BGE_PCIMISCCTL_CLOCKCTL_RW 0x00000020 #define BGE_PCIMISCCTL_REG_WORDSWAP 0x00000040 #define BGE_PCIMISCCTL_INDIRECT_ACCESS 0x00000080 +#define BGE_PCIMISCCTL_TAGGED_STATUS 0x00000200 #define BGE_PCIMISCCTL_ASICREV 0xFFFF0000 #define BGE_PCIMISCCTL_ASICREV_SHIFT 16 @@ -1253,6 +1254,8 @@ #define BGE_HCCMODE_COAL_NOW 0x00000008 #define BGE_HCCMODE_MSI_BITS 0x00000070 #define BGE_HCCMODE_STATBLK_SIZE 0x00000180 +#define BGE_HCCMODE_CLRTICK_RXBD 0x00000200 +#define BGE_HCCMODE_CLRTICK_TXBD 0x00000400 #define BGE_STATBLKSZ_FULL 0x00000000 #define BGE_STATBLKSZ_64BYTE 0x00000080 @@ -2071,7 +2074,7 @@ struct bge_status_block { uint32_t bge_status; - uint32_t bge_rsvd0; + uint32_t bge_status_tag; #if BYTE_ORDER == LITTLE_ENDIAN uint16_t bge_rx_jumbo_cons_idx; uint16_t bge_rx_std_cons_idx; @@ -2604,6 +2607,7 @@ #define BGE_FLAG_WIRESPEED 0x00000004 #define BGE_FLAG_EADDR 0x00000008 #define BGE_FLAG_MII_SERDES 0x00000010 +#define BGE_FLAG_TAGGED_STATUS 0x00000020 #define BGE_FLAG_MSI 0x00000100 #define BGE_FLAG_PCIX 0x00000200 #define BGE_FLAG_PCIE 0x00000400 @@ -2654,10 +2658,12 @@ uint32_t bge_rx_discards; uint32_t bge_tx_discards; uint32_t bge_tx_collisions; + uint32_t bge_acked_tag; #ifdef DEVICE_POLLING int rxcycles; #endif /* DEVICE_POLLING */ struct task bge_intr_task; + struct task bge_msi_intr_task; struct taskqueue *bge_tq; }; Index: sys/dev/bge/if_bge.c =================================================================== --- sys/dev/bge/if_bge.c (revision 206998) +++ sys/dev/bge/if_bge.c (working copy) @@ -376,8 +376,10 @@ static int bge_encap(struct bge_softc *, struct mbuf **, uint32_t *); static void bge_intr(void *); +static int bge_intr_fast(void *); +static void bge_intr_task(void *, int); static int bge_msi_intr(void *); -static void bge_intr_task(void *, int); +static void bge_msi_intr_task(void *, int); static void bge_start_locked(struct ifnet *); static void bge_start(struct ifnet *); static int bge_ioctl(struct ifnet *, u_long, caddr_t); @@ -490,6 +492,11 @@ SYSCTL_INT(_hw_bge, OID_AUTO, allow_asf, CTLFLAG_RD, &bge_allow_asf, 0, "Allow ASF mode if available"); +static int bge_legacy_intr = 0; +TUNABLE_INT("hw.bge.legacy_intr", &bge_legacy_intr); +SYSCTL_INT(_hw_bge, OID_AUTO, lecacy_intr, CTLFLAG_RD, &bge_legacy_intr, 0, + "Use legacy interrupt"); + #define SPARC64_BLADE_1500_MODEL "SUNW,Sun-Blade-1500" #define SPARC64_BLADE_1500_PATH_BGE "/pci@1f,700000/network@2" #define SPARC64_BLADE_2500_MODEL "SUNW,Sun-Blade-2500" @@ -1319,7 +1326,10 @@ int i; /* Set endianness before we access any non-PCI registers. */ - pci_write_config(sc->bge_dev, BGE_PCI_MISC_CTL, BGE_INIT, 4); + val = BGE_INIT; + if (sc->bge_flags & BGE_FLAG_TAGGED_STATUS) + val |= BGE_PCIMISCCTL_TAGGED_STATUS; + pci_write_config(sc->bge_dev, BGE_PCI_MISC_CTL, val, 4); /* Clear the MAC control register */ CSR_WRITE_4(sc, BGE_MAC_MODE, 0); @@ -1754,16 +1764,22 @@ BGE_ADDR_HI(sc->bge_ldata.bge_status_block_paddr)); CSR_WRITE_4(sc, BGE_HCC_STATUSBLK_ADDR_LO, BGE_ADDR_LO(sc->bge_ldata.bge_status_block_paddr)); - sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx = 0; - sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx = 0; - /* Set up status block size. */ if (sc->bge_asicrev == BGE_ASICREV_BCM5700 && - sc->bge_chipid != BGE_CHIPID_BCM5700_C0) + sc->bge_chipid != BGE_CHIPID_BCM5700_C0) { val = BGE_STATBLKSZ_FULL; - else + bzero(sc->bge_ldata.bge_status_block, BGE_STATUS_BLK_SZ); + } else { val = BGE_STATBLKSZ_32BYTE; + bzero(sc->bge_ldata.bge_status_block, 32); + } + bus_dmamap_sync(sc->bge_cdata.bge_status_tag, + sc->bge_cdata.bge_status_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + /* Enable clear ticks on BD events mode. */ + if (sc->bge_flags & BGE_FLAG_TAGGED_STATUS) + val |= BGE_HCCMODE_CLRTICK_RXBD | BGE_HCCMODE_CLRTICK_TXBD; /* Turn on host coalescing state machine */ CSR_WRITE_4(sc, BGE_HCC_MODE, val | BGE_HCCMODE_ENABLE); @@ -2517,6 +2533,7 @@ sc->bge_dev = dev; TASK_INIT(&sc->bge_intr_task, 0, bge_intr_task, sc); + TASK_INIT(&sc->bge_msi_intr_task, 0, bge_msi_intr_task, sc); /* * Map control/status registers. @@ -2709,6 +2726,22 @@ } } + /* Force legacy interrupt for BCM5700 controller and polling. */ + if (sc->bge_asicrev == BGE_ASICREV_BCM5700) + bge_legacy_intr = 1; + /* + * Use tagged status mode for supported controllers unless + * legacy interrupt mode is requested. + */ + if (sc->bge_asicrev != BGE_ASICREV_BCM5700 && + (sc->bge_flags & BGE_FLAG_5788) == 0 && bge_legacy_intr == 0) + sc->bge_flags |= BGE_FLAG_TAGGED_STATUS; +#ifdef DEVICE_POLLING + /* Use legacy interrupt and disable tagged status. */ + sc->bge_flags &= ~BGE_FLAG_TAGGED_STATUS; + bge_legacy_intr = 1; +#endif + sc->bge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); @@ -2932,10 +2965,8 @@ * Hookup IRQ last. */ #if __FreeBSD_version > 700030 - if (BGE_IS_5755_PLUS(sc) && sc->bge_flags & BGE_FLAG_MSI) { - /* Take advantage of single-shot MSI. */ - CSR_WRITE_4(sc, BGE_MSI_MODE, CSR_READ_4(sc, BGE_MSI_MODE) & - ~BGE_MSIMODE_ONE_SHOT_DISABLE); + if ((BGE_IS_5755_PLUS(sc) && sc->bge_flags & BGE_FLAG_MSI) || + bge_legacy_intr == 0) { sc->bge_tq = taskqueue_create_fast("bge_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->bge_tq); if (sc->bge_tq == NULL) { @@ -2946,9 +2977,18 @@ } taskqueue_start_threads(&sc->bge_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->bge_dev)); - error = bus_setup_intr(dev, sc->bge_irq, - INTR_TYPE_NET | INTR_MPSAFE, bge_msi_intr, NULL, sc, - &sc->bge_intrhand); + if (BGE_IS_5755_PLUS(sc) && (sc->bge_flags & BGE_FLAG_MSI)) { + /* Take advantage of single-shot MSI. */ + CSR_WRITE_4(sc, BGE_MSI_MODE, + CSR_READ_4(sc, BGE_MSI_MODE) & + ~BGE_MSIMODE_ONE_SHOT_DISABLE); + error = bus_setup_intr(dev, sc->bge_irq, + INTR_TYPE_NET | INTR_MPSAFE, bge_msi_intr, NULL, + sc, &sc->bge_intrhand); + } else + error = bus_setup_intr(dev, sc->bge_irq, + INTR_TYPE_NET | INTR_MPSAFE, bge_intr_fast, NULL, + sc, &sc->bge_intrhand); if (error) ether_ifdetach(ifp); } else @@ -3553,16 +3593,16 @@ * This interrupt is not shared and controller already * disabled further interrupt. */ - taskqueue_enqueue(sc->bge_tq, &sc->bge_intr_task); + taskqueue_enqueue(sc->bge_tq, &sc->bge_msi_intr_task); return (FILTER_HANDLED); } static void -bge_intr_task(void *arg, int pending) +bge_msi_intr_task(void *arg, int pending) { struct bge_softc *sc; struct ifnet *ifp; - uint32_t status; + uint32_t status, status_tag; uint16_t rx_prod, tx_cons; sc = (struct bge_softc *)arg; @@ -3580,12 +3620,13 @@ rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx; tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx; status = sc->bge_ldata.bge_status_block->bge_status; + status_tag = sc->bge_ldata.bge_status_block->bge_status_tag << 24; sc->bge_ldata.bge_status_block->bge_status = 0; bus_dmamap_sync(sc->bge_cdata.bge_status_tag, sc->bge_cdata.bge_status_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Let controller work. */ - bge_writembx(sc, BGE_MBX_IRQ0_LO, 0); + bge_writembx(sc, BGE_MBX_IRQ0_LO, status_tag); if ((status & BGE_STATFLAG_LINKSTATE_CHANGED) != 0) { BGE_LOCK(sc); @@ -3687,7 +3728,72 @@ BGE_UNLOCK(sc); } +static int +bge_intr_fast(void *arg) +{ + struct bge_softc *sc; + + sc = (struct bge_softc *)arg; + /* Disable interrupts. */ + bge_writembx(sc, BGE_MBX_IRQ0_LO, 1); + taskqueue_enqueue(sc->bge_tq, &sc->bge_intr_task); + return (FILTER_HANDLED); +} + static void +bge_intr_task(void *arg, int pending) +{ + struct bge_softc *sc; + struct ifnet *ifp; + uint32_t status, status_tag, statusword; + uint16_t rx_prod, tx_cons; + + sc = (struct bge_softc *)arg; + ifp = sc->bge_ifp; + + BGE_LOCK(sc); + /* + * Do the mandatory PCI flush as well as get the link status. + */ + statusword = CSR_READ_4(sc, BGE_MAC_STS) & BGE_MACSTAT_LINK_CHANGED; + /* Get updated status block. */ + bus_dmamap_sync(sc->bge_cdata.bge_status_tag, + sc->bge_cdata.bge_status_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + /* Save producer/consumer indexess. */ + rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx; + tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx; + status = sc->bge_ldata.bge_status_block->bge_status; + status_tag = sc->bge_ldata.bge_status_block->bge_status_tag << 24; + if (status_tag == sc->bge_acked_tag || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + bge_writembx(sc, BGE_MBX_IRQ0_LO, sc->bge_acked_tag); + BGE_UNLOCK(sc); + return; + } + sc->bge_acked_tag = status_tag; + sc->bge_ldata.bge_status_block->bge_status = 0; + bus_dmamap_sync(sc->bge_cdata.bge_status_tag, + sc->bge_cdata.bge_status_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + /* + * Ack interrupts and have controller raise another interrupt if + * the tag written is not equivalent to the tag of the status + * block DMA'd. + */ + bge_writembx(sc, BGE_MBX_IRQ0_LO, sc->bge_acked_tag); + if (statusword || sc->bge_link_evt || + (status & BGE_STATFLAG_LINKSTATE_CHANGED)) + bge_link_upd(sc); + /* Check TX ring producer/consumer. */ + bge_txeof(sc, tx_cons); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + bge_start_locked(ifp); + BGE_UNLOCK(sc); + bge_rxeof(sc, rx_prod, 0); +} + +static void bge_asf_driver_up(struct bge_softc *sc) { if (sc->bge_asf_mode & ASF_STACKUP) { @@ -4237,8 +4343,9 @@ } } - /* Init our RX return ring index. */ + /* Init our RX return ring index and tag. */ sc->bge_rx_saved_considx = 0; + sc->bge_acked_tag = 0; /* Init our RX/TX stat counters. */ sc->bge_rx_discards = sc->bge_tx_discards = sc->bge_tx_collisions = 0;