Index: head/sys/dev/advansys/adv_pci.c =================================================================== --- head/sys/dev/advansys/adv_pci.c (revision 232853) +++ head/sys/dev/advansys/adv_pci.c (revision 232854) @@ -1,336 +1,336 @@ /*- * Device probe and attach routines for the following * Advanced Systems Inc. SCSI controllers: * * Connectivity Products: * ABP902/3902 - Bus-Master PCI (16 CDB) * ABP3905 - Bus-Master PCI (16 CDB) * ABP915 - Bus-Master PCI (16 CDB) * ABP920 - Bus-Master PCI (16 CDB) * ABP3922 - Bus-Master PCI (16 CDB) * ABP3925 - Bus-Master PCI (16 CDB) * ABP930 - Bus-Master PCI (16 CDB) * * ABP930U - Bus-Master PCI Ultra (16 CDB) * ABP930UA - Bus-Master PCI Ultra (16 CDB) * ABP960 - Bus-Master PCI MAC/PC (16 CDB) ** * ABP960U - Bus-Master PCI MAC/PC (16 CDB) ** * * Single Channel Products: * ABP940 - Bus-Master PCI (240 CDB) * ABP940U - Bus-Master PCI Ultra (240 CDB) * ABP940UA/3940UA - Bus-Master PCI Ultra (240 CDB) * ABP3960UA - Bus-Master PCI MAC/PC (240 CDB) * ABP970 - Bus-Master PCI MAC/PC (240 CDB) * ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB) * * Dual Channel Products: * ABP950 - Dual Channel Bus-Master PCI (240 CDB Per Channel) * ABP980 - Four Channel Bus-Master PCI (240 CDB Per Channel) * ABP980U - Four Channel Bus-Master PCI Ultra (240 CDB Per Channel) * ABP980UA/3980UA - Four Channel Bus-Master PCI Ultra (16 CDB Per Chan.) * * Footnotes: * * This board has been sold by SIIG as the Fast SCSI Pro PCI. * ** This board has been sold by Iomega as a Jaz Jet PCI adapter. * * Copyright (c) 1997 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_BASEADR0 PCIR_BAR(0) /* I/O Address */ #define PCI_BASEADR1 PCIR_BAR(1) /* Mem I/O Address */ #define PCI_DEVICE_ID_ADVANSYS_1200A 0x110010CD #define PCI_DEVICE_ID_ADVANSYS_1200B 0x120010CD #define PCI_DEVICE_ID_ADVANSYS_3000 0x130010CD #define PCI_DEVICE_REV_ADVANSYS_3150 0x02 #define PCI_DEVICE_REV_ADVANSYS_3050 0x03 #define ADV_PCI_MAX_DMA_ADDR (0xFFFFFFFFL) #define ADV_PCI_MAX_DMA_COUNT (0xFFFFFFFFL) static int adv_pci_probe(device_t); static int adv_pci_attach(device_t); /* * The overrun buffer shared amongst all PCI adapters. */ static void* overrun_buf; static bus_dma_tag_t overrun_dmat; static bus_dmamap_t overrun_dmamap; static bus_addr_t overrun_physbase; static int adv_pci_probe(device_t dev) { int rev = pci_get_revid(dev); switch (pci_get_devid(dev)) { case PCI_DEVICE_ID_ADVANSYS_1200A: device_set_desc(dev, "AdvanSys ASC1200A SCSI controller"); return BUS_PROBE_DEFAULT; case PCI_DEVICE_ID_ADVANSYS_1200B: device_set_desc(dev, "AdvanSys ASC1200B SCSI controller"); return BUS_PROBE_DEFAULT; case PCI_DEVICE_ID_ADVANSYS_3000: if (rev == PCI_DEVICE_REV_ADVANSYS_3150) { device_set_desc(dev, "AdvanSys ASC3150 SCSI controller"); return BUS_PROBE_DEFAULT; } else if (rev == PCI_DEVICE_REV_ADVANSYS_3050) { device_set_desc(dev, "AdvanSys ASC3030/50 SCSI controller"); return BUS_PROBE_DEFAULT; } else if (rev >= PCI_DEVICE_REV_ADVANSYS_3150) { device_set_desc(dev, "Unknown AdvanSys controller"); return BUS_PROBE_DEFAULT; } break; default: break; } return ENXIO; } static int adv_pci_attach(device_t dev) { struct adv_softc *adv; u_int32_t id; u_int32_t command; int error, rid, irqrid; void *ih; struct resource *iores, *irqres; /* * Determine the chip version. */ id = pci_read_config(dev, PCIR_DEVVENDOR, /*bytes*/4); command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); /* * These cards do not allow memory mapped accesses, so we must * ensure that I/O accesses are available or we won't be able * to talk to them. */ if ((command & (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN)) != (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN)) { command |= PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, /*bytes*/1); } /* * Early chips can't handle non-zero latency timer settings. */ if (id == PCI_DEVICE_ID_ADVANSYS_1200A || id == PCI_DEVICE_ID_ADVANSYS_1200B) { pci_write_config(dev, PCIR_LATTIMER, /*value*/0, /*bytes*/1); } rid = PCI_BASEADR0; iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (iores == NULL) return ENXIO; if (adv_find_signature(rman_get_bustag(iores), rman_get_bushandle(iores)) == 0) { bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } adv = adv_alloc(dev, rman_get_bustag(iores), rman_get_bushandle(iores)); if (adv == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } /* Allocate a dmatag for our transfer DMA maps */ /* XXX Should be a child of the PCI bus dma tag */ error = bus_dma_tag_create( - /* parent */ NULL, + /* parent */ bus_get_dma_tag(dev), /* alignment */ 1, /* boundary */ 0, /* lowaddr */ ADV_PCI_MAX_DMA_ADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* nsegments */ ~0, /* maxsegsz */ ADV_PCI_MAX_DMA_COUNT, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &adv->parent_dmat); if (error != 0) { printf("%s: Could not allocate DMA tag - error %d\n", adv_name(adv), error); adv_free(adv); bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } adv->init_level++; if (overrun_buf == NULL) { /* Need to allocate our overrun buffer */ if (bus_dma_tag_create( /* parent */ adv->parent_dmat, /* alignment */ 8, /* boundary */ 0, /* lowaddr */ ADV_PCI_MAX_DMA_ADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ ADV_OVERRUN_BSIZE, /* nsegments */ 1, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &overrun_dmat) != 0) { bus_dma_tag_destroy(adv->parent_dmat); adv_free(adv); bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } if (bus_dmamem_alloc(overrun_dmat, &overrun_buf, BUS_DMA_NOWAIT, &overrun_dmamap) != 0) { bus_dma_tag_destroy(overrun_dmat); bus_dma_tag_destroy(adv->parent_dmat); adv_free(adv); bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } /* And permanently map it in */ bus_dmamap_load(overrun_dmat, overrun_dmamap, overrun_buf, ADV_OVERRUN_BSIZE, adv_map, &overrun_physbase, /*flags*/0); } adv->overrun_physbase = overrun_physbase; /* * Stop the chip. */ ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT); ADV_OUTW(adv, ADV_CHIP_STATUS, 0); adv->chip_version = ADV_INB(adv, ADV_NONEISA_CHIP_REVISION); adv->type = ADV_PCI; /* * Setup active negation and signal filtering. */ { u_int8_t extra_cfg; if (adv->chip_version >= ADV_CHIP_VER_PCI_ULTRA_3150) adv->type |= ADV_ULTRA; if (adv->chip_version == ADV_CHIP_VER_PCI_ULTRA_3050) extra_cfg = ADV_IFC_ACT_NEG | ADV_IFC_WR_EN_FILTER; else extra_cfg = ADV_IFC_ACT_NEG | ADV_IFC_SLEW_RATE; ADV_OUTB(adv, ADV_REG_IFC, extra_cfg); } if (adv_init(adv) != 0) { adv_free(adv); bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } adv->max_dma_count = ADV_PCI_MAX_DMA_COUNT; adv->max_dma_addr = ADV_PCI_MAX_DMA_ADDR; #if defined(CC_DISABLE_PCI_PARITY_INT) && CC_DISABLE_PCI_PARITY_INT { u_int16_t config_msw; config_msw = ADV_INW(adv, ADV_CONFIG_MSW); config_msw &= 0xFFC0; ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw); } #endif if (id == PCI_DEVICE_ID_ADVANSYS_1200A || id == PCI_DEVICE_ID_ADVANSYS_1200B) { adv->bug_fix_control |= ADV_BUG_FIX_IF_NOT_DWB; adv->bug_fix_control |= ADV_BUG_FIX_ASYN_USE_SYN; adv->fix_asyn_xfer = ~0; } irqrid = 0; irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqrid, RF_SHAREABLE | RF_ACTIVE); if (irqres == NULL || bus_setup_intr(dev, irqres, INTR_TYPE_CAM|INTR_ENTROPY, NULL, adv_intr, adv, &ih)) { adv_free(adv); bus_release_resource(dev, SYS_RES_IOPORT, rid, iores); return ENXIO; } adv_attach(adv); return 0; } static device_method_t adv_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, adv_pci_probe), DEVMETHOD(device_attach, adv_pci_attach), { 0, 0 } }; static driver_t adv_pci_driver = { "adv", adv_pci_methods, sizeof(struct adv_softc) }; static devclass_t adv_pci_devclass; DRIVER_MODULE(adv, pci, adv_pci_driver, adv_pci_devclass, 0, 0); MODULE_DEPEND(adv, pci, 1, 1, 1); Index: head/sys/dev/advansys/adw_pci.c =================================================================== --- head/sys/dev/advansys/adw_pci.c (revision 232853) +++ head/sys/dev/advansys/adw_pci.c (revision 232854) @@ -1,401 +1,401 @@ /*- * Device probe and attach routines for the following * Advanced Systems Inc. SCSI controllers: * * ABP[3]940UW - Bus-Master PCI Ultra-Wide (253 CDB) * ABP950UW - Dual Channel Bus-Master PCI Ultra-Wide (253 CDB/Channel) * ABP970UW - Bus-Master PCI Ultra-Wide (253 CDB) * ABP3940U2W - Bus-Master PCI LVD/Ultra2-Wide (253 CDB) * ABP3950U2W - Bus-Master PCI LVD/Ultra2-Wide (253 CDB) * * Copyright (c) 1998, 1999, 2000 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ADW_PCI_IOBASE PCIR_BAR(0) /* I/O Address */ #define ADW_PCI_MEMBASE PCIR_BAR(1) /* Mem I/O Address */ #define PCI_ID_ADVANSYS_3550 0x230010CD00000000ull #define PCI_ID_ADVANSYS_38C0800_REV1 0x250010CD00000000ull #define PCI_ID_ADVANSYS_38C1600_REV1 0x270010CD00000000ull #define PCI_ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull #define PCI_ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull struct adw_pci_identity; typedef int (adw_device_setup_t)(device_t, struct adw_pci_identity *, struct adw_softc *adw); struct adw_pci_identity { u_int64_t full_id; u_int64_t id_mask; char *name; adw_device_setup_t *setup; const struct adw_mcode *mcode_data; const struct adw_eeprom *default_eeprom; }; static adw_device_setup_t adw_asc3550_setup; static adw_device_setup_t adw_asc38C0800_setup; #ifdef NOTYET static adw_device_setup_t adw_asc38C1600_setup; #endif struct adw_pci_identity adw_pci_ident_table[] = { /* asc3550 based controllers */ { PCI_ID_ADVANSYS_3550, PCI_ID_DEV_VENDOR_MASK, "AdvanSys 3550 Ultra SCSI Adapter", adw_asc3550_setup, &adw_asc3550_mcode_data, &adw_asc3550_default_eeprom }, /* asc38C0800 based controllers */ { PCI_ID_ADVANSYS_38C0800_REV1, PCI_ID_DEV_VENDOR_MASK, "AdvanSys 38C0800 Ultra2 SCSI Adapter", adw_asc38C0800_setup, &adw_asc38C0800_mcode_data, &adw_asc38C0800_default_eeprom }, #ifdef NOTYET /* XXX Disabled until I have hardware to test with */ /* asc38C1600 based controllers */ { PCI_ID_ADVANSYS_38C1600_REV1, PCI_ID_DEV_VENDOR_MASK, "AdvanSys 38C1600 Ultra160 SCSI Adapter", adw_asc38C1600_setup, NULL, /* None provided by vendor thus far */ NULL /* None provided by vendor thus far */ } #endif }; static const int adw_num_pci_devs = sizeof(adw_pci_ident_table) / sizeof(*adw_pci_ident_table); #define ADW_PCI_MAX_DMA_ADDR (0xFFFFFFFFUL) #define ADW_PCI_MAX_DMA_COUNT (0xFFFFFFFFUL) static int adw_pci_probe(device_t dev); static int adw_pci_attach(device_t dev); static device_method_t adw_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, adw_pci_probe), DEVMETHOD(device_attach, adw_pci_attach), { 0, 0 } }; static driver_t adw_pci_driver = { "adw", adw_pci_methods, sizeof(struct adw_softc) }; static devclass_t adw_devclass; DRIVER_MODULE(adw, pci, adw_pci_driver, adw_devclass, 0, 0); MODULE_DEPEND(adw, pci, 1, 1, 1); static __inline u_int64_t adw_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor) { u_int64_t id; id = subvendor | (subdevice << 16) | ((u_int64_t)vendor << 32) | ((u_int64_t)device << 48); return (id); } static struct adw_pci_identity * adw_find_pci_device(device_t dev) { u_int64_t full_id; struct adw_pci_identity *entry; u_int i; full_id = adw_compose_id(pci_get_device(dev), pci_get_vendor(dev), pci_get_subdevice(dev), pci_get_subvendor(dev)); for (i = 0; i < adw_num_pci_devs; i++) { entry = &adw_pci_ident_table[i]; if (entry->full_id == (full_id & entry->id_mask)) return (entry); } return (NULL); } static int adw_pci_probe(device_t dev) { struct adw_pci_identity *entry; entry = adw_find_pci_device(dev); if (entry != NULL) { device_set_desc(dev, entry->name); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int adw_pci_attach(device_t dev) { struct adw_softc *adw; struct adw_pci_identity *entry; u_int32_t command; struct resource *regs; int regs_type; int regs_id; int error; int zero; command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); entry = adw_find_pci_device(dev); if (entry == NULL) return (ENXIO); regs = NULL; regs_type = 0; regs_id = 0; #ifdef ADW_ALLOW_MEMIO if ((command & PCIM_CMD_MEMEN) != 0) { regs_type = SYS_RES_MEMORY; regs_id = ADW_PCI_MEMBASE; regs = bus_alloc_resource_any(dev, regs_type, ®s_id, RF_ACTIVE); } #endif if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { regs_type = SYS_RES_IOPORT; regs_id = ADW_PCI_IOBASE; regs = bus_alloc_resource_any(dev, regs_type, ®s_id, RF_ACTIVE); } if (regs == NULL) { device_printf(dev, "can't allocate register resources\n"); return (ENOMEM); } adw = adw_alloc(dev, regs, regs_type, regs_id); if (adw == NULL) return(ENOMEM); /* * Now that we have access to our registers, just verify that * this really is an AdvanSys device. */ if (adw_find_signature(adw) == 0) { adw_free(adw); return (ENXIO); } adw_reset_chip(adw); error = entry->setup(dev, entry, adw); if (error != 0) return (error); /* Ensure busmastering is enabled */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, /*bytes*/1); /* Allocate a dmatag for our transfer DMA maps */ /* XXX Should be a child of the PCI bus dma tag */ error = bus_dma_tag_create( - /* parent */ NULL, + /* parent */ bus_get_dma_tag(dev), /* alignment */ 1, /* boundary */ 0, /* lowaddr */ ADW_PCI_MAX_DMA_ADDR, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* nsegments */ ~0, /* maxsegsz */ ADW_PCI_MAX_DMA_COUNT, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &adw->parent_dmat); adw->init_level++; if (error != 0) { printf("%s: Could not allocate DMA tag - error %d\n", adw_name(adw), error); adw_free(adw); return (error); } adw->init_level++; error = adw_init(adw); if (error != 0) { adw_free(adw); return (error); } /* * If the PCI Configuration Command Register "Parity Error Response * Control" Bit was clear (0), then set the microcode variable * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode * to ignore DMA parity errors. */ if ((command & PCIM_CMD_PERRESPEN) == 0) adw_lram_write_16(adw, ADW_MC_CONTROL_FLAG, adw_lram_read_16(adw, ADW_MC_CONTROL_FLAG) | ADW_MC_CONTROL_IGN_PERR); zero = 0; adw->irq_res_type = SYS_RES_IRQ; adw->irq = bus_alloc_resource_any(dev, adw->irq_res_type, &zero, RF_ACTIVE | RF_SHAREABLE); if (adw->irq == NULL) { adw_free(adw); return (ENOMEM); } error = adw_attach(adw); if (error != 0) adw_free(adw); return (error); } static int adw_generic_setup(device_t dev, struct adw_pci_identity *entry, struct adw_softc *adw) { adw->channel = pci_get_function(dev) == 1 ? 'B' : 'A'; adw->chip = ADW_CHIP_NONE; adw->features = ADW_FENONE; adw->flags = ADW_FNONE; adw->mcode_data = entry->mcode_data; adw->default_eeprom = entry->default_eeprom; return (0); } static int adw_asc3550_setup(device_t dev, struct adw_pci_identity *entry, struct adw_softc *adw) { int error; error = adw_generic_setup(dev, entry, adw); if (error != 0) return (error); adw->chip = ADW_CHIP_ASC3550; adw->features = ADW_ASC3550_FE; adw->memsize = ADW_3550_MEMSIZE; /* * For ASC-3550, setting the START_CTL_EMFU [3:2] bits * sets a FIFO threshold of 128 bytes. This register is * only accessible to the host. */ adw_outb(adw, ADW_DMA_CFG0, ADW_DMA_CFG0_START_CTL_EM_FU|ADW_DMA_CFG0_READ_CMD_MRM); adw_outb(adw, ADW_MEM_CFG, adw_inb(adw, ADW_MEM_CFG) | ADW_MEM_CFG_RAM_SZ_8KB); return (0); } static int adw_asc38C0800_setup(device_t dev, struct adw_pci_identity *entry, struct adw_softc *adw) { int error; error = adw_generic_setup(dev, entry, adw); if (error != 0) return (error); /* * For ASC-38C0800, set FIFO_THRESH_80B [6:4] bits and * START_CTL_TH [3:2] bits for the default FIFO threshold. * * Note: ASC-38C0800 FIFO threshold has been changed to 256 bytes. * * For DMA Errata #4 set the BC_THRESH_ENB bit. */ adw_outb(adw, ADW_DMA_CFG0, ADW_DMA_CFG0_BC_THRESH_ENB|ADW_DMA_CFG0_FIFO_THRESH_80B |ADW_DMA_CFG0_START_CTL_TH|ADW_DMA_CFG0_READ_CMD_MRM); adw_outb(adw, ADW_MEM_CFG, adw_inb(adw, ADW_MEM_CFG) | ADW_MEM_CFG_RAM_SZ_16KB); adw->chip = ADW_CHIP_ASC38C0800; adw->features = ADW_ASC38C0800_FE; adw->memsize = ADW_38C0800_MEMSIZE; return (error); } #ifdef NOTYET static int adw_asc38C1600_setup(device_t dev, struct adw_pci_identity *entry, struct adw_softc *adw) { int error; error = adw_generic_setup(dev, entry, adw); if (error != 0) return (error); adw->chip = ADW_CHIP_ASC38C1600; adw->features = ADW_ASC38C1600_FE; adw->memsize = ADW_38C1600_MEMSIZE; return (error); } #endif Index: head/sys/dev/amr/amr_pci.c =================================================================== --- head/sys/dev/amr/amr_pci.c (revision 232853) +++ head/sys/dev/amr/amr_pci.c (revision 232854) @@ -1,717 +1,717 @@ /*- * Copyright (c) 1999,2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2002 Eric Moore * Copyright (c) 2002, 2004 LSI Logic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The party using or redistributing the source code and binary forms * agrees to the disclaimer below and the terms and conditions set forth * herein. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int amr_pci_probe(device_t dev); static int amr_pci_attach(device_t dev); static int amr_pci_detach(device_t dev); static int amr_pci_shutdown(device_t dev); static int amr_pci_suspend(device_t dev); static int amr_pci_resume(device_t dev); static void amr_pci_intr(void *arg); static void amr_pci_free(struct amr_softc *sc); static void amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int amr_sglist_map(struct amr_softc *sc); static int amr_setup_mbox(struct amr_softc *sc); static int amr_ccb_map(struct amr_softc *sc); static u_int amr_force_sg32 = 0; TUNABLE_INT("hw.amr.force_sg32", &amr_force_sg32); SYSCTL_DECL(_hw_amr); SYSCTL_UINT(_hw_amr, OID_AUTO, force_sg32, CTLFLAG_RDTUN, &amr_force_sg32, 0, "Force the AMR driver to use 32bit scatter gather"); static device_method_t amr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, amr_pci_probe), DEVMETHOD(device_attach, amr_pci_attach), DEVMETHOD(device_detach, amr_pci_detach), DEVMETHOD(device_shutdown, amr_pci_shutdown), DEVMETHOD(device_suspend, amr_pci_suspend), DEVMETHOD(device_resume, amr_pci_resume), DEVMETHOD_END }; static driver_t amr_pci_driver = { "amr", amr_methods, sizeof(struct amr_softc) }; static devclass_t amr_devclass; DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0); MODULE_DEPEND(amr, pci, 1, 1, 1); MODULE_DEPEND(amr, cam, 1, 1, 1); static struct amr_ident { int vendor; int device; int flags; #define AMR_ID_PROBE_SIG (1<<0) /* generic i960RD, check signature */ #define AMR_ID_DO_SG64 (1<<1) #define AMR_ID_QUARTZ (1<<2) } amr_device_ids[] = { {0x101e, 0x9010, 0}, {0x101e, 0x9060, 0}, {0x8086, 0x1960, AMR_ID_QUARTZ | AMR_ID_PROBE_SIG}, {0x101e, 0x1960, AMR_ID_QUARTZ}, {0x1000, 0x1960, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, {0x1000, 0x0407, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, {0x1000, 0x0408, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, {0x1000, 0x0409, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, {0x1028, 0x000e, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, /* perc4/di i960 */ {0x1028, 0x000f, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di Verde*/ {0x1028, 0x0013, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di */ {0, 0, 0} }; static struct amr_ident * amr_find_ident(device_t dev) { struct amr_ident *id; int sig; for (id = amr_device_ids; id->vendor != 0; id++) { if ((pci_get_vendor(dev) == id->vendor) && (pci_get_device(dev) == id->device)) { /* do we need to test for a signature? */ if (id->flags & AMR_ID_PROBE_SIG) { sig = pci_read_config(dev, AMR_CFG_SIG, 2); if ((sig != AMR_SIGNATURE_1) && (sig != AMR_SIGNATURE_2)) continue; } return (id); } } return (NULL); } static int amr_pci_probe(device_t dev) { debug_called(1); if (amr_find_ident(dev) != NULL) { device_set_desc(dev, LSI_DESC_PCI); return(BUS_PROBE_DEFAULT); } return(ENXIO); } static int amr_pci_attach(device_t dev) { struct amr_softc *sc; struct amr_ident *id; int rid, rtype, error; u_int32_t command; debug_called(1); /* * Initialise softc. */ sc = device_get_softc(dev); bzero(sc, sizeof(*sc)); sc->amr_dev = dev; /* assume failure is 'not configured' */ error = ENXIO; /* * Determine board type. */ if ((id = amr_find_ident(dev)) == NULL) return (ENXIO); command = pci_read_config(dev, PCIR_COMMAND, 1); if (id->flags & AMR_ID_QUARTZ) { /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "memory window not available\n"); return (ENXIO); } sc->amr_type |= AMR_TYPE_QUARTZ; } else { /* * Make sure we are going to be able to talk to this board. */ if ((command & PCIM_CMD_PORTEN) == 0) { device_printf(dev, "I/O window not available\n"); return (ENXIO); } } if ((amr_force_sg32 == 0) && (id->flags & AMR_ID_DO_SG64) && (sizeof(vm_paddr_t) > 4)) { device_printf(dev, "Using 64-bit DMA\n"); sc->amr_type |= AMR_TYPE_SG64; } /* force the busmaster enable bit on */ if (!(command & PCIM_CMD_BUSMASTEREN)) { device_printf(dev, "busmaster bit not set, enabling\n"); command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); } /* * Allocate the PCI register window. */ rid = PCIR_BAR(0); rtype = AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT; sc->amr_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE); if (sc->amr_reg == NULL) { device_printf(sc->amr_dev, "can't allocate register window\n"); goto out; } sc->amr_btag = rman_get_bustag(sc->amr_reg); sc->amr_bhandle = rman_get_bushandle(sc->amr_reg); /* * Allocate and connect our interrupt. */ rid = 0; sc->amr_irq = bus_alloc_resource_any(sc->amr_dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->amr_irq == NULL) { device_printf(sc->amr_dev, "can't allocate interrupt\n"); goto out; } if (bus_setup_intr(sc->amr_dev, sc->amr_irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE, NULL, amr_pci_intr, sc, &sc->amr_intr)) { device_printf(sc->amr_dev, "can't set up interrupt\n"); goto out; } debug(2, "interrupt attached"); /* assume failure is 'out of memory' */ error = ENOMEM; /* * Allocate the parent bus DMA tag appropriate for PCI. */ - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* alignment,boundary */ AMR_IS_SG64(sc) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->amr_parent_dmat)) { device_printf(dev, "can't allocate parent DMA tag\n"); goto out; } /* * Create DMA tag for mapping buffers into controller-addressable space. */ if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ MAXBSIZE, /* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->amr_list_lock, /* lockarg */ &sc->amr_buffer_dmat)) { device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); goto out; } if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ MAXBSIZE, /* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->amr_list_lock, /* lockarg */ &sc->amr_buffer64_dmat)) { device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); goto out; } debug(2, "dma tag done"); /* * Allocate and set up mailbox in a bus-visible fashion. */ mtx_init(&sc->amr_list_lock, "AMR List Lock", NULL, MTX_DEF); mtx_init(&sc->amr_hw_lock, "AMR HW Lock", NULL, MTX_DEF); if ((error = amr_setup_mbox(sc)) != 0) goto out; debug(2, "mailbox setup"); /* * Build the scatter/gather buffers. */ if ((error = amr_sglist_map(sc)) != 0) goto out; debug(2, "s/g list mapped"); if ((error = amr_ccb_map(sc)) != 0) goto out; debug(2, "ccb mapped"); /* * Do bus-independant initialisation, bring controller online. */ error = amr_attach(sc); out: if (error) amr_pci_free(sc); return(error); } /******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ static int amr_pci_detach(device_t dev) { struct amr_softc *sc = device_get_softc(dev); int error; debug_called(1); if (sc->amr_state & AMR_STATE_OPEN) return(EBUSY); if ((error = amr_pci_shutdown(dev))) return(error); amr_pci_free(sc); return(0); } /******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach, system shutdown, or before performing * an operation which may add or delete system disks. (Call amr_startup to * resume normal operation.) * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int amr_pci_shutdown(device_t dev) { struct amr_softc *sc = device_get_softc(dev); int i,error; debug_called(1); /* mark ourselves as in-shutdown */ sc->amr_state |= AMR_STATE_SHUTDOWN; /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); printf("%s\n", amr_flush(sc) ? "failed" : "done"); error = 0; /* delete all our child devices */ for(i = 0 ; i < AMR_MAXLD; i++) { if( sc->amr_drive[i].al_disk != 0) { if((error = device_delete_child(sc->amr_dev,sc->amr_drive[i].al_disk)) != 0) goto shutdown_out; sc->amr_drive[i].al_disk = 0; } } /* XXX disable interrupts? */ shutdown_out: return(error); } /******************************************************************************** * Bring the controller to a quiescent state, ready for system suspend. */ static int amr_pci_suspend(device_t dev) { struct amr_softc *sc = device_get_softc(dev); debug_called(1); sc->amr_state |= AMR_STATE_SUSPEND; /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); printf("%s\n", amr_flush(sc) ? "failed" : "done"); /* XXX disable interrupts? */ return(0); } /******************************************************************************** * Bring the controller back to a state ready for operation. */ static int amr_pci_resume(device_t dev) { struct amr_softc *sc = device_get_softc(dev); debug_called(1); sc->amr_state &= ~AMR_STATE_SUSPEND; /* XXX enable interrupts? */ return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ static void amr_pci_intr(void *arg) { struct amr_softc *sc = (struct amr_softc *)arg; debug_called(3); /* collect finished commands, queue anything waiting */ amr_done(sc); } /******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ static void amr_pci_free(struct amr_softc *sc) { void *p; debug_called(1); amr_free(sc); /* destroy data-transfer DMA tag */ if (sc->amr_buffer_dmat) bus_dma_tag_destroy(sc->amr_buffer_dmat); if (sc->amr_buffer64_dmat) bus_dma_tag_destroy(sc->amr_buffer64_dmat); /* free and destroy DMA memory and tag for passthrough pool */ if (sc->amr_ccb) bus_dmamem_free(sc->amr_ccb_dmat, sc->amr_ccb, sc->amr_ccb_dmamap); if (sc->amr_ccb_dmat) bus_dma_tag_destroy(sc->amr_ccb_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->amr_sgtable) bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); if (sc->amr_sg_dmat) bus_dma_tag_destroy(sc->amr_sg_dmat); /* free and destroy DMA memory and tag for mailbox */ p = (void *)(uintptr_t)(volatile void *)sc->amr_mailbox64; if (sc->amr_mailbox) { bus_dmamem_free(sc->amr_mailbox_dmat, p, sc->amr_mailbox_dmamap); } if (sc->amr_mailbox_dmat) bus_dma_tag_destroy(sc->amr_mailbox_dmat); /* disconnect the interrupt handler */ if (sc->amr_intr) bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr); if (sc->amr_irq != NULL) bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq); /* destroy the parent DMA tag */ if (sc->amr_parent_dmat) bus_dma_tag_destroy(sc->amr_parent_dmat); /* release the register window mapping */ if (sc->amr_reg != NULL) bus_release_resource(sc->amr_dev, AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT, PCIR_BAR(0), sc->amr_reg); } /******************************************************************************** * Allocate and map the scatter/gather table in bus space. */ static void amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { uint32_t *addr; debug_called(1); addr = arg; *addr = segs[0].ds_addr; } static int amr_sglist_map(struct amr_softc *sc) { size_t segsize; void *p; int error; debug_called(1); /* * Create a single tag describing a region large enough to hold all of * the s/g lists we will need. * * Note that we could probably use AMR_LIMITCMD here, but that may become * tunable. */ if (AMR_IS_SG64(sc)) segsize = sizeof(struct amr_sg64entry) * AMR_NSEG * AMR_MAXCMD; else segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD; error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 512, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->amr_sg_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n"); return(ENOMEM); } /* * Allocate enough s/g maps for all commands and permanently map them into * controller-visible space. * * XXX this assumes we can get enough space for all the s/g maps in one * contiguous slab. We may need to switch to a more complex arrangement * where we allocate in smaller chunks and keep a lookup table from slot * to bus address. * * XXX HACK ALERT: at least some controllers don't like the s/g memory * being allocated below 0x2000. We leak some memory if * we get some below this mark and allocate again. We * should be able to avoid this with the tag setup, but * that does't seem to work. */ retry: error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate s/g table\n"); return(ENOMEM); } bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, p, segsize, amr_sglist_helper, &sc->amr_sgbusaddr, 0); if (sc->amr_sgbusaddr < 0x2000) { debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr); goto retry; } if (AMR_IS_SG64(sc)) sc->amr_sg64table = (struct amr_sg64entry *)p; sc->amr_sgtable = (struct amr_sgentry *)p; return(0); } /******************************************************************************** * Allocate and set up mailbox areas for the controller (sc) * * The basic mailbox structure should be 16-byte aligned. */ static int amr_setup_mbox(struct amr_softc *sc) { int error; void *p; uint32_t baddr; debug_called(1); /* * Create a single tag describing a region large enough to hold the entire * mailbox. */ error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 16, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(struct amr_mailbox64), /* maxsize */ 1, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->amr_mailbox_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate mailbox tag\n"); return(ENOMEM); } /* * Allocate the mailbox structure and permanently map it into * controller-visible space. */ error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_mailbox_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate mailbox memory\n"); return(ENOMEM); } bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p, sizeof(struct amr_mailbox64), amr_sglist_helper, &baddr, 0); /* * Conventional mailbox is inside the mailbox64 region. */ /* save physical base of the basic mailbox structure */ sc->amr_mailboxphys = baddr + offsetof(struct amr_mailbox64, mb); bzero(p, sizeof(struct amr_mailbox64)); sc->amr_mailbox64 = (struct amr_mailbox64 *)p; sc->amr_mailbox = &sc->amr_mailbox64->mb; return(0); } static int amr_ccb_map(struct amr_softc *sc) { int ccbsize, error; /* * Passthrough and Extended passthrough structures will share the same * memory. */ ccbsize = sizeof(union amr_ccb) * AMR_MAXCMD; error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ 128, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ ccbsize, /* maxsize */ 1, /* nsegments */ ccbsize, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->amr_ccb_dmat); if (error != 0) { device_printf(sc->amr_dev, "can't allocate ccb tag\n"); return (ENOMEM); } error = bus_dmamem_alloc(sc->amr_ccb_dmat, (void **)&sc->amr_ccb, BUS_DMA_NOWAIT, &sc->amr_ccb_dmamap); if (error) { device_printf(sc->amr_dev, "can't allocate ccb memory\n"); return (ENOMEM); } bus_dmamap_load(sc->amr_ccb_dmat, sc->amr_ccb_dmamap, sc->amr_ccb, ccbsize, amr_sglist_helper, &sc->amr_ccb_busaddr, 0); bzero(sc->amr_ccb, ccbsize); return (0); } Index: head/sys/dev/an/if_an_pci.c =================================================================== --- head/sys/dev/an/if_an_pci.c (revision 232853) +++ head/sys/dev/an/if_an_pci.c (revision 232854) @@ -1,281 +1,281 @@ /*- * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * This is a PCI shim for the Aironet PC4500/4800 wireless network * driver. Aironet makes PCMCIA, ISA and PCI versions of these devices, * which all have basically the same interface. The ISA and PCI cards * are actually bridge adapters with PCMCIA cards inserted into them, * however they appear as normal PCI or ISA devices to the host. * * All we do here is handle the PCI probe and attach and set up an * interrupt handler entry point. The PCI version of the card uses * a PLX 9050 PCI to "dumb bus" bridge chip, which provides us with * multiple PCI address space mappings. The primary mapping at PCI * register 0x14 is for the PLX chip itself, *NOT* the Aironet card. * The I/O address of the Aironet is actually at register 0x18, which * is the local bus mapping register for bus space 0. There are also * registers for additional register spaces at registers 0x1C and * 0x20, but these are unused in the Aironet devices. To find out * more, you need a datasheet for the 9050 from PLX, but you have * to go through their sales office to get it. Bleh. */ #include "opt_inet.h" #ifdef INET #define ANCACHE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct an_type { u_int16_t an_vid; u_int16_t an_did; char *an_name; }; #define AIRONET_VENDORID 0x14B9 #define AIRONET_DEVICEID_35x 0x0350 #define AIRONET_DEVICEID_4500 0x4500 #define AIRONET_DEVICEID_4800 0x4800 #define AIRONET_DEVICEID_4xxx 0x0001 #define AIRONET_DEVICEID_MPI350 0xA504 #define AN_PCI_PLX_LOIO 0x14 /* PLX chip iobase */ #define AN_PCI_LOIO 0x18 /* Aironet iobase */ static struct an_type an_devs[] = { { AIRONET_VENDORID, AIRONET_DEVICEID_35x, "Cisco Aironet 350 Series" }, { AIRONET_VENDORID, AIRONET_DEVICEID_MPI350, "Cisco Aironet MPI350" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4500, "Aironet PCI4500" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4800, "Aironet PCI4800" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4xxx, "Aironet PCI4500/PCI4800" }, { 0, 0, NULL } }; static int an_probe_pci (device_t); static int an_attach_pci (device_t); static int an_suspend_pci (device_t); static int an_resume_pci (device_t); static int an_probe_pci(device_t dev) { struct an_type *t; struct an_softc *sc = device_get_softc(dev); bzero(sc, sizeof(struct an_softc)); t = an_devs; while (t->an_name != NULL) { if (pci_get_vendor(dev) == t->an_vid && pci_get_device(dev) == t->an_did) { device_set_desc(dev, t->an_name); an_pci_probe(dev); return(BUS_PROBE_DEFAULT); } t++; } return(ENXIO); } static int an_attach_pci(dev) device_t dev; { u_int32_t command; struct an_softc *sc; int flags, error = 0; sc = device_get_softc(dev); flags = device_get_flags(dev); if (pci_get_vendor(dev) == AIRONET_VENDORID && pci_get_device(dev) == AIRONET_DEVICEID_MPI350) { sc->mpi350 = 1; sc->port_rid = PCIR_BAR(0); } else { /* * Map control/status registers. */ command = pci_read_config(dev, PCIR_COMMAND, 4); command |= PCIM_CMD_PORTEN; pci_write_config(dev, PCIR_COMMAND, command, 4); command = pci_read_config(dev, PCIR_COMMAND, 4); if (!(command & PCIM_CMD_PORTEN)) { device_printf(dev, "failed to enable I/O ports!\n"); error = ENXIO; goto fail; } sc->port_rid = AN_PCI_LOIO; } error = an_alloc_port(dev, sc->port_rid, 1); if (error) { device_printf(dev, "couldn't map ports\n"); goto fail; } /* Allocate memory for MPI350 */ if (sc->mpi350) { /* Allocate memory */ sc->mem_rid = PCIR_BAR(1); error = an_alloc_memory(dev, sc->mem_rid, 1); if (error) { device_printf(dev, "couldn't map memory\n"); goto fail; } /* Allocate aux. memory */ sc->mem_aux_rid = PCIR_BAR(2); error = an_alloc_aux_memory(dev, sc->mem_aux_rid, AN_AUX_MEM_SIZE); if (error) { device_printf(dev, "couldn't map aux memory\n"); goto fail; } /* Allocate DMA region */ - error = bus_dma_tag_create(NULL, /* parent */ + error = bus_dma_tag_create(bus_get_dma_tag(dev),/* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ 1, /* nsegments */ 0xffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->an_dtag); if (error) { device_printf(dev, "couldn't get DMA region\n"); goto fail; } } /* Allocate interrupt */ error = an_alloc_irq(dev, 0, RF_SHAREABLE); if (error) { device_printf(dev, "couldn't get interrupt\n"); goto fail; } sc->an_dev = dev; error = an_attach(sc, flags); if (error) { device_printf(dev, "couldn't attach\n"); goto fail; } /* * Must setup the interrupt after the an_attach to prevent racing. */ error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, NULL, an_intr, sc, &sc->irq_handle); if (error) device_printf(dev, "couldn't setup interrupt\n"); fail: if (error) an_release_resources(dev); return(error); } static int an_suspend_pci(device_t dev) { an_shutdown(dev); return (0); } static int an_resume_pci(device_t dev) { an_resume(dev); return (0); } static device_method_t an_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, an_probe_pci), DEVMETHOD(device_attach, an_attach_pci), DEVMETHOD(device_detach, an_detach), DEVMETHOD(device_shutdown, an_shutdown), DEVMETHOD(device_suspend, an_suspend_pci), DEVMETHOD(device_resume, an_resume_pci), { 0, 0 } }; static driver_t an_pci_driver = { "an", an_pci_methods, sizeof(struct an_softc), }; static devclass_t an_devclass; DRIVER_MODULE(an, pci, an_pci_driver, an_devclass, 0, 0); MODULE_DEPEND(an, pci, 1, 1, 1); MODULE_DEPEND(an, wlan, 1, 1, 1); Index: head/sys/dev/arcmsr/arcmsr.c =================================================================== --- head/sys/dev/arcmsr/arcmsr.c (revision 232853) +++ head/sys/dev/arcmsr/arcmsr.c (revision 232854) @@ -1,3948 +1,3948 @@ /* ***************************************************************************************** ** O.S : FreeBSD ** FILE NAME : arcmsr.c ** BY : Erich Chen, Ching Huang ** Description: SCSI RAID Device Driver for ** ARECA (ARC11XX/ARC12XX/ARC13XX/ARC16XX/ARC188x) SATA/SAS RAID HOST Adapter ** ARCMSR RAID Host adapter ** [RAID controller:INTEL 331(PCI-X) 341(PCI-EXPRESS) chip set] ****************************************************************************************** ************************************************************************ ** ** Copyright (c) 2004-2010 ARECA Co. Ltd. ** Erich Chen, Taipei Taiwan All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT **(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ************************************************************************** ** History ** ** REV# DATE NAME DESCRIPTION ** 1.00.00.00 03/31/2004 Erich Chen First release ** 1.20.00.02 11/29/2004 Erich Chen bug fix with arcmsr_bus_reset when PHY error ** 1.20.00.03 04/19/2005 Erich Chen add SATA 24 Ports adapter type support ** clean unused function ** 1.20.00.12 09/12/2005 Erich Chen bug fix with abort command handling, ** firmware version check ** and firmware update notify for hardware bug fix ** handling if none zero high part physical address ** of srb resource ** 1.20.00.13 08/18/2006 Erich Chen remove pending srb and report busy ** add iop message xfer ** with scsi pass-through command ** add new device id of sas raid adapters ** code fit for SPARC64 & PPC ** 1.20.00.14 02/05/2007 Erich Chen bug fix for incorrect ccb_h.status report ** and cause g_vfs_done() read write error ** 1.20.00.15 10/10/2007 Erich Chen support new RAID adapter type ARC120x ** 1.20.00.16 10/10/2009 Erich Chen Bug fix for RAID adapter type ARC120x ** bus_dmamem_alloc() with BUS_DMA_ZERO ** 1.20.00.17 07/15/2010 Ching Huang Added support ARC1880 ** report CAM_DEV_NOT_THERE instead of CAM_SEL_TIMEOUT when device failed, ** prevent cam_periph_error removing all LUN devices of one Target id ** for any one LUN device failed ** 1.20.00.18 10/14/2010 Ching Huang Fixed "inquiry data fails comparion at DV1 step" ** 10/25/2010 Ching Huang Fixed bad range input in bus_alloc_resource for ADAPTER_TYPE_B ** 1.20.00.19 11/11/2010 Ching Huang Fixed arcmsr driver prevent arcsas support for Areca SAS HBA ARC13x0 ** 1.20.00.20 12/08/2010 Ching Huang Avoid calling atomic_set_int function ** 1.20.00.21 02/08/2011 Ching Huang Implement I/O request timeout ** 02/14/2011 Ching Huang Modified pktRequestCount ** 1.20.00.21 03/03/2011 Ching Huang if a command timeout, then wait its ccb back before free it ** 1.20.00.22 07/04/2011 Ching Huang Fixed multiple MTX panic ****************************************************************************************** */ #include __FBSDID("$FreeBSD$"); #if 0 #define ARCMSR_DEBUG1 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ************************************************************************** ************************************************************************** */ #if __FreeBSD_version >= 500005 #include #include #include #include #include #define ARCMSR_LOCK_INIT(l, s) mtx_init(l, s, NULL, MTX_DEF) #define ARCMSR_LOCK_DESTROY(l) mtx_destroy(l) #define ARCMSR_LOCK_ACQUIRE(l) mtx_lock(l) #define ARCMSR_LOCK_RELEASE(l) mtx_unlock(l) #define ARCMSR_LOCK_TRY(l) mtx_trylock(l) #define arcmsr_htole32(x) htole32(x) typedef struct mtx arcmsr_lock_t; #else #include #include #include #define ARCMSR_LOCK_INIT(l, s) simple_lock_init(l) #define ARCMSR_LOCK_DESTROY(l) #define ARCMSR_LOCK_ACQUIRE(l) simple_lock(l) #define ARCMSR_LOCK_RELEASE(l) simple_unlock(l) #define ARCMSR_LOCK_TRY(l) simple_lock_try(l) #define arcmsr_htole32(x) (x) typedef struct simplelock arcmsr_lock_t; #endif #if !defined(CAM_NEW_TRAN_CODE) && __FreeBSD_version >= 700025 #define CAM_NEW_TRAN_CODE 1 #endif #if __FreeBSD_version > 500000 #define arcmsr_callout_init(a) callout_init(a, /*mpsafe*/1); #else #define arcmsr_callout_init(a) callout_init(a); #endif #define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.22 2011-07-04" #include #define SRB_SIZE ((sizeof(struct CommandControlBlock)+0x1f) & 0xffe0) #define ARCMSR_SRBS_POOL_SIZE (SRB_SIZE * ARCMSR_MAX_FREESRB_NUM) /* ************************************************************************** ************************************************************************** */ #define CHIP_REG_READ32(s, b, r) bus_space_read_4(acb->btag[b], acb->bhandle[b], offsetof(struct s, r)) #define CHIP_REG_WRITE32(s, b, r, d) bus_space_write_4(acb->btag[b], acb->bhandle[b], offsetof(struct s, r), d) /* ************************************************************************** ************************************************************************** */ static void arcmsr_free_srb(struct CommandControlBlock *srb); static struct CommandControlBlock * arcmsr_get_freesrb(struct AdapterControlBlock *acb); static u_int8_t arcmsr_seek_cmd2abort(union ccb * abortccb); static int arcmsr_probe(device_t dev); static int arcmsr_attach(device_t dev); static int arcmsr_detach(device_t dev); static u_int32_t arcmsr_iop_ioctlcmd(struct AdapterControlBlock *acb, u_int32_t ioctl_cmd, caddr_t arg); static void arcmsr_iop_parking(struct AdapterControlBlock *acb); static int arcmsr_shutdown(device_t dev); static void arcmsr_interrupt(struct AdapterControlBlock *acb); static void arcmsr_polling_srbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_srb); static void arcmsr_free_resource(struct AdapterControlBlock *acb); static void arcmsr_bus_reset(struct AdapterControlBlock *acb); static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb); static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb); static void arcmsr_iop_init(struct AdapterControlBlock *acb); static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb); static void arcmsr_post_ioctldata2iop(struct AdapterControlBlock *acb); static void arcmsr_abort_allcmd(struct AdapterControlBlock *acb); static void arcmsr_srb_complete(struct CommandControlBlock *srb, int stand_flag); static void arcmsr_iop_reset(struct AdapterControlBlock *acb); static void arcmsr_report_sense_info(struct CommandControlBlock *srb); static void arcmsr_build_srb(struct CommandControlBlock *srb, bus_dma_segment_t * dm_segs, u_int32_t nseg); static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, union ccb * pccb); static int arcmsr_resume(device_t dev); static int arcmsr_suspend(device_t dev); static void arcmsr_rescanLun_cb(struct cam_periph *periph, union ccb *ccb); static void arcmsr_polling_devmap(void* arg); static void arcmsr_srb_timeout(void* arg); #ifdef ARCMSR_DEBUG1 static void arcmsr_dump_data(struct AdapterControlBlock *acb); #endif /* ************************************************************************** ************************************************************************** */ static void UDELAY(u_int32_t us) { DELAY(us); } /* ************************************************************************** ************************************************************************** */ static bus_dmamap_callback_t arcmsr_map_free_srb; static bus_dmamap_callback_t arcmsr_execute_srb; /* ************************************************************************** ************************************************************************** */ static d_open_t arcmsr_open; static d_close_t arcmsr_close; static d_ioctl_t arcmsr_ioctl; static device_method_t arcmsr_methods[]={ DEVMETHOD(device_probe, arcmsr_probe), DEVMETHOD(device_attach, arcmsr_attach), DEVMETHOD(device_detach, arcmsr_detach), DEVMETHOD(device_shutdown, arcmsr_shutdown), DEVMETHOD(device_suspend, arcmsr_suspend), DEVMETHOD(device_resume, arcmsr_resume), DEVMETHOD_END }; static driver_t arcmsr_driver={ "arcmsr", arcmsr_methods, sizeof(struct AdapterControlBlock) }; static devclass_t arcmsr_devclass; DRIVER_MODULE(arcmsr, pci, arcmsr_driver, arcmsr_devclass, 0, 0); MODULE_DEPEND(arcmsr, pci, 1, 1, 1); MODULE_DEPEND(arcmsr, cam, 1, 1, 1); #ifndef BUS_DMA_COHERENT #define BUS_DMA_COHERENT 0x04 /* hint: map memory in a coherent way */ #endif #if __FreeBSD_version >= 501000 static struct cdevsw arcmsr_cdevsw={ #if __FreeBSD_version >= 503000 .d_version = D_VERSION, #endif #if (__FreeBSD_version>=503000 && __FreeBSD_version<600034) .d_flags = D_NEEDGIANT, #endif .d_open = arcmsr_open, /* open */ .d_close = arcmsr_close, /* close */ .d_ioctl = arcmsr_ioctl, /* ioctl */ .d_name = "arcmsr", /* name */ }; #else #define ARCMSR_CDEV_MAJOR 180 static struct cdevsw arcmsr_cdevsw = { arcmsr_open, /* open */ arcmsr_close, /* close */ noread, /* read */ nowrite, /* write */ arcmsr_ioctl, /* ioctl */ nopoll, /* poll */ nommap, /* mmap */ nostrategy, /* strategy */ "arcmsr", /* name */ ARCMSR_CDEV_MAJOR, /* major */ nodump, /* dump */ nopsize, /* psize */ 0 /* flags */ }; #endif /* ************************************************************************** ************************************************************************** */ #if __FreeBSD_version < 500005 static int arcmsr_open(dev_t dev, int flags, int fmt, struct proc *proc) #else #if __FreeBSD_version < 503000 static int arcmsr_open(dev_t dev, int flags, int fmt, struct thread *proc) #else static int arcmsr_open(struct cdev *dev, int flags, int fmt, struct thread *proc) #endif #endif { #if __FreeBSD_version < 503000 struct AdapterControlBlock *acb=dev->si_drv1; #else int unit = dev2unit(dev); struct AdapterControlBlock *acb = devclass_get_softc(arcmsr_devclass, unit); #endif if(acb==NULL) { return ENXIO; } return 0; } /* ************************************************************************** ************************************************************************** */ #if __FreeBSD_version < 500005 static int arcmsr_close(dev_t dev, int flags, int fmt, struct proc *proc) #else #if __FreeBSD_version < 503000 static int arcmsr_close(dev_t dev, int flags, int fmt, struct thread *proc) #else static int arcmsr_close(struct cdev *dev, int flags, int fmt, struct thread *proc) #endif #endif { #if __FreeBSD_version < 503000 struct AdapterControlBlock *acb=dev->si_drv1; #else int unit = dev2unit(dev); struct AdapterControlBlock *acb = devclass_get_softc(arcmsr_devclass, unit); #endif if(acb==NULL) { return ENXIO; } return 0; } /* ************************************************************************** ************************************************************************** */ #if __FreeBSD_version < 500005 static int arcmsr_ioctl(dev_t dev, u_long ioctl_cmd, caddr_t arg, int flags, struct proc *proc) #else #if __FreeBSD_version < 503000 static int arcmsr_ioctl(dev_t dev, u_long ioctl_cmd, caddr_t arg, int flags, struct thread *proc) #else static int arcmsr_ioctl(struct cdev *dev, u_long ioctl_cmd, caddr_t arg, int flags, struct thread *proc) #endif #endif { #if __FreeBSD_version < 503000 struct AdapterControlBlock *acb=dev->si_drv1; #else int unit = dev2unit(dev); struct AdapterControlBlock *acb = devclass_get_softc(arcmsr_devclass, unit); #endif if(acb==NULL) { return ENXIO; } return(arcmsr_iop_ioctlcmd(acb, ioctl_cmd, arg)); } /* ********************************************************************** ********************************************************************** */ static u_int32_t arcmsr_disable_allintr( struct AdapterControlBlock *acb) { u_int32_t intmask_org=0; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { /* disable all outbound interrupt */ intmask_org=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_intmask); /* disable outbound message0 int */ CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intmask, intmask_org|ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE); } break; case ACB_ADAPTER_TYPE_B: { /* disable all outbound interrupt */ intmask_org=CHIP_REG_READ32(HBB_DOORBELL, 0, iop2drv_doorbell_mask) & (~ARCMSR_IOP2DRV_MESSAGE_CMD_DONE); /* disable outbound message0 int */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell_mask, 0); /* disable all interrupt */ } break; case ACB_ADAPTER_TYPE_C: { /* disable all outbound interrupt */ intmask_org=CHIP_REG_READ32(HBC_MessageUnit, 0, host_int_mask) ; /* disable outbound message0 int */ CHIP_REG_WRITE32(HBC_MessageUnit, 0, host_int_mask, intmask_org|ARCMSR_HBCMU_ALL_INTMASKENABLE); } break; } return(intmask_org); } /* ********************************************************************** ********************************************************************** */ static void arcmsr_enable_allintr( struct AdapterControlBlock *acb, u_int32_t intmask_org) { u_int32_t mask; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { /* enable outbound Post Queue, outbound doorbell Interrupt */ mask=~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE|ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE|ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE); CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intmask, intmask_org & mask); acb->outbound_int_enable = ~(intmask_org & mask) & 0x000000ff; } break; case ACB_ADAPTER_TYPE_B: { /* enable ARCMSR_IOP2DRV_MESSAGE_CMD_DONE */ mask=(ARCMSR_IOP2DRV_DATA_WRITE_OK|ARCMSR_IOP2DRV_DATA_READ_OK|ARCMSR_IOP2DRV_CDB_DONE|ARCMSR_IOP2DRV_MESSAGE_CMD_DONE); CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell_mask, intmask_org | mask); /*1=interrupt enable, 0=interrupt disable*/ acb->outbound_int_enable = (intmask_org | mask) & 0x0000000f; } break; case ACB_ADAPTER_TYPE_C: { /* enable outbound Post Queue, outbound doorbell Interrupt */ mask=~(ARCMSR_HBCMU_UTILITY_A_ISR_MASK | ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR_MASK | ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR_MASK); CHIP_REG_WRITE32(HBC_MessageUnit, 0, host_int_mask, intmask_org & mask); acb->outbound_int_enable= ~(intmask_org & mask) & 0x0000000f; } break; } return; } /* ********************************************************************** ********************************************************************** */ static u_int8_t arcmsr_hba_wait_msgint_ready(struct AdapterControlBlock *acb) { u_int32_t Index; u_int8_t Retries=0x00; do { for(Index=0; Index < 100; Index++) { if(CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_intstatus) & ARCMSR_MU_OUTBOUND_MESSAGE0_INT) { CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intstatus, ARCMSR_MU_OUTBOUND_MESSAGE0_INT);/*clear interrupt*/ return TRUE; } UDELAY(10000); }/*max 1 seconds*/ }while(Retries++ < 20);/*max 20 sec*/ return FALSE; } /* ********************************************************************** ********************************************************************** */ static u_int8_t arcmsr_hbb_wait_msgint_ready(struct AdapterControlBlock *acb) { u_int32_t Index; u_int8_t Retries=0x00; do { for(Index=0; Index < 100; Index++) { if(CHIP_REG_READ32(HBB_DOORBELL, 0, iop2drv_doorbell) & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) { CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ARCMSR_MESSAGE_INT_CLEAR_PATTERN);/*clear interrupt*/ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_END_OF_INTERRUPT); return TRUE; } UDELAY(10000); }/*max 1 seconds*/ }while(Retries++ < 20);/*max 20 sec*/ return FALSE; } /* ********************************************************************** ********************************************************************** */ static u_int8_t arcmsr_hbc_wait_msgint_ready(struct AdapterControlBlock *acb) { u_int32_t Index; u_int8_t Retries=0x00; do { for(Index=0; Index < 100; Index++) { if(CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_doorbell) & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) { CHIP_REG_WRITE32(HBC_MessageUnit, 0, outbound_doorbell_clear, ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR);/*clear interrupt*/ return TRUE; } UDELAY(10000); }/*max 1 seconds*/ }while(Retries++ < 20);/*max 20 sec*/ return FALSE; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb) { int retry_count=30;/* enlarge wait flush adapter cache time: 10 minute */ CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_FLUSH_CACHE); do { if(arcmsr_hba_wait_msgint_ready(acb)) { break; } else { retry_count--; } }while(retry_count!=0); return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb) { int retry_count=30;/* enlarge wait flush adapter cache time: 10 minute */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_FLUSH_CACHE); do { if(arcmsr_hbb_wait_msgint_ready(acb)) { break; } else { retry_count--; } }while(retry_count!=0); return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_flush_hbc_cache(struct AdapterControlBlock *acb) { int retry_count=30;/* enlarge wait flush adapter cache time: 10 minute */ CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_FLUSH_CACHE); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); do { if(arcmsr_hbc_wait_msgint_ready(acb)) { break; } else { retry_count--; } }while(retry_count!=0); return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { arcmsr_flush_hba_cache(acb); } break; case ACB_ADAPTER_TYPE_B: { arcmsr_flush_hbb_cache(acb); } break; case ACB_ADAPTER_TYPE_C: { arcmsr_flush_hbc_cache(acb); } break; } return; } /* ******************************************************************************* ******************************************************************************* */ static int arcmsr_suspend(device_t dev) { struct AdapterControlBlock *acb = device_get_softc(dev); /* flush controller */ arcmsr_iop_parking(acb); /* disable all outbound interrupt */ arcmsr_disable_allintr(acb); return(0); } /* ******************************************************************************* ******************************************************************************* */ static int arcmsr_resume(device_t dev) { struct AdapterControlBlock *acb = device_get_softc(dev); arcmsr_iop_init(acb); return(0); } /* ********************************************************************************* ********************************************************************************* */ static void arcmsr_async(void *cb_arg, u_int32_t code, struct cam_path *path, void *arg) { struct AdapterControlBlock *acb; u_int8_t target_id, target_lun; struct cam_sim * sim; sim=(struct cam_sim *) cb_arg; acb =(struct AdapterControlBlock *) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: target_id=xpt_path_target_id(path); target_lun=xpt_path_lun_id(path); if((target_id > ARCMSR_MAX_TARGETID) || (target_lun > ARCMSR_MAX_TARGETLUN)) { break; } printf("%s:scsi id=%d lun=%d device lost \n", device_get_name(acb->pci_dev), target_id, target_lun); break; default: break; } } /* ********************************************************************** ********************************************************************** */ static void arcmsr_report_sense_info(struct CommandControlBlock *srb) { union ccb * pccb=srb->pccb; pccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; pccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; if(&pccb->csio.sense_data) { memset(&pccb->csio.sense_data, 0, sizeof(pccb->csio.sense_data)); memcpy(&pccb->csio.sense_data, srb->arcmsr_cdb.SenseData, get_min(sizeof(struct SENSE_DATA), sizeof(pccb->csio.sense_data))); ((u_int8_t *)&pccb->csio.sense_data)[0] = (0x1 << 7 | 0x70); /* Valid,ErrorCode */ pccb->ccb_h.status |= CAM_AUTOSNS_VALID; } return; } /* ********************************************************************* ********************************************************************* */ static void arcmsr_abort_hba_allcmd(struct AdapterControlBlock *acb) { CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_ABORT_CMD); if(!arcmsr_hba_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'abort all outstanding command' timeout \n", acb->pci_unit); } return; } /* ********************************************************************* ********************************************************************* */ static void arcmsr_abort_hbb_allcmd(struct AdapterControlBlock *acb) { CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_ABORT_CMD); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'abort all outstanding command' timeout \n", acb->pci_unit); } return; } /* ********************************************************************* ********************************************************************* */ static void arcmsr_abort_hbc_allcmd(struct AdapterControlBlock *acb) { CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_ABORT_CMD); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); if(!arcmsr_hbc_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'abort all outstanding command' timeout \n", acb->pci_unit); } return; } /* ********************************************************************* ********************************************************************* */ static void arcmsr_abort_allcmd(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { arcmsr_abort_hba_allcmd(acb); } break; case ACB_ADAPTER_TYPE_B: { arcmsr_abort_hbb_allcmd(acb); } break; case ACB_ADAPTER_TYPE_C: { arcmsr_abort_hbc_allcmd(acb); } break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_srb_complete(struct CommandControlBlock *srb, int stand_flag) { struct AdapterControlBlock *acb=srb->acb; union ccb * pccb=srb->pccb; if(srb->srb_flags & SRB_FLAG_TIMER_START) callout_stop(&srb->ccb_callout); if((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { op = BUS_DMASYNC_POSTREAD; } else { op = BUS_DMASYNC_POSTWRITE; } bus_dmamap_sync(acb->dm_segs_dmat, srb->dm_segs_dmamap, op); bus_dmamap_unload(acb->dm_segs_dmat, srb->dm_segs_dmamap); } if(stand_flag==1) { atomic_subtract_int(&acb->srboutstandingcount, 1); if((acb->acb_flags & ACB_F_CAM_DEV_QFRZN) && ( acb->srboutstandingcount < ARCMSR_RELEASE_SIMQ_LEVEL)) { acb->acb_flags &= ~ACB_F_CAM_DEV_QFRZN; pccb->ccb_h.status |= CAM_RELEASE_SIMQ; } } if(srb->srb_state != ARCMSR_SRB_TIMEOUT) arcmsr_free_srb(srb); #ifdef ARCMSR_DEBUG1 acb->pktReturnCount++; #endif xpt_done(pccb); return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_report_srb_state(struct AdapterControlBlock *acb, struct CommandControlBlock *srb, u_int16_t error) { int target, lun; target=srb->pccb->ccb_h.target_id; lun=srb->pccb->ccb_h.target_lun; if(error == FALSE) { if(acb->devstate[target][lun]==ARECA_RAID_GONE) { acb->devstate[target][lun]=ARECA_RAID_GOOD; } srb->pccb->ccb_h.status |= CAM_REQ_CMP; arcmsr_srb_complete(srb, 1); } else { switch(srb->arcmsr_cdb.DeviceStatus) { case ARCMSR_DEV_SELECT_TIMEOUT: { if(acb->devstate[target][lun]==ARECA_RAID_GOOD) { printf( "arcmsr%d: Target=%x, Lun=%x, selection timeout, raid volume was lost\n", acb->pci_unit, target, lun); } acb->devstate[target][lun]=ARECA_RAID_GONE; srb->pccb->ccb_h.status |= CAM_DEV_NOT_THERE; arcmsr_srb_complete(srb, 1); } break; case ARCMSR_DEV_ABORTED: case ARCMSR_DEV_INIT_FAIL: { acb->devstate[target][lun]=ARECA_RAID_GONE; srb->pccb->ccb_h.status |= CAM_DEV_NOT_THERE; arcmsr_srb_complete(srb, 1); } break; case SCSISTAT_CHECK_CONDITION: { acb->devstate[target][lun]=ARECA_RAID_GOOD; arcmsr_report_sense_info(srb); arcmsr_srb_complete(srb, 1); } break; default: printf("arcmsr%d: scsi id=%d lun=%d isr got command error done,but got unknow DeviceStatus=0x%x \n" , acb->pci_unit, target, lun ,srb->arcmsr_cdb.DeviceStatus); acb->devstate[target][lun]=ARECA_RAID_GONE; srb->pccb->ccb_h.status |= CAM_UNCOR_PARITY; /*unknow error or crc error just for retry*/ arcmsr_srb_complete(srb, 1); break; } } return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, u_int32_t flag_srb, u_int16_t error) { struct CommandControlBlock *srb; /* check if command done with no error*/ switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_C: srb = (struct CommandControlBlock *)(acb->vir2phy_offset+(flag_srb & 0xFFFFFFE0));/*frame must be 32 bytes aligned*/ break; case ACB_ADAPTER_TYPE_A: case ACB_ADAPTER_TYPE_B: default: srb = (struct CommandControlBlock *)(acb->vir2phy_offset+(flag_srb << 5));/*frame must be 32 bytes aligned*/ break; } if((srb->acb!=acb) || (srb->srb_state!=ARCMSR_SRB_START)) { if(srb->srb_state == ARCMSR_SRB_TIMEOUT) { arcmsr_free_srb(srb); printf("arcmsr%d: srb='%p' return srb has been timeouted\n", acb->pci_unit, srb); return; } printf("arcmsr%d: return srb has been completed\n" "srb='%p' srb_state=0x%x outstanding srb count=%d \n", acb->pci_unit, srb, srb->srb_state, acb->srboutstandingcount); return; } arcmsr_report_srb_state(acb, srb, error); return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_srb_timeout(void* arg) { struct CommandControlBlock *srb = (struct CommandControlBlock *)arg; struct AdapterControlBlock *acb; int target, lun; u_int8_t cmd; target=srb->pccb->ccb_h.target_id; lun=srb->pccb->ccb_h.target_lun; acb = srb->acb; ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); if(srb->srb_state == ARCMSR_SRB_START) { cmd = srb->pccb->csio.cdb_io.cdb_bytes[0]; srb->srb_state = ARCMSR_SRB_TIMEOUT; srb->pccb->ccb_h.status |= CAM_CMD_TIMEOUT; arcmsr_srb_complete(srb, 1); printf("arcmsr%d: scsi id %d lun %d cmd=0x%x srb='%p' ccb command time out!\n", acb->pci_unit, target, lun, cmd, srb); } ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); #ifdef ARCMSR_DEBUG1 arcmsr_dump_data(acb); #endif } /* ********************************************************************** ********************************************************************** */ static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb) { int i=0; u_int32_t flag_srb; u_int16_t error; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { u_int32_t outbound_intstatus; /*clear and abort all outbound posted Q*/ outbound_intstatus=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_intstatus) & acb->outbound_int_enable; CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intstatus, outbound_intstatus);/*clear interrupt*/ while(((flag_srb=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_queueport)) != 0xFFFFFFFF) && (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) { error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); } } break; case ACB_ADAPTER_TYPE_B: { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; /*clear all outbound posted Q*/ CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ARCMSR_DOORBELL_INT_CLEAR_PATTERN); /* clear doorbell interrupt */ for(i=0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) { if((flag_srb=phbbmu->done_qbuffer[i])!=0) { phbbmu->done_qbuffer[i]=0; error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); } phbbmu->post_qbuffer[i]=0; }/*drain reply FIFO*/ phbbmu->doneq_index=0; phbbmu->postq_index=0; } break; case ACB_ADAPTER_TYPE_C: { while((CHIP_REG_READ32(HBC_MessageUnit, 0, host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) && (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) { flag_srb=CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_queueport_low); error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE1)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); } } break; } return; } /* **************************************************************************** **************************************************************************** */ static void arcmsr_iop_reset(struct AdapterControlBlock *acb) { struct CommandControlBlock *srb; u_int32_t intmask_org; u_int32_t i=0; if(acb->srboutstandingcount>0) { /* disable all outbound interrupt */ intmask_org=arcmsr_disable_allintr(acb); /*clear and abort all outbound posted Q*/ arcmsr_done4abort_postqueue(acb); /* talk to iop 331 outstanding command aborted*/ arcmsr_abort_allcmd(acb); for(i=0;ipsrb_pool[i]; if(srb->srb_state==ARCMSR_SRB_START) { srb->srb_state=ARCMSR_SRB_ABORTED; srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); printf("arcmsr%d: scsi id=%d lun=%d srb='%p' aborted\n" , acb->pci_unit, srb->pccb->ccb_h.target_id , srb->pccb->ccb_h.target_lun, srb); } } /* enable all outbound interrupt */ arcmsr_enable_allintr(acb, intmask_org); } acb->srboutstandingcount=0; acb->workingsrb_doneindex=0; acb->workingsrb_startindex=0; #ifdef ARCMSR_DEBUG1 acb->pktRequestCount = 0; acb->pktReturnCount = 0; #endif return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_build_srb(struct CommandControlBlock *srb, bus_dma_segment_t *dm_segs, u_int32_t nseg) { struct ARCMSR_CDB * arcmsr_cdb= &srb->arcmsr_cdb; u_int8_t * psge=(u_int8_t *)&arcmsr_cdb->u; u_int32_t address_lo, address_hi; union ccb * pccb=srb->pccb; struct ccb_scsiio * pcsio= &pccb->csio; u_int32_t arccdbsize=0x30; memset(arcmsr_cdb, 0, sizeof(struct ARCMSR_CDB)); arcmsr_cdb->Bus=0; arcmsr_cdb->TargetID=pccb->ccb_h.target_id; arcmsr_cdb->LUN=pccb->ccb_h.target_lun; arcmsr_cdb->Function=1; arcmsr_cdb->CdbLength=(u_int8_t)pcsio->cdb_len; arcmsr_cdb->Context=0; bcopy(pcsio->cdb_io.cdb_bytes, arcmsr_cdb->Cdb, pcsio->cdb_len); if(nseg != 0) { struct AdapterControlBlock *acb=srb->acb; bus_dmasync_op_t op; u_int32_t length, i, cdb_sgcount=0; if((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { op=BUS_DMASYNC_PREREAD; } else { op=BUS_DMASYNC_PREWRITE; arcmsr_cdb->Flags|=ARCMSR_CDB_FLAG_WRITE; srb->srb_flags|=SRB_FLAG_WRITE; } bus_dmamap_sync(acb->dm_segs_dmat, srb->dm_segs_dmamap, op); for(i=0;iaddress=address_lo; pdma_sg->length=length; psge += sizeof(struct SG32ENTRY); arccdbsize += sizeof(struct SG32ENTRY); } else { u_int32_t sg64s_size=0, tmplength=length; while(1) { u_int64_t span4G, length0; struct SG64ENTRY * pdma_sg=(struct SG64ENTRY *)psge; span4G=(u_int64_t)address_lo + tmplength; pdma_sg->addresshigh=address_hi; pdma_sg->address=address_lo; if(span4G > 0x100000000) { /*see if cross 4G boundary*/ length0=0x100000000-address_lo; pdma_sg->length=(u_int32_t)length0|IS_SG64_ADDR; address_hi=address_hi+1; address_lo=0; tmplength=tmplength-(u_int32_t)length0; sg64s_size += sizeof(struct SG64ENTRY); psge += sizeof(struct SG64ENTRY); cdb_sgcount++; } else { pdma_sg->length=tmplength|IS_SG64_ADDR; sg64s_size += sizeof(struct SG64ENTRY); psge += sizeof(struct SG64ENTRY); break; } } arccdbsize += sg64s_size; } cdb_sgcount++; } arcmsr_cdb->sgcount=(u_int8_t)cdb_sgcount; arcmsr_cdb->DataLength=pcsio->dxfer_len; if( arccdbsize > 256) { arcmsr_cdb->Flags|=ARCMSR_CDB_FLAG_SGL_BSIZE; } } else { arcmsr_cdb->DataLength = 0; } srb->arc_cdb_size=arccdbsize; return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_post_srb(struct AdapterControlBlock *acb, struct CommandControlBlock *srb) { u_int32_t cdb_shifted_phyaddr=(u_int32_t) srb->cdb_shifted_phyaddr; struct ARCMSR_CDB * arcmsr_cdb=(struct ARCMSR_CDB *)&srb->arcmsr_cdb; bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, (srb->srb_flags & SRB_FLAG_WRITE) ? BUS_DMASYNC_POSTWRITE:BUS_DMASYNC_POSTREAD); atomic_add_int(&acb->srboutstandingcount, 1); srb->srb_state=ARCMSR_SRB_START; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { if(arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) { CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_queueport, cdb_shifted_phyaddr|ARCMSR_SRBPOST_FLAG_SGL_BSIZE); } else { CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_queueport, cdb_shifted_phyaddr); } } break; case ACB_ADAPTER_TYPE_B: { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; int ending_index, index; index=phbbmu->postq_index; ending_index=((index+1)%ARCMSR_MAX_HBB_POSTQUEUE); phbbmu->post_qbuffer[ending_index]=0; if(arcmsr_cdb->Flags & ARCMSR_CDB_FLAG_SGL_BSIZE) { phbbmu->post_qbuffer[index]= cdb_shifted_phyaddr|ARCMSR_SRBPOST_FLAG_SGL_BSIZE; } else { phbbmu->post_qbuffer[index]= cdb_shifted_phyaddr; } index++; index %= ARCMSR_MAX_HBB_POSTQUEUE; /*if last index number set it to 0 */ phbbmu->postq_index=index; CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_CDB_POSTED); } break; case ACB_ADAPTER_TYPE_C: { u_int32_t ccb_post_stamp, arc_cdb_size, cdb_phyaddr_hi32; arc_cdb_size=(srb->arc_cdb_size>0x300)?0x300:srb->arc_cdb_size; ccb_post_stamp=(cdb_shifted_phyaddr | ((arc_cdb_size-1) >> 6) | 1); cdb_phyaddr_hi32 = acb->srb_phyaddr.B.phyadd_high; if(cdb_phyaddr_hi32) { CHIP_REG_WRITE32(HBC_MessageUnit,0,inbound_queueport_high, cdb_phyaddr_hi32); CHIP_REG_WRITE32(HBC_MessageUnit,0,inbound_queueport_low, ccb_post_stamp); } else { CHIP_REG_WRITE32(HBC_MessageUnit,0,inbound_queueport_low, ccb_post_stamp); } } break; } return; } /* ************************************************************************ ************************************************************************ */ static struct QBUFFER * arcmsr_get_iop_rqbuffer( struct AdapterControlBlock *acb) { struct QBUFFER *qbuffer=NULL; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { struct HBA_MessageUnit *phbamu=(struct HBA_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbamu->message_rbuffer; } break; case ACB_ADAPTER_TYPE_B: { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbbmu->hbb_rwbuffer->message_rbuffer; } break; case ACB_ADAPTER_TYPE_C: { struct HBC_MessageUnit *phbcmu=(struct HBC_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbcmu->message_rbuffer; } break; } return(qbuffer); } /* ************************************************************************ ************************************************************************ */ static struct QBUFFER * arcmsr_get_iop_wqbuffer( struct AdapterControlBlock *acb) { struct QBUFFER *qbuffer=NULL; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { struct HBA_MessageUnit *phbamu=(struct HBA_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbamu->message_wbuffer; } break; case ACB_ADAPTER_TYPE_B: { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbbmu->hbb_rwbuffer->message_wbuffer; } break; case ACB_ADAPTER_TYPE_C: { struct HBC_MessageUnit *phbcmu=(struct HBC_MessageUnit *)acb->pmu; qbuffer=(struct QBUFFER *)&phbcmu->message_wbuffer; } break; } return(qbuffer); } /* ************************************************************************** ************************************************************************** */ static void arcmsr_iop_message_read(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { /* let IOP know data has been read */ CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_doorbell, ARCMSR_INBOUND_DRIVER_DATA_READ_OK); } break; case ACB_ADAPTER_TYPE_B: { /* let IOP know data has been read */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_DATA_READ_OK); } break; case ACB_ADAPTER_TYPE_C: { /* let IOP know data has been read */ CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK); } } return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_iop_message_wrote(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { /* ** push inbound doorbell tell iop, driver data write ok ** and wait reply on next hwinterrupt for next Qbuffer post */ CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_doorbell, ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK); } break; case ACB_ADAPTER_TYPE_B: { /* ** push inbound doorbell tell iop, driver data write ok ** and wait reply on next hwinterrupt for next Qbuffer post */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_DATA_WRITE_OK); } break; case ACB_ADAPTER_TYPE_C: { /* ** push inbound doorbell tell iop, driver data write ok ** and wait reply on next hwinterrupt for next Qbuffer post */ CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_DATA_WRITE_OK); } break; } } /* ********************************************************************** ********************************************************************** */ static void arcmsr_post_ioctldata2iop(struct AdapterControlBlock *acb) { u_int8_t *pQbuffer; struct QBUFFER *pwbuffer; u_int8_t * iop_data; int32_t allxfer_len=0; pwbuffer=arcmsr_get_iop_wqbuffer(acb); iop_data=(u_int8_t *)pwbuffer->data; if(acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_READ) { acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READ); while((acb->wqbuf_firstindex!=acb->wqbuf_lastindex) && (allxfer_len<124)) { pQbuffer=&acb->wqbuffer[acb->wqbuf_firstindex]; memcpy(iop_data, pQbuffer, 1); acb->wqbuf_firstindex++; acb->wqbuf_firstindex %=ARCMSR_MAX_QBUFFER; /*if last index number set it to 0 */ iop_data++; allxfer_len++; } pwbuffer->data_len=allxfer_len; /* ** push inbound doorbell and wait reply at hwinterrupt routine for next Qbuffer post */ arcmsr_iop_message_wrote(acb); } return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_stop_hba_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags &=~ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_STOP_BGRB); if(!arcmsr_hba_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'stop adapter background rebulid' timeout \n" , acb->pci_unit); } return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_stop_hbb_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags &= ~ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_STOP_BGRB); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: wait 'stop adapter background rebulid' timeout \n" , acb->pci_unit); } return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_stop_hbc_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags &=~ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_STOP_BGRB); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell,ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); if(!arcmsr_hbc_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'stop adapter background rebulid' timeout \n", acb->pci_unit); } return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { arcmsr_stop_hba_bgrb(acb); } break; case ACB_ADAPTER_TYPE_B: { arcmsr_stop_hbb_bgrb(acb); } break; case ACB_ADAPTER_TYPE_C: { arcmsr_stop_hbc_bgrb(acb); } break; } return; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_poll(struct cam_sim * psim) { struct AdapterControlBlock *acb; int mutex; acb = (struct AdapterControlBlock *)cam_sim_softc(psim); mutex = mtx_owned(&acb->qbuffer_lock); if( mutex == 0 ) ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); arcmsr_interrupt(acb); if( mutex == 0 ) ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_iop2drv_data_wrote_handle(struct AdapterControlBlock *acb) { struct QBUFFER *prbuffer; u_int8_t *pQbuffer; u_int8_t *iop_data; int my_empty_len, iop_len, rqbuf_firstindex, rqbuf_lastindex; /*check this iop data if overflow my rqbuffer*/ rqbuf_lastindex=acb->rqbuf_lastindex; rqbuf_firstindex=acb->rqbuf_firstindex; prbuffer=arcmsr_get_iop_rqbuffer(acb); iop_data=(u_int8_t *)prbuffer->data; iop_len=prbuffer->data_len; my_empty_len=(rqbuf_firstindex-rqbuf_lastindex-1)&(ARCMSR_MAX_QBUFFER-1); if(my_empty_len>=iop_len) { while(iop_len > 0) { pQbuffer=&acb->rqbuffer[rqbuf_lastindex]; memcpy(pQbuffer, iop_data, 1); rqbuf_lastindex++; rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;/*if last index number set it to 0 */ iop_data++; iop_len--; } acb->rqbuf_lastindex=rqbuf_lastindex; arcmsr_iop_message_read(acb); /*signature, let IOP know data has been read */ } else { acb->acb_flags|=ACB_F_IOPDATA_OVERFLOW; } return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_iop2drv_data_read_handle(struct AdapterControlBlock *acb) { acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_READ; /* ***************************************************************** ** check if there are any mail packages from user space program ** in my post bag, now is the time to send them into Areca's firmware ***************************************************************** */ if(acb->wqbuf_firstindex!=acb->wqbuf_lastindex) { u_int8_t *pQbuffer; struct QBUFFER *pwbuffer; u_int8_t *iop_data; int allxfer_len=0; acb->acb_flags &= (~ACB_F_MESSAGE_WQBUFFER_READ); pwbuffer=arcmsr_get_iop_wqbuffer(acb); iop_data=(u_int8_t *)pwbuffer->data; while((acb->wqbuf_firstindex!=acb->wqbuf_lastindex) && (allxfer_len<124)) { pQbuffer=&acb->wqbuffer[acb->wqbuf_firstindex]; memcpy(iop_data, pQbuffer, 1); acb->wqbuf_firstindex++; acb->wqbuf_firstindex %=ARCMSR_MAX_QBUFFER; /*if last index number set it to 0 */ iop_data++; allxfer_len++; } pwbuffer->data_len=allxfer_len; /* ** push inbound doorbell tell iop driver data write ok ** and wait reply on next hwinterrupt for next Qbuffer post */ arcmsr_iop_message_wrote(acb); } if(acb->wqbuf_firstindex==acb->wqbuf_lastindex) { acb->acb_flags |= ACB_F_MESSAGE_WQBUFFER_CLEARED; } return; } static void arcmsr_rescanLun_cb(struct cam_periph *periph, union ccb *ccb) { /* if (ccb->ccb_h.status != CAM_REQ_CMP) printf("arcmsr_rescanLun_cb: Rescan Target=%x, lun=%x, failure status=%x\n",ccb->ccb_h.target_id,ccb->ccb_h.target_lun,ccb->ccb_h.status); else printf("arcmsr_rescanLun_cb: Rescan lun successfully!\n"); */ xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); } static void arcmsr_rescan_lun(struct AdapterControlBlock *acb, int target, int lun) { struct cam_path *path; union ccb *ccb; if ((ccb = (union ccb *)xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&path, xpt_periph, cam_sim_path(acb->psim), target, lun) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } /* printf("arcmsr_rescan_lun: Rescan Target=%x, Lun=%x\n", target, lun); */ bzero(ccb, sizeof(union ccb)); xpt_setup_ccb(&ccb->ccb_h, path, 5); ccb->ccb_h.func_code = XPT_SCAN_LUN; ccb->ccb_h.cbfcnp = arcmsr_rescanLun_cb; ccb->crcn.flags = CAM_FLAG_NONE; xpt_action(ccb); return; } static void arcmsr_abort_dr_ccbs(struct AdapterControlBlock *acb, int target, int lun) { struct CommandControlBlock *srb; u_int32_t intmask_org; int i; ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); /* disable all outbound interrupts */ intmask_org = arcmsr_disable_allintr(acb); for (i = 0; i < ARCMSR_MAX_FREESRB_NUM; i++) { srb = acb->psrb_pool[i]; if (srb->srb_state == ARCMSR_SRB_START) { if((target == srb->pccb->ccb_h.target_id) && (lun == srb->pccb->ccb_h.target_lun)) { srb->srb_state = ARCMSR_SRB_ABORTED; srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); printf("arcmsr%d: abort scsi id %d lun %d srb=%p \n", acb->pci_unit, target, lun, srb); } } } /* enable outbound Post Queue, outbound doorbell Interrupt */ arcmsr_enable_allintr(acb, intmask_org); ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); } /* ************************************************************************** ************************************************************************** */ static void arcmsr_dr_handle(struct AdapterControlBlock *acb) { u_int32_t devicemap; u_int32_t target, lun; u_int32_t deviceMapCurrent[4]={0}; u_int8_t *pDevMap; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: devicemap = offsetof(struct HBA_MessageUnit, msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); for (target= 0; target < 4; target++) { deviceMapCurrent[target]=bus_space_read_4(acb->btag[0], acb->bhandle[0], devicemap); devicemap += 4; } break; case ACB_ADAPTER_TYPE_B: devicemap = offsetof(struct HBB_RWBUFFER, msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); for (target= 0; target < 4; target++) { deviceMapCurrent[target]=bus_space_read_4(acb->btag[1], acb->bhandle[1], devicemap); devicemap += 4; } break; case ACB_ADAPTER_TYPE_C: devicemap = offsetof(struct HBC_MessageUnit, msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); for (target= 0; target < 4; target++) { deviceMapCurrent[target]=bus_space_read_4(acb->btag[0], acb->bhandle[0], devicemap); devicemap += 4; } break; } if(acb->acb_flags & ACB_F_BUS_HANG_ON) { acb->acb_flags &= ~ACB_F_BUS_HANG_ON; } /* ** adapter posted CONFIG message ** copy the new map, note if there are differences with the current map */ pDevMap = (u_int8_t *)&deviceMapCurrent[0]; for (target= 0; target < ARCMSR_MAX_TARGETID - 1; target++) { if (*pDevMap != acb->device_map[target]) { u_int8_t difference, bit_check; difference= *pDevMap ^ acb->device_map[target]; for(lun=0; lun < ARCMSR_MAX_TARGETLUN; lun++) { bit_check=(1 << lun); /*check bit from 0....31*/ if(difference & bit_check) { if(acb->device_map[target] & bit_check) {/* unit departed */ printf("arcmsr_dr_handle: Target=%x, lun=%x, GONE!!!\n",target,lun); arcmsr_abort_dr_ccbs(acb, target, lun); arcmsr_rescan_lun(acb, target, lun); acb->devstate[target][lun] = ARECA_RAID_GONE; } else {/* unit arrived */ printf("arcmsr_dr_handle: Target=%x, lun=%x, Plug-IN!!!\n",target,lun); arcmsr_rescan_lun(acb, target, lun); acb->devstate[target][lun] = ARECA_RAID_GOOD; } } } /* printf("arcmsr_dr_handle: acb->device_map[%x]=0x%x, deviceMapCurrent[%x]=%x\n",target,acb->device_map[target],target,*pDevMap); */ acb->device_map[target]= *pDevMap; } pDevMap++; } } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hba_message_isr(struct AdapterControlBlock *acb) { u_int32_t outbound_message; CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intstatus, ARCMSR_MU_OUTBOUND_MESSAGE0_INT); outbound_message = CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[0]); if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG) arcmsr_dr_handle( acb ); } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hbb_message_isr(struct AdapterControlBlock *acb) { u_int32_t outbound_message; /* clear interrupts */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ARCMSR_MESSAGE_INT_CLEAR_PATTERN); outbound_message = CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[0]); if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG) arcmsr_dr_handle( acb ); } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hbc_message_isr(struct AdapterControlBlock *acb) { u_int32_t outbound_message; CHIP_REG_WRITE32(HBC_MessageUnit, 0, outbound_doorbell_clear, ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR); outbound_message = CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[0]); if (outbound_message == ARCMSR_SIGNATURE_GET_CONFIG) arcmsr_dr_handle( acb ); } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hba_doorbell_isr(struct AdapterControlBlock *acb) { u_int32_t outbound_doorbell; /* ******************************************************************* ** Maybe here we need to check wrqbuffer_lock is lock or not ** DOORBELL: din! don! ** check if there are any mail need to pack from firmware ******************************************************************* */ outbound_doorbell=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_doorbell); CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_doorbell, outbound_doorbell); /* clear doorbell interrupt */ if(outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK) { arcmsr_iop2drv_data_wrote_handle(acb); } if(outbound_doorbell & ARCMSR_OUTBOUND_IOP331_DATA_READ_OK) { arcmsr_iop2drv_data_read_handle(acb); } return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hbc_doorbell_isr(struct AdapterControlBlock *acb) { u_int32_t outbound_doorbell; /* ******************************************************************* ** Maybe here we need to check wrqbuffer_lock is lock or not ** DOORBELL: din! don! ** check if there are any mail need to pack from firmware ******************************************************************* */ outbound_doorbell=CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_doorbell); CHIP_REG_WRITE32(HBC_MessageUnit, 0, outbound_doorbell_clear, outbound_doorbell); /* clear doorbell interrupt */ if(outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_WRITE_OK) { arcmsr_iop2drv_data_wrote_handle(acb); } if(outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_DATA_READ_OK) { arcmsr_iop2drv_data_read_handle(acb); } if(outbound_doorbell & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) { arcmsr_hbc_message_isr(acb); /* messenger of "driver to iop commands" */ } return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hba_postqueue_isr(struct AdapterControlBlock *acb) { u_int32_t flag_srb; u_int16_t error; /* ***************************************************************************** ** areca cdb command done ***************************************************************************** */ bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); while((flag_srb=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_queueport)) != 0xFFFFFFFF) { /* check if command done with no error*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); } /*drain reply FIFO*/ return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hbb_postqueue_isr(struct AdapterControlBlock *acb) { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; u_int32_t flag_srb; int index; u_int16_t error; /* ***************************************************************************** ** areca cdb command done ***************************************************************************** */ bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); index=phbbmu->doneq_index; while((flag_srb=phbbmu->done_qbuffer[index]) != 0) { phbbmu->done_qbuffer[index]=0; index++; index %= ARCMSR_MAX_HBB_POSTQUEUE; /*if last index number set it to 0 */ phbbmu->doneq_index=index; /* check if command done with no error*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); } /*drain reply FIFO*/ return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_hbc_postqueue_isr(struct AdapterControlBlock *acb) { u_int32_t flag_srb,throttling=0; u_int16_t error; /* ***************************************************************************** ** areca cdb command done ***************************************************************************** */ bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); while(CHIP_REG_READ32(HBC_MessageUnit, 0, host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) { flag_srb=CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_queueport_low); /* check if command done with no error*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE1)?TRUE:FALSE; arcmsr_drain_donequeue(acb, flag_srb, error); if(throttling==ARCMSR_HBC_ISR_THROTTLING_LEVEL) { CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell,ARCMSR_HBCMU_DRV2IOP_POSTQUEUE_THROTTLING); break; } throttling++; } /*drain reply FIFO*/ return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_handle_hba_isr( struct AdapterControlBlock *acb) { u_int32_t outbound_intstatus; /* ********************************************* ** check outbound intstatus ********************************************* */ outbound_intstatus=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_intstatus) & acb->outbound_int_enable; if(!outbound_intstatus) { /*it must be share irq*/ return; } CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intstatus, outbound_intstatus);/*clear interrupt*/ /* MU doorbell interrupts*/ if(outbound_intstatus & ARCMSR_MU_OUTBOUND_DOORBELL_INT) { arcmsr_hba_doorbell_isr(acb); } /* MU post queue interrupts*/ if(outbound_intstatus & ARCMSR_MU_OUTBOUND_POSTQUEUE_INT) { arcmsr_hba_postqueue_isr(acb); } if(outbound_intstatus & ARCMSR_MU_OUTBOUND_MESSAGE0_INT) { arcmsr_hba_message_isr(acb); } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_handle_hbb_isr( struct AdapterControlBlock *acb) { u_int32_t outbound_doorbell; /* ********************************************* ** check outbound intstatus ********************************************* */ outbound_doorbell=CHIP_REG_READ32(HBB_DOORBELL, 0, iop2drv_doorbell) & acb->outbound_int_enable; if(!outbound_doorbell) { /*it must be share irq*/ return; } CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ~outbound_doorbell); /* clear doorbell interrupt */ CHIP_REG_READ32(HBB_DOORBELL, 0, iop2drv_doorbell); CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_END_OF_INTERRUPT); /* MU ioctl transfer doorbell interrupts*/ if(outbound_doorbell & ARCMSR_IOP2DRV_DATA_WRITE_OK) { arcmsr_iop2drv_data_wrote_handle(acb); } if(outbound_doorbell & ARCMSR_IOP2DRV_DATA_READ_OK) { arcmsr_iop2drv_data_read_handle(acb); } /* MU post queue interrupts*/ if(outbound_doorbell & ARCMSR_IOP2DRV_CDB_DONE) { arcmsr_hbb_postqueue_isr(acb); } if(outbound_doorbell & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) { arcmsr_hbb_message_isr(acb); } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_handle_hbc_isr( struct AdapterControlBlock *acb) { u_int32_t host_interrupt_status; /* ********************************************* ** check outbound intstatus ********************************************* */ host_interrupt_status=CHIP_REG_READ32(HBC_MessageUnit, 0, host_int_status); if(!host_interrupt_status) { /*it must be share irq*/ return; } /* MU doorbell interrupts*/ if(host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_DOORBELL_ISR) { arcmsr_hbc_doorbell_isr(acb); } /* MU post queue interrupts*/ if(host_interrupt_status & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR) { arcmsr_hbc_postqueue_isr(acb); } return; } /* ****************************************************************************** ****************************************************************************** */ static void arcmsr_interrupt(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: arcmsr_handle_hba_isr(acb); break; case ACB_ADAPTER_TYPE_B: arcmsr_handle_hbb_isr(acb); break; case ACB_ADAPTER_TYPE_C: arcmsr_handle_hbc_isr(acb); break; default: printf("arcmsr%d: interrupt service," " unknow adapter type =%d\n", acb->pci_unit, acb->adapter_type); break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_intr_handler(void *arg) { struct AdapterControlBlock *acb=(struct AdapterControlBlock *)arg; ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); arcmsr_interrupt(acb); ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); } /* ****************************************************************************** ****************************************************************************** */ static void arcmsr_polling_devmap(void* arg) { struct AdapterControlBlock *acb = (struct AdapterControlBlock *)arg; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG); break; case ACB_ADAPTER_TYPE_B: CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_GET_CONFIG); break; case ACB_ADAPTER_TYPE_C: CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); break; } if((acb->acb_flags & ACB_F_SCSISTOPADAPTER) == 0) { callout_reset(&acb->devmap_callout, 5 * hz, arcmsr_polling_devmap, acb); /* polling per 5 seconds */ } } /* ******************************************************************************* ** ******************************************************************************* */ static void arcmsr_iop_parking(struct AdapterControlBlock *acb) { u_int32_t intmask_org; if(acb!=NULL) { /* stop adapter background rebuild */ if(acb->acb_flags & ACB_F_MSG_START_BGRB) { intmask_org = arcmsr_disable_allintr(acb); arcmsr_stop_adapter_bgrb(acb); arcmsr_flush_adapter_cache(acb); arcmsr_enable_allintr(acb, intmask_org); } } } /* *********************************************************************** ** ************************************************************************ */ u_int32_t arcmsr_iop_ioctlcmd(struct AdapterControlBlock *acb, u_int32_t ioctl_cmd, caddr_t arg) { struct CMD_MESSAGE_FIELD * pcmdmessagefld; u_int32_t retvalue=EINVAL; pcmdmessagefld=(struct CMD_MESSAGE_FIELD *) arg; if(memcmp(pcmdmessagefld->cmdmessage.Signature, "ARCMSR", 6)!=0) { return retvalue; } ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); switch(ioctl_cmd) { case ARCMSR_MESSAGE_READ_RQBUFFER: { u_int8_t * pQbuffer; u_int8_t * ptmpQbuffer=pcmdmessagefld->messagedatabuffer; u_int32_t allxfer_len=0; while((acb->rqbuf_firstindex!=acb->rqbuf_lastindex) && (allxfer_len<1031)) { /*copy READ QBUFFER to srb*/ pQbuffer= &acb->rqbuffer[acb->rqbuf_firstindex]; memcpy(ptmpQbuffer, pQbuffer, 1); acb->rqbuf_firstindex++; acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER; /*if last index number set it to 0 */ ptmpQbuffer++; allxfer_len++; } if(acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { struct QBUFFER * prbuffer; u_int8_t * iop_data; u_int32_t iop_len; acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; prbuffer=arcmsr_get_iop_rqbuffer(acb); iop_data=(u_int8_t *)prbuffer->data; iop_len=(u_int32_t)prbuffer->data_len; /*this iop data does no chance to make me overflow again here, so just do it*/ while(iop_len>0) { pQbuffer= &acb->rqbuffer[acb->rqbuf_lastindex]; memcpy(pQbuffer, iop_data, 1); acb->rqbuf_lastindex++; acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; /*if last index number set it to 0 */ iop_data++; iop_len--; } arcmsr_iop_message_read(acb); /*signature, let IOP know data has been readed */ } pcmdmessagefld->cmdmessage.Length=allxfer_len; pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_WRITE_WQBUFFER: { u_int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; u_int8_t * pQbuffer; u_int8_t * ptmpuserbuffer=pcmdmessagefld->messagedatabuffer; user_len=pcmdmessagefld->cmdmessage.Length; /*check if data xfer length of this request will overflow my array qbuffer */ wqbuf_lastindex=acb->wqbuf_lastindex; wqbuf_firstindex=acb->wqbuf_firstindex; if(wqbuf_lastindex!=wqbuf_firstindex) { arcmsr_post_ioctldata2iop(acb); pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_ERROR; } else { my_empty_len=(wqbuf_firstindex-wqbuf_lastindex-1)&(ARCMSR_MAX_QBUFFER-1); if(my_empty_len>=user_len) { while(user_len>0) { /*copy srb data to wqbuffer*/ pQbuffer= &acb->wqbuffer[acb->wqbuf_lastindex]; memcpy(pQbuffer, ptmpuserbuffer, 1); acb->wqbuf_lastindex++; acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER; /*if last index number set it to 0 */ ptmpuserbuffer++; user_len--; } /*post fist Qbuffer*/ if(acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { acb->acb_flags &=~ACB_F_MESSAGE_WQBUFFER_CLEARED; arcmsr_post_ioctldata2iop(acb); } pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; } else { pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_ERROR; } } retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_CLEAR_RQBUFFER: { u_int8_t * pQbuffer=acb->rqbuffer; if(acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); /*signature, let IOP know data has been readed */ } acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED; acb->rqbuf_firstindex=0; acb->rqbuf_lastindex=0; memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_CLEAR_WQBUFFER: { u_int8_t * pQbuffer=acb->wqbuffer; if(acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); /*signature, let IOP know data has been readed */ } acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED|ACB_F_MESSAGE_WQBUFFER_READ); acb->wqbuf_firstindex=0; acb->wqbuf_lastindex=0; memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER: { u_int8_t * pQbuffer; if(acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); /*signature, let IOP know data has been readed */ } acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |ACB_F_MESSAGE_RQBUFFER_CLEARED |ACB_F_MESSAGE_WQBUFFER_READ); acb->rqbuf_firstindex=0; acb->rqbuf_lastindex=0; acb->wqbuf_firstindex=0; acb->wqbuf_lastindex=0; pQbuffer=acb->rqbuffer; memset(pQbuffer, 0, sizeof(struct QBUFFER)); pQbuffer=acb->wqbuffer; memset(pQbuffer, 0, sizeof(struct QBUFFER)); pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_REQUEST_RETURNCODE_3F: { pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_3F; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_SAY_HELLO: { u_int8_t * hello_string="Hello! I am ARCMSR"; u_int8_t * puserbuffer=(u_int8_t *)pcmdmessagefld->messagedatabuffer; if(memcpy(puserbuffer, hello_string, (int16_t)strlen(hello_string))) { pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_ERROR; ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); return ENOIOCTL; } pcmdmessagefld->cmdmessage.ReturnCode=ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_SAY_GOODBYE: { arcmsr_iop_parking(acb); retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE: { arcmsr_flush_adapter_cache(acb); retvalue=ARCMSR_MESSAGE_SUCCESS; } break; } ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); return retvalue; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_free_srb(struct CommandControlBlock *srb) { struct AdapterControlBlock *acb; int mutex; acb = srb->acb; mutex = mtx_owned(&acb->qbuffer_lock); if( mutex == 0 ) ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); srb->srb_state=ARCMSR_SRB_DONE; srb->srb_flags=0; acb->srbworkingQ[acb->workingsrb_doneindex]=srb; acb->workingsrb_doneindex++; acb->workingsrb_doneindex %= ARCMSR_MAX_FREESRB_NUM; if( mutex == 0 ) ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); } /* ************************************************************************** ************************************************************************** */ struct CommandControlBlock * arcmsr_get_freesrb(struct AdapterControlBlock *acb) { struct CommandControlBlock *srb=NULL; u_int32_t workingsrb_startindex, workingsrb_doneindex; int mutex; mutex = mtx_owned(&acb->qbuffer_lock); if( mutex == 0 ) ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); workingsrb_doneindex=acb->workingsrb_doneindex; workingsrb_startindex=acb->workingsrb_startindex; srb=acb->srbworkingQ[workingsrb_startindex]; workingsrb_startindex++; workingsrb_startindex %= ARCMSR_MAX_FREESRB_NUM; if(workingsrb_doneindex!=workingsrb_startindex) { acb->workingsrb_startindex=workingsrb_startindex; } else { srb=NULL; } if( mutex == 0 ) ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); return(srb); } /* ************************************************************************** ************************************************************************** */ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, union ccb * pccb) { struct CMD_MESSAGE_FIELD * pcmdmessagefld; int retvalue = 0, transfer_len = 0; char *buffer; u_int32_t controlcode = (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[5] << 24 | (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[6] << 16 | (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[7] << 8 | (u_int32_t ) pccb->csio.cdb_io.cdb_bytes[8]; /* 4 bytes: Areca io control code */ if((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { buffer = pccb->csio.data_ptr; transfer_len = pccb->csio.dxfer_len; } else { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } if (transfer_len > sizeof(struct CMD_MESSAGE_FIELD)) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } pcmdmessagefld = (struct CMD_MESSAGE_FIELD *) buffer; switch(controlcode) { case ARCMSR_MESSAGE_READ_RQBUFFER: { u_int8_t *pQbuffer; u_int8_t *ptmpQbuffer=pcmdmessagefld->messagedatabuffer; int32_t allxfer_len = 0; while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex) && (allxfer_len < 1031)) { pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex]; memcpy(ptmpQbuffer, pQbuffer, 1); acb->rqbuf_firstindex++; acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER; ptmpQbuffer++; allxfer_len++; } if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { struct QBUFFER *prbuffer; u_int8_t *iop_data; int32_t iop_len; acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; prbuffer=arcmsr_get_iop_rqbuffer(acb); iop_data = (u_int8_t *)prbuffer->data; iop_len =(u_int32_t)prbuffer->data_len; while (iop_len > 0) { pQbuffer= &acb->rqbuffer[acb->rqbuf_lastindex]; memcpy(pQbuffer, iop_data, 1); acb->rqbuf_lastindex++; acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER; iop_data++; iop_len--; } arcmsr_iop_message_read(acb); } pcmdmessagefld->cmdmessage.Length = allxfer_len; pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; retvalue=ARCMSR_MESSAGE_SUCCESS; } break; case ARCMSR_MESSAGE_WRITE_WQBUFFER: { int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; u_int8_t *pQbuffer; u_int8_t *ptmpuserbuffer=pcmdmessagefld->messagedatabuffer; user_len = pcmdmessagefld->cmdmessage.Length; wqbuf_lastindex = acb->wqbuf_lastindex; wqbuf_firstindex = acb->wqbuf_firstindex; if (wqbuf_lastindex != wqbuf_firstindex) { arcmsr_post_ioctldata2iop(acb); /* has error report sensedata */ if(&pccb->csio.sense_data) { ((u_int8_t *)&pccb->csio.sense_data)[0] = (0x1 << 7 | 0x70); /* Valid,ErrorCode */ ((u_int8_t *)&pccb->csio.sense_data)[2] = 0x05; /* FileMark,EndOfMedia,IncorrectLength,Reserved,SenseKey */ ((u_int8_t *)&pccb->csio.sense_data)[7] = 0x0A; /* AdditionalSenseLength */ ((u_int8_t *)&pccb->csio.sense_data)[12] = 0x20; /* AdditionalSenseCode */ } retvalue = ARCMSR_MESSAGE_FAIL; } else { my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1) &(ARCMSR_MAX_QBUFFER - 1); if (my_empty_len >= user_len) { while (user_len > 0) { pQbuffer = &acb->wqbuffer[acb->wqbuf_lastindex]; memcpy(pQbuffer, ptmpuserbuffer, 1); acb->wqbuf_lastindex++; acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER; ptmpuserbuffer++; user_len--; } if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) { acb->acb_flags &= ~ACB_F_MESSAGE_WQBUFFER_CLEARED; arcmsr_post_ioctldata2iop(acb); } } else { /* has error report sensedata */ if(&pccb->csio.sense_data) { ((u_int8_t *)&pccb->csio.sense_data)[0] = (0x1 << 7 | 0x70); /* Valid,ErrorCode */ ((u_int8_t *)&pccb->csio.sense_data)[2] = 0x05; /* FileMark,EndOfMedia,IncorrectLength,Reserved,SenseKey */ ((u_int8_t *)&pccb->csio.sense_data)[7] = 0x0A; /* AdditionalSenseLength */ ((u_int8_t *)&pccb->csio.sense_data)[12] = 0x20; /* AdditionalSenseCode */ } retvalue = ARCMSR_MESSAGE_FAIL; } } } break; case ARCMSR_MESSAGE_CLEAR_RQBUFFER: { u_int8_t *pQbuffer = acb->rqbuffer; if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); } acb->acb_flags |= ACB_F_MESSAGE_RQBUFFER_CLEARED; acb->rqbuf_firstindex = 0; acb->rqbuf_lastindex = 0; memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; } break; case ARCMSR_MESSAGE_CLEAR_WQBUFFER: { u_int8_t *pQbuffer = acb->wqbuffer; if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); } acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED | ACB_F_MESSAGE_WQBUFFER_READ); acb->wqbuf_firstindex = 0; acb->wqbuf_lastindex = 0; memset(pQbuffer, 0, ARCMSR_MAX_QBUFFER); pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; } break; case ARCMSR_MESSAGE_CLEAR_ALLQBUFFER: { u_int8_t *pQbuffer; if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) { acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW; arcmsr_iop_message_read(acb); } acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED | ACB_F_MESSAGE_RQBUFFER_CLEARED | ACB_F_MESSAGE_WQBUFFER_READ); acb->rqbuf_firstindex = 0; acb->rqbuf_lastindex = 0; acb->wqbuf_firstindex = 0; acb->wqbuf_lastindex = 0; pQbuffer = acb->rqbuffer; memset(pQbuffer, 0, sizeof (struct QBUFFER)); pQbuffer = acb->wqbuffer; memset(pQbuffer, 0, sizeof (struct QBUFFER)); pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; } break; case ARCMSR_MESSAGE_REQUEST_RETURNCODE_3F: { pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_3F; } break; case ARCMSR_MESSAGE_SAY_HELLO: { int8_t * hello_string = "Hello! I am ARCMSR"; memcpy(pcmdmessagefld->messagedatabuffer, hello_string , (int16_t)strlen(hello_string)); pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; } break; case ARCMSR_MESSAGE_SAY_GOODBYE: arcmsr_iop_parking(acb); break; case ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE: arcmsr_flush_adapter_cache(acb); break; default: retvalue = ARCMSR_MESSAGE_FAIL; } message_out: return retvalue; } /* ********************************************************************* ********************************************************************* */ static void arcmsr_execute_srb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct CommandControlBlock *srb=(struct CommandControlBlock *)arg; struct AdapterControlBlock *acb=(struct AdapterControlBlock *)srb->acb; union ccb * pccb; int target, lun; pccb=srb->pccb; target=pccb->ccb_h.target_id; lun=pccb->ccb_h.target_lun; #ifdef ARCMSR_DEBUG1 acb->pktRequestCount++; #endif if(error != 0) { if(error != EFBIG) { printf("arcmsr%d: unexpected error %x" " returned from 'bus_dmamap_load' \n" , acb->pci_unit, error); } if((pccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { pccb->ccb_h.status |= CAM_REQ_TOO_BIG; } arcmsr_srb_complete(srb, 0); return; } if(nseg > ARCMSR_MAX_SG_ENTRIES) { pccb->ccb_h.status |= CAM_REQ_TOO_BIG; arcmsr_srb_complete(srb, 0); return; } if(acb->acb_flags & ACB_F_BUS_RESET) { printf("arcmsr%d: bus reset and return busy \n", acb->pci_unit); pccb->ccb_h.status |= CAM_SCSI_BUS_RESET; arcmsr_srb_complete(srb, 0); return; } if(acb->devstate[target][lun]==ARECA_RAID_GONE) { u_int8_t block_cmd, cmd; cmd = pccb->csio.cdb_io.cdb_bytes[0]; block_cmd= cmd & 0x0f; if(block_cmd==0x08 || block_cmd==0x0a) { printf("arcmsr%d:block 'read/write' command " "with gone raid volume Cmd=0x%2x, TargetId=%d, Lun=%d \n" , acb->pci_unit, cmd, target, lun); pccb->ccb_h.status |= CAM_DEV_NOT_THERE; arcmsr_srb_complete(srb, 0); return; } } if((pccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { if(nseg != 0) { bus_dmamap_unload(acb->dm_segs_dmat, srb->dm_segs_dmamap); } arcmsr_srb_complete(srb, 0); return; } if(acb->srboutstandingcount >= ARCMSR_MAX_OUTSTANDING_CMD) { xpt_freeze_simq(acb->psim, 1); pccb->ccb_h.status = CAM_REQUEUE_REQ; acb->acb_flags |= ACB_F_CAM_DEV_QFRZN; arcmsr_srb_complete(srb, 0); return; } pccb->ccb_h.status |= CAM_SIM_QUEUED; arcmsr_build_srb(srb, dm_segs, nseg); arcmsr_post_srb(acb, srb); if (pccb->ccb_h.timeout != CAM_TIME_INFINITY) { arcmsr_callout_init(&srb->ccb_callout); callout_reset(&srb->ccb_callout, (pccb->ccb_h.timeout * hz ) / 1000, arcmsr_srb_timeout, srb); srb->srb_flags |= SRB_FLAG_TIMER_START; } return; } /* ***************************************************************************************** ***************************************************************************************** */ static u_int8_t arcmsr_seek_cmd2abort(union ccb * abortccb) { struct CommandControlBlock *srb; struct AdapterControlBlock *acb=(struct AdapterControlBlock *) abortccb->ccb_h.arcmsr_ccbacb_ptr; u_int32_t intmask_org; int i=0; acb->num_aborts++; /* *************************************************************************** ** It is the upper layer do abort command this lock just prior to calling us. ** First determine if we currently own this command. ** Start by searching the device queue. If not found ** at all, and the system wanted us to just abort the ** command return success. *************************************************************************** */ if(acb->srboutstandingcount!=0) { /* disable all outbound interrupt */ intmask_org=arcmsr_disable_allintr(acb); for(i=0;ipsrb_pool[i]; if(srb->srb_state==ARCMSR_SRB_START) { if(srb->pccb==abortccb) { srb->srb_state=ARCMSR_SRB_ABORTED; printf("arcmsr%d:scsi id=%d lun=%d abort srb '%p'" "outstanding command \n" , acb->pci_unit, abortccb->ccb_h.target_id , abortccb->ccb_h.target_lun, srb); arcmsr_polling_srbdone(acb, srb); /* enable outbound Post Queue, outbound doorbell Interrupt */ arcmsr_enable_allintr(acb, intmask_org); return (TRUE); } } } /* enable outbound Post Queue, outbound doorbell Interrupt */ arcmsr_enable_allintr(acb, intmask_org); } return(FALSE); } /* **************************************************************************** **************************************************************************** */ static void arcmsr_bus_reset(struct AdapterControlBlock *acb) { int retry=0; acb->num_resets++; acb->acb_flags |=ACB_F_BUS_RESET; while(acb->srboutstandingcount!=0 && retry < 400) { arcmsr_interrupt(acb); UDELAY(25000); retry++; } arcmsr_iop_reset(acb); acb->acb_flags &= ~ACB_F_BUS_RESET; return; } /* ************************************************************************** ************************************************************************** */ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb, union ccb * pccb) { pccb->ccb_h.status |= CAM_REQ_CMP; switch (pccb->csio.cdb_io.cdb_bytes[0]) { case INQUIRY: { unsigned char inqdata[36]; char *buffer=pccb->csio.data_ptr; if (pccb->ccb_h.target_lun) { pccb->ccb_h.status |= CAM_SEL_TIMEOUT; xpt_done(pccb); return; } inqdata[0] = T_PROCESSOR; /* Periph Qualifier & Periph Dev Type */ inqdata[1] = 0; /* rem media bit & Dev Type Modifier */ inqdata[2] = 0; /* ISO, ECMA, & ANSI versions */ inqdata[3] = 0; inqdata[4] = 31; /* length of additional data */ inqdata[5] = 0; inqdata[6] = 0; inqdata[7] = 0; strncpy(&inqdata[8], "Areca ", 8); /* Vendor Identification */ strncpy(&inqdata[16], "RAID controller ", 16); /* Product Identification */ strncpy(&inqdata[32], "R001", 4); /* Product Revision */ memcpy(buffer, inqdata, sizeof(inqdata)); xpt_done(pccb); } break; case WRITE_BUFFER: case READ_BUFFER: { if (arcmsr_iop_message_xfer(acb, pccb)) { pccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; pccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } xpt_done(pccb); } break; default: xpt_done(pccb); } } /* ********************************************************************* ********************************************************************* */ static void arcmsr_action(struct cam_sim * psim, union ccb * pccb) { struct AdapterControlBlock * acb; acb=(struct AdapterControlBlock *) cam_sim_softc(psim); if(acb==NULL) { pccb->ccb_h.status |= CAM_REQ_INVALID; xpt_done(pccb); return; } switch (pccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct CommandControlBlock *srb; int target=pccb->ccb_h.target_id; if(target == 16) { /* virtual device for iop message transfer */ arcmsr_handle_virtual_command(acb, pccb); return; } if((srb=arcmsr_get_freesrb(acb)) == NULL) { pccb->ccb_h.status |= CAM_RESRC_UNAVAIL; xpt_done(pccb); return; } pccb->ccb_h.arcmsr_ccbsrb_ptr=srb; pccb->ccb_h.arcmsr_ccbacb_ptr=acb; srb->pccb=pccb; if((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if(!(pccb->ccb_h.flags & CAM_SCATTER_VALID)) { /* Single buffer */ if(!(pccb->ccb_h.flags & CAM_DATA_PHYS)) { /* Buffer is virtual */ u_int32_t error, s; s=splsoftvm(); error = bus_dmamap_load(acb->dm_segs_dmat , srb->dm_segs_dmamap , pccb->csio.data_ptr , pccb->csio.dxfer_len , arcmsr_execute_srb, srb, /*flags*/0); if(error == EINPROGRESS) { xpt_freeze_simq(acb->psim, 1); pccb->ccb_h.status |= CAM_RELEASE_SIMQ; } splx(s); } else { /* Buffer is physical */ #ifdef PAE panic("arcmsr: CAM_DATA_PHYS not supported"); #else struct bus_dma_segment seg; seg.ds_addr = (bus_addr_t)pccb->csio.data_ptr; seg.ds_len = pccb->csio.dxfer_len; arcmsr_execute_srb(srb, &seg, 1, 0); #endif } } else { /* Scatter/gather list */ struct bus_dma_segment *segs; if((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { pccb->ccb_h.status |= CAM_PROVIDE_FAIL; xpt_done(pccb); free(srb, M_DEVBUF); return; } segs=(struct bus_dma_segment *)pccb->csio.data_ptr; arcmsr_execute_srb(srb, segs, pccb->csio.sglist_cnt, 0); } } else { arcmsr_execute_srb(srb, NULL, 0, 0); } break; } case XPT_TARGET_IO: { /* target mode not yet support vendor specific commands. */ pccb->ccb_h.status |= CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_PATH_INQ: { struct ccb_pathinq *cpi= &pccb->cpi; cpi->version_num=1; cpi->hba_inquiry=PI_SDTR_ABLE | PI_TAG_ABLE; cpi->target_sprt=0; cpi->hba_misc=0; cpi->hba_eng_cnt=0; cpi->max_target=ARCMSR_MAX_TARGETID; /* 0-16 */ cpi->max_lun=ARCMSR_MAX_TARGETLUN; /* 0-7 */ cpi->initiator_id=ARCMSR_SCSI_INITIATOR_ID; /* 255 */ cpi->bus_id=cam_sim_bus(psim); strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "ARCMSR", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); cpi->unit_number=cam_sim_unit(psim); #ifdef CAM_NEW_TRAN_CODE cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; #endif cpi->ccb_h.status |= CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_ABORT: { union ccb *pabort_ccb; pabort_ccb=pccb->cab.abort_ccb; switch (pabort_ccb->ccb_h.func_code) { case XPT_ACCEPT_TARGET_IO: case XPT_IMMED_NOTIFY: case XPT_CONT_TARGET_IO: if(arcmsr_seek_cmd2abort(pabort_ccb)==TRUE) { pabort_ccb->ccb_h.status |= CAM_REQ_ABORTED; xpt_done(pabort_ccb); pccb->ccb_h.status |= CAM_REQ_CMP; } else { xpt_print_path(pabort_ccb->ccb_h.path); printf("Not found\n"); pccb->ccb_h.status |= CAM_PATH_INVALID; } break; case XPT_SCSI_IO: pccb->ccb_h.status |= CAM_UA_ABORT; break; default: pccb->ccb_h.status |= CAM_REQ_INVALID; break; } xpt_done(pccb); break; } case XPT_RESET_BUS: case XPT_RESET_DEV: { u_int32_t i; arcmsr_bus_reset(acb); for (i=0; i < 500; i++) { DELAY(1000); } pccb->ccb_h.status |= CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_TERM_IO: { pccb->ccb_h.status |= CAM_REQ_INVALID; xpt_done(pccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; if(pccb->ccb_h.target_id == 16) { pccb->ccb_h.status |= CAM_FUNC_NOTAVAIL; xpt_done(pccb); break; } cts= &pccb->cts; #ifdef CAM_NEW_TRAN_CODE { struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_spi *spi; scsi = &cts->proto_specific.scsi; spi = &cts->xport_specific.spi; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; spi->flags = CTS_SPI_FLAGS_DISC_ENB; spi->sync_period=3; spi->sync_offset=32; spi->bus_width=MSG_EXT_WDTR_BUS_16_BIT; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; spi->valid = CTS_SPI_VALID_DISC | CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH; scsi->valid = CTS_SCSI_VALID_TQ; } #else { cts->flags=(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); cts->sync_period=3; cts->sync_offset=32; cts->bus_width=MSG_EXT_WDTR_BUS_16_BIT; cts->valid=CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; } #endif pccb->ccb_h.status |= CAM_REQ_CMP; xpt_done(pccb); break; } case XPT_SET_TRAN_SETTINGS: { pccb->ccb_h.status |= CAM_FUNC_NOTAVAIL; xpt_done(pccb); break; } case XPT_CALC_GEOMETRY: if(pccb->ccb_h.target_id == 16) { pccb->ccb_h.status |= CAM_FUNC_NOTAVAIL; xpt_done(pccb); break; } #if __FreeBSD_version >= 500000 cam_calc_geometry(&pccb->ccg, 1); #else { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg= &pccb->ccg; if (ccg->block_size == 0) { pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; } if(((1024L * 1024L)/ccg->block_size) < 0) { pccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(pccb); break; } size_mb=ccg->volume_size/((1024L * 1024L)/ccg->block_size); if(size_mb > 1024 ) { ccg->heads=255; ccg->secs_per_track=63; } else { ccg->heads=64; ccg->secs_per_track=32; } secs_per_cylinder=ccg->heads * ccg->secs_per_track; ccg->cylinders=ccg->volume_size / secs_per_cylinder; pccb->ccb_h.status |= CAM_REQ_CMP; } #endif xpt_done(pccb); break; default: pccb->ccb_h.status |= CAM_REQ_INVALID; xpt_done(pccb); break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_start_hba_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags |= ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_START_BGRB); if(!arcmsr_hba_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'start adapter background rebulid' timeout \n", acb->pci_unit); } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_start_hbb_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags |= ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_START_BGRB); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: wait 'start adapter background rebulid' timeout \n", acb->pci_unit); } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_start_hbc_bgrb(struct AdapterControlBlock *acb) { acb->acb_flags |= ACB_F_MSG_START_BGRB; CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_START_BGRB); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); if(!arcmsr_hbc_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'start adapter background rebulid' timeout \n", acb->pci_unit); } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: arcmsr_start_hba_bgrb(acb); break; case ACB_ADAPTER_TYPE_B: arcmsr_start_hbb_bgrb(acb); break; case ACB_ADAPTER_TYPE_C: arcmsr_start_hbc_bgrb(acb); break; } return; } /* ********************************************************************** ** ********************************************************************** */ static void arcmsr_polling_hba_srbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_srb) { struct CommandControlBlock *srb; u_int32_t flag_srb, outbound_intstatus, poll_srb_done=0, poll_count=0; u_int16_t error; polling_ccb_retry: poll_count++; outbound_intstatus=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_intstatus) & acb->outbound_int_enable; CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_intstatus, outbound_intstatus); /*clear interrupt*/ bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); while(1) { if((flag_srb=CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_queueport))==0xFFFFFFFF) { if(poll_srb_done) { break;/*chip FIFO no ccb for completion already*/ } else { UDELAY(25000); if ((poll_count > 100) && (poll_srb != NULL)) { break; } goto polling_ccb_retry; } } /* check if command done with no error*/ srb=(struct CommandControlBlock *) (acb->vir2phy_offset+(flag_srb << 5));/*frame must be 32 bytes aligned*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; poll_srb_done = (srb==poll_srb) ? 1:0; if((srb->acb!=acb) || (srb->srb_state!=ARCMSR_SRB_START)) { if(srb->srb_state==ARCMSR_SRB_ABORTED) { printf("arcmsr%d: scsi id=%d lun=%d srb='%p'" "poll command abort successfully \n" , acb->pci_unit , srb->pccb->ccb_h.target_id , srb->pccb->ccb_h.target_lun, srb); srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); continue; } printf("arcmsr%d: polling get an illegal srb command done srb='%p'" "srboutstandingcount=%d \n" , acb->pci_unit , srb, acb->srboutstandingcount); continue; } arcmsr_report_srb_state(acb, srb, error); } /*drain reply FIFO*/ return; } /* ********************************************************************** ** ********************************************************************** */ static void arcmsr_polling_hbb_srbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_srb) { struct HBB_MessageUnit *phbbmu=(struct HBB_MessageUnit *)acb->pmu; struct CommandControlBlock *srb; u_int32_t flag_srb, poll_srb_done=0, poll_count=0; int index; u_int16_t error; polling_ccb_retry: poll_count++; CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ARCMSR_DOORBELL_INT_CLEAR_PATTERN); /* clear doorbell interrupt */ bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); while(1) { index=phbbmu->doneq_index; if((flag_srb=phbbmu->done_qbuffer[index]) == 0) { if(poll_srb_done) { break;/*chip FIFO no ccb for completion already*/ } else { UDELAY(25000); if ((poll_count > 100) && (poll_srb != NULL)) { break; } goto polling_ccb_retry; } } phbbmu->done_qbuffer[index]=0; index++; index %= ARCMSR_MAX_HBB_POSTQUEUE; /*if last index number set it to 0 */ phbbmu->doneq_index=index; /* check if command done with no error*/ srb=(struct CommandControlBlock *) (acb->vir2phy_offset+(flag_srb << 5));/*frame must be 32 bytes aligned*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE0)?TRUE:FALSE; poll_srb_done = (srb==poll_srb) ? 1:0; if((srb->acb!=acb) || (srb->srb_state!=ARCMSR_SRB_START)) { if(srb->srb_state==ARCMSR_SRB_ABORTED) { printf("arcmsr%d: scsi id=%d lun=%d srb='%p'" "poll command abort successfully \n" , acb->pci_unit , srb->pccb->ccb_h.target_id , srb->pccb->ccb_h.target_lun, srb); srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); continue; } printf("arcmsr%d: polling get an illegal srb command done srb='%p'" "srboutstandingcount=%d \n" , acb->pci_unit , srb, acb->srboutstandingcount); continue; } arcmsr_report_srb_state(acb, srb, error); } /*drain reply FIFO*/ return; } /* ********************************************************************** ** ********************************************************************** */ static void arcmsr_polling_hbc_srbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_srb) { struct CommandControlBlock *srb; u_int32_t flag_srb, poll_srb_done=0, poll_count=0; u_int16_t error; polling_ccb_retry: poll_count++; bus_dmamap_sync(acb->srb_dmat, acb->srb_dmamap, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); while(1) { if(!(CHIP_REG_READ32(HBC_MessageUnit, 0, host_int_status) & ARCMSR_HBCMU_OUTBOUND_POSTQUEUE_ISR)) { if(poll_srb_done) { break;/*chip FIFO no ccb for completion already*/ } else { UDELAY(25000); if ((poll_count > 100) && (poll_srb != NULL)) { break; } if (acb->srboutstandingcount == 0) { break; } goto polling_ccb_retry; } } flag_srb = CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_queueport_low); /* check if command done with no error*/ srb=(struct CommandControlBlock *)(acb->vir2phy_offset+(flag_srb & 0xFFFFFFE0));/*frame must be 32 bytes aligned*/ error=(flag_srb & ARCMSR_SRBREPLY_FLAG_ERROR_MODE1)?TRUE:FALSE; if (poll_srb != NULL) poll_srb_done = (srb==poll_srb) ? 1:0; if((srb->acb!=acb) || (srb->srb_state!=ARCMSR_SRB_START)) { if(srb->srb_state==ARCMSR_SRB_ABORTED) { printf("arcmsr%d: scsi id=%d lun=%d srb='%p'poll command abort successfully \n" , acb->pci_unit, srb->pccb->ccb_h.target_id, srb->pccb->ccb_h.target_lun, srb); srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); continue; } printf("arcmsr%d: polling get an illegal srb command done srb='%p'srboutstandingcount=%d \n" , acb->pci_unit, srb, acb->srboutstandingcount); continue; } arcmsr_report_srb_state(acb, srb, error); } /*drain reply FIFO*/ return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_polling_srbdone(struct AdapterControlBlock *acb, struct CommandControlBlock *poll_srb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { arcmsr_polling_hba_srbdone(acb, poll_srb); } break; case ACB_ADAPTER_TYPE_B: { arcmsr_polling_hbb_srbdone(acb, poll_srb); } break; case ACB_ADAPTER_TYPE_C: { arcmsr_polling_hbc_srbdone(acb, poll_srb); } break; } } /* ********************************************************************** ********************************************************************** */ static void arcmsr_get_hba_config(struct AdapterControlBlock *acb) { char *acb_firm_model=acb->firm_model; char *acb_firm_version=acb->firm_version; char *acb_device_map = acb->device_map; size_t iop_firm_model=offsetof(struct HBA_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]); /*firm_model,15,60-67*/ size_t iop_firm_version=offsetof(struct HBA_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]); /*firm_version,17,68-83*/ size_t iop_device_map = offsetof(struct HBA_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); int i; CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG); if(!arcmsr_hba_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'get adapter firmware miscellaneous data' timeout \n", acb->pci_unit); } i=0; while(i<8) { *acb_firm_model=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_firm_model+i); /* 8 bytes firm_model, 15, 60-67*/ acb_firm_model++; i++; } i=0; while(i<16) { *acb_firm_version=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_firm_version+i); /* 16 bytes firm_version, 17, 68-83*/ acb_firm_version++; i++; } i=0; while(i<16) { *acb_device_map=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_device_map+i); acb_device_map++; i++; } printf("ARECA RAID ADAPTER%d: %s \n", acb->pci_unit, ARCMSR_DRIVER_VERSION); printf("ARECA RAID ADAPTER%d: FIRMWARE VERSION %s \n", acb->pci_unit, acb->firm_version); acb->firm_request_len=CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[1]); /*firm_request_len, 1, 04-07*/ acb->firm_numbers_queue=CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[2]); /*firm_numbers_queue, 2, 08-11*/ acb->firm_sdram_size=CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[3]); /*firm_sdram_size, 3, 12-15*/ acb->firm_ide_channels=CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[4]); /*firm_ide_channels, 4, 16-19*/ acb->firm_cfg_version=CHIP_REG_READ32(HBA_MessageUnit, 0, msgcode_rwbuffer[ARCMSR_FW_CFGVER_OFFSET]); /*firm_cfg_version, 25, */ return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_get_hbb_config(struct AdapterControlBlock *acb) { char *acb_firm_model=acb->firm_model; char *acb_firm_version=acb->firm_version; char *acb_device_map = acb->device_map; size_t iop_firm_model=offsetof(struct HBB_RWBUFFER, msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]); /*firm_model,15,60-67*/ size_t iop_firm_version=offsetof(struct HBB_RWBUFFER, msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]); /*firm_version,17,68-83*/ size_t iop_device_map = offsetof(struct HBB_RWBUFFER, msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); int i; CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_GET_CONFIG); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: wait" "'get adapter firmware miscellaneous data' timeout \n", acb->pci_unit); } i=0; while(i<8) { *acb_firm_model=bus_space_read_1(acb->btag[1], acb->bhandle[1], iop_firm_model+i); /* 8 bytes firm_model, 15, 60-67*/ acb_firm_model++; i++; } i=0; while(i<16) { *acb_firm_version=bus_space_read_1(acb->btag[1], acb->bhandle[1], iop_firm_version+i); /* 16 bytes firm_version, 17, 68-83*/ acb_firm_version++; i++; } i=0; while(i<16) { *acb_device_map=bus_space_read_1(acb->btag[1], acb->bhandle[1], iop_device_map+i); acb_device_map++; i++; } printf("ARECA RAID ADAPTER%d: %s \n", acb->pci_unit, ARCMSR_DRIVER_VERSION); printf("ARECA RAID ADAPTER%d: FIRMWARE VERSION %s \n", acb->pci_unit, acb->firm_version); acb->firm_request_len=CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[1]); /*firm_request_len, 1, 04-07*/ acb->firm_numbers_queue=CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[2]); /*firm_numbers_queue, 2, 08-11*/ acb->firm_sdram_size=CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[3]); /*firm_sdram_size, 3, 12-15*/ acb->firm_ide_channels=CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[4]); /*firm_ide_channels, 4, 16-19*/ acb->firm_cfg_version=CHIP_REG_READ32(HBB_RWBUFFER, 1, msgcode_rwbuffer[ARCMSR_FW_CFGVER_OFFSET]); /*firm_cfg_version, 25, */ return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_get_hbc_config(struct AdapterControlBlock *acb) { char *acb_firm_model=acb->firm_model; char *acb_firm_version=acb->firm_version; char *acb_device_map = acb->device_map; size_t iop_firm_model=offsetof(struct HBC_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_MODEL_OFFSET]); /*firm_model,15,60-67*/ size_t iop_firm_version=offsetof(struct HBC_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_VERS_OFFSET]); /*firm_version,17,68-83*/ size_t iop_device_map = offsetof(struct HBC_MessageUnit,msgcode_rwbuffer[ARCMSR_FW_DEVMAP_OFFSET]); int i; CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_GET_CONFIG); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); if(!arcmsr_hbc_wait_msgint_ready(acb)) { printf("arcmsr%d: wait 'get adapter firmware miscellaneous data' timeout \n", acb->pci_unit); } i=0; while(i<8) { *acb_firm_model=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_firm_model+i); /* 8 bytes firm_model, 15, 60-67*/ acb_firm_model++; i++; } i=0; while(i<16) { *acb_firm_version=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_firm_version+i); /* 16 bytes firm_version, 17, 68-83*/ acb_firm_version++; i++; } i=0; while(i<16) { *acb_device_map=bus_space_read_1(acb->btag[0], acb->bhandle[0], iop_device_map+i); acb_device_map++; i++; } printf("ARECA RAID ADAPTER%d: %s \n", acb->pci_unit, ARCMSR_DRIVER_VERSION); printf("ARECA RAID ADAPTER%d: FIRMWARE VERSION %s \n", acb->pci_unit, acb->firm_version); acb->firm_request_len =CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[1]); /*firm_request_len, 1, 04-07*/ acb->firm_numbers_queue =CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[2]); /*firm_numbers_queue, 2, 08-11*/ acb->firm_sdram_size =CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[3]); /*firm_sdram_size, 3, 12-15*/ acb->firm_ide_channels =CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[4]); /*firm_ide_channels, 4, 16-19*/ acb->firm_cfg_version =CHIP_REG_READ32(HBC_MessageUnit, 0, msgcode_rwbuffer[ARCMSR_FW_CFGVER_OFFSET]); /*firm_cfg_version, 25, */ return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_get_firmware_spec(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { arcmsr_get_hba_config(acb); } break; case ACB_ADAPTER_TYPE_B: { arcmsr_get_hbb_config(acb); } break; case ACB_ADAPTER_TYPE_C: { arcmsr_get_hbc_config(acb); } break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_wait_firmware_ready( struct AdapterControlBlock *acb) { int timeout=0; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { while ((CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_msgaddr1) & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0) { if (timeout++ > 2000) /* (2000*15)/1000 = 30 sec */ { printf( "arcmsr%d:timed out waiting for firmware \n", acb->pci_unit); return; } UDELAY(15000); /* wait 15 milli-seconds */ } } break; case ACB_ADAPTER_TYPE_B: { while ((CHIP_REG_READ32(HBB_DOORBELL, 0, iop2drv_doorbell) & ARCMSR_MESSAGE_FIRMWARE_OK) == 0) { if (timeout++ > 2000) /* (2000*15)/1000 = 30 sec */ { printf( "arcmsr%d: timed out waiting for firmware \n", acb->pci_unit); return; } UDELAY(15000); /* wait 15 milli-seconds */ } CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_END_OF_INTERRUPT); } break; case ACB_ADAPTER_TYPE_C: { while ((CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_msgaddr1) & ARCMSR_HBCMU_MESSAGE_FIRMWARE_OK) == 0) { if (timeout++ > 2000) /* (2000*15)/1000 = 30 sec */ { printf( "arcmsr%d:timed out waiting for firmware ready\n", acb->pci_unit); return; } UDELAY(15000); /* wait 15 milli-seconds */ } } break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_clear_doorbell_queue_buffer( struct AdapterControlBlock *acb) { u_int32_t outbound_doorbell; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { /* empty doorbell Qbuffer if door bell ringed */ outbound_doorbell = CHIP_REG_READ32(HBA_MessageUnit, 0, outbound_doorbell); CHIP_REG_WRITE32(HBA_MessageUnit, 0, outbound_doorbell, outbound_doorbell); /*clear doorbell interrupt */ CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_doorbell, ARCMSR_INBOUND_DRIVER_DATA_READ_OK); } break; case ACB_ADAPTER_TYPE_B: { CHIP_REG_WRITE32(HBB_DOORBELL, 0, iop2drv_doorbell, ARCMSR_MESSAGE_INT_CLEAR_PATTERN);/*clear interrupt and message state*/ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_DRV2IOP_DATA_READ_OK); /* let IOP know data has been read */ } break; case ACB_ADAPTER_TYPE_C: { /* empty doorbell Qbuffer if door bell ringed */ outbound_doorbell = CHIP_REG_READ32(HBC_MessageUnit, 0, outbound_doorbell); CHIP_REG_WRITE32(HBC_MessageUnit, 0, outbound_doorbell_clear, outbound_doorbell); /*clear doorbell interrupt */ CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell, ARCMSR_HBCMU_DRV2IOP_DATA_READ_OK); } break; } return; } /* ************************************************************************ ************************************************************************ */ static u_int32_t arcmsr_iop_confirm(struct AdapterControlBlock *acb) { unsigned long srb_phyaddr; u_int32_t srb_phyaddr_hi32; /* ******************************************************************** ** here we need to tell iop 331 our freesrb.HighPart ** if freesrb.HighPart is not zero ******************************************************************** */ srb_phyaddr= (unsigned long) acb->srb_phyaddr.phyaddr; // srb_phyaddr_hi32=(u_int32_t) ((srb_phyaddr>>16)>>16); srb_phyaddr_hi32=acb->srb_phyaddr.B.phyadd_high; switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { if(srb_phyaddr_hi32!=0) { CHIP_REG_WRITE32(HBA_MessageUnit, 0, msgcode_rwbuffer[0], ARCMSR_SIGNATURE_SET_CONFIG); CHIP_REG_WRITE32(HBA_MessageUnit, 0, msgcode_rwbuffer[1], srb_phyaddr_hi32); CHIP_REG_WRITE32(HBA_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_SET_CONFIG); if(!arcmsr_hba_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'set srb high part physical address' timeout \n", acb->pci_unit); return FALSE; } } } break; /* *********************************************************************** ** if adapter type B, set window of "post command Q" *********************************************************************** */ case ACB_ADAPTER_TYPE_B: { u_int32_t post_queue_phyaddr; struct HBB_MessageUnit *phbbmu; phbbmu=(struct HBB_MessageUnit *)acb->pmu; phbbmu->postq_index=0; phbbmu->doneq_index=0; CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_SET_POST_WINDOW); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'set window of post command Q' timeout\n", acb->pci_unit); return FALSE; } post_queue_phyaddr = srb_phyaddr + ARCMSR_SRBS_POOL_SIZE + offsetof(struct HBB_MessageUnit, post_qbuffer); CHIP_REG_WRITE32(HBB_RWBUFFER, 1, msgcode_rwbuffer[0], ARCMSR_SIGNATURE_SET_CONFIG); /* driver "set config" signature */ CHIP_REG_WRITE32(HBB_RWBUFFER, 1, msgcode_rwbuffer[1], srb_phyaddr_hi32); /* normal should be zero */ CHIP_REG_WRITE32(HBB_RWBUFFER, 1, msgcode_rwbuffer[2], post_queue_phyaddr); /* postQ size (256+8)*4 */ CHIP_REG_WRITE32(HBB_RWBUFFER, 1, msgcode_rwbuffer[3], post_queue_phyaddr+1056); /* doneQ size (256+8)*4 */ CHIP_REG_WRITE32(HBB_RWBUFFER, 1, msgcode_rwbuffer[4], 1056); /* srb maxQ size must be --> [(256+8)*4] */ CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_SET_CONFIG); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'set command Q window' timeout \n", acb->pci_unit); return FALSE; } CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell, ARCMSR_MESSAGE_START_DRIVER_MODE); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'start diver mode' timeout \n", acb->pci_unit); return FALSE; } } break; case ACB_ADAPTER_TYPE_C: { if(srb_phyaddr_hi32!=0) { CHIP_REG_WRITE32(HBC_MessageUnit, 0, msgcode_rwbuffer[0], ARCMSR_SIGNATURE_SET_CONFIG); CHIP_REG_WRITE32(HBC_MessageUnit, 0, msgcode_rwbuffer[1], srb_phyaddr_hi32); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_msgaddr0, ARCMSR_INBOUND_MESG0_SET_CONFIG); CHIP_REG_WRITE32(HBC_MessageUnit, 0, inbound_doorbell,ARCMSR_HBCMU_DRV2IOP_MESSAGE_CMD_DONE); if(!arcmsr_hbc_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'set srb high part physical address' timeout \n", acb->pci_unit); return FALSE; } } } break; } return TRUE; } /* ************************************************************************ ************************************************************************ */ static void arcmsr_enable_eoi_mode(struct AdapterControlBlock *acb) { switch (acb->adapter_type) { case ACB_ADAPTER_TYPE_A: case ACB_ADAPTER_TYPE_C: break; case ACB_ADAPTER_TYPE_B: { CHIP_REG_WRITE32(HBB_DOORBELL, 0, drv2iop_doorbell,ARCMSR_MESSAGE_ACTIVE_EOI_MODE); if(!arcmsr_hbb_wait_msgint_ready(acb)) { printf( "arcmsr%d: 'iop enable eoi mode' timeout \n", acb->pci_unit); return; } } break; } return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_iop_init(struct AdapterControlBlock *acb) { u_int32_t intmask_org; /* disable all outbound interrupt */ intmask_org=arcmsr_disable_allintr(acb); arcmsr_wait_firmware_ready(acb); arcmsr_iop_confirm(acb); arcmsr_get_firmware_spec(acb); /*start background rebuild*/ arcmsr_start_adapter_bgrb(acb); /* empty doorbell Qbuffer if door bell ringed */ arcmsr_clear_doorbell_queue_buffer(acb); arcmsr_enable_eoi_mode(acb); /* enable outbound Post Queue, outbound doorbell Interrupt */ arcmsr_enable_allintr(acb, intmask_org); acb->acb_flags |=ACB_F_IOP_INITED; return; } /* ********************************************************************** ********************************************************************** */ static void arcmsr_map_free_srb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct AdapterControlBlock *acb=arg; struct CommandControlBlock *srb_tmp; u_int8_t * dma_memptr; u_int32_t i; unsigned long srb_phyaddr=(unsigned long)segs->ds_addr; dma_memptr=acb->uncacheptr; acb->srb_phyaddr.phyaddr=srb_phyaddr; srb_tmp=(struct CommandControlBlock *)dma_memptr; for(i=0;idm_segs_dmat, /*flags*/0, &srb_tmp->dm_segs_dmamap)!=0) { acb->acb_flags |= ACB_F_MAPFREESRB_FAILD; printf("arcmsr%d:" " srb dmamap bus_dmamap_create error\n", acb->pci_unit); return; } srb_tmp->cdb_shifted_phyaddr=(acb->adapter_type==ACB_ADAPTER_TYPE_C)?srb_phyaddr:(srb_phyaddr >> 5); srb_tmp->acb=acb; acb->srbworkingQ[i]=acb->psrb_pool[i]=srb_tmp; srb_phyaddr=srb_phyaddr+SRB_SIZE; srb_tmp = (struct CommandControlBlock *)((unsigned long)srb_tmp+SRB_SIZE); } acb->vir2phy_offset=(unsigned long)srb_tmp-(unsigned long)srb_phyaddr; return; } /* ************************************************************************ ** ** ************************************************************************ */ static void arcmsr_free_resource(struct AdapterControlBlock *acb) { /* remove the control device */ if(acb->ioctl_dev != NULL) { destroy_dev(acb->ioctl_dev); } bus_dmamap_unload(acb->srb_dmat, acb->srb_dmamap); bus_dmamap_destroy(acb->srb_dmat, acb->srb_dmamap); bus_dma_tag_destroy(acb->srb_dmat); bus_dma_tag_destroy(acb->dm_segs_dmat); bus_dma_tag_destroy(acb->parent_dmat); return; } /* ************************************************************************ ************************************************************************ */ static u_int32_t arcmsr_initialize(device_t dev) { struct AdapterControlBlock *acb=device_get_softc(dev); u_int16_t pci_command; int i, j,max_coherent_size; switch (pci_get_devid(dev)) { case PCIDevVenIDARC1880: { acb->adapter_type=ACB_ADAPTER_TYPE_C; max_coherent_size=ARCMSR_SRBS_POOL_SIZE; } break; case PCIDevVenIDARC1200: case PCIDevVenIDARC1201: { acb->adapter_type=ACB_ADAPTER_TYPE_B; max_coherent_size=ARCMSR_SRBS_POOL_SIZE+(sizeof(struct HBB_MessageUnit)); } break; case PCIDevVenIDARC1110: case PCIDevVenIDARC1120: case PCIDevVenIDARC1130: case PCIDevVenIDARC1160: case PCIDevVenIDARC1170: case PCIDevVenIDARC1210: case PCIDevVenIDARC1220: case PCIDevVenIDARC1230: case PCIDevVenIDARC1231: case PCIDevVenIDARC1260: case PCIDevVenIDARC1261: case PCIDevVenIDARC1270: case PCIDevVenIDARC1280: case PCIDevVenIDARC1212: case PCIDevVenIDARC1222: case PCIDevVenIDARC1380: case PCIDevVenIDARC1381: case PCIDevVenIDARC1680: case PCIDevVenIDARC1681: { acb->adapter_type=ACB_ADAPTER_TYPE_A; max_coherent_size=ARCMSR_SRBS_POOL_SIZE; } break; default: { printf("arcmsr%d:" " unknown RAID adapter type \n", device_get_unit(dev)); return ENOMEM; } } - if(bus_dma_tag_create( /*parent*/ NULL, + if(bus_dma_tag_create( /*PCI parent*/ bus_get_dma_tag(dev), /*alignemnt*/ 1, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ BUS_SPACE_MAXSIZE_32BIT, /*nsegments*/ BUS_SPACE_UNRESTRICTED, /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, /*flags*/ 0, #if __FreeBSD_version >= 501102 /*lockfunc*/ NULL, /*lockarg*/ NULL, #endif &acb->parent_dmat) != 0) { printf("arcmsr%d: parent_dmat bus_dma_tag_create failure!\n", device_get_unit(dev)); return ENOMEM; } /* Create a single tag describing a region large enough to hold all of the s/g lists we will need. */ if(bus_dma_tag_create( /*parent_dmat*/ acb->parent_dmat, /*alignment*/ 1, /*boundary*/ 0, #ifdef PAE /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, #else /*lowaddr*/ BUS_SPACE_MAXADDR, #endif /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ ARCMSR_MAX_SG_ENTRIES * PAGE_SIZE * ARCMSR_MAX_FREESRB_NUM, /*nsegments*/ ARCMSR_MAX_SG_ENTRIES, /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, /*flags*/ 0, #if __FreeBSD_version >= 501102 /*lockfunc*/ busdma_lock_mutex, /*lockarg*/ &acb->qbuffer_lock, #endif &acb->dm_segs_dmat) != 0) { bus_dma_tag_destroy(acb->parent_dmat); printf("arcmsr%d: dm_segs_dmat bus_dma_tag_create failure!\n", device_get_unit(dev)); return ENOMEM; } /* DMA tag for our srb structures.... Allocate the freesrb memory */ if(bus_dma_tag_create( /*parent_dmat*/ acb->parent_dmat, /*alignment*/ 0x20, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ max_coherent_size, /*nsegments*/ 1, /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, /*flags*/ 0, #if __FreeBSD_version >= 501102 /*lockfunc*/ NULL, /*lockarg*/ NULL, #endif &acb->srb_dmat) != 0) { bus_dma_tag_destroy(acb->dm_segs_dmat); bus_dma_tag_destroy(acb->parent_dmat); printf("arcmsr%d: srb_dmat bus_dma_tag_create failure!\n", device_get_unit(dev)); return ENXIO; } /* Allocation for our srbs */ if(bus_dmamem_alloc(acb->srb_dmat, (void **)&acb->uncacheptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &acb->srb_dmamap) != 0) { bus_dma_tag_destroy(acb->srb_dmat); bus_dma_tag_destroy(acb->dm_segs_dmat); bus_dma_tag_destroy(acb->parent_dmat); printf("arcmsr%d: srb_dmat bus_dmamem_alloc failure!\n", device_get_unit(dev)); return ENXIO; } /* And permanently map them */ if(bus_dmamap_load(acb->srb_dmat, acb->srb_dmamap, acb->uncacheptr, max_coherent_size, arcmsr_map_free_srb, acb, /*flags*/0)) { bus_dma_tag_destroy(acb->srb_dmat); bus_dma_tag_destroy(acb->dm_segs_dmat); bus_dma_tag_destroy(acb->parent_dmat); printf("arcmsr%d: srb_dmat bus_dmamap_load failure!\n", device_get_unit(dev)); return ENXIO; } pci_command=pci_read_config(dev, PCIR_COMMAND, 2); pci_command |= PCIM_CMD_BUSMASTEREN; pci_command |= PCIM_CMD_PERRESPEN; pci_command |= PCIM_CMD_MWRICEN; /* Enable Busmaster/Mem */ pci_command |= PCIM_CMD_MEMEN; pci_write_config(dev, PCIR_COMMAND, pci_command, 2); switch(acb->adapter_type) { case ACB_ADAPTER_TYPE_A: { u_int32_t rid0=PCIR_BAR(0); vm_offset_t mem_base0; acb->sys_res_arcmsr[0]=bus_alloc_resource(dev,SYS_RES_MEMORY, &rid0, 0ul, ~0ul, 0x1000, RF_ACTIVE); if(acb->sys_res_arcmsr[0] == NULL) { arcmsr_free_resource(acb); printf("arcmsr%d: bus_alloc_resource failure!\n", device_get_unit(dev)); return ENOMEM; } if(rman_get_start(acb->sys_res_arcmsr[0]) <= 0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_start failure!\n", device_get_unit(dev)); return ENXIO; } mem_base0=(vm_offset_t) rman_get_virtual(acb->sys_res_arcmsr[0]); if(mem_base0==0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_virtual failure!\n", device_get_unit(dev)); return ENXIO; } acb->btag[0]=rman_get_bustag(acb->sys_res_arcmsr[0]); acb->bhandle[0]=rman_get_bushandle(acb->sys_res_arcmsr[0]); acb->pmu=(struct MessageUnit_UNION *)mem_base0; } break; case ACB_ADAPTER_TYPE_B: { struct HBB_MessageUnit *phbbmu; struct CommandControlBlock *freesrb; u_int32_t rid[]={ PCIR_BAR(0), PCIR_BAR(2) }; vm_offset_t mem_base[]={0,0}; for(i=0; i<2; i++) { if(i==0) { acb->sys_res_arcmsr[i]=bus_alloc_resource(dev,SYS_RES_MEMORY, &rid[i], 0ul, ~0ul, sizeof(struct HBB_DOORBELL), RF_ACTIVE); } else { acb->sys_res_arcmsr[i]=bus_alloc_resource(dev, SYS_RES_MEMORY, &rid[i], 0ul, ~0ul, sizeof(struct HBB_RWBUFFER), RF_ACTIVE); } if(acb->sys_res_arcmsr[i] == NULL) { arcmsr_free_resource(acb); printf("arcmsr%d: bus_alloc_resource %d failure!\n", device_get_unit(dev), i); return ENOMEM; } if(rman_get_start(acb->sys_res_arcmsr[i]) <= 0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_start %d failure!\n", device_get_unit(dev), i); return ENXIO; } mem_base[i]=(vm_offset_t) rman_get_virtual(acb->sys_res_arcmsr[i]); if(mem_base[i]==0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_virtual %d failure!\n", device_get_unit(dev), i); return ENXIO; } acb->btag[i]=rman_get_bustag(acb->sys_res_arcmsr[i]); acb->bhandle[i]=rman_get_bushandle(acb->sys_res_arcmsr[i]); } freesrb=(struct CommandControlBlock *)acb->uncacheptr; // acb->pmu=(struct MessageUnit_UNION *)&freesrb[ARCMSR_MAX_FREESRB_NUM]; acb->pmu=(struct MessageUnit_UNION *)((unsigned long)freesrb+ARCMSR_SRBS_POOL_SIZE); phbbmu=(struct HBB_MessageUnit *)acb->pmu; phbbmu->hbb_doorbell=(struct HBB_DOORBELL *)mem_base[0]; phbbmu->hbb_rwbuffer=(struct HBB_RWBUFFER *)mem_base[1]; } break; case ACB_ADAPTER_TYPE_C: { u_int32_t rid0=PCIR_BAR(1); vm_offset_t mem_base0; acb->sys_res_arcmsr[0]=bus_alloc_resource(dev,SYS_RES_MEMORY, &rid0, 0ul, ~0ul, sizeof(struct HBC_MessageUnit), RF_ACTIVE); if(acb->sys_res_arcmsr[0] == NULL) { arcmsr_free_resource(acb); printf("arcmsr%d: bus_alloc_resource failure!\n", device_get_unit(dev)); return ENOMEM; } if(rman_get_start(acb->sys_res_arcmsr[0]) <= 0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_start failure!\n", device_get_unit(dev)); return ENXIO; } mem_base0=(vm_offset_t) rman_get_virtual(acb->sys_res_arcmsr[0]); if(mem_base0==0) { arcmsr_free_resource(acb); printf("arcmsr%d: rman_get_virtual failure!\n", device_get_unit(dev)); return ENXIO; } acb->btag[0]=rman_get_bustag(acb->sys_res_arcmsr[0]); acb->bhandle[0]=rman_get_bushandle(acb->sys_res_arcmsr[0]); acb->pmu=(struct MessageUnit_UNION *)mem_base0; } break; } if(acb->acb_flags & ACB_F_MAPFREESRB_FAILD) { arcmsr_free_resource(acb); printf("arcmsr%d: map free srb failure!\n", device_get_unit(dev)); return ENXIO; } acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED|ACB_F_MESSAGE_RQBUFFER_CLEARED|ACB_F_MESSAGE_WQBUFFER_READ); acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER; /* ******************************************************************** ** init raid volume state ******************************************************************** */ for(i=0;idevstate[i][j]=ARECA_RAID_GONE; } } arcmsr_iop_init(acb); return(0); } /* ************************************************************************ ************************************************************************ */ static int arcmsr_attach(device_t dev) { struct AdapterControlBlock *acb=(struct AdapterControlBlock *)device_get_softc(dev); u_int32_t unit=device_get_unit(dev); struct ccb_setasync csa; struct cam_devq *devq; /* Device Queue to use for this SIM */ struct resource *irqres; int rid; if(acb == NULL) { printf("arcmsr%d: cannot allocate softc\n", unit); return (ENOMEM); } ARCMSR_LOCK_INIT(&acb->qbuffer_lock, "arcmsr Q buffer lock"); if(arcmsr_initialize(dev)) { printf("arcmsr%d: initialize failure!\n", unit); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); return ENXIO; } /* After setting up the adapter, map our interrupt */ rid=0; irqres=bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE); if(irqres == NULL || #if __FreeBSD_version >= 700025 bus_setup_intr(dev, irqres, INTR_TYPE_CAM|INTR_ENTROPY|INTR_MPSAFE, NULL, arcmsr_intr_handler, acb, &acb->ih)) { #else bus_setup_intr(dev, irqres, INTR_TYPE_CAM|INTR_ENTROPY|INTR_MPSAFE, arcmsr_intr_handler, acb, &acb->ih)) { #endif arcmsr_free_resource(acb); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); printf("arcmsr%d: unable to register interrupt handler!\n", unit); return ENXIO; } acb->irqres=irqres; acb->pci_dev=dev; acb->pci_unit=unit; /* * Now let the CAM generic SCSI layer find the SCSI devices on * the bus * start queue to reset to the idle loop. * * Create device queue of SIM(s) * (MAX_START_JOB - 1) : * max_sim_transactions */ devq=cam_simq_alloc(ARCMSR_MAX_START_JOB); if(devq == NULL) { arcmsr_free_resource(acb); bus_release_resource(dev, SYS_RES_IRQ, 0, acb->irqres); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); printf("arcmsr%d: cam_simq_alloc failure!\n", unit); return ENXIO; } #if __FreeBSD_version >= 700025 acb->psim=cam_sim_alloc(arcmsr_action, arcmsr_poll, "arcmsr", acb, unit, &acb->qbuffer_lock, 1, ARCMSR_MAX_OUTSTANDING_CMD, devq); #else acb->psim=cam_sim_alloc(arcmsr_action, arcmsr_poll, "arcmsr", acb, unit, 1, ARCMSR_MAX_OUTSTANDING_CMD, devq); #endif if(acb->psim == NULL) { arcmsr_free_resource(acb); bus_release_resource(dev, SYS_RES_IRQ, 0, acb->irqres); cam_simq_free(devq); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); printf("arcmsr%d: cam_sim_alloc failure!\n", unit); return ENXIO; } ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); #if __FreeBSD_version >= 700044 if(xpt_bus_register(acb->psim, dev, 0) != CAM_SUCCESS) { #else if(xpt_bus_register(acb->psim, 0) != CAM_SUCCESS) { #endif arcmsr_free_resource(acb); bus_release_resource(dev, SYS_RES_IRQ, 0, acb->irqres); cam_sim_free(acb->psim, /*free_devq*/TRUE); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); printf("arcmsr%d: xpt_bus_register failure!\n", unit); return ENXIO; } if(xpt_create_path(&acb->ppath, /* periph */ NULL, cam_sim_path(acb->psim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { arcmsr_free_resource(acb); bus_release_resource(dev, SYS_RES_IRQ, 0, acb->irqres); xpt_bus_deregister(cam_sim_path(acb->psim)); cam_sim_free(acb->psim, /* free_simq */ TRUE); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); printf("arcmsr%d: xpt_create_path failure!\n", unit); return ENXIO; } /* **************************************************** */ xpt_setup_ccb(&csa.ccb_h, acb->ppath, /*priority*/5); csa.ccb_h.func_code=XPT_SASYNC_CB; csa.event_enable=AC_FOUND_DEVICE|AC_LOST_DEVICE; csa.callback=arcmsr_async; csa.callback_arg=acb->psim; xpt_action((union ccb *)&csa); ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); /* Create the control device. */ acb->ioctl_dev=make_dev(&arcmsr_cdevsw, unit, UID_ROOT, GID_WHEEL /* GID_OPERATOR */, S_IRUSR | S_IWUSR, "arcmsr%d", unit); #if __FreeBSD_version < 503000 acb->ioctl_dev->si_drv1=acb; #endif #if __FreeBSD_version > 500005 (void)make_dev_alias(acb->ioctl_dev, "arc%d", unit); #endif arcmsr_callout_init(&acb->devmap_callout); callout_reset(&acb->devmap_callout, 60 * hz, arcmsr_polling_devmap, acb); return 0; } /* ************************************************************************ ************************************************************************ */ static int arcmsr_probe(device_t dev) { u_int32_t id; static char buf[256]; char x_type[]={"X-TYPE"}; char *type; int raid6 = 1; if (pci_get_vendor(dev) != PCI_VENDOR_ID_ARECA) { return (ENXIO); } switch(id=pci_get_devid(dev)) { case PCIDevVenIDARC1110: case PCIDevVenIDARC1200: case PCIDevVenIDARC1201: case PCIDevVenIDARC1210: raid6 = 0; /*FALLTHRU*/ case PCIDevVenIDARC1120: case PCIDevVenIDARC1130: case PCIDevVenIDARC1160: case PCIDevVenIDARC1170: case PCIDevVenIDARC1220: case PCIDevVenIDARC1230: case PCIDevVenIDARC1231: case PCIDevVenIDARC1260: case PCIDevVenIDARC1261: case PCIDevVenIDARC1270: case PCIDevVenIDARC1280: type = "SATA"; break; case PCIDevVenIDARC1212: case PCIDevVenIDARC1222: case PCIDevVenIDARC1380: case PCIDevVenIDARC1381: case PCIDevVenIDARC1680: case PCIDevVenIDARC1681: type = "SAS 3G"; break; case PCIDevVenIDARC1880: type = "SAS 6G"; break; default: type = x_type; break; } if(type == x_type) return(ENXIO); sprintf(buf, "Areca %s Host Adapter RAID Controller %s\n", type, raid6 ? "(RAID6 capable)" : ""); device_set_desc_copy(dev, buf); return (BUS_PROBE_DEFAULT); } /* ************************************************************************ ************************************************************************ */ static int arcmsr_shutdown(device_t dev) { u_int32_t i; u_int32_t intmask_org; struct CommandControlBlock *srb; struct AdapterControlBlock *acb=(struct AdapterControlBlock *)device_get_softc(dev); /* stop adapter background rebuild */ ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); /* disable all outbound interrupt */ intmask_org=arcmsr_disable_allintr(acb); arcmsr_stop_adapter_bgrb(acb); arcmsr_flush_adapter_cache(acb); /* abort all outstanding command */ acb->acb_flags |= ACB_F_SCSISTOPADAPTER; acb->acb_flags &= ~ACB_F_IOP_INITED; if(acb->srboutstandingcount!=0) { /*clear and abort all outbound posted Q*/ arcmsr_done4abort_postqueue(acb); /* talk to iop 331 outstanding command aborted*/ arcmsr_abort_allcmd(acb); for(i=0;ipsrb_pool[i]; if(srb->srb_state==ARCMSR_SRB_START) { srb->srb_state=ARCMSR_SRB_ABORTED; srb->pccb->ccb_h.status |= CAM_REQ_ABORTED; arcmsr_srb_complete(srb, 1); } } } acb->srboutstandingcount=0; acb->workingsrb_doneindex=0; acb->workingsrb_startindex=0; #ifdef ARCMSR_DEBUG1 acb->pktRequestCount = 0; acb->pktReturnCount = 0; #endif ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); return (0); } /* ************************************************************************ ************************************************************************ */ static int arcmsr_detach(device_t dev) { struct AdapterControlBlock *acb=(struct AdapterControlBlock *)device_get_softc(dev); int i; callout_stop(&acb->devmap_callout); bus_teardown_intr(dev, acb->irqres, acb->ih); arcmsr_shutdown(dev); arcmsr_free_resource(acb); for(i=0; (acb->sys_res_arcmsr[i]!=NULL) && (i<2); i++) { bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(i), acb->sys_res_arcmsr[i]); } bus_release_resource(dev, SYS_RES_IRQ, 0, acb->irqres); ARCMSR_LOCK_ACQUIRE(&acb->qbuffer_lock); xpt_async(AC_LOST_DEVICE, acb->ppath, NULL); xpt_free_path(acb->ppath); xpt_bus_deregister(cam_sim_path(acb->psim)); cam_sim_free(acb->psim, TRUE); ARCMSR_LOCK_RELEASE(&acb->qbuffer_lock); ARCMSR_LOCK_DESTROY(&acb->qbuffer_lock); return (0); } #ifdef ARCMSR_DEBUG1 static void arcmsr_dump_data(struct AdapterControlBlock *acb) { if((acb->pktRequestCount - acb->pktReturnCount) == 0) return; printf("Command Request Count =0x%x\n",acb->pktRequestCount); printf("Command Return Count =0x%x\n",acb->pktReturnCount); printf("Command (Req-Rtn) Count =0x%x\n",(acb->pktRequestCount - acb->pktReturnCount)); printf("Queued Command Count =0x%x\n",acb->srboutstandingcount); } #endif Index: head/sys/dev/asr/asr.c =================================================================== --- head/sys/dev/asr/asr.c (revision 232853) +++ head/sys/dev/asr/asr.c (revision 232854) @@ -1,3838 +1,3838 @@ /*- * Copyright (c) 1996-2000 Distributed Processing Technology Corporation * Copyright (c) 2000-2001 Adaptec Corporation * All rights reserved. * * TERMS AND CONDITIONS OF USE * * Redistribution and use in source form, with or without modification, are * permitted provided that redistributions of source code must retain the * above copyright notice, this list of conditions and the following disclaimer. * * This software is provided `as is' by Adaptec and any express or implied * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose, are disclaimed. In no * event shall Adaptec be liable for any direct, indirect, incidental, special, * exemplary or consequential damages (including, but not limited to, * procurement of substitute goods or services; loss of use, data, or profits; * or business interruptions) however caused and on any theory of liability, * whether in contract, strict liability, or tort (including negligence or * otherwise) arising in any way out of the use of this driver software, even * if advised of the possibility of such damage. * * SCSI I2O host adapter driver * * V1.10 2004/05/05 scottl@freebsd.org * - Massive cleanup of the driver to remove dead code and * non-conformant style. * - Removed most i386-specific code to make it more portable. * - Converted to the bus_space API. * V1.08 2001/08/21 Mark_Salyzyn@adaptec.com * - The 2000S and 2005S do not initialize on some machines, * increased timeout to 255ms from 50ms for the StatusGet * command. * V1.07 2001/05/22 Mark_Salyzyn@adaptec.com * - I knew this one was too good to be true. The error return * on ioctl commands needs to be compared to CAM_REQ_CMP, not * to the bit masked status. * V1.06 2001/05/08 Mark_Salyzyn@adaptec.com * - The 2005S that was supported is affectionately called the * Conjoined BAR Firmware. In order to support RAID-5 in a * 16MB low-cost configuration, Firmware was forced to go * to a Split BAR Firmware. This requires a separate IOP and * Messaging base address. * V1.05 2001/04/25 Mark_Salyzyn@adaptec.com * - Handle support for 2005S Zero Channel RAID solution. * - System locked up if the Adapter locked up. Do not try * to send other commands if the resetIOP command fails. The * fail outstanding command discovery loop was flawed as the * removal of the command from the list prevented discovering * all the commands. * - Comment changes to clarify driver. * - SysInfo searched for an EATA SmartROM, not an I2O SmartROM. * - We do not use the AC_FOUND_DEV event because of I2O. * Removed asr_async. * V1.04 2000/09/22 Mark_Salyzyn@adaptec.com, msmith@freebsd.org, * lampa@fee.vutbr.cz and Scott_Long@adaptec.com. * - Removed support for PM1554, PM2554 and PM2654 in Mode-0 * mode as this is confused with competitor adapters in run * mode. * - critical locking needed in ASR_ccbAdd and ASR_ccbRemove * to prevent operating system panic. * - moved default major number to 154 from 97. * V1.03 2000/07/12 Mark_Salyzyn@adaptec.com * - The controller is not actually an ASR (Adaptec SCSI RAID) * series that is visible, it's more of an internal code name. * remove any visible references within reason for now. * - bus_ptr->LUN was not correctly zeroed when initially * allocated causing a possible panic of the operating system * during boot. * V1.02 2000/06/26 Mark_Salyzyn@adaptec.com * - Code always fails for ASR_getTid affecting performance. * - initiated a set of changes that resulted from a formal * code inspection by Mark_Salyzyn@adaptec.com, * George_Dake@adaptec.com, Jeff_Zeak@adaptec.com, * Martin_Wilson@adaptec.com and Vincent_Trandoan@adaptec.com. * Their findings were focussed on the LCT & TID handler, and * all resulting changes were to improve code readability, * consistency or have a positive effect on performance. * V1.01 2000/06/14 Mark_Salyzyn@adaptec.com * - Passthrough returned an incorrect error. * - Passthrough did not migrate the intrinsic scsi layer wakeup * on command completion. * - generate control device nodes using make_dev and delete_dev. * - Performance affected by TID caching reallocing. * - Made suggested changes by Justin_Gibbs@adaptec.com * - use splcam instead of splbio. * - use cam_imask instead of bio_imask. * - use u_int8_t instead of u_char. * - use u_int16_t instead of u_short. * - use u_int32_t instead of u_long where appropriate. * - use 64 bit context handler instead of 32 bit. * - create_ccb should only allocate the worst case * requirements for the driver since CAM may evolve * making union ccb much larger than needed here. * renamed create_ccb to asr_alloc_ccb. * - go nutz justifying all debug prints as macros * defined at the top and remove unsightly ifdefs. * - INLINE STATIC viewed as confusing. Historically * utilized to affect code performance and debug * issues in OS, Compiler or OEM specific situations. * V1.00 2000/05/31 Mark_Salyzyn@adaptec.com * - Ported from FreeBSD 2.2.X DPT I2O driver. * changed struct scsi_xfer to union ccb/struct ccb_hdr * changed variable name xs to ccb * changed struct scsi_link to struct cam_path * changed struct scsibus_data to struct cam_sim * stopped using fordriver for holding on to the TID * use proprietary packet creation instead of scsi_inquire * CAM layer sends synchronize commands. */ #include #include /* TRUE=1 and FALSE=0 defined here */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) #include "opt_asr.h" #include #if defined(ASR_COMPAT) #define ASR_IOCTL_COMPAT #endif /* ASR_COMPAT */ #endif #include #include #include #define osdSwap4(x) ((u_long)ntohl((u_long)(x))) #define KVTOPHYS(x) vtophys(x) #include #include #include #include #include #include __FBSDID("$FreeBSD$"); #define ASR_VERSION 1 #define ASR_REVISION '1' #define ASR_SUBREVISION '0' #define ASR_MONTH 5 #define ASR_DAY 5 #define ASR_YEAR (2004 - 1980) /* * Debug macros to reduce the unsightly ifdefs */ #if (defined(DEBUG_ASR) || defined(DEBUG_ASR_USR_CMD) || defined(DEBUG_ASR_CMD)) static __inline void debug_asr_message(PI2O_MESSAGE_FRAME message) { u_int32_t * pointer = (u_int32_t *)message; u_int32_t length = I2O_MESSAGE_FRAME_getMessageSize(message); u_int32_t counter = 0; while (length--) { printf("%08lx%c", (u_long)*(pointer++), (((++counter & 7) == 0) || (length == 0)) ? '\n' : ' '); } } #endif /* DEBUG_ASR || DEBUG_ASR_USR_CMD || DEBUG_ASR_CMD */ #ifdef DEBUG_ASR /* Breaks on none STDC based compilers :-( */ #define debug_asr_printf(fmt,args...) printf(fmt, ##args) #define debug_asr_dump_message(message) debug_asr_message(message) #define debug_asr_print_path(ccb) xpt_print_path(ccb->ccb_h.path); #else /* DEBUG_ASR */ #define debug_asr_printf(fmt,args...) #define debug_asr_dump_message(message) #define debug_asr_print_path(ccb) #endif /* DEBUG_ASR */ /* * If DEBUG_ASR_CMD is defined: * 0 - Display incoming SCSI commands * 1 - add in a quick character before queueing. * 2 - add in outgoing message frames. */ #if (defined(DEBUG_ASR_CMD)) #define debug_asr_cmd_printf(fmt,args...) printf(fmt,##args) static __inline void debug_asr_dump_ccb(union ccb *ccb) { u_int8_t *cp = (unsigned char *)&(ccb->csio.cdb_io); int len = ccb->csio.cdb_len; while (len) { debug_asr_cmd_printf (" %02x", *(cp++)); --len; } } #if (DEBUG_ASR_CMD > 0) #define debug_asr_cmd1_printf debug_asr_cmd_printf #else #define debug_asr_cmd1_printf(fmt,args...) #endif #if (DEBUG_ASR_CMD > 1) #define debug_asr_cmd2_printf debug_asr_cmd_printf #define debug_asr_cmd2_dump_message(message) debug_asr_message(message) #else #define debug_asr_cmd2_printf(fmt,args...) #define debug_asr_cmd2_dump_message(message) #endif #else /* DEBUG_ASR_CMD */ #define debug_asr_cmd_printf(fmt,args...) #define debug_asr_dump_ccb(ccb) #define debug_asr_cmd1_printf(fmt,args...) #define debug_asr_cmd2_printf(fmt,args...) #define debug_asr_cmd2_dump_message(message) #endif /* DEBUG_ASR_CMD */ #if (defined(DEBUG_ASR_USR_CMD)) #define debug_usr_cmd_printf(fmt,args...) printf(fmt,##args) #define debug_usr_cmd_dump_message(message) debug_usr_message(message) #else /* DEBUG_ASR_USR_CMD */ #define debug_usr_cmd_printf(fmt,args...) #define debug_usr_cmd_dump_message(message) #endif /* DEBUG_ASR_USR_CMD */ #ifdef ASR_IOCTL_COMPAT #define dsDescription_size 46 /* Snug as a bug in a rug */ #endif /* ASR_IOCTL_COMPAT */ #include "dev/asr/dptsig.h" static dpt_sig_S ASR_sig = { { 'd', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION, PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM, FT_HBADRVR, 0, OEM_DPT, OS_FREE_BSD, CAP_ABOVE16MB, DEV_ALL, ADF_ALL_SC5, 0, 0, ASR_VERSION, ASR_REVISION, ASR_SUBREVISION, ASR_MONTH, ASR_DAY, ASR_YEAR, /* 01234567890123456789012345678901234567890123456789 < 50 chars */ "Adaptec FreeBSD 4.0.0 Unix SCSI I2O HBA Driver" /* ^^^^^ asr_attach alters these to match OS */ }; /* Configuration Definitions */ #define SG_SIZE 58 /* Scatter Gather list Size */ #define MAX_TARGET_ID 126 /* Maximum Target ID supported */ #define MAX_LUN 255 /* Maximum LUN Supported */ #define MAX_CHANNEL 7 /* Maximum Channel # Supported by driver */ #define MAX_INBOUND 2000 /* Max CCBs, Also Max Queue Size */ #define MAX_OUTBOUND 256 /* Maximum outbound frames/adapter */ #define MAX_INBOUND_SIZE 512 /* Maximum inbound frame size */ #define MAX_MAP 4194304L /* Maximum mapping size of IOP */ /* Also serves as the minimum map for */ /* the 2005S zero channel RAID product */ /* I2O register set */ #define I2O_REG_STATUS 0x30 #define I2O_REG_MASK 0x34 #define I2O_REG_TOFIFO 0x40 #define I2O_REG_FROMFIFO 0x44 #define Mask_InterruptsDisabled 0x08 /* * A MIX of performance and space considerations for TID lookups */ typedef u_int16_t tid_t; typedef struct { u_int32_t size; /* up to MAX_LUN */ tid_t TID[1]; } lun2tid_t; typedef struct { u_int32_t size; /* up to MAX_TARGET */ lun2tid_t * LUN[1]; } target2lun_t; /* * To ensure that we only allocate and use the worst case ccb here, lets * make our own local ccb union. If asr_alloc_ccb is utilized for another * ccb type, ensure that you add the additional structures into our local * ccb union. To ensure strict type checking, we will utilize the local * ccb definition wherever possible. */ union asr_ccb { struct ccb_hdr ccb_h; /* For convenience */ struct ccb_scsiio csio; struct ccb_setasync csa; }; struct Asr_status_mem { I2O_EXEC_STATUS_GET_REPLY status; U32 rstatus; }; /************************************************************************** ** ASR Host Adapter structure - One Structure For Each Host Adapter That ** ** Is Configured Into The System. The Structure Supplies Configuration ** ** Information, Status Info, Queue Info And An Active CCB List Pointer. ** ***************************************************************************/ typedef struct Asr_softc { device_t ha_dev; u_int16_t ha_irq; u_long ha_Base; /* base port for each board */ bus_size_t ha_blinkLED; bus_space_handle_t ha_i2o_bhandle; bus_space_tag_t ha_i2o_btag; bus_space_handle_t ha_frame_bhandle; bus_space_tag_t ha_frame_btag; I2O_IOP_ENTRY ha_SystemTable; LIST_HEAD(,ccb_hdr) ha_ccb; /* ccbs in use */ bus_dma_tag_t ha_parent_dmat; bus_dma_tag_t ha_statusmem_dmat; bus_dmamap_t ha_statusmem_dmamap; struct Asr_status_mem * ha_statusmem; u_int32_t ha_rstatus_phys; u_int32_t ha_status_phys; struct cam_path * ha_path[MAX_CHANNEL+1]; struct cam_sim * ha_sim[MAX_CHANNEL+1]; struct resource * ha_mem_res; struct resource * ha_mes_res; struct resource * ha_irq_res; void * ha_intr; PI2O_LCT ha_LCT; /* Complete list of devices */ #define le_type IdentityTag[0] #define I2O_BSA 0x20 #define I2O_FCA 0x40 #define I2O_SCSI 0x00 #define I2O_PORT 0x80 #define I2O_UNKNOWN 0x7F #define le_bus IdentityTag[1] #define le_target IdentityTag[2] #define le_lun IdentityTag[3] target2lun_t * ha_targets[MAX_CHANNEL+1]; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME ha_Msgs; u_long ha_Msgs_Phys; u_int8_t ha_in_reset; #define HA_OPERATIONAL 0 #define HA_IN_RESET 1 #define HA_OFF_LINE 2 #define HA_OFF_LINE_RECOVERY 3 /* Configuration information */ /* The target id maximums we take */ u_int8_t ha_MaxBus; /* Maximum bus */ u_int8_t ha_MaxId; /* Maximum target ID */ u_int8_t ha_MaxLun; /* Maximum target LUN */ u_int8_t ha_SgSize; /* Max SG elements */ u_int8_t ha_pciBusNum; u_int8_t ha_pciDeviceNum; u_int8_t ha_adapter_target[MAX_CHANNEL+1]; u_int16_t ha_QueueSize; /* Max outstanding commands */ u_int16_t ha_Msgs_Count; /* Links into other parents and HBAs */ struct Asr_softc * ha_next; /* HBA list */ struct cdev *ha_devt; } Asr_softc_t; static Asr_softc_t *Asr_softc_list; /* * Prototypes of the routines we have in this object. */ /* I2O HDM interface */ static int asr_probe(device_t dev); static int asr_attach(device_t dev); static int asr_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td); static int asr_open(struct cdev *dev, int32_t flags, int32_t ifmt, struct thread *td); static int asr_close(struct cdev *dev, int flags, int ifmt, struct thread *td); static int asr_intr(Asr_softc_t *sc); static void asr_timeout(void *arg); static int ASR_init(Asr_softc_t *sc); static int ASR_acquireLct(Asr_softc_t *sc); static int ASR_acquireHrt(Asr_softc_t *sc); static void asr_action(struct cam_sim *sim, union ccb *ccb); static void asr_poll(struct cam_sim *sim); static int ASR_queue(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message); /* * Here is the auto-probe structure used to nest our tests appropriately * during the startup phase of the operating system. */ static device_method_t asr_methods[] = { DEVMETHOD(device_probe, asr_probe), DEVMETHOD(device_attach, asr_attach), { 0, 0 } }; static driver_t asr_driver = { "asr", asr_methods, sizeof(Asr_softc_t) }; static devclass_t asr_devclass; DRIVER_MODULE(asr, pci, asr_driver, asr_devclass, 0, 0); MODULE_DEPEND(asr, pci, 1, 1, 1); MODULE_DEPEND(asr, cam, 1, 1, 1); /* * devsw for asr hba driver * * only ioctl is used. the sd driver provides all other access. */ static struct cdevsw asr_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = asr_open, .d_close = asr_close, .d_ioctl = asr_ioctl, .d_name = "asr", }; /* I2O support routines */ static __inline u_int32_t asr_get_FromFIFO(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_FROMFIFO)); } static __inline u_int32_t asr_get_ToFIFO(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_TOFIFO)); } static __inline u_int32_t asr_get_intr(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_MASK)); } static __inline u_int32_t asr_get_status(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_STATUS)); } static __inline void asr_set_FromFIFO(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_FROMFIFO, val); } static __inline void asr_set_ToFIFO(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_TOFIFO, val); } static __inline void asr_set_intr(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_MASK, val); } static __inline void asr_set_frame(Asr_softc_t *sc, void *frame, u_int32_t offset, int len) { bus_space_write_region_4(sc->ha_frame_btag, sc->ha_frame_bhandle, offset, (u_int32_t *)frame, len); } /* * Fill message with default. */ static PI2O_MESSAGE_FRAME ASR_fillMessage(void *Message, u_int16_t size) { PI2O_MESSAGE_FRAME Message_Ptr; Message_Ptr = (I2O_MESSAGE_FRAME *)Message; bzero(Message_Ptr, size); I2O_MESSAGE_FRAME_setVersionOffset(Message_Ptr, I2O_VERSION_11); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, (size + sizeof(U32) - 1) >> 2); I2O_MESSAGE_FRAME_setInitiatorAddress (Message_Ptr, 1); KASSERT(Message_Ptr != NULL, ("Message_Ptr == NULL")); return (Message_Ptr); } /* ASR_fillMessage */ #define EMPTY_QUEUE (0xffffffff) static __inline U32 ASR_getMessage(Asr_softc_t *sc) { U32 MessageOffset; MessageOffset = asr_get_ToFIFO(sc); if (MessageOffset == EMPTY_QUEUE) MessageOffset = asr_get_ToFIFO(sc); return (MessageOffset); } /* ASR_getMessage */ /* Issue a polled command */ static U32 ASR_initiateCp(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { U32 Mask = 0xffffffff; U32 MessageOffset; u_int Delay = 1500; /* * ASR_initiateCp is only used for synchronous commands and will * be made more resiliant to adapter delays since commands like * resetIOP can cause the adapter to be deaf for a little time. */ while (((MessageOffset = ASR_getMessage(sc)) == EMPTY_QUEUE) && (--Delay != 0)) { DELAY (10000); } if (MessageOffset != EMPTY_QUEUE) { asr_set_frame(sc, Message, MessageOffset, I2O_MESSAGE_FRAME_getMessageSize(Message)); /* * Disable the Interrupts */ Mask = asr_get_intr(sc); asr_set_intr(sc, Mask | Mask_InterruptsDisabled); asr_set_ToFIFO(sc, MessageOffset); } return (Mask); } /* ASR_initiateCp */ /* * Reset the adapter. */ static U32 ASR_resetIOP(Asr_softc_t *sc) { I2O_EXEC_IOP_RESET_MESSAGE Message; PI2O_EXEC_IOP_RESET_MESSAGE Message_Ptr; U32 * Reply_Ptr; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_IOP_RESET_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_IOP_RESET_MESSAGE)); I2O_EXEC_IOP_RESET_MESSAGE_setFunction(Message_Ptr, I2O_EXEC_IOP_RESET); /* * Reset the Reply Status */ Reply_Ptr = &sc->ha_statusmem->rstatus; *Reply_Ptr = 0; I2O_EXEC_IOP_RESET_MESSAGE_setStatusWordLowAddress(Message_Ptr, sc->ha_rstatus_phys); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { /* * Wait for a response (Poll), timeouts are dangerous if * the card is truly responsive. We assume response in 2s. */ u_int8_t Delay = 200; while ((*Reply_Ptr == 0) && (--Delay != 0)) { DELAY (10000); } /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); KASSERT(*Reply_Ptr != 0, ("*Reply_Ptr == 0")); return(*Reply_Ptr); } KASSERT(Old != 0xffffffff, ("Old == -1")); return (0); } /* ASR_resetIOP */ /* * Get the curent state of the adapter */ static PI2O_EXEC_STATUS_GET_REPLY ASR_getStatus(Asr_softc_t *sc) { I2O_EXEC_STATUS_GET_MESSAGE Message; PI2O_EXEC_STATUS_GET_MESSAGE Message_Ptr; PI2O_EXEC_STATUS_GET_REPLY buffer; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_STATUS_GET_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_STATUS_GET_MESSAGE)); I2O_EXEC_STATUS_GET_MESSAGE_setFunction(Message_Ptr, I2O_EXEC_STATUS_GET); I2O_EXEC_STATUS_GET_MESSAGE_setReplyBufferAddressLow(Message_Ptr, sc->ha_status_phys); /* This one is a Byte Count */ I2O_EXEC_STATUS_GET_MESSAGE_setReplyBufferLength(Message_Ptr, sizeof(I2O_EXEC_STATUS_GET_REPLY)); /* * Reset the Reply Status */ buffer = &sc->ha_statusmem->status; bzero(buffer, sizeof(I2O_EXEC_STATUS_GET_REPLY)); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { /* * Wait for a response (Poll), timeouts are dangerous if * the card is truly responsive. We assume response in 50ms. */ u_int8_t Delay = 255; while (*((U8 * volatile)&(buffer->SyncByte)) == 0) { if (--Delay == 0) { buffer = NULL; break; } DELAY (1000); } /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); return (buffer); } return (NULL); } /* ASR_getStatus */ /* * Check if the device is a SCSI I2O HBA, and add it to the list. */ /* * Probe for ASR controller. If we find it, we will use it. * virtual adapters. */ static int asr_probe(device_t dev) { u_int32_t id; id = (pci_get_device(dev) << 16) | pci_get_vendor(dev); if ((id == 0xA5011044) || (id == 0xA5111044)) { device_set_desc(dev, "Adaptec Caching SCSI RAID"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } /* asr_probe */ static __inline union asr_ccb * asr_alloc_ccb(Asr_softc_t *sc) { union asr_ccb *new_ccb; if ((new_ccb = (union asr_ccb *)malloc(sizeof(*new_ccb), M_DEVBUF, M_WAITOK | M_ZERO)) != NULL) { new_ccb->ccb_h.pinfo.priority = 1; new_ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX; new_ccb->ccb_h.spriv_ptr0 = sc; } return (new_ccb); } /* asr_alloc_ccb */ static __inline void asr_free_ccb(union asr_ccb *free_ccb) { free(free_ccb, M_DEVBUF); } /* asr_free_ccb */ /* * Print inquiry data `carefully' */ static void ASR_prstring(u_int8_t *s, int len) { while ((--len >= 0) && (*s) && (*s != ' ') && (*s != '-')) { printf ("%c", *(s++)); } } /* ASR_prstring */ /* * Send a message synchronously and without Interrupt to a ccb. */ static int ASR_queue_s(union asr_ccb *ccb, PI2O_MESSAGE_FRAME Message) { int s; U32 Mask; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64(Message, (long)ccb); /* Prevent interrupt service */ s = splcam (); Mask = asr_get_intr(sc); asr_set_intr(sc, Mask | Mask_InterruptsDisabled); if (ASR_queue(sc, Message) == EMPTY_QUEUE) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_REQUEUE_REQ; } /* * Wait for this board to report a finished instruction. */ while ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { (void)asr_intr (sc); } /* Re-enable Interrupts */ asr_set_intr(sc, Mask); splx(s); return (ccb->ccb_h.status); } /* ASR_queue_s */ /* * Send a message synchronously to an Asr_softc_t. */ static int ASR_queue_c(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { union asr_ccb *ccb; int status; if ((ccb = asr_alloc_ccb (sc)) == NULL) { return (CAM_REQUEUE_REQ); } status = ASR_queue_s (ccb, Message); asr_free_ccb(ccb); return (status); } /* ASR_queue_c */ /* * Add the specified ccb to the active queue */ static __inline void ASR_ccbAdd(Asr_softc_t *sc, union asr_ccb *ccb) { int s; s = splcam(); LIST_INSERT_HEAD(&(sc->ha_ccb), &(ccb->ccb_h), sim_links.le); if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) { /* * RAID systems can take considerable time to * complete some commands given the large cache * flashes switching from write back to write thru. */ ccb->ccb_h.timeout = 6 * 60 * 1000; } ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, (ccb->ccb_h.timeout * hz) / 1000); } splx(s); } /* ASR_ccbAdd */ /* * Remove the specified ccb from the active queue. */ static __inline void ASR_ccbRemove(Asr_softc_t *sc, union asr_ccb *ccb) { int s; s = splcam(); untimeout(asr_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch); LIST_REMOVE(&(ccb->ccb_h), sim_links.le); splx(s); } /* ASR_ccbRemove */ /* * Fail all the active commands, so they get re-issued by the operating * system. */ static void ASR_failActiveCommands(Asr_softc_t *sc) { struct ccb_hdr *ccb; int s; s = splcam(); /* * We do not need to inform the CAM layer that we had a bus * reset since we manage it on our own, this also prevents the * SCSI_DELAY settling that would be required on other systems. * The `SCSI_DELAY' has already been handled by the card via the * acquisition of the LCT table while we are at CAM priority level. * for (int bus = 0; bus <= sc->ha_MaxBus; ++bus) { * xpt_async (AC_BUS_RESET, sc->ha_path[bus], NULL); * } */ while ((ccb = LIST_FIRST(&(sc->ha_ccb))) != NULL) { ASR_ccbRemove (sc, (union asr_ccb *)ccb); ccb->status &= ~CAM_STATUS_MASK; ccb->status |= CAM_REQUEUE_REQ; /* Nothing Transfered */ ((struct ccb_scsiio *)ccb)->resid = ((struct ccb_scsiio *)ccb)->dxfer_len; if (ccb->path) { xpt_done ((union ccb *)ccb); } else { wakeup (ccb); } } splx(s); } /* ASR_failActiveCommands */ /* * The following command causes the HBA to reset the specific bus */ static void ASR_resetBus(Asr_softc_t *sc, int bus) { I2O_HBA_BUS_RESET_MESSAGE Message; I2O_HBA_BUS_RESET_MESSAGE *Message_Ptr; PI2O_LCT_ENTRY Device; Message_Ptr = (I2O_HBA_BUS_RESET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_HBA_BUS_RESET_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&Message_Ptr->StdMessageFrame, I2O_HBA_BUS_RESET); for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (((Device->le_type & I2O_PORT) != 0) && (Device->le_bus == bus)) { I2O_MESSAGE_FRAME_setTargetAddress( &Message_Ptr->StdMessageFrame, I2O_LCT_ENTRY_getLocalTID(Device)); /* Asynchronous command, with no expectations */ (void)ASR_queue(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); break; } } } /* ASR_resetBus */ static __inline int ASR_getBlinkLedCode(Asr_softc_t *sc) { U8 blink; if (sc == NULL) return (0); blink = bus_space_read_1(sc->ha_frame_btag, sc->ha_frame_bhandle, sc->ha_blinkLED + 1); if (blink != 0xBC) return (0); blink = bus_space_read_1(sc->ha_frame_btag, sc->ha_frame_bhandle, sc->ha_blinkLED); return (blink); } /* ASR_getBlinkCode */ /* * Determine the address of an TID lookup. Must be done at high priority * since the address can be changed by other threads of execution. * * Returns NULL pointer if not indexible (but will attempt to generate * an index if `new_entry' flag is set to TRUE). * * All addressible entries are to be guaranteed zero if never initialized. */ static tid_t * ASR_getTidAddress(Asr_softc_t *sc, int bus, int target, int lun, int new_entry) { target2lun_t *bus_ptr; lun2tid_t *target_ptr; unsigned new_size; /* * Validity checking of incoming parameters. More of a bound * expansion limit than an issue with the code dealing with the * values. * * sc must be valid before it gets here, so that check could be * dropped if speed a critical issue. */ if ((sc == NULL) || (bus > MAX_CHANNEL) || (target > sc->ha_MaxId) || (lun > sc->ha_MaxLun)) { debug_asr_printf("(%lx,%d,%d,%d) target out of range\n", (u_long)sc, bus, target, lun); return (NULL); } /* * See if there is an associated bus list. * * for performance, allocate in size of BUS_CHUNK chunks. * BUS_CHUNK must be a power of two. This is to reduce * fragmentation effects on the allocations. */ #define BUS_CHUNK 8 new_size = ((target + BUS_CHUNK - 1) & ~(BUS_CHUNK - 1)); if ((bus_ptr = sc->ha_targets[bus]) == NULL) { /* * Allocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((sc->ha_targets[bus] = bus_ptr = (target2lun_t *)malloc ( sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to allocate bus list\n"); return (NULL); } bus_ptr->size = new_size + 1; } else if (bus_ptr->size <= new_size) { target2lun_t * new_bus_ptr; /* * Reallocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((new_bus_ptr = (target2lun_t *)malloc ( sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to reallocate bus list\n"); return (NULL); } /* * Copy the whole thing, safer, simpler coding * and not really performance critical at this point. */ bcopy(bus_ptr, new_bus_ptr, sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * (bus_ptr->size - 1))); sc->ha_targets[bus] = new_bus_ptr; free(bus_ptr, M_TEMP); bus_ptr = new_bus_ptr; bus_ptr->size = new_size + 1; } /* * We now have the bus list, lets get to the target list. * Since most systems have only *one* lun, we do not allocate * in chunks as above, here we allow one, then in chunk sizes. * TARGET_CHUNK must be a power of two. This is to reduce * fragmentation effects on the allocations. */ #define TARGET_CHUNK 8 if ((new_size = lun) != 0) { new_size = ((lun + TARGET_CHUNK - 1) & ~(TARGET_CHUNK - 1)); } if ((target_ptr = bus_ptr->LUN[target]) == NULL) { /* * Allocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((bus_ptr->LUN[target] = target_ptr = (lun2tid_t *)malloc ( sizeof(*target_ptr) + (sizeof(target_ptr->TID) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to allocate target list\n"); return (NULL); } target_ptr->size = new_size + 1; } else if (target_ptr->size <= new_size) { lun2tid_t * new_target_ptr; /* * Reallocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((new_target_ptr = (lun2tid_t *)malloc ( sizeof(*target_ptr) + (sizeof(target_ptr->TID) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to reallocate target list\n"); return (NULL); } /* * Copy the whole thing, safer, simpler coding * and not really performance critical at this point. */ bcopy(target_ptr, new_target_ptr, sizeof(*target_ptr) + (sizeof(target_ptr->TID) * (target_ptr->size - 1))); bus_ptr->LUN[target] = new_target_ptr; free(target_ptr, M_TEMP); target_ptr = new_target_ptr; target_ptr->size = new_size + 1; } /* * Now, acquire the TID address from the LUN indexed list. */ return (&(target_ptr->TID[lun])); } /* ASR_getTidAddress */ /* * Get a pre-existing TID relationship. * * If the TID was never set, return (tid_t)-1. * * should use mutex rather than spl. */ static __inline tid_t ASR_getTid(Asr_softc_t *sc, int bus, int target, int lun) { tid_t *tid_ptr; int s; tid_t retval; s = splcam(); if (((tid_ptr = ASR_getTidAddress(sc, bus, target, lun, FALSE)) == NULL) /* (tid_t)0 or (tid_t)-1 indicate no TID */ || (*tid_ptr == (tid_t)0)) { splx(s); return ((tid_t)-1); } retval = *tid_ptr; splx(s); return (retval); } /* ASR_getTid */ /* * Set a TID relationship. * * If the TID was not set, return (tid_t)-1. * * should use mutex rather than spl. */ static __inline tid_t ASR_setTid(Asr_softc_t *sc, int bus, int target, int lun, tid_t TID) { tid_t *tid_ptr; int s; if (TID != (tid_t)-1) { if (TID == 0) { return ((tid_t)-1); } s = splcam(); if ((tid_ptr = ASR_getTidAddress(sc, bus, target, lun, TRUE)) == NULL) { splx(s); return ((tid_t)-1); } *tid_ptr = TID; splx(s); } return (TID); } /* ASR_setTid */ /*-------------------------------------------------------------------------*/ /* Function ASR_rescan */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* */ /* This Function Will rescan the adapter and resynchronize any data */ /* */ /* Return : 0 For OK, Error Code Otherwise */ /*-------------------------------------------------------------------------*/ static int ASR_rescan(Asr_softc_t *sc) { int bus; int error; /* * Re-acquire the LCT table and synchronize us to the adapter. */ if ((error = ASR_acquireLct(sc)) == 0) { error = ASR_acquireHrt(sc); } if (error != 0) { return error; } bus = sc->ha_MaxBus; /* Reset all existing cached TID lookups */ do { int target, event = 0; /* * Scan for all targets on this bus to see if they * got affected by the rescan. */ for (target = 0; target <= sc->ha_MaxId; ++target) { int lun; /* Stay away from the controller ID */ if (target == sc->ha_adapter_target[bus]) { continue; } for (lun = 0; lun <= sc->ha_MaxLun; ++lun) { PI2O_LCT_ENTRY Device; tid_t TID = (tid_t)-1; tid_t LastTID; /* * See if the cached TID changed. Search for * the device in our new LCT. */ for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY)(((U32 *)sc->ha_LCT) + I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if ((Device->le_type != I2O_UNKNOWN) && (Device->le_bus == bus) && (Device->le_target == target) && (Device->le_lun == lun) && (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF)) { TID = I2O_LCT_ENTRY_getLocalTID( Device); break; } } /* * Indicate to the OS that the label needs * to be recalculated, or that the specific * open device is no longer valid (Merde) * because the cached TID changed. */ LastTID = ASR_getTid (sc, bus, target, lun); if (LastTID != TID) { struct cam_path * path; if (xpt_create_path(&path, /*periph*/NULL, cam_sim_path(sc->ha_sim[bus]), target, lun) != CAM_REQ_CMP) { if (TID == (tid_t)-1) { event |= AC_LOST_DEVICE; } else { event |= AC_INQ_CHANGED | AC_GETDEV_CHANGED; } } else { if (TID == (tid_t)-1) { xpt_async( AC_LOST_DEVICE, path, NULL); } else if (LastTID == (tid_t)-1) { struct ccb_getdev ccb; xpt_setup_ccb( &(ccb.ccb_h), path, /*priority*/5); xpt_async( AC_FOUND_DEVICE, path, &ccb); } else { xpt_async( AC_INQ_CHANGED, path, NULL); xpt_async( AC_GETDEV_CHANGED, path, NULL); } } } /* * We have the option of clearing the * cached TID for it to be rescanned, or to * set it now even if the device never got * accessed. We chose the later since we * currently do not use the condition that * the TID ever got cached. */ ASR_setTid (sc, bus, target, lun, TID); } } /* * The xpt layer can not handle multiple events at the * same call. */ if (event & AC_LOST_DEVICE) { xpt_async(AC_LOST_DEVICE, sc->ha_path[bus], NULL); } if (event & AC_INQ_CHANGED) { xpt_async(AC_INQ_CHANGED, sc->ha_path[bus], NULL); } if (event & AC_GETDEV_CHANGED) { xpt_async(AC_GETDEV_CHANGED, sc->ha_path[bus], NULL); } } while (--bus >= 0); return (error); } /* ASR_rescan */ /*-------------------------------------------------------------------------*/ /* Function ASR_reset */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* */ /* This Function Will reset the adapter and resynchronize any data */ /* */ /* Return : None */ /*-------------------------------------------------------------------------*/ static int ASR_reset(Asr_softc_t *sc) { int s, retVal; s = splcam(); if ((sc->ha_in_reset == HA_IN_RESET) || (sc->ha_in_reset == HA_OFF_LINE_RECOVERY)) { splx (s); return (EBUSY); } /* * Promotes HA_OPERATIONAL to HA_IN_RESET, * or HA_OFF_LINE to HA_OFF_LINE_RECOVERY. */ ++(sc->ha_in_reset); if (ASR_resetIOP(sc) == 0) { debug_asr_printf ("ASR_resetIOP failed\n"); /* * We really need to take this card off-line, easier said * than make sense. Better to keep retrying for now since if a * UART cable is connected the blinkLEDs the adapter is now in * a hard state requiring action from the monitor commands to * the HBA to continue. For debugging waiting forever is a * good thing. In a production system, however, one may wish * to instead take the card off-line ... */ /* Wait Forever */ while (ASR_resetIOP(sc) == 0); } retVal = ASR_init (sc); splx (s); if (retVal != 0) { debug_asr_printf ("ASR_init failed\n"); sc->ha_in_reset = HA_OFF_LINE; return (ENXIO); } if (ASR_rescan (sc) != 0) { debug_asr_printf ("ASR_rescan failed\n"); } ASR_failActiveCommands (sc); if (sc->ha_in_reset == HA_OFF_LINE_RECOVERY) { printf ("asr%d: Brining adapter back on-line\n", sc->ha_path[0] ? cam_sim_unit(xpt_path_sim(sc->ha_path[0])) : 0); } sc->ha_in_reset = HA_OPERATIONAL; return (0); } /* ASR_reset */ /* * Device timeout handler. */ static void asr_timeout(void *arg) { union asr_ccb *ccb = (union asr_ccb *)arg; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); int s; debug_asr_print_path(ccb); debug_asr_printf("timed out"); /* * Check if the adapter has locked up? */ if ((s = ASR_getBlinkLedCode(sc)) != 0) { /* Reset Adapter */ printf ("asr%d: Blink LED 0x%x resetting adapter\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), s); if (ASR_reset (sc) == ENXIO) { /* Try again later */ ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, (ccb->ccb_h.timeout * hz) / 1000); } return; } /* * Abort does not function on the ASR card!!! Walking away from * the SCSI command is also *very* dangerous. A SCSI BUS reset is * our best bet, followed by a complete adapter reset if that fails. */ s = splcam(); /* Check if we already timed out once to raise the issue */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_CMD_TIMEOUT) { debug_asr_printf (" AGAIN\nreinitializing adapter\n"); if (ASR_reset (sc) == ENXIO) { ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, (ccb->ccb_h.timeout * hz) / 1000); } splx(s); return; } debug_asr_printf ("\nresetting bus\n"); /* If the BUS reset does not take, then an adapter reset is next! */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT; ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, (ccb->ccb_h.timeout * hz) / 1000); ASR_resetBus (sc, cam_sim_bus(xpt_path_sim(ccb->ccb_h.path))); xpt_async (AC_BUS_RESET, ccb->ccb_h.path, NULL); splx(s); } /* asr_timeout */ /* * send a message asynchronously */ static int ASR_queue(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { U32 MessageOffset; union asr_ccb *ccb; debug_asr_printf("Host Command Dump:\n"); debug_asr_dump_message(Message); ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64(Message); if ((MessageOffset = ASR_getMessage(sc)) != EMPTY_QUEUE) { asr_set_frame(sc, Message, MessageOffset, I2O_MESSAGE_FRAME_getMessageSize(Message)); if (ccb) { ASR_ccbAdd (sc, ccb); } /* Post the command */ asr_set_ToFIFO(sc, MessageOffset); } else { if (ASR_getBlinkLedCode(sc)) { /* * Unlikely we can do anything if we can't grab a * message frame :-(, but lets give it a try. */ (void)ASR_reset(sc); } } return (MessageOffset); } /* ASR_queue */ /* Simple Scatter Gather elements */ #define SG(SGL,Index,Flags,Buffer,Size) \ I2O_FLAGS_COUNT_setCount( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index].FlagsCount), \ Size); \ I2O_FLAGS_COUNT_setFlags( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index].FlagsCount), \ I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | (Flags)); \ I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index]), \ (Buffer == NULL) ? 0 : KVTOPHYS(Buffer)) /* * Retrieve Parameter Group. */ static void * ASR_getParams(Asr_softc_t *sc, tid_t TID, int Group, void *Buffer, unsigned BufferSize) { struct paramGetMessage { I2O_UTIL_PARAMS_GET_MESSAGE M; char F[sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)]; struct Operations { I2O_PARAM_OPERATIONS_LIST_HEADER Header; I2O_PARAM_OPERATION_ALL_TEMPLATE Template[1]; } O; } Message; struct Operations *Operations_Ptr; I2O_UTIL_PARAMS_GET_MESSAGE *Message_Ptr; struct ParamBuffer { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; char Info[1]; } *Buffer_Ptr; Message_Ptr = (I2O_UTIL_PARAMS_GET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) + sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)); Operations_Ptr = (struct Operations *)((char *)Message_Ptr + sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) + sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)); bzero(Operations_Ptr, sizeof(struct Operations)); I2O_PARAM_OPERATIONS_LIST_HEADER_setOperationCount( &(Operations_Ptr->Header), 1); I2O_PARAM_OPERATION_ALL_TEMPLATE_setOperation( &(Operations_Ptr->Template[0]), I2O_PARAMS_OPERATION_FIELD_GET); I2O_PARAM_OPERATION_ALL_TEMPLATE_setFieldCount( &(Operations_Ptr->Template[0]), 0xFFFF); I2O_PARAM_OPERATION_ALL_TEMPLATE_setGroupNumber( &(Operations_Ptr->Template[0]), Group); Buffer_Ptr = (struct ParamBuffer *)Buffer; bzero(Buffer_Ptr, BufferSize); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), I2O_VERSION_11 + (((sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setTargetAddress (&(Message_Ptr->StdMessageFrame), TID); I2O_MESSAGE_FRAME_setFunction (&(Message_Ptr->StdMessageFrame), I2O_UTIL_PARAMS_GET); /* * Set up the buffers as scatter gather elements. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER, Operations_Ptr, sizeof(struct Operations)); SG(&(Message_Ptr->SGL), 1, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, Buffer_Ptr, BufferSize); if ((ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) == CAM_REQ_CMP) && (Buffer_Ptr->Header.ResultCount)) { return ((void *)(Buffer_Ptr->Info)); } return (NULL); } /* ASR_getParams */ /* * Acquire the LCT information. */ static int ASR_acquireLct(Asr_softc_t *sc) { PI2O_EXEC_LCT_NOTIFY_MESSAGE Message_Ptr; PI2O_SGE_SIMPLE_ELEMENT sg; int MessageSizeInBytes; caddr_t v; int len; I2O_LCT Table; PI2O_LCT_ENTRY Entry; /* * sc value assumed valid */ MessageSizeInBytes = sizeof(I2O_EXEC_LCT_NOTIFY_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT); if ((Message_Ptr = (PI2O_EXEC_LCT_NOTIFY_MESSAGE)malloc( MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { return (ENOMEM); } (void)ASR_fillMessage((void *)Message_Ptr, MessageSizeInBytes); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_LCT_NOTIFY_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_LCT_NOTIFY); I2O_EXEC_LCT_NOTIFY_MESSAGE_setClassIdentifier(Message_Ptr, I2O_CLASS_MATCH_ANYCLASS); /* * Call the LCT table to determine the number of device entries * to reserve space for. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &Table, sizeof(I2O_LCT)); /* * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ I2O_LCT_setTableSize(&Table, (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY)) >> 2); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); /* * Determine the size of the LCT table. */ if (sc->ha_LCT) { free(sc->ha_LCT, M_TEMP); } /* * malloc only generates contiguous memory when less than a * page is expected. We must break the request up into an SG list ... */ if (((len = (I2O_LCT_getTableSize(&Table) << 2)) <= (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY))) || (len > (128 * 1024))) { /* Arbitrary */ free(Message_Ptr, M_TEMP); return (EINVAL); } if ((sc->ha_LCT = (PI2O_LCT)malloc (len, M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); return (ENOMEM); } /* * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ I2O_LCT_setTableSize(sc->ha_LCT, (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY)) >> 2); /* * Convert the access to the LCT table into a SG list. */ sg = Message_Ptr->SGL.u.Simple; v = (caddr_t)(sc->ha_LCT); for (;;) { int next, base, span; span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go contiguously */ while ((len > 0) && (base == next)) { int size; next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } /* Construct the Flags */ I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); { int rw = I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT; if (len <= 0) { rw = (I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER); } I2O_FLAGS_COUNT_setFlags(&(sg->FlagsCount), rw); } if (len <= 0) { break; } /* * Incrementing requires resizing of the packet. */ ++sg; MessageSizeInBytes += sizeof(*sg); I2O_MESSAGE_FRAME_setMessageSize( &(Message_Ptr->StdMessageFrame), I2O_MESSAGE_FRAME_getMessageSize( &(Message_Ptr->StdMessageFrame)) + (sizeof(*sg) / sizeof(U32))); { PI2O_EXEC_LCT_NOTIFY_MESSAGE NewMessage_Ptr; if ((NewMessage_Ptr = (PI2O_EXEC_LCT_NOTIFY_MESSAGE) malloc(MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { free(sc->ha_LCT, M_TEMP); sc->ha_LCT = NULL; free(Message_Ptr, M_TEMP); return (ENOMEM); } span = ((caddr_t)sg) - (caddr_t)Message_Ptr; bcopy(Message_Ptr, NewMessage_Ptr, span); free(Message_Ptr, M_TEMP); sg = (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)NewMessage_Ptr) + span); Message_Ptr = NewMessage_Ptr; } } { int retval; retval = ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); if (retval != CAM_REQ_CMP) { return (ENODEV); } } /* If the LCT table grew, lets truncate accesses */ if (I2O_LCT_getTableSize(&Table) < I2O_LCT_getTableSize(sc->ha_LCT)) { I2O_LCT_setTableSize(sc->ha_LCT, I2O_LCT_getTableSize(&Table)); } for (Entry = sc->ha_LCT->LCTEntry; Entry < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Entry) { Entry->le_type = I2O_UNKNOWN; switch (I2O_CLASS_ID_getClass(&(Entry->ClassID))) { case I2O_CLASS_RANDOM_BLOCK_STORAGE: Entry->le_type = I2O_BSA; break; case I2O_CLASS_SCSI_PERIPHERAL: Entry->le_type = I2O_SCSI; break; case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: Entry->le_type = I2O_FCA; break; case I2O_CLASS_BUS_ADAPTER_PORT: Entry->le_type = I2O_PORT | I2O_SCSI; /* FALLTHRU */ case I2O_CLASS_FIBRE_CHANNEL_PORT: if (I2O_CLASS_ID_getClass(&(Entry->ClassID)) == I2O_CLASS_FIBRE_CHANNEL_PORT) { Entry->le_type = I2O_PORT | I2O_FCA; } { struct ControllerInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_HBA_SCSI_CONTROLLER_INFO_SCALAR Info; } Buffer; PI2O_HBA_SCSI_CONTROLLER_INFO_SCALAR Info; Entry->le_bus = 0xff; Entry->le_target = 0xff; Entry->le_lun = 0xff; if ((Info = (PI2O_HBA_SCSI_CONTROLLER_INFO_SCALAR) ASR_getParams(sc, I2O_LCT_ENTRY_getLocalTID(Entry), I2O_HBA_SCSI_CONTROLLER_INFO_GROUP_NO, &Buffer, sizeof(struct ControllerInfo))) == NULL) { continue; } Entry->le_target = I2O_HBA_SCSI_CONTROLLER_INFO_SCALAR_getInitiatorID( Info); Entry->le_lun = 0; } /* FALLTHRU */ default: continue; } { struct DeviceInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_DPT_DEVICE_INFO_SCALAR Info; } Buffer; PI2O_DPT_DEVICE_INFO_SCALAR Info; Entry->le_bus = 0xff; Entry->le_target = 0xff; Entry->le_lun = 0xff; if ((Info = (PI2O_DPT_DEVICE_INFO_SCALAR) ASR_getParams(sc, I2O_LCT_ENTRY_getLocalTID(Entry), I2O_DPT_DEVICE_INFO_GROUP_NO, &Buffer, sizeof(struct DeviceInfo))) == NULL) { continue; } Entry->le_type |= I2O_DPT_DEVICE_INFO_SCALAR_getDeviceType(Info); Entry->le_bus = I2O_DPT_DEVICE_INFO_SCALAR_getBus(Info); if ((Entry->le_bus > sc->ha_MaxBus) && (Entry->le_bus <= MAX_CHANNEL)) { sc->ha_MaxBus = Entry->le_bus; } Entry->le_target = I2O_DPT_DEVICE_INFO_SCALAR_getIdentifier(Info); Entry->le_lun = I2O_DPT_DEVICE_INFO_SCALAR_getLunInfo(Info); } } /* * A zero return value indicates success. */ return (0); } /* ASR_acquireLct */ /* * Initialize a message frame. * We assume that the CDB has already been set up, so all we do here is * generate the Scatter Gather list. */ static PI2O_MESSAGE_FRAME ASR_init_message(union asr_ccb *ccb, PI2O_MESSAGE_FRAME Message) { PI2O_MESSAGE_FRAME Message_Ptr; PI2O_SGE_SIMPLE_ELEMENT sg; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); vm_size_t size, len; caddr_t v; U32 MessageSize; int next, span, base, rw; int target = ccb->ccb_h.target_id; int lun = ccb->ccb_h.target_lun; int bus =cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); tid_t TID; /* We only need to zero out the PRIVATE_SCSI_SCB_EXECUTE_MESSAGE */ Message_Ptr = (I2O_MESSAGE_FRAME *)Message; bzero(Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT))); if ((TID = ASR_getTid (sc, bus, target, lun)) == (tid_t)-1) { PI2O_LCT_ENTRY Device; TID = 0; for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT) + I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if ((Device->le_type != I2O_UNKNOWN) && (Device->le_bus == bus) && (Device->le_target == target) && (Device->le_lun == lun) && (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF)) { TID = I2O_LCT_ENTRY_getLocalTID(Device); ASR_setTid(sc, Device->le_bus, Device->le_target, Device->le_lun, TID); break; } } } if (TID == (tid_t)0) { return (NULL); } I2O_MESSAGE_FRAME_setTargetAddress(Message_Ptr, TID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setTID( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, TID); I2O_MESSAGE_FRAME_setVersionOffset(Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress (Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction(Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode ( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags ( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); /* * We do not need any (optional byteswapping) method access to * the Initiator & Transaction context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64(Message, (long)ccb); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); /* * copy the cdb over */ PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, ccb->csio.cdb_len); bcopy(&(ccb->csio.cdb_io), ((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)->CDB, ccb->csio.cdb_len); /* * Given a buffer describing a transfer, set up a scatter/gather map * in a ccb to map that SCSI transfer. */ rw = (ccb->ccb_h.flags & CAM_DIR_IN) ? 0 : I2O_SGL_FLAGS_DIR; PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags ( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, (ccb->csio.dxfer_len) ? ((rw) ? (I2O_SCB_FLAG_XFER_TO_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER) : (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)) : (I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); /* * Given a transfer described by a `data', fill in the SG list. */ sg = &((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)->SGL.u.Simple[0]; len = ccb->csio.dxfer_len; v = ccb->csio.data_ptr; KASSERT(ccb->csio.dxfer_len >= 0, ("csio.dxfer_len < 0")); MessageSize = I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setByteCount( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, len); while ((len > 0) && (sg < &((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE) Message_Ptr)->SGL.u.Simple[SG_SIZE])) { span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go contiguously */ while ((len > 0) && (base == next)) { next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); if (len == 0) { rw |= I2O_SGL_FLAGS_LAST_ELEMENT; } I2O_FLAGS_COUNT_setFlags(&(sg->FlagsCount), I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | rw); ++sg; MessageSize += sizeof(*sg) / sizeof(U32); } /* We always do the request sense ... */ if ((span = ccb->csio.sense_len) == 0) { span = sizeof(ccb->csio.sense_data); } SG(sg, 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &(ccb->csio.sense_data), span); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, MessageSize + (sizeof(*sg) / sizeof(U32))); return (Message_Ptr); } /* ASR_init_message */ /* * Reset the adapter. */ static U32 ASR_initOutBound(Asr_softc_t *sc) { struct initOutBoundMessage { I2O_EXEC_OUTBOUND_INIT_MESSAGE M; U32 R; } Message; PI2O_EXEC_OUTBOUND_INIT_MESSAGE Message_Ptr; U32 *volatile Reply_Ptr; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_OUTBOUND_INIT_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_OUTBOUND_INIT_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_OUTBOUND_INIT); I2O_EXEC_OUTBOUND_INIT_MESSAGE_setHostPageFrameSize(Message_Ptr, PAGE_SIZE); I2O_EXEC_OUTBOUND_INIT_MESSAGE_setOutboundMFrameSize(Message_Ptr, sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)); /* * Reset the Reply Status */ *(Reply_Ptr = (U32 *)((char *)Message_Ptr + sizeof(I2O_EXEC_OUTBOUND_INIT_MESSAGE))) = 0; SG (&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT, Reply_Ptr, sizeof(U32)); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { u_long size, addr; /* * Wait for a response (Poll). */ while (*Reply_Ptr < I2O_EXEC_OUTBOUND_INIT_REJECTED); /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); /* * Populate the outbound table. */ if (sc->ha_Msgs == NULL) { /* Allocate the reply frames */ size = sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) * sc->ha_Msgs_Count; /* * contigmalloc only works reliably at * initialization time. */ if ((sc->ha_Msgs = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) contigmalloc (size, M_DEVBUF, M_WAITOK, 0ul, 0xFFFFFFFFul, (u_long)sizeof(U32), 0ul)) != NULL) { bzero(sc->ha_Msgs, size); sc->ha_Msgs_Phys = KVTOPHYS(sc->ha_Msgs); } } /* Initialize the outbound FIFO */ if (sc->ha_Msgs != NULL) for(size = sc->ha_Msgs_Count, addr = sc->ha_Msgs_Phys; size; --size) { asr_set_FromFIFO(sc, addr); addr += sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME); } return (*Reply_Ptr); } return (0); } /* ASR_initOutBound */ /* * Set the system table */ static int ASR_setSysTab(Asr_softc_t *sc) { PI2O_EXEC_SYS_TAB_SET_MESSAGE Message_Ptr; PI2O_SET_SYSTAB_HEADER SystemTable; Asr_softc_t * ha; PI2O_SGE_SIMPLE_ELEMENT sg; int retVal; if ((SystemTable = (PI2O_SET_SYSTAB_HEADER)malloc ( sizeof(I2O_SET_SYSTAB_HEADER), M_TEMP, M_WAITOK | M_ZERO)) == NULL) { return (ENOMEM); } for (ha = Asr_softc_list; ha; ha = ha->ha_next) { ++SystemTable->NumberEntries; } if ((Message_Ptr = (PI2O_EXEC_SYS_TAB_SET_MESSAGE)malloc ( sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + ((3+SystemTable->NumberEntries) * sizeof(I2O_SGE_SIMPLE_ELEMENT)), M_TEMP, M_WAITOK)) == NULL) { free(SystemTable, M_TEMP); return (ENOMEM); } (void)ASR_fillMessage((void *)Message_Ptr, sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + ((3+SystemTable->NumberEntries) * sizeof(I2O_SGE_SIMPLE_ELEMENT))); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_SYS_TAB_SET); /* * Call the LCT table to determine the number of device entries * to reserve space for. * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ sg = (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( &(Message_Ptr->StdMessageFrame)) & 0xF0) >> 2)); SG(sg, 0, I2O_SGL_FLAGS_DIR, SystemTable, sizeof(I2O_SET_SYSTAB_HEADER)); ++sg; for (ha = Asr_softc_list; ha; ha = ha->ha_next) { SG(sg, 0, ((ha->ha_next) ? (I2O_SGL_FLAGS_DIR) : (I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER)), &(ha->ha_SystemTable), sizeof(ha->ha_SystemTable)); ++sg; } SG(sg, 0, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER, NULL, 0); SG(sg, 1, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, NULL, 0); retVal = ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); free(SystemTable, M_TEMP); return (retVal); } /* ASR_setSysTab */ static int ASR_acquireHrt(Asr_softc_t *sc) { I2O_EXEC_HRT_GET_MESSAGE Message; I2O_EXEC_HRT_GET_MESSAGE *Message_Ptr; struct { I2O_HRT Header; I2O_HRT_ENTRY Entry[MAX_CHANNEL]; } Hrt; u_int8_t NumberOfEntries; PI2O_HRT_ENTRY Entry; bzero(&Hrt, sizeof (Hrt)); Message_Ptr = (I2O_EXEC_HRT_GET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_EXEC_HRT_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_HRT_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction (&(Message_Ptr->StdMessageFrame), I2O_EXEC_HRT_GET); /* * Set up the buffers as scatter gather elements. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &Hrt, sizeof(Hrt)); if (ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) != CAM_REQ_CMP) { return (ENODEV); } if ((NumberOfEntries = I2O_HRT_getNumberEntries(&Hrt.Header)) > (MAX_CHANNEL + 1)) { NumberOfEntries = MAX_CHANNEL + 1; } for (Entry = Hrt.Header.HRTEntry; NumberOfEntries != 0; ++Entry, --NumberOfEntries) { PI2O_LCT_ENTRY Device; for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (I2O_LCT_ENTRY_getLocalTID(Device) == (I2O_HRT_ENTRY_getAdapterID(Entry) & 0xFFF)) { Device->le_bus = I2O_HRT_ENTRY_getAdapterID( Entry) >> 16; if ((Device->le_bus > sc->ha_MaxBus) && (Device->le_bus <= MAX_CHANNEL)) { sc->ha_MaxBus = Device->le_bus; } } } } return (0); } /* ASR_acquireHrt */ /* * Enable the adapter. */ static int ASR_enableSys(Asr_softc_t *sc) { I2O_EXEC_SYS_ENABLE_MESSAGE Message; PI2O_EXEC_SYS_ENABLE_MESSAGE Message_Ptr; Message_Ptr = (PI2O_EXEC_SYS_ENABLE_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_SYS_ENABLE_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_SYS_ENABLE); return (ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) != 0); } /* ASR_enableSys */ /* * Perform the stages necessary to initialize the adapter */ static int ASR_init(Asr_softc_t *sc) { return ((ASR_initOutBound(sc) == 0) || (ASR_setSysTab(sc) != CAM_REQ_CMP) || (ASR_enableSys(sc) != CAM_REQ_CMP)); } /* ASR_init */ /* * Send a Synchronize Cache command to the target device. */ static void ASR_sync(Asr_softc_t *sc, int bus, int target, int lun) { tid_t TID; /* * We will not synchronize the device when there are outstanding * commands issued by the OS (this is due to a locked up device, * as the OS normally would flush all outstanding commands before * issuing a shutdown or an adapter reset). */ if ((sc != NULL) && (LIST_FIRST(&(sc->ha_ccb)) != NULL) && ((TID = ASR_getTid (sc, bus, target, lun)) != (tid_t)-1) && (TID != (tid_t)0)) { PRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message; PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr; Message_Ptr = (PRIVATE_SCSI_SCB_EXECUTE_MESSAGE *)&Message; bzero(Message_Ptr, sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize( (PI2O_MESSAGE_FRAME)Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress ( (PI2O_MESSAGE_FRAME)Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_MESSAGE_FRAME_setTargetAddress( (PI2O_MESSAGE_FRAME)Message_Ptr, TID); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode ( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setTID(Message_Ptr, TID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength(Message_Ptr, 6); Message_Ptr->CDB[0] = SYNCHRONIZE_CACHE; Message_Ptr->CDB[1] = (lun << 5); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); } } static void ASR_synchronize(Asr_softc_t *sc) { int bus, target, lun; for (bus = 0; bus <= sc->ha_MaxBus; ++bus) { for (target = 0; target <= sc->ha_MaxId; ++target) { for (lun = 0; lun <= sc->ha_MaxLun; ++lun) { ASR_sync(sc,bus,target,lun); } } } } /* * Reset the HBA, targets and BUS. * Currently this resets *all* the SCSI busses. */ static __inline void asr_hbareset(Asr_softc_t *sc) { ASR_synchronize(sc); (void)ASR_reset(sc); } /* asr_hbareset */ /* * A reduced copy of the real pci_map_mem, incorporating the MAX_MAP * limit and a reduction in error checking (in the pre 4.0 case). */ static int asr_pci_map_mem(device_t dev, Asr_softc_t *sc) { int rid; u_int32_t p, l, s; /* * I2O specification says we must find first *memory* mapped BAR */ for (rid = 0; rid < 4; rid++) { p = pci_read_config(dev, PCIR_BAR(rid), sizeof(p)); if ((p & 1) == 0) { break; } } /* * Give up? */ if (rid >= 4) { rid = 0; } rid = PCIR_BAR(rid); p = pci_read_config(dev, rid, sizeof(p)); pci_write_config(dev, rid, -1, sizeof(p)); l = 0 - (pci_read_config(dev, rid, sizeof(l)) & ~15); pci_write_config(dev, rid, p, sizeof(p)); if (l > MAX_MAP) { l = MAX_MAP; } /* * The 2005S Zero Channel RAID solution is not a perfect PCI * citizen. It asks for 4MB on BAR0, and 0MB on BAR1, once * enabled it rewrites the size of BAR0 to 2MB, sets BAR1 to * BAR0+2MB and sets it's size to 2MB. The IOP registers are * accessible via BAR0, the messaging registers are accessible * via BAR1. If the subdevice code is 50 to 59 decimal. */ s = pci_read_config(dev, PCIR_DEVVENDOR, sizeof(s)); if (s != 0xA5111044) { s = pci_read_config(dev, PCIR_SUBVEND_0, sizeof(s)); if ((((ADPTDOMINATOR_SUB_ID_START ^ s) & 0xF000FFFF) == 0) && (ADPTDOMINATOR_SUB_ID_START <= s) && (s <= ADPTDOMINATOR_SUB_ID_END)) { l = MAX_MAP; /* Conjoined BAR Raptor Daptor */ } } p &= ~15; sc->ha_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, p, p + l, l, RF_ACTIVE); if (sc->ha_mem_res == NULL) { return (0); } sc->ha_Base = rman_get_start(sc->ha_mem_res); sc->ha_i2o_bhandle = rman_get_bushandle(sc->ha_mem_res); sc->ha_i2o_btag = rman_get_bustag(sc->ha_mem_res); if (s == 0xA5111044) { /* Split BAR Raptor Daptor */ if ((rid += sizeof(u_int32_t)) >= PCIR_BAR(4)) { return (0); } p = pci_read_config(dev, rid, sizeof(p)); pci_write_config(dev, rid, -1, sizeof(p)); l = 0 - (pci_read_config(dev, rid, sizeof(l)) & ~15); pci_write_config(dev, rid, p, sizeof(p)); if (l > MAX_MAP) { l = MAX_MAP; } p &= ~15; sc->ha_mes_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, p, p + l, l, RF_ACTIVE); if (sc->ha_mes_res == NULL) { return (0); } sc->ha_frame_bhandle = rman_get_bushandle(sc->ha_mes_res); sc->ha_frame_btag = rman_get_bustag(sc->ha_mes_res); } else { sc->ha_frame_bhandle = sc->ha_i2o_bhandle; sc->ha_frame_btag = sc->ha_i2o_btag; } return (1); } /* asr_pci_map_mem */ /* * A simplified copy of the real pci_map_int with additional * registration requirements. */ static int asr_pci_map_int(device_t dev, Asr_softc_t *sc) { int rid = 0; sc->ha_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->ha_irq_res == NULL) { return (0); } if (bus_setup_intr(dev, sc->ha_irq_res, INTR_TYPE_CAM | INTR_ENTROPY, NULL, (driver_intr_t *)asr_intr, (void *)sc, &(sc->ha_intr))) { return (0); } sc->ha_irq = pci_read_config(dev, PCIR_INTLINE, sizeof(char)); return (1); } /* asr_pci_map_int */ static void asr_status_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { Asr_softc_t *sc; if (error) return; sc = (Asr_softc_t *)arg; /* XXX * The status word can be at a 64-bit address, but the existing * accessor macros simply cannot manipulate 64-bit addresses. */ sc->ha_status_phys = (u_int32_t)segs[0].ds_addr + offsetof(struct Asr_status_mem, status); sc->ha_rstatus_phys = (u_int32_t)segs[0].ds_addr + offsetof(struct Asr_status_mem, rstatus); } static int asr_alloc_dma(Asr_softc_t *sc) { device_t dev; dev = sc->ha_dev; - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ha_parent_dmat)) { device_printf(dev, "Cannot allocate parent DMA tag\n"); return (ENOMEM); } if (bus_dma_tag_create(sc->ha_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(sc->ha_statusmem),/* maxsize */ 1, /* nsegments */ sizeof(sc->ha_statusmem),/* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ha_statusmem_dmat)) { device_printf(dev, "Cannot allocate status DMA tag\n"); bus_dma_tag_destroy(sc->ha_parent_dmat); return (ENOMEM); } if (bus_dmamem_alloc(sc->ha_statusmem_dmat, (void **)&sc->ha_statusmem, BUS_DMA_NOWAIT, &sc->ha_statusmem_dmamap)) { device_printf(dev, "Cannot allocate status memory\n"); bus_dma_tag_destroy(sc->ha_statusmem_dmat); bus_dma_tag_destroy(sc->ha_parent_dmat); return (ENOMEM); } (void)bus_dmamap_load(sc->ha_statusmem_dmat, sc->ha_statusmem_dmamap, sc->ha_statusmem, sizeof(sc->ha_statusmem), asr_status_cb, sc, 0); return (0); } static void asr_release_dma(Asr_softc_t *sc) { if (sc->ha_rstatus_phys != 0) bus_dmamap_unload(sc->ha_statusmem_dmat, sc->ha_statusmem_dmamap); if (sc->ha_statusmem != NULL) bus_dmamem_free(sc->ha_statusmem_dmat, sc->ha_statusmem, sc->ha_statusmem_dmamap); if (sc->ha_statusmem_dmat != NULL) bus_dma_tag_destroy(sc->ha_statusmem_dmat); if (sc->ha_parent_dmat != NULL) bus_dma_tag_destroy(sc->ha_parent_dmat); } /* * Attach the devices, and virtual devices to the driver list. */ static int asr_attach(device_t dev) { PI2O_EXEC_STATUS_GET_REPLY status; PI2O_LCT_ENTRY Device; Asr_softc_t *sc, **ha; struct scsi_inquiry_data *iq; int bus, size, unit; int error; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ha_dev = dev; if (Asr_softc_list == NULL) { /* * Fixup the OS revision as saved in the dptsig for the * engine (dptioctl.h) to pick up. */ bcopy(osrelease, &ASR_sig.dsDescription[16], 5); } /* * Initialize the software structure */ LIST_INIT(&(sc->ha_ccb)); /* Link us into the HA list */ for (ha = &Asr_softc_list; *ha; ha = &((*ha)->ha_next)); *(ha) = sc; /* * This is the real McCoy! */ if (!asr_pci_map_mem(dev, sc)) { device_printf(dev, "could not map memory\n"); return(ENXIO); } /* Enable if not formerly enabled */ pci_write_config(dev, PCIR_COMMAND, pci_read_config(dev, PCIR_COMMAND, sizeof(char)) | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN, sizeof(char)); sc->ha_pciBusNum = pci_get_bus(dev); sc->ha_pciDeviceNum = (pci_get_slot(dev) << 3) | pci_get_function(dev); if ((error = asr_alloc_dma(sc)) != 0) return (error); /* Check if the device is there? */ if (ASR_resetIOP(sc) == 0) { device_printf(dev, "Cannot reset adapter\n"); asr_release_dma(sc); return (EIO); } status = &sc->ha_statusmem->status; if (ASR_getStatus(sc) == NULL) { device_printf(dev, "could not initialize hardware\n"); asr_release_dma(sc); return(ENODEV); } sc->ha_SystemTable.OrganizationID = status->OrganizationID; sc->ha_SystemTable.IOP_ID = status->IOP_ID; sc->ha_SystemTable.I2oVersion = status->I2oVersion; sc->ha_SystemTable.IopState = status->IopState; sc->ha_SystemTable.MessengerType = status->MessengerType; sc->ha_SystemTable.InboundMessageFrameSize = status->InboundMFrameSize; sc->ha_SystemTable.MessengerInfo.InboundMessagePortAddressLow = (U32)(sc->ha_Base + I2O_REG_TOFIFO); /* XXX 64-bit */ if (!asr_pci_map_int(dev, (void *)sc)) { device_printf(dev, "could not map interrupt\n"); asr_release_dma(sc); return(ENXIO); } /* Adjust the maximim inbound count */ if (((sc->ha_QueueSize = I2O_EXEC_STATUS_GET_REPLY_getMaxInboundMFrames(status)) > MAX_INBOUND) || (sc->ha_QueueSize == 0)) { sc->ha_QueueSize = MAX_INBOUND; } /* Adjust the maximum outbound count */ if (((sc->ha_Msgs_Count = I2O_EXEC_STATUS_GET_REPLY_getMaxOutboundMFrames(status)) > MAX_OUTBOUND) || (sc->ha_Msgs_Count == 0)) { sc->ha_Msgs_Count = MAX_OUTBOUND; } if (sc->ha_Msgs_Count > sc->ha_QueueSize) { sc->ha_Msgs_Count = sc->ha_QueueSize; } /* Adjust the maximum SG size to adapter */ if ((size = (I2O_EXEC_STATUS_GET_REPLY_getInboundMFrameSize(status) << 2)) > MAX_INBOUND_SIZE) { size = MAX_INBOUND_SIZE; } sc->ha_SgSize = (size - sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) + sizeof(I2O_SG_ELEMENT)) / sizeof(I2O_SGE_SIMPLE_ELEMENT); /* * Only do a bus/HBA reset on the first time through. On this * first time through, we do not send a flush to the devices. */ if (ASR_init(sc) == 0) { struct BufferInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_DPT_EXEC_IOP_BUFFERS_SCALAR Info; } Buffer; PI2O_DPT_EXEC_IOP_BUFFERS_SCALAR Info; #define FW_DEBUG_BLED_OFFSET 8 if ((Info = (PI2O_DPT_EXEC_IOP_BUFFERS_SCALAR) ASR_getParams(sc, 0, I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO, &Buffer, sizeof(struct BufferInfo))) != NULL) { sc->ha_blinkLED = FW_DEBUG_BLED_OFFSET + I2O_DPT_EXEC_IOP_BUFFERS_SCALAR_getSerialOutputOffset(Info); } if (ASR_acquireLct(sc) == 0) { (void)ASR_acquireHrt(sc); } } else { device_printf(dev, "failed to initialize\n"); asr_release_dma(sc); return(ENXIO); } /* * Add in additional probe responses for more channels. We * are reusing the variable `target' for a channel loop counter. * Done here because of we need both the acquireLct and * acquireHrt data. */ for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (Device->le_type == I2O_UNKNOWN) { continue; } if (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF) { if (Device->le_target > sc->ha_MaxId) { sc->ha_MaxId = Device->le_target; } if (Device->le_lun > sc->ha_MaxLun) { sc->ha_MaxLun = Device->le_lun; } } if (((Device->le_type & I2O_PORT) != 0) && (Device->le_bus <= MAX_CHANNEL)) { /* Do not increase MaxId for efficiency */ sc->ha_adapter_target[Device->le_bus] = Device->le_target; } } /* * Print the HBA model number as inquired from the card. */ device_printf(dev, " "); if ((iq = (struct scsi_inquiry_data *)malloc( sizeof(struct scsi_inquiry_data), M_TEMP, M_WAITOK | M_ZERO)) != NULL) { PRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message; PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr; int posted = 0; Message_Ptr = (PRIVATE_SCSI_SCB_EXECUTE_MESSAGE *)&Message; bzero(Message_Ptr, sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize( (PI2O_MESSAGE_FRAME)Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress( (PI2O_MESSAGE_FRAME)Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setInterpret(Message_Ptr, 1); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength(Message_Ptr, 6); Message_Ptr->CDB[0] = INQUIRY; Message_Ptr->CDB[4] = (unsigned char)sizeof(struct scsi_inquiry_data); if (Message_Ptr->CDB[4] == 0) { Message_Ptr->CDB[4] = 255; } PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setByteCount( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, sizeof(struct scsi_inquiry_data)); SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, iq, sizeof(struct scsi_inquiry_data)); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); if (iq->vendor[0] && (iq->vendor[0] != ' ')) { printf (" "); ASR_prstring (iq->vendor, 8); ++posted; } if (iq->product[0] && (iq->product[0] != ' ')) { printf (" "); ASR_prstring (iq->product, 16); ++posted; } if (iq->revision[0] && (iq->revision[0] != ' ')) { printf (" FW Rev. "); ASR_prstring (iq->revision, 4); ++posted; } free(iq, M_TEMP); if (posted) { printf (","); } } printf (" %d channel, %d CCBs, Protocol I2O\n", sc->ha_MaxBus + 1, (sc->ha_QueueSize > MAX_INBOUND) ? MAX_INBOUND : sc->ha_QueueSize); for (bus = 0; bus <= sc->ha_MaxBus; ++bus) { struct cam_devq * devq; int QueueSize = sc->ha_QueueSize; if (QueueSize > MAX_INBOUND) { QueueSize = MAX_INBOUND; } /* * Create the device queue for our SIM(s). */ if ((devq = cam_simq_alloc(QueueSize)) == NULL) { continue; } /* * Construct our first channel SIM entry */ sc->ha_sim[bus] = cam_sim_alloc(asr_action, asr_poll, "asr", sc, unit, &Giant, 1, QueueSize, devq); if (sc->ha_sim[bus] == NULL) { continue; } if (xpt_bus_register(sc->ha_sim[bus], dev, bus) != CAM_SUCCESS){ cam_sim_free(sc->ha_sim[bus], /*free_devq*/TRUE); sc->ha_sim[bus] = NULL; continue; } if (xpt_create_path(&(sc->ha_path[bus]), /*periph*/NULL, cam_sim_path(sc->ha_sim[bus]), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister( cam_sim_path(sc->ha_sim[bus])); cam_sim_free(sc->ha_sim[bus], /*free_devq*/TRUE); sc->ha_sim[bus] = NULL; continue; } } /* * Generate the device node information */ sc->ha_devt = make_dev(&asr_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "asr%d", unit); if (sc->ha_devt != NULL) (void)make_dev_alias(sc->ha_devt, "rdpti%d", unit); sc->ha_devt->si_drv1 = sc; return(0); } /* asr_attach */ static void asr_poll(struct cam_sim *sim) { asr_intr(cam_sim_softc(sim)); } /* asr_poll */ static void asr_action(struct cam_sim *sim, union ccb *ccb) { struct Asr_softc *sc; debug_asr_printf("asr_action(%lx,%lx{%x})\n", (u_long)sim, (u_long)ccb, ccb->ccb_h.func_code); CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("asr_action\n")); ccb->ccb_h.spriv_ptr0 = sc = (struct Asr_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct Message { char M[MAX_INBOUND_SIZE]; } Message; PI2O_MESSAGE_FRAME Message_Ptr; /* Reject incoming commands while we are resetting the card */ if (sc->ha_in_reset != HA_OPERATIONAL) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; if (sc->ha_in_reset >= HA_OFF_LINE) { /* HBA is now off-line */ ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; } else { /* HBA currently resetting, try again later. */ ccb->ccb_h.status |= CAM_REQUEUE_REQ; } debug_asr_cmd_printf (" e\n"); xpt_done(ccb); debug_asr_cmd_printf (" q\n"); break; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { printf( "asr%d WARNING: scsi_cmd(%x) already done on b%dt%du%d\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), ccb->csio.cdb_io.cdb_bytes[0], cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); } debug_asr_cmd_printf("(%d,%d,%d,%d)", cam_sim_unit(sim), cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); debug_asr_dump_ccb(ccb); if ((Message_Ptr = ASR_init_message((union asr_ccb *)ccb, (PI2O_MESSAGE_FRAME)&Message)) != NULL) { debug_asr_cmd2_printf ("TID=%x:\n", PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_getTID( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)); debug_asr_cmd2_dump_message(Message_Ptr); debug_asr_cmd1_printf (" q"); if (ASR_queue (sc, Message_Ptr) == EMPTY_QUEUE) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_REQUEUE_REQ; debug_asr_cmd_printf (" E\n"); xpt_done(ccb); } debug_asr_cmd_printf(" Q\n"); break; } /* * We will get here if there is no valid TID for the device * referenced in the scsi command packet. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_SEL_TIMEOUT; debug_asr_cmd_printf (" B\n"); xpt_done(ccb); break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ /* Rese HBA device ... */ asr_hbareset (sc); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; #if (defined(REPORT_LUNS)) case REPORT_LUNS: #endif case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: /* XXX Implement */ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &(ccb->cts); struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; if (cts->type == CTS_TYPE_USER_SETTINGS) { cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; spi->flags = CTS_SPI_FLAGS_DISC_ENB; spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; spi->sync_period = 6; /* 40MHz */ spi->sync_offset = 15; spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_DISC; scsi->valid = CTS_SCSI_VALID_TQ; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; } xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &(ccb->ccg); size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb > 4096) { ccg->heads = 255; ccg->secs_per_track = 63; } else if (size_mb > 2048) { ccg->heads = 128; ccg->secs_per_track = 63; } else if (size_mb > 1024) { ccg->heads = 65; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ ASR_resetBus (sc, cam_sim_bus(sim)); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &(ccb->cpi); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->target_sprt = 0; /* Not necessary to reset bus, done by HDM initialization */ cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = sc->ha_MaxId; cpi->max_lun = sc->ha_MaxLun; cpi->initiator_id = sc->ha_adapter_target[cam_sim_bus(sim)]; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /* asr_action */ /* * Handle processing of current CCB as pointed to by the Status. */ static int asr_intr(Asr_softc_t *sc) { int processed; for(processed = 0; asr_get_status(sc) & Mask_InterruptsDisabled; processed = 1) { union asr_ccb *ccb; u_int dsc; U32 ReplyOffset; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply; if (((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE) && ((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE)) { break; } Reply = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)(ReplyOffset - sc->ha_Msgs_Phys + (char *)(sc->ha_Msgs)); /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64( &(Reply->StdReplyFrame.StdMessageFrame)); if (I2O_MESSAGE_FRAME_getMsgFlags( &(Reply->StdReplyFrame.StdMessageFrame)) & I2O_MESSAGE_FLAGS_FAIL) { I2O_UTIL_NOP_MESSAGE Message; PI2O_UTIL_NOP_MESSAGE Message_Ptr; U32 MessageOffset; MessageOffset = (u_long) I2O_FAILURE_REPLY_MESSAGE_FRAME_getPreservedMFA( (PI2O_FAILURE_REPLY_MESSAGE_FRAME)Reply); /* * Get the Original Message Frame's address, and get * it's Transaction Context into our space. (Currently * unused at original authorship, but better to be * safe than sorry). Straight copy means that we * need not concern ourselves with the (optional * byteswapping) method access. */ Reply->StdReplyFrame.TransactionContext = bus_space_read_4(sc->ha_frame_btag, sc->ha_frame_bhandle, MessageOffset + offsetof(I2O_SINGLE_REPLY_MESSAGE_FRAME, TransactionContext)); /* * For 64 bit machines, we need to reconstruct the * 64 bit context. */ ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64( &(Reply->StdReplyFrame.StdMessageFrame)); /* * Unique error code for command failure. */ I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply->StdReplyFrame), (u_int16_t)-2); /* * Modify the message frame to contain a NOP and * re-issue it to the controller. */ Message_Ptr = (PI2O_UTIL_NOP_MESSAGE)ASR_fillMessage( &Message, sizeof(I2O_UTIL_NOP_MESSAGE)); #if (I2O_UTIL_NOP != 0) I2O_MESSAGE_FRAME_setFunction ( &(Message_Ptr->StdMessageFrame), I2O_UTIL_NOP); #endif /* * Copy the packet out to the Original Message */ asr_set_frame(sc, Message_Ptr, MessageOffset, sizeof(I2O_UTIL_NOP_MESSAGE)); /* * Issue the NOP */ asr_set_ToFIFO(sc, MessageOffset); } /* * Asynchronous command with no return requirements, * and a generic handler for immunity against odd error * returns from the adapter. */ if (ccb == NULL) { /* * Return Reply so that it can be used for the * next command */ asr_set_FromFIFO(sc, ReplyOffset); continue; } /* Welease Wadjah! (and stop timeouts) */ ASR_ccbRemove (sc, ccb); dsc = I2O_SINGLE_REPLY_MESSAGE_FRAME_getDetailedStatusCode( &(Reply->StdReplyFrame)); ccb->csio.scsi_status = dsc & I2O_SCSI_DEVICE_DSC_MASK; ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (dsc) { case I2O_SCSI_DSC_SUCCESS: ccb->ccb_h.status |= CAM_REQ_CMP; break; case I2O_SCSI_DSC_CHECK_CONDITION: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; break; case I2O_SCSI_DSC_BUSY: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_ADAPTER_BUSY: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_SCSI_BUS_RESET: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_BUS_BUSY: ccb->ccb_h.status |= CAM_SCSI_BUSY; break; case I2O_SCSI_HBA_DSC_SELECTION_TIMEOUT: ccb->ccb_h.status |= CAM_SEL_TIMEOUT; break; case I2O_SCSI_HBA_DSC_COMMAND_TIMEOUT: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_DEVICE_NOT_PRESENT: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_LUN_INVALID: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_SCSI_TID_INVALID: ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; case I2O_SCSI_HBA_DSC_DATA_OVERRUN: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_REQUEST_LENGTH_ERROR: ccb->ccb_h.status |= CAM_DATA_RUN_ERR; break; default: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; } if ((ccb->csio.resid = ccb->csio.dxfer_len) != 0) { ccb->csio.resid -= I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_getTransferCount( Reply); } /* Sense data in reply packet */ if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { u_int16_t size = I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_getAutoSenseTransferCount(Reply); if (size) { if (size > sizeof(ccb->csio.sense_data)) { size = sizeof(ccb->csio.sense_data); } if (size > I2O_SCSI_SENSE_DATA_SZ) { size = I2O_SCSI_SENSE_DATA_SZ; } if ((ccb->csio.sense_len) && (size > ccb->csio.sense_len)) { size = ccb->csio.sense_len; } if (size < ccb->csio.sense_len) { ccb->csio.sense_resid = ccb->csio.sense_len - size; } else { ccb->csio.sense_resid = 0; } bzero(&(ccb->csio.sense_data), sizeof(ccb->csio.sense_data)); bcopy(Reply->SenseData, &(ccb->csio.sense_data), size); } } /* * Return Reply so that it can be used for the next command * since we have no more need for it now */ asr_set_FromFIFO(sc, ReplyOffset); if (ccb->ccb_h.path) { xpt_done ((union ccb *)ccb); } else { wakeup (ccb); } } return (processed); } /* asr_intr */ #undef QueueSize /* Grrrr */ #undef SG_Size /* Grrrr */ /* * Meant to be included at the bottom of asr.c !!! */ /* * Included here as hard coded. Done because other necessary include * files utilize C++ comment structures which make them a nuisance to * included here just to pick up these three typedefs. */ typedef U32 DPT_TAG_T; typedef U32 DPT_MSG_T; typedef U32 DPT_RTN_T; #undef SCSI_RESET /* Conflicts with "scsi/scsiconf.h" defintion */ #include "dev/asr/osd_unix.h" #define asr_unit(dev) dev2unit(dev) static u_int8_t ASR_ctlr_held; static int asr_open(struct cdev *dev, int32_t flags, int32_t ifmt, struct thread *td) { int s; int error; if (dev->si_drv1 == NULL) { return (ENODEV); } s = splcam (); if (ASR_ctlr_held) { error = EBUSY; } else if ((error = priv_check(td, PRIV_DRIVER)) == 0) { ++ASR_ctlr_held; } splx(s); return (error); } /* asr_open */ static int asr_close(struct cdev *dev, int flags, int ifmt, struct thread *td) { ASR_ctlr_held = 0; return (0); } /* asr_close */ /*-------------------------------------------------------------------------*/ /* Function ASR_queue_i */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* PI2O_MESSAGE_FRAME : Msg Structure Pointer For This Command */ /* I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME following the Msg Structure */ /* */ /* This Function Will Take The User Request Packet And Convert It To An */ /* I2O MSG And Send It Off To The Adapter. */ /* */ /* Return : 0 For OK, Error Code Otherwise */ /*-------------------------------------------------------------------------*/ static int ASR_queue_i(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Packet) { union asr_ccb * ccb; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply; PI2O_MESSAGE_FRAME Message_Ptr; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply_Ptr; int MessageSizeInBytes; int ReplySizeInBytes; int error; int s; /* Scatter Gather buffer list */ struct ioctlSgList_S { SLIST_ENTRY(ioctlSgList_S) link; caddr_t UserSpace; I2O_FLAGS_COUNT FlagsCount; char KernelSpace[sizeof(long)]; } * elm; /* Generates a `first' entry */ SLIST_HEAD(ioctlSgListHead_S, ioctlSgList_S) sgList; if (ASR_getBlinkLedCode(sc)) { debug_usr_cmd_printf ("Adapter currently in BlinkLed %x\n", ASR_getBlinkLedCode(sc)); return (EIO); } /* Copy in the message into a local allocation */ if ((Message_Ptr = (PI2O_MESSAGE_FRAME)malloc ( sizeof(I2O_MESSAGE_FRAME), M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to acquire I2O_MESSAGE_FRAME memory\n"); return (ENOMEM); } if ((error = copyin ((caddr_t)Packet, (caddr_t)Message_Ptr, sizeof(I2O_MESSAGE_FRAME))) != 0) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Can't copy in packet errno=%d\n", error); return (error); } /* Acquire information to determine type of packet */ MessageSizeInBytes = (I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr)<<2); /* The offset of the reply information within the user packet */ Reply = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)((char *)Packet + MessageSizeInBytes); /* Check if the message is a synchronous initialization command */ s = I2O_MESSAGE_FRAME_getFunction(Message_Ptr); free(Message_Ptr, M_TEMP); switch (s) { case I2O_EXEC_IOP_RESET: { U32 status; status = ASR_resetIOP(sc); ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("resetIOP done\n"); return (copyout ((caddr_t)&status, (caddr_t)Reply, ReplySizeInBytes)); } case I2O_EXEC_STATUS_GET: { PI2O_EXEC_STATUS_GET_REPLY status; status = &sc->ha_statusmem->status; if (ASR_getStatus(sc) == NULL) { debug_usr_cmd_printf ("getStatus failed\n"); return (ENXIO); } ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("getStatus done\n"); return (copyout ((caddr_t)status, (caddr_t)Reply, ReplySizeInBytes)); } case I2O_EXEC_OUTBOUND_INIT: { U32 status; status = ASR_initOutBound(sc); ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("intOutBound done\n"); return (copyout ((caddr_t)&status, (caddr_t)Reply, ReplySizeInBytes)); } } /* Determine if the message size is valid */ if ((MessageSizeInBytes < sizeof(I2O_MESSAGE_FRAME)) || (MAX_INBOUND_SIZE < MessageSizeInBytes)) { debug_usr_cmd_printf ("Packet size %d incorrect\n", MessageSizeInBytes); return (EINVAL); } if ((Message_Ptr = (PI2O_MESSAGE_FRAME)malloc (MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ("Failed to acquire frame[%d] memory\n", MessageSizeInBytes); return (ENOMEM); } if ((error = copyin ((caddr_t)Packet, (caddr_t)Message_Ptr, MessageSizeInBytes)) != 0) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Can't copy in packet[%d] errno=%d\n", MessageSizeInBytes, error); return (error); } /* Check the size of the reply frame, and start constructing */ if ((Reply_Ptr = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)malloc ( sizeof(I2O_MESSAGE_FRAME), M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to acquire I2O_MESSAGE_FRAME memory\n"); return (ENOMEM); } if ((error = copyin ((caddr_t)Reply, (caddr_t)Reply_Ptr, sizeof(I2O_MESSAGE_FRAME))) != 0) { free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to copy in reply frame, errno=%d\n", error); return (error); } ReplySizeInBytes = (I2O_MESSAGE_FRAME_getMessageSize( &(Reply_Ptr->StdReplyFrame.StdMessageFrame)) << 2); free(Reply_Ptr, M_TEMP); if (ReplySizeInBytes < sizeof(I2O_SINGLE_REPLY_MESSAGE_FRAME)) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to copy in reply frame[%d], errno=%d\n", ReplySizeInBytes, error); return (EINVAL); } if ((Reply_Ptr = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)malloc ( ((ReplySizeInBytes > sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)) ? ReplySizeInBytes : sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)), M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Failed to acquire frame[%d] memory\n", ReplySizeInBytes); return (ENOMEM); } (void)ASR_fillMessage((void *)Reply_Ptr, ReplySizeInBytes); Reply_Ptr->StdReplyFrame.StdMessageFrame.InitiatorContext = Message_Ptr->InitiatorContext; Reply_Ptr->StdReplyFrame.TransactionContext = ((PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr)->TransactionContext; I2O_MESSAGE_FRAME_setMsgFlags( &(Reply_Ptr->StdReplyFrame.StdMessageFrame), I2O_MESSAGE_FRAME_getMsgFlags( &(Reply_Ptr->StdReplyFrame.StdMessageFrame)) | I2O_MESSAGE_FLAGS_REPLY); /* Check if the message is a special case command */ switch (I2O_MESSAGE_FRAME_getFunction(Message_Ptr)) { case I2O_EXEC_SYS_TAB_SET: /* Special Case of empty Scatter Gather */ if (MessageSizeInBytes == ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)) { free(Message_Ptr, M_TEMP); I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply_Ptr->StdReplyFrame), (ASR_setSysTab(sc) != CAM_REQ_CMP)); I2O_MESSAGE_FRAME_setMessageSize( &(Reply_Ptr->StdReplyFrame.StdMessageFrame), sizeof(I2O_SINGLE_REPLY_MESSAGE_FRAME)); error = copyout ((caddr_t)Reply_Ptr, (caddr_t)Reply, ReplySizeInBytes); free(Reply_Ptr, M_TEMP); return (error); } } /* Deal in the general case */ /* First allocate and optionally copy in each scatter gather element */ SLIST_INIT(&sgList); if ((I2O_MESSAGE_FRAME_getVersionOffset(Message_Ptr) & 0xF0) != 0) { PI2O_SGE_SIMPLE_ELEMENT sg; /* * since this code is reused in several systems, code * efficiency is greater by using a shift operation rather * than a divide by sizeof(u_int32_t). */ sg = (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset(Message_Ptr) & 0xF0) >> 2)); while (sg < (PI2O_SGE_SIMPLE_ELEMENT)(((caddr_t)Message_Ptr) + MessageSizeInBytes)) { caddr_t v; int len; if ((I2O_FLAGS_COUNT_getFlags(&(sg->FlagsCount)) & I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT) == 0) { error = EINVAL; break; } len = I2O_FLAGS_COUNT_getCount(&(sg->FlagsCount)); debug_usr_cmd_printf ("SG[%d] = %x[%d]\n", sg - (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)), I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg), len); if ((elm = (struct ioctlSgList_S *)malloc ( sizeof(*elm) - sizeof(elm->KernelSpace) + len, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to allocate SG[%d]\n", len); error = ENOMEM; break; } SLIST_INSERT_HEAD(&sgList, elm, link); elm->FlagsCount = sg->FlagsCount; elm->UserSpace = (caddr_t) (I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg)); v = elm->KernelSpace; /* Copy in outgoing data (DIR bit could be invalid) */ if ((error = copyin (elm->UserSpace, (caddr_t)v, len)) != 0) { break; } /* * If the buffer is not contiguous, lets * break up the scatter/gather entries. */ while ((len > 0) && (sg < (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)Message_Ptr) + MAX_INBOUND_SIZE))) { int next, base, span; span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go physically contiguously */ while ((len > 0) && (base == next)) { int size; next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } /* Construct the Flags */ I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); { int flags = I2O_FLAGS_COUNT_getFlags( &(elm->FlagsCount)); /* Any remaining length? */ if (len > 0) { flags &= ~(I2O_SGL_FLAGS_END_OF_BUFFER | I2O_SGL_FLAGS_LAST_ELEMENT); } I2O_FLAGS_COUNT_setFlags( &(sg->FlagsCount), flags); } debug_usr_cmd_printf ("sg[%d] = %x[%d]\n", sg - (PI2O_SGE_SIMPLE_ELEMENT) ((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)), I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg), span); if (len <= 0) { break; } /* * Incrementing requires resizing of the * packet, and moving up the existing SG * elements. */ ++sg; MessageSizeInBytes += sizeof(*sg); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr) + (sizeof(*sg) / sizeof(U32))); { PI2O_MESSAGE_FRAME NewMessage_Ptr; if ((NewMessage_Ptr = (PI2O_MESSAGE_FRAME) malloc (MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to acquire frame[%d] memory\n", MessageSizeInBytes); error = ENOMEM; break; } span = ((caddr_t)sg) - (caddr_t)Message_Ptr; bcopy(Message_Ptr,NewMessage_Ptr, span); bcopy((caddr_t)(sg-1), ((caddr_t)NewMessage_Ptr) + span, MessageSizeInBytes - span); free(Message_Ptr, M_TEMP); sg = (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)NewMessage_Ptr) + span); Message_Ptr = NewMessage_Ptr; } } if ((error) || ((I2O_FLAGS_COUNT_getFlags(&(sg->FlagsCount)) & I2O_SGL_FLAGS_LAST_ELEMENT) != 0)) { break; } ++sg; } if (error) { while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); return (error); } } debug_usr_cmd_printf ("Inbound: "); debug_usr_cmd_dump_message(Message_Ptr); /* Send the command */ if ((ccb = asr_alloc_ccb (sc)) == NULL) { /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); return (ENOMEM); } /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64( (PI2O_MESSAGE_FRAME)Message_Ptr, (long)ccb); (void)ASR_queue (sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); /* * Wait for the board to report a finished instruction. */ s = splcam(); while ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { if (ASR_getBlinkLedCode(sc)) { /* Reset Adapter */ printf ("asr%d: Blink LED 0x%x resetting adapter\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), ASR_getBlinkLedCode(sc)); if (ASR_reset (sc) == ENXIO) { /* Command Cleanup */ ASR_ccbRemove(sc, ccb); } splx(s); /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); asr_free_ccb(ccb); return (EIO); } /* Check every second for BlinkLed */ /* There is no PRICAM, but outwardly PRIBIO is functional */ tsleep(ccb, PRIBIO, "asr", hz); } splx(s); debug_usr_cmd_printf ("Outbound: "); debug_usr_cmd_dump_message(Reply_Ptr); I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply_Ptr->StdReplyFrame), (ccb->ccb_h.status != CAM_REQ_CMP)); if (ReplySizeInBytes >= (sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ - sizeof(U32))) { I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_setTransferCount(Reply_Ptr, ccb->csio.dxfer_len - ccb->csio.resid); } if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) && (ReplySizeInBytes > (sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ))) { int size = ReplySizeInBytes - sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ; if (size > sizeof(ccb->csio.sense_data)) { size = sizeof(ccb->csio.sense_data); } if (size < ccb->csio.sense_len) { ccb->csio.sense_resid = ccb->csio.sense_len - size; } else { ccb->csio.sense_resid = 0; } bzero(&(ccb->csio.sense_data), sizeof(ccb->csio.sense_data)); bcopy(&(ccb->csio.sense_data), Reply_Ptr->SenseData, size); I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_setAutoSenseTransferCount( Reply_Ptr, size); } /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { /* Copy out as necessary */ if ((error == 0) /* DIR bit considered `valid', error due to ignorance works */ && ((I2O_FLAGS_COUNT_getFlags(&(elm->FlagsCount)) & I2O_SGL_FLAGS_DIR) == 0)) { error = copyout((caddr_t)(elm->KernelSpace), elm->UserSpace, I2O_FLAGS_COUNT_getCount(&(elm->FlagsCount))); } SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } if (error == 0) { /* Copy reply frame to user space */ error = copyout((caddr_t)Reply_Ptr, (caddr_t)Reply, ReplySizeInBytes); } free(Reply_Ptr, M_TEMP); asr_free_ccb(ccb); return (error); } /* ASR_queue_i */ /*----------------------------------------------------------------------*/ /* Function asr_ioctl */ /*----------------------------------------------------------------------*/ /* The parameters passed to this function are : */ /* dev : Device number. */ /* cmd : Ioctl Command */ /* data : User Argument Passed In. */ /* flag : Mode Parameter */ /* proc : Process Parameter */ /* */ /* This function is the user interface into this adapter driver */ /* */ /* Return : zero if OK, error code if not */ /*----------------------------------------------------------------------*/ static int asr_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { Asr_softc_t *sc = dev->si_drv1; int i, error = 0; #ifdef ASR_IOCTL_COMPAT int j; #endif /* ASR_IOCTL_COMPAT */ if (sc != NULL) switch(cmd) { case DPT_SIGNATURE: #ifdef ASR_IOCTL_COMPAT #if (dsDescription_size != 50) case DPT_SIGNATURE + ((50 - dsDescription_size) << 16): #endif if (cmd & 0xFFFF0000) { bcopy(&ASR_sig, data, sizeof(dpt_sig_S)); return (0); } /* Traditional version of the ioctl interface */ case DPT_SIGNATURE & 0x0000FFFF: #endif return (copyout((caddr_t)(&ASR_sig), *((caddr_t *)data), sizeof(dpt_sig_S))); /* Traditional version of the ioctl interface */ case DPT_CTRLINFO & 0x0000FFFF: case DPT_CTRLINFO: { struct { u_int16_t length; u_int16_t drvrHBAnum; u_int32_t baseAddr; u_int16_t blinkState; u_int8_t pciBusNum; u_int8_t pciDeviceNum; u_int16_t hbaFlags; u_int16_t Interrupt; u_int32_t reserved1; u_int32_t reserved2; u_int32_t reserved3; } CtlrInfo; bzero(&CtlrInfo, sizeof(CtlrInfo)); CtlrInfo.length = sizeof(CtlrInfo) - sizeof(u_int16_t); CtlrInfo.drvrHBAnum = asr_unit(dev); CtlrInfo.baseAddr = sc->ha_Base; i = ASR_getBlinkLedCode (sc); if (i == -1) i = 0; CtlrInfo.blinkState = i; CtlrInfo.pciBusNum = sc->ha_pciBusNum; CtlrInfo.pciDeviceNum = sc->ha_pciDeviceNum; #define FLG_OSD_PCI_VALID 0x0001 #define FLG_OSD_DMA 0x0002 #define FLG_OSD_I2O 0x0004 CtlrInfo.hbaFlags = FLG_OSD_PCI_VALID|FLG_OSD_DMA|FLG_OSD_I2O; CtlrInfo.Interrupt = sc->ha_irq; #ifdef ASR_IOCTL_COMPAT if (cmd & 0xffff0000) bcopy(&CtlrInfo, data, sizeof(CtlrInfo)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&CtlrInfo, *(caddr_t *)data, sizeof(CtlrInfo)); } return (error); /* Traditional version of the ioctl interface */ case DPT_SYSINFO & 0x0000FFFF: case DPT_SYSINFO: { sysInfo_S Info; #ifdef ASR_IOCTL_COMPAT char * cp; /* Kernel Specific ptok `hack' */ #define ptok(a) ((char *)(uintptr_t)(a) + KERNBASE) bzero(&Info, sizeof(Info)); /* Appears I am the only person in the Kernel doing this */ outb (0x70, 0x12); i = inb(0x71); j = i >> 4; if (i == 0x0f) { outb (0x70, 0x19); j = inb (0x71); } Info.drive0CMOS = j; j = i & 0x0f; if (i == 0x0f) { outb (0x70, 0x1a); j = inb (0x71); } Info.drive1CMOS = j; Info.numDrives = *((char *)ptok(0x475)); #else /* ASR_IOCTL_COMPAT */ bzero(&Info, sizeof(Info)); #endif /* ASR_IOCTL_COMPAT */ Info.processorFamily = ASR_sig.dsProcessorFamily; #if defined(__i386__) switch (cpu) { case CPU_386SX: case CPU_386: Info.processorType = PROC_386; break; case CPU_486SX: case CPU_486: Info.processorType = PROC_486; break; case CPU_586: Info.processorType = PROC_PENTIUM; break; case CPU_686: Info.processorType = PROC_SEXIUM; break; } #endif Info.osType = OS_BSDI_UNIX; Info.osMajorVersion = osrelease[0] - '0'; Info.osMinorVersion = osrelease[2] - '0'; /* Info.osRevision = 0; */ /* Info.osSubRevision = 0; */ Info.busType = SI_PCI_BUS; Info.flags = SI_OSversionValid|SI_BusTypeValid|SI_NO_SmartROM; #ifdef ASR_IOCTL_COMPAT Info.flags |= SI_CMOS_Valid | SI_NumDrivesValid; /* Go Out And Look For I2O SmartROM */ for(j = 0xC8000; j < 0xE0000; j += 2048) { int k; cp = ptok(j); if (*((unsigned short *)cp) != 0xAA55) { continue; } j += (cp[2] * 512) - 2048; if ((*((u_long *)(cp + 6)) != ('S' + (' ' * 256) + (' ' * 65536L))) || (*((u_long *)(cp + 10)) != ('I' + ('2' * 256) + ('0' * 65536L)))) { continue; } cp += 0x24; for (k = 0; k < 64; ++k) { if (*((unsigned short *)cp) == (' ' + ('v' * 256))) { break; } } if (k < 64) { Info.smartROMMajorVersion = *((unsigned char *)(cp += 4)) - '0'; Info.smartROMMinorVersion = *((unsigned char *)(cp += 2)); Info.smartROMRevision = *((unsigned char *)(++cp)); Info.flags |= SI_SmartROMverValid; Info.flags &= ~SI_NO_SmartROM; break; } } /* Get The Conventional Memory Size From CMOS */ outb (0x70, 0x16); j = inb (0x71); j <<= 8; outb (0x70, 0x15); j |= inb(0x71); Info.conventionalMemSize = j; /* Get The Extended Memory Found At Power On From CMOS */ outb (0x70, 0x31); j = inb (0x71); j <<= 8; outb (0x70, 0x30); j |= inb(0x71); Info.extendedMemSize = j; Info.flags |= SI_MemorySizeValid; /* Copy Out The Info Structure To The User */ if (cmd & 0xFFFF0000) bcopy(&Info, data, sizeof(Info)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&Info, *(caddr_t *)data, sizeof(Info)); return (error); } /* Get The BlinkLED State */ case DPT_BLINKLED: i = ASR_getBlinkLedCode (sc); if (i == -1) i = 0; #ifdef ASR_IOCTL_COMPAT if (cmd & 0xffff0000) bcopy(&i, data, sizeof(i)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&i, *(caddr_t *)data, sizeof(i)); break; /* Send an I2O command */ case I2OUSRCMD: return (ASR_queue_i(sc, *((PI2O_MESSAGE_FRAME *)data))); /* Reset and re-initialize the adapter */ case I2ORESETCMD: return (ASR_reset(sc)); /* Rescan the LCT table and resynchronize the information */ case I2ORESCANCMD: return (ASR_rescan(sc)); } return (EINVAL); } /* asr_ioctl */ Index: head/sys/dev/buslogic/bt_pci.c =================================================================== --- head/sys/dev/buslogic/bt_pci.c (revision 232853) +++ head/sys/dev/buslogic/bt_pci.c (revision 232854) @@ -1,238 +1,238 @@ /*- * Product specific probe and attach routines for: * Buslogic BT946, BT948, BT956, BT958 SCSI controllers * * Copyright (c) 1995, 1997, 1998 Justin T. Gibbs * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define BT_PCI_IOADDR PCIR_BAR(0) #define BT_PCI_MEMADDR PCIR_BAR(1) #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040104Bul #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140104Bul #define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130104Bul static int bt_pci_alloc_resources(device_t dev) { int command, type = 0, rid, zero; struct resource *regs = 0; struct resource *irq = 0; command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); #if 0 /* XXX Memory Mapped I/O seems to cause problems */ if (command & PCIM_CMD_MEMEN) { type = SYS_RES_MEMORY; rid = BT_PCI_MEMADDR; regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); } #else if (!regs && (command & PCIM_CMD_PORTEN)) { type = SYS_RES_IOPORT; rid = BT_PCI_IOADDR; regs = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); } #endif if (!regs) return (ENOMEM); zero = 0; irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &zero, RF_ACTIVE | RF_SHAREABLE); if (!irq) { bus_release_resource(dev, type, rid, regs); return (ENOMEM); } bt_init_softc(dev, regs, irq, 0); return (0); } static void bt_pci_release_resources(device_t dev) { struct bt_softc *bt = device_get_softc(dev); if (bt->port) /* XXX can't cope with memory registers anyway */ bus_release_resource(dev, SYS_RES_IOPORT, BT_PCI_IOADDR, bt->port); if (bt->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, bt->irq); bt_free_softc(dev); } static int bt_pci_probe(device_t dev) { switch (pci_get_devid(dev)) { case PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER: case PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC: { struct bt_softc *bt = device_get_softc(dev); pci_info_data_t pci_info; int error; error = bt_pci_alloc_resources(dev); if (error) return (error); /* * Determine if an ISA compatible I/O port has been * enabled. If so, record the port so it will not * be probed by our ISA probe. If the PCI I/O port * was not set to the compatibility port, disable it. */ error = bt_cmd(bt, BOP_INQUIRE_PCI_INFO, /*param*/NULL, /*paramlen*/0, (u_int8_t*)&pci_info, sizeof(pci_info), DEFAULT_CMD_TIMEOUT); if (error == 0 && pci_info.io_port < BIO_DISABLED) { bt_mark_probed_bio(pci_info.io_port); if (rman_get_start(bt->port) != bt_iop_from_bio(pci_info.io_port)) { u_int8_t new_addr; new_addr = BIO_DISABLED; bt_cmd(bt, BOP_MODIFY_IO_ADDR, /*param*/&new_addr, /*paramlen*/1, /*reply_buf*/NULL, /*reply_len*/0, DEFAULT_CMD_TIMEOUT); } } bt_pci_release_resources(dev); device_set_desc(dev, "Buslogic Multi-Master SCSI Host Adapter"); return (0); } default: break; } return (ENXIO); } static int bt_pci_attach(device_t dev) { struct bt_softc *bt = device_get_softc(dev); int opri; int error; /* Initialize softc */ error = bt_pci_alloc_resources(dev); if (error) { device_printf(dev, "can't allocate resources in bt_pci_attach\n"); return error; } /* Allocate a dmatag for our CCB DMA maps */ /* XXX Should be a child of the PCI bus dma tag */ - if (bus_dma_tag_create( /* parent */ NULL, + if (bus_dma_tag_create( /* PCI parent */ bus_get_dma_tag(dev), /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* nsegments */ ~0, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &bt->parent_dmat) != 0) { bt_pci_release_resources(dev); return (ENOMEM); } /* * Protect ourself from spurrious interrupts during * intialization and attach. We should really rely * on interrupts during attach, but we don't have * access to our interrupts during ISA probes, so until * that changes, we mask our interrupts during attach * too. */ opri = splcam(); if (bt_probe(dev) || bt_fetch_adapter_info(dev) || bt_init(dev)) { bt_pci_release_resources(dev); splx(opri); return (ENXIO); } error = bt_attach(dev); splx(opri); if (error) { bt_pci_release_resources(dev); return (error); } return (0); } static device_method_t bt_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bt_pci_probe), DEVMETHOD(device_attach, bt_pci_attach), { 0, 0 } }; static driver_t bt_pci_driver = { "bt", bt_pci_methods, sizeof(struct bt_softc), }; static devclass_t bt_devclass; DRIVER_MODULE(bt, pci, bt_pci_driver, bt_devclass, 0, 0); MODULE_DEPEND(bt, pci, 1, 1, 1); Index: head/sys/dev/bxe/if_bxe.c =================================================================== --- head/sys/dev/bxe/if_bxe.c (revision 232853) +++ head/sys/dev/bxe/if_bxe.c (revision 232854) @@ -1,17583 +1,17584 @@ /*- * Copyright (c) 2007-2011 Broadcom Corporation. All rights reserved. * * Gary Zambrano * David Christensen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Broadcom Corporation nor the name of its contributors * may be used to endorse or promote products derived from this software * without specific prior written consent. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * The following controllers are supported by this driver: * BCM57710 A1+ * BCM57711 A0+ * BCM57711E A0+ * * The following controllers are not supported by this driver: * BCM57710 A0 (pre-production) * * External PHY References: * ------------------------ * BCM8073 - Dual Port 10GBase-KR Ethernet PHY * BCM8705 - 10Gb Ethernet Serial Transceiver * BCM8706 - 10Gb Ethernet LRM PHY * BCM8726 - Dual Port 10Gb Ethernet LRM PHY * BCM8727 - Dual Port 10Gb Ethernet LRM PHY * BCM8481 - Single Port 10GBase-T Ethernet PHY * BCM84823 - Dual Port 10GBase-T Ethernet PHY * SFX7101 - Solarflare 10GBase-T Ethernet PHY * */ #include "opt_bxe.h" #include "bxe_include.h" #include "if_bxe.h" #include "bxe_init.h" #include "hw_dump_reg_st.h" #include "dump_e1.h" #include "dump_e1h.h" #include "bxe_self_test.h" /* BXE Debug Options */ #ifdef BXE_DEBUG uint32_t bxe_debug = BXE_WARN; /* 0 = Never */ /* 1 = 1 in 2,147,483,648 */ /* 256 = 1 in 8,388,608 */ /* 2048 = 1 in 1,048,576 */ /* 65536 = 1 in 32,768 */ /* 1048576 = 1 in 2,048 */ /* 268435456 = 1 in 8 */ /* 536870912 = 1 in 4 */ /* 1073741824 = 1 in 2 */ /* Controls how often to simulate an mbuf allocation failure. */ int bxe_debug_mbuf_allocation_failure = 0; /* Controls how often to simulate a DMA mapping failure. */ int bxe_debug_dma_map_addr_failure = 0; /* Controls how often to simulate a bootcode failure. */ int bxe_debug_bootcode_running_failure = 0; #endif #define MDIO_INDIRECT_REG_ADDR 0x1f #define MDIO_SET_REG_BANK(sc, reg_bank) \ bxe_mdio22_write(sc, MDIO_INDIRECT_REG_ADDR, reg_bank) #define MDIO_ACCESS_TIMEOUT 1000 #define BMAC_CONTROL_RX_ENABLE 2 /* BXE Build Time Options */ /* #define BXE_NVRAM_WRITE 1 */ #define BXE_USE_DMAE 1 /* * PCI Device ID Table * Used by bxe_probe() to identify the devices supported by this driver. */ #define BXE_DEVDESC_MAX 64 static struct bxe_type bxe_devs[] = { /* BCM57710 Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57710, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57710 10GbE" }, /* BCM57711 Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57711, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57711 10GbE" }, /* BCM57711E Controllers and OEM boards. */ { BRCM_VENDORID, BRCM_DEVICEID_BCM57711E, PCI_ANY_ID, PCI_ANY_ID, "Broadcom NetXtreme II BCM57711E 10GbE" }, {0, 0, 0, 0, NULL} }; /* * FreeBSD device entry points. */ static int bxe_probe(device_t); static int bxe_attach(device_t); static int bxe_detach(device_t); static int bxe_shutdown(device_t); /* * Driver local functions. */ static void bxe_tunables_set(struct bxe_softc *); static void bxe_print_adapter_info(struct bxe_softc *); static void bxe_probe_pci_caps(struct bxe_softc *); static void bxe_link_settings_supported(struct bxe_softc *, uint32_t); static void bxe_link_settings_requested(struct bxe_softc *); static int bxe_hwinfo_function_get(struct bxe_softc *); static int bxe_hwinfo_port_get(struct bxe_softc *); static int bxe_hwinfo_common_get(struct bxe_softc *); static void bxe_undi_unload(struct bxe_softc *); static int bxe_setup_leading(struct bxe_softc *); static int bxe_stop_leading(struct bxe_softc *); static int bxe_setup_multi(struct bxe_softc *, int); static int bxe_stop_multi(struct bxe_softc *, int); static int bxe_stop_locked(struct bxe_softc *, int); static int bxe_alloc_buf_rings(struct bxe_softc *); static void bxe_free_buf_rings(struct bxe_softc *); static void bxe_init_locked(struct bxe_softc *, int); static int bxe_wait_ramrod(struct bxe_softc *, int, int, int *, int); static void bxe_init_str_wr(struct bxe_softc *, uint32_t, const uint32_t *, uint32_t); static void bxe_init_ind_wr(struct bxe_softc *, uint32_t, const uint32_t *, uint16_t); static void bxe_init_wr_64(struct bxe_softc *, uint32_t, const uint32_t *, uint32_t); static void bxe_write_big_buf(struct bxe_softc *, uint32_t, uint32_t); static void bxe_init_fill(struct bxe_softc *, uint32_t, int, uint32_t); static void bxe_init_block(struct bxe_softc *, uint32_t, uint32_t); static void bxe_init(void *); static void bxe_release_resources(struct bxe_softc *); static void bxe_reg_wr_ind(struct bxe_softc *, uint32_t, uint32_t); static uint32_t bxe_reg_rd_ind(struct bxe_softc *, uint32_t); static void bxe_post_dmae(struct bxe_softc *, struct dmae_command *, int); static void bxe_wb_wr(struct bxe_softc *, int, uint32_t, uint32_t); static __inline uint32_t bxe_reg_poll(struct bxe_softc *, uint32_t, uint32_t, int, int); static int bxe_mc_assert(struct bxe_softc *); static void bxe_panic_dump(struct bxe_softc *); static void bxe_int_enable(struct bxe_softc *); static void bxe_int_disable(struct bxe_softc *); static int bxe_nvram_acquire_lock(struct bxe_softc *); static int bxe_nvram_release_lock(struct bxe_softc *); static void bxe_nvram_enable_access(struct bxe_softc *); static void bxe_nvram_disable_access(struct bxe_softc *); static int bxe_nvram_read_dword (struct bxe_softc *, uint32_t, uint32_t *, uint32_t); static int bxe_nvram_read(struct bxe_softc *, uint32_t, uint8_t *, int); #ifdef BXE_NVRAM_WRITE_SUPPORT static int bxe_nvram_write_dword(struct bxe_softc *, uint32_t, uint32_t, uint32_t); static int bxe_nvram_write1(struct bxe_softc *, uint32_t, uint8_t *, int); static int bxe_nvram_write(struct bxe_softc *, uint32_t, uint8_t *, int); #endif static int bxe_nvram_test(struct bxe_softc *); static __inline void bxe_ack_sb(struct bxe_softc *, uint8_t, uint8_t, uint16_t, uint8_t, uint8_t); static __inline uint16_t bxe_update_fpsb_idx(struct bxe_fastpath *); static uint16_t bxe_ack_int(struct bxe_softc *); static void bxe_sp_event(struct bxe_fastpath *, union eth_rx_cqe *); static int bxe_acquire_hw_lock(struct bxe_softc *, uint32_t); static int bxe_release_hw_lock(struct bxe_softc *, uint32_t); static void bxe_acquire_phy_lock(struct bxe_softc *); static void bxe_release_phy_lock(struct bxe_softc *); static void bxe_pmf_update(struct bxe_softc *); static void bxe_init_port_minmax(struct bxe_softc *); static void bxe_link_attn(struct bxe_softc *); static int bxe_sp_post(struct bxe_softc *, int, int, uint32_t, uint32_t, int); static int bxe_acquire_alr(struct bxe_softc *); static void bxe_release_alr(struct bxe_softc *); static uint16_t bxe_update_dsb_idx(struct bxe_softc *); static void bxe_attn_int_asserted(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted0(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted1(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted2(struct bxe_softc *, uint32_t); static __inline void bxe_attn_int_deasserted3(struct bxe_softc *, uint32_t); static void bxe_attn_int_deasserted(struct bxe_softc *, uint32_t); static void bxe_attn_int(struct bxe_softc *); static void bxe_stats_storm_post(struct bxe_softc *); static void bxe_stats_init(struct bxe_softc *); static void bxe_stats_hw_post(struct bxe_softc *); static int bxe_stats_comp(struct bxe_softc *); static void bxe_stats_pmf_update(struct bxe_softc *); static void bxe_stats_port_base_init(struct bxe_softc *); static void bxe_stats_port_init(struct bxe_softc *); static void bxe_stats_func_base_init(struct bxe_softc *); static void bxe_stats_func_init(struct bxe_softc *); static void bxe_stats_start(struct bxe_softc *); static void bxe_stats_pmf_start(struct bxe_softc *); static void bxe_stats_restart(struct bxe_softc *); static void bxe_stats_bmac_update(struct bxe_softc *); static void bxe_stats_emac_update(struct bxe_softc *); static int bxe_stats_hw_update(struct bxe_softc *); static int bxe_stats_storm_update(struct bxe_softc *); static void bxe_stats_func_base_update(struct bxe_softc *); static void bxe_stats_update(struct bxe_softc *); static void bxe_stats_port_stop(struct bxe_softc *); static void bxe_stats_stop(struct bxe_softc *); static void bxe_stats_do_nothing(struct bxe_softc *); static void bxe_stats_handle(struct bxe_softc *, enum bxe_stats_event); static int bxe_tx_encap(struct bxe_fastpath *, struct mbuf **); static void bxe_tx_start(struct ifnet *); static void bxe_tx_start_locked(struct ifnet *, struct bxe_fastpath *); static int bxe_tx_mq_start(struct ifnet *, struct mbuf *); static int bxe_tx_mq_start_locked(struct ifnet *, struct bxe_fastpath *, struct mbuf *); static void bxe_mq_flush(struct ifnet *ifp); static int bxe_ioctl(struct ifnet *, u_long, caddr_t); static __inline int bxe_has_rx_work(struct bxe_fastpath *); static __inline int bxe_has_tx_work(struct bxe_fastpath *); static void bxe_intr_legacy(void *); static void bxe_task_sp(void *, int); static void bxe_intr_sp(void *); static void bxe_task_fp(void *, int); static void bxe_intr_fp(void *); static void bxe_zero_sb(struct bxe_softc *, int); static void bxe_init_sb(struct bxe_softc *, struct host_status_block *, bus_addr_t, int); static void bxe_zero_def_sb(struct bxe_softc *); static void bxe_init_def_sb(struct bxe_softc *, struct host_def_status_block *, bus_addr_t, int); static void bxe_update_coalesce(struct bxe_softc *); static __inline void bxe_update_rx_prod(struct bxe_softc *, struct bxe_fastpath *, uint16_t, uint16_t, uint16_t); static void bxe_clear_sge_mask_next_elems(struct bxe_fastpath *); static __inline void bxe_init_sge_ring_bit_mask(struct bxe_fastpath *); static int bxe_alloc_tpa_mbuf(struct bxe_fastpath *, int); static int bxe_fill_tpa_pool(struct bxe_fastpath *); static void bxe_free_tpa_pool(struct bxe_fastpath *); static int bxe_alloc_rx_sge_mbuf(struct bxe_fastpath *, uint16_t); static int bxe_fill_sg_chain(struct bxe_fastpath *); static void bxe_free_sg_chain(struct bxe_fastpath *); static int bxe_alloc_rx_bd_mbuf(struct bxe_fastpath *, uint16_t); static int bxe_fill_rx_bd_chain(struct bxe_fastpath *); static void bxe_free_rx_bd_chain(struct bxe_fastpath *); static void bxe_mutexes_alloc(struct bxe_softc *); static void bxe_mutexes_free(struct bxe_softc *); static void bxe_clear_rx_chains(struct bxe_softc *); static int bxe_init_rx_chains(struct bxe_softc *); static void bxe_clear_tx_chains(struct bxe_softc *); static void bxe_init_tx_chains(struct bxe_softc *); static void bxe_init_sp_ring(struct bxe_softc *); static void bxe_init_context(struct bxe_softc *); static void bxe_init_ind_table(struct bxe_softc *); static void bxe_set_client_config(struct bxe_softc *); static void bxe_set_storm_rx_mode(struct bxe_softc *); static void bxe_init_internal_common(struct bxe_softc *); static void bxe_init_internal_port(struct bxe_softc *); static void bxe_init_internal_func(struct bxe_softc *); static void bxe_init_internal(struct bxe_softc *, uint32_t); static int bxe_init_nic(struct bxe_softc *, uint32_t); static void bxe_lb_pckt(struct bxe_softc *); static int bxe_int_mem_test(struct bxe_softc *); static void bxe_enable_blocks_attention (struct bxe_softc *); static void bxe_init_pxp(struct bxe_softc *); static int bxe_init_common(struct bxe_softc *); static int bxe_init_port(struct bxe_softc *); static void bxe_ilt_wr(struct bxe_softc *, uint32_t, bus_addr_t); static int bxe_init_func(struct bxe_softc *); static int bxe_init_hw(struct bxe_softc *, uint32_t); static int bxe_fw_command(struct bxe_softc *, uint32_t); static void bxe_host_structures_free(struct bxe_softc *); static void bxe_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int bxe_host_structures_alloc(device_t); static void bxe_set_mac_addr_e1(struct bxe_softc *, int); static void bxe_set_mac_addr_e1h(struct bxe_softc *, int); static void bxe_set_rx_mode(struct bxe_softc *); static void bxe_reset_func(struct bxe_softc *); static void bxe_reset_port(struct bxe_softc *); static void bxe_reset_common(struct bxe_softc *); static void bxe_reset_chip(struct bxe_softc *, uint32_t); static int bxe_ifmedia_upd(struct ifnet *); static void bxe_ifmedia_status(struct ifnet *, struct ifmediareq *); static __inline void bxe_update_last_max_sge(struct bxe_fastpath *, uint16_t); static void bxe_update_sge_prod(struct bxe_fastpath *, struct eth_fast_path_rx_cqe *); static void bxe_tpa_start(struct bxe_fastpath *, uint16_t, uint16_t, uint16_t); static int bxe_fill_frag_mbuf(struct bxe_softc *, struct bxe_fastpath *, struct mbuf *, struct eth_fast_path_rx_cqe *, uint16_t); static void bxe_tpa_stop(struct bxe_softc *, struct bxe_fastpath *, uint16_t, int, int, union eth_rx_cqe *, uint16_t); static void bxe_rxeof(struct bxe_fastpath *); static void bxe_txeof(struct bxe_fastpath *); static int bxe_watchdog(struct bxe_fastpath *fp); static void bxe_tick(void *); static void bxe_add_sysctls(struct bxe_softc *); static void bxe_write_dmae_phys_len(struct bxe_softc *, bus_addr_t, uint32_t, uint32_t); void bxe_write_dmae(struct bxe_softc *, bus_addr_t, uint32_t, uint32_t); void bxe_read_dmae(struct bxe_softc *, uint32_t, uint32_t); int bxe_set_gpio(struct bxe_softc *, int, uint32_t, uint8_t); int bxe_get_gpio(struct bxe_softc *, int, uint8_t); int bxe_set_spio(struct bxe_softc *, int, uint32_t); int bxe_set_gpio_int(struct bxe_softc *, int, uint32_t, uint8_t); /* * BXE Debug Data Structure Dump Routines */ #ifdef BXE_DEBUG static int bxe_sysctl_driver_state(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_hw_state(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_fw(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_rx_cq_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_rx_bd_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_dump_tx_chain(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_reg_read(SYSCTL_HANDLER_ARGS); static int bxe_sysctl_breakpoint(SYSCTL_HANDLER_ARGS); static __noinline void bxe_validate_rx_packet(struct bxe_fastpath *, uint16_t, union eth_rx_cqe *, struct mbuf *); static void bxe_grcdump(struct bxe_softc *, int); static __noinline void bxe_dump_enet(struct bxe_softc *,struct mbuf *); static __noinline void bxe_dump_mbuf (struct bxe_softc *, struct mbuf *); static __noinline void bxe_dump_tx_mbuf_chain(struct bxe_softc *, int, int); static __noinline void bxe_dump_rx_mbuf_chain(struct bxe_softc *, int, int); static __noinline void bxe_dump_tx_parsing_bd(struct bxe_fastpath *,int, struct eth_tx_parse_bd *); static __noinline void bxe_dump_txbd(struct bxe_fastpath *, int, union eth_tx_bd_types *); static __noinline void bxe_dump_rxbd(struct bxe_fastpath *, int, struct eth_rx_bd *); static __noinline void bxe_dump_cqe(struct bxe_fastpath *, int, union eth_rx_cqe *); static __noinline void bxe_dump_tx_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_rx_cq_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_rx_bd_chain(struct bxe_fastpath *, int, int); static __noinline void bxe_dump_status_block(struct bxe_softc *); static __noinline void bxe_dump_stats_block(struct bxe_softc *); static __noinline void bxe_dump_fp_state(struct bxe_fastpath *); static __noinline void bxe_dump_port_state_locked(struct bxe_softc *); static __noinline void bxe_dump_link_vars_state_locked(struct bxe_softc *); static __noinline void bxe_dump_link_params_state_locked(struct bxe_softc *); static __noinline void bxe_dump_driver_state(struct bxe_softc *); static __noinline void bxe_dump_hw_state(struct bxe_softc *); static __noinline void bxe_dump_fw(struct bxe_softc *); static void bxe_decode_mb_msgs(struct bxe_softc *, uint32_t, uint32_t); static void bxe_decode_ramrod_cmd(struct bxe_softc *, int); static void bxe_breakpoint(struct bxe_softc *); #endif #define BXE_DRIVER_VERSION "1.5.52" static void bxe_init_e1_firmware(struct bxe_softc *sc); static void bxe_init_e1h_firmware(struct bxe_softc *sc); /* * FreeBSD device dispatch table. */ static device_method_t bxe_methods[] = { /* Device interface (device_if.h) */ DEVMETHOD(device_probe, bxe_probe), DEVMETHOD(device_attach, bxe_attach), DEVMETHOD(device_detach, bxe_detach), DEVMETHOD(device_shutdown, bxe_shutdown), DEVMETHOD_END }; static driver_t bxe_driver = { "bxe", bxe_methods, sizeof(struct bxe_softc) }; static devclass_t bxe_devclass; MODULE_DEPEND(bxe, pci, 1, 1, 1); MODULE_DEPEND(bxe, ether, 1, 1, 1); DRIVER_MODULE(bxe, pci, bxe_driver, bxe_devclass, 0, 0); /* * Tunable device values */ static SYSCTL_NODE(_hw, OID_AUTO, bxe, CTLFLAG_RD, 0, "bxe driver parameters"); /* Allowable values are TRUE (1) or FALSE (0). */ static int bxe_dcc_enable = FALSE; TUNABLE_INT("hw.bxe.dcc_enable", &bxe_dcc_enable); SYSCTL_UINT(_hw_bxe, OID_AUTO, dcc_enable, CTLFLAG_RDTUN, &bxe_dcc_enable, 0, "dcc Enable/Disable"); /* Allowable values are TRUE (1) or FALSE (0). */ static int bxe_tso_enable = TRUE; TUNABLE_INT("hw.bxe.tso_enable", &bxe_tso_enable); SYSCTL_UINT(_hw_bxe, OID_AUTO, tso_enable, CTLFLAG_RDTUN, &bxe_tso_enable, 0, "TSO Enable/Disable"); /* Allowable values are 0 (IRQ), 1 (MSI/IRQ), and 2 (MSI-X/MSI/IRQ). */ static int bxe_int_mode = 2; TUNABLE_INT("hw.bxe.int_mode", &bxe_int_mode); SYSCTL_UINT(_hw_bxe, OID_AUTO, int_mode, CTLFLAG_RDTUN, &bxe_int_mode, 0, "Interrupt (MSI-X|MSI|INTx) mode"); /* * Specifies the number of queues that will be used when a multi-queue * RSS mode is selected using bxe_multi_mode below. * * Allowable values are 0 (Auto) or 1 to MAX_CONTEXT (fixed queue number). */ static int bxe_queue_count = 0; TUNABLE_INT("hw.bxe.queue_count", &bxe_queue_count); SYSCTL_UINT(_hw_bxe, OID_AUTO, queue_count, CTLFLAG_RDTUN, &bxe_queue_count, 0, "Multi-Queue queue count"); /* * ETH_RSS_MODE_DISABLED (0) * Disables all multi-queue/packet sorting algorithms. All * received frames are routed to a single receive queue. * * ETH_RSS_MODE_REGULAR (1) * The default mode which assigns incoming frames to receive * queues according to RSS (i.e a 2-tuple match on the source/ * destination IP address or a 4-tuple match on the source/ * destination IP address and the source/destination TCP port). * */ static int bxe_multi_mode = ETH_RSS_MODE_REGULAR; TUNABLE_INT("hw.bxe.multi_mode", &bxe_multi_mode); SYSCTL_UINT(_hw_bxe, OID_AUTO, multi_mode, CTLFLAG_RDTUN, &bxe_multi_mode, 0, "Multi-Queue Mode"); /* * Host interrupt coalescing is controller by these values. * The first frame always causes an interrupt but subsequent * frames are coalesced until the RX/TX ticks timer value * expires and another interrupt occurs. (Ticks are measured * in microseconds.) */ static uint32_t bxe_rx_ticks = 25; TUNABLE_INT("hw.bxe.rx_ticks", &bxe_rx_ticks); SYSCTL_UINT(_hw_bxe, OID_AUTO, rx_ticks, CTLFLAG_RDTUN, &bxe_rx_ticks, 0, "Receive ticks"); static uint32_t bxe_tx_ticks = 50; TUNABLE_INT("hw.bxe.tx_ticks", &bxe_tx_ticks); SYSCTL_UINT(_hw_bxe, OID_AUTO, tx_ticks, CTLFLAG_RDTUN, &bxe_tx_ticks, 0, "Transmit ticks"); /* * Allows the PCIe maximum read request size value to be manually * set during initialization rather than automatically determined * by the driver. * * Allowable values are: * -1 (Auto), 0 (128B), 1 (256B), 2 (512B), 3 (1KB) */ static int bxe_mrrs = -1; TUNABLE_INT("hw.bxe.mrrs", &bxe_mrrs); SYSCTL_UINT(_hw_bxe, OID_AUTO, mrrs, CTLFLAG_RDTUN, &bxe_mrrs, 0, "PCIe maximum read request size."); #if 0 /* * Allows setting the maximum number of received frames to process * during an interrupt. * * Allowable values are: * -1 (Unlimited), 0 (None), otherwise specifies the number of RX frames. */ static int bxe_rx_limit = -1; TUNABLE_INT("hw.bxe.rx_limit", &bxe_rx_limit); SYSCTL_UINT(_hw_bxe, OID_AUTO, rx_limit, CTLFLAG_RDTUN, &bxe_rx_limit, 0, "Maximum received frames processed during an interrupt."); /* * Allows setting the maximum number of transmit frames to process * during an interrupt. * * Allowable values are: * -1 (Unlimited), 0 (None), otherwise specifies the number of TX frames. */ static int bxe_tx_limit = -1; TUNABLE_INT("hw.bxe.tx_limit", &bxe_tx_limit); SYSCTL_UINT(_hw_bxe, OID_AUTO, tx_limit, CTLFLAG_RDTUN, &bxe_tx_limit, 0, "Maximum transmit frames processed during an interrupt."); #endif /* * Global variables */ /* 0 is common, 1 is port 0, 2 is port 1. */ static int load_count[3]; /* Tracks whether MCP firmware is running. */ static int nomcp; #ifdef BXE_DEBUG /* * A debug version of the 32 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ void bxe_reg_write32(struct bxe_softc *sc, bus_size_t offset, uint32_t val) { if ((offset % 4) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned write to 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_4(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 16 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ static void bxe_reg_write16(struct bxe_softc *sc, bus_size_t offset, uint16_t val) { if ((offset % 2) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned write to 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%04X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_2(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 8 bit OS register write function to * capture/display values written to the controller. * * Returns: * None. */ static void bxe_reg_write8(struct bxe_softc *sc, bus_size_t offset, uint8_t val) { DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%02X\n", __FUNCTION__, (uintmax_t)offset, val); bus_space_write_1(sc->bxe_btag, sc->bxe_bhandle, offset, val); } /* * A debug version of the 32 bit OS register read function to * capture/display values read from the controller. * * Returns: * 32bit value read. */ uint32_t bxe_reg_read32(struct bxe_softc *sc, bus_size_t offset) { uint32_t val; if ((offset % 4) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned read from 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } val = bus_space_read_4(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } /* * A debug version of the 16 bit OS register read function to * capture/display values read from the controller. * * Returns: * 16bit value read. */ static uint16_t bxe_reg_read16(struct bxe_softc *sc, bus_size_t offset) { uint16_t val; if ((offset % 2) != 0) { DBPRINT(sc, BXE_WARN, "%s(): Warning! Unaligned read from 0x%jX!\n", __FUNCTION__, (uintmax_t)offset); } val = bus_space_read_2(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%08X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } /* * A debug version of the 8 bit OS register write function to * capture/display values written to the controller. * * Returns: * 8bit value read. */ static uint8_t bxe_reg_read8(struct bxe_softc *sc, bus_size_t offset) { uint8_t val = bus_space_read_1(sc->bxe_btag, sc->bxe_bhandle, offset); DBPRINT(sc, BXE_INSANE_REGS, "%s(): offset = 0x%jX, val = 0x%02X\n", __FUNCTION__, (uintmax_t)offset, val); return (val); } #endif static void bxe_read_mf_cfg(struct bxe_softc *sc) { int func, vn; for (vn = VN_0; vn < E1HVN_MAX; vn++) { func = 2 * vn + BP_PORT(sc); sc->mf_config[vn] = SHMEM_RD(sc,mf_cfg.func_mf_config[func].config); } } static void bxe_e1h_disable(struct bxe_softc *sc) { int port; port = BP_PORT(sc); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 0); sc->bxe_ifp->if_drv_flags = 0; } static void bxe_e1h_enable(struct bxe_softc *sc) { int port; port = BP_PORT(sc); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 1); sc->bxe_ifp->if_drv_flags = IFF_DRV_RUNNING; } /* * Calculates the sum of vn_min_rates. * It's needed for further normalizing of the min_rates. * Returns: * sum of vn_min_rates. * or * 0 - if all the min_rates are 0. In the later case fainess * algorithm should be deactivated. If not all min_rates are * zero then those that are zeroes will be set to 1. */ static void bxe_calc_vn_wsum(struct bxe_softc *sc) { uint32_t vn_cfg, vn_min_rate; int all_zero, vn; DBENTER(BXE_VERBOSE_LOAD); all_zero = 1; sc->vn_wsum = 0; for (vn = VN_0; vn < E1HVN_MAX; vn++) { vn_cfg = sc->mf_config[vn]; vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; /* Skip hidden vns */ if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) continue; /* If min rate is zero - set it to 1. */ if (!vn_min_rate) vn_min_rate = DEF_MIN_RATE; else all_zero = 0; sc->vn_wsum += vn_min_rate; } /* ... only if all min rates are zeros - disable fairness */ if (all_zero) sc->cmng.flags.cmng_enables &= ~CMNG_FLAGS_PER_PORT_FAIRNESS_VN; else sc->cmng.flags.cmng_enables |= CMNG_FLAGS_PER_PORT_FAIRNESS_VN; DBEXIT(BXE_VERBOSE_LOAD); } /* * * Returns: * None. */ static void bxe_init_vn_minmax(struct bxe_softc *sc, int vn) { struct rate_shaping_vars_per_vn m_rs_vn; struct fairness_vars_per_vn m_fair_vn; uint32_t vn_cfg; uint16_t vn_min_rate, vn_max_rate; int func, i; vn_cfg = sc->mf_config[vn]; func = 2 * vn + BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD); /* If function is hidden - set min and max to zeroes. */ if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) { vn_min_rate = 0; vn_max_rate = 0; } else { vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; /* * If fairness is enabled (i.e. not all min rates are zero), * and if the current min rate is zero, set it to 1. * This is a requirement of the algorithm. */ if (sc->vn_wsum && (vn_min_rate == 0)) vn_min_rate = DEF_MIN_RATE; vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; if (vn_max_rate == 0) return; } DBPRINT(sc, BXE_INFO_LOAD, "%s(): func %d: vn_min_rate = %d, vn_max_rate = %d, wsum = %d.\n", __FUNCTION__, func, vn_min_rate, vn_max_rate, sc->vn_wsum); memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn)); memset(&m_fair_vn, 0, sizeof(struct fairness_vars_per_vn)); /* Global VNIC counter - maximal Mbps for this VNIC. */ m_rs_vn.vn_counter.rate = vn_max_rate; /* Quota - number of bytes transmitted in this period. */ m_rs_vn.vn_counter.quota = (vn_max_rate * RS_PERIODIC_TIMEOUT_USEC) / 8; if (sc->vn_wsum) { /* * Credit for each period of the fairness algorithm. The * number of bytes in T_FAIR (the VNIC shares the port rate). * vn_wsum should not be larger than 10000, thus * T_FAIR_COEF / (8 * vn_wsum) will always be grater than zero. */ m_fair_vn.vn_credit_delta = max((uint32_t)(vn_min_rate * (T_FAIR_COEF / (8 * sc->vn_wsum))), (uint32_t)(sc->cmng.fair_vars.fair_threshold * 2)); } func = BP_FUNC(sc); /* Store it to internal memory */ for (i = 0; i < sizeof(struct rate_shaping_vars_per_vn) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func) + (i * 4), ((uint32_t *)(&m_rs_vn))[i]); for (i = 0; i < sizeof(struct fairness_vars_per_vn) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + (i * 4), ((uint32_t *)(&m_fair_vn))[i]); DBEXIT(BXE_VERBOSE_LOAD); } static void bxe_congestionmgmt(struct bxe_softc *sc, uint8_t readshm) { int vn; DBENTER(BXE_VERBOSE_LOAD); /* Read mf conf from shmem. */ if (readshm) bxe_read_mf_cfg(sc); /* Init rate shaping and fairness contexts */ bxe_init_port_minmax(sc); /* vn_weight_sum and enable fairness if not 0 */ bxe_calc_vn_wsum(sc); /* calculate and set min-max rate for each vn */ for (vn = 0; vn < E1HVN_MAX; vn++) bxe_init_vn_minmax(sc, vn); /* Always enable rate shaping and fairness. */ sc->cmng.flags.cmng_enables |= CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Rate shaping set\n", __FUNCTION__); if (!sc->vn_wsum) DBPRINT(sc, BXE_INFO_LOAD, "%s(): All MIN values " "are zeroes, fairness is disabled\n", __FUNCTION__); DBEXIT(BXE_VERBOSE_LOAD); } static void bxe_dcc_event(struct bxe_softc *sc, uint32_t dcc_event) { int i, port; DBENTER(BXE_VERBOSE_LOAD); if (dcc_event & DRV_STATUS_DCC_DISABLE_ENABLE_PF) { if (sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_FUNC_DISABLED) { DBPRINT(sc, BXE_INFO_LOAD, "%s(): mf_cfg function " "disabled\n", __FUNCTION__); sc->state = BXE_STATE_DISABLED; bxe_e1h_disable(sc); } else { DBPRINT(sc, BXE_INFO_LOAD, "%s(): mf_cfg function " "enabled\n", __FUNCTION__); sc->state = BXE_STATE_OPEN; bxe_e1h_enable(sc); } dcc_event &= ~DRV_STATUS_DCC_DISABLE_ENABLE_PF; } if (dcc_event & DRV_STATUS_DCC_BANDWIDTH_ALLOCATION) { port = BP_PORT(sc); bxe_congestionmgmt(sc, TRUE); for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i*4, ((uint32_t *)(&sc->cmng))[i]); dcc_event &= ~DRV_STATUS_DCC_BANDWIDTH_ALLOCATION; } /* Report results to MCP */ if (dcc_event) bxe_fw_command(sc, DRV_MSG_CODE_DCC_FAILURE); else bxe_fw_command(sc, DRV_MSG_CODE_DCC_OK); DBEXIT(BXE_VERBOSE_LOAD); } /* * Device probe function. * * Compares the device to the driver's list of supported devices and * reports back to the OS whether this is the right driver for the device. * * Returns: * BUS_PROBE_DEFAULT on success, positive value on failure. */ static int bxe_probe(device_t dev) { struct bxe_softc *sc; struct bxe_type *t; char *descbuf; uint16_t did, sdid, svid, vid; sc = device_get_softc(dev); sc->dev = dev; t = bxe_devs; /* Get the data for the device to be probed. */ vid = pci_get_vendor(dev); did = pci_get_device(dev); svid = pci_get_subvendor(dev); sdid = pci_get_subdevice(dev); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(); VID = 0x%04X, DID = 0x%04X, SVID = 0x%04X, " "SDID = 0x%04X\n", __FUNCTION__, vid, did, svid, sdid); /* Look through the list of known devices for a match. */ while (t->bxe_name != NULL) { if ((vid == t->bxe_vid) && (did == t->bxe_did) && ((svid == t->bxe_svid) || (t->bxe_svid == PCI_ANY_ID)) && ((sdid == t->bxe_sdid) || (t->bxe_sdid == PCI_ANY_ID))) { descbuf = malloc(BXE_DEVDESC_MAX, M_TEMP, M_NOWAIT); if (descbuf == NULL) return (ENOMEM); /* Print out the device identity. */ snprintf(descbuf, BXE_DEVDESC_MAX, "%s (%c%d) BXE v:%s\n", t->bxe_name, (((pci_read_config(dev, PCIR_REVID, 4) & 0xf0) >> 4) + 'A'), (pci_read_config(dev, PCIR_REVID, 4) & 0xf), BXE_DRIVER_VERSION); device_set_desc_copy(dev, descbuf); free(descbuf, M_TEMP); return (BUS_PROBE_DEFAULT); } t++; } return (ENXIO); } /* * Prints useful adapter info. * * Returns: * None. */ /* ToDo: Create a sysctl for this info. */ static void bxe_print_adapter_info(struct bxe_softc *sc) { int i = 0; DBENTER(BXE_EXTREME_LOAD); /* Hardware chip info. */ BXE_PRINTF("ASIC (0x%08X); ", sc->common.chip_id); printf("Rev (%c%d); ", (CHIP_REV(sc) >> 12) + 'A', (CHIP_METAL(sc) >> 4)); /* Bus info. */ printf("Bus (PCIe x%d, ", sc->pcie_link_width); switch (sc->pcie_link_speed) { case 1: printf("2.5Gbps"); break; case 2: printf("5Gbps"); break; default: printf("Unknown link speed"); } /* Device features. */ printf("); Flags ("); /* Miscellaneous flags. */ if (sc->msi_count > 0) printf("MSI"); if (sc->msix_count > 0) { if (i > 0) printf("|"); printf("MSI-X"); i++; } if (TPA_ENABLED(sc)) { if (i > 0) printf("|"); printf("TPA"); i++; } printf("); Queues ("); switch (sc->multi_mode) { case ETH_RSS_MODE_DISABLED: printf("None"); break; case ETH_RSS_MODE_REGULAR: printf("RSS:%d", sc->num_queues); break; default: printf("Unknown"); break; } printf("); BD's (RX:%d,TX:%d", (int) USABLE_RX_BD, (int) USABLE_TX_BD); /* Firmware versions and device features. */ printf("); Firmware (%d.%d.%d); Bootcode (%d.%d.%d)\n", BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION, BCM_5710_FW_REVISION_VERSION, (int)((sc->common.bc_ver & 0xff0000) >> 16), (int)((sc->common.bc_ver & 0x00ff00) >> 8), (int)((sc->common.bc_ver & 0x0000ff))); DBEXIT(BXE_EXTREME_LOAD); } /* * Release any interrupts allocated by the driver. * * Returns: * None */ static void bxe_interrupt_free(struct bxe_softc *sc) { device_t dev; int i; DBENTER(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); dev = sc->dev; if (sc->msix_count > 0) { /* Free MSI-X resources. */ for (i = 0; i < sc->msix_count; i++) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing MSI-X[%d] " "vector.\n", __FUNCTION__, i); if (sc->bxe_msix_res[i] && sc->bxe_msix_rid[i]) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_msix_rid[i], sc->bxe_msix_res[i]); } pci_release_msi(dev); } else if (sc->msi_count > 0) { /* Free MSI resources. */ for (i = 0; i < sc->msi_count; i++) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing MSI[%d] " "vector.\n", __FUNCTION__, i); if (sc->bxe_msi_res[i] && sc->bxe_msi_rid[i]) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_msi_rid[i], sc->bxe_msi_res[i]); } pci_release_msi(dev); } else { /* Free legacy interrupt resources. */ DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Releasing legacy interrupt.\n", __FUNCTION__); if (sc->bxe_irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, sc->bxe_irq_rid, sc->bxe_irq_res); } DBEXIT(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * This function determines and allocates the appropriate * interrupt based on system capabilites and user request. * * The user may force a particular interrupt mode, specify * the number of receive queues, specify the method for * distribuitng received frames to receive queues, or use * the default settings which will automatically select the * best supported combination. In addition, the OS may or * may not support certain combinations of these settings. * This routine attempts to reconcile the settings requested * by the user with the capabilites available from the system * to select the optimal combination of features. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_interrupt_alloc(struct bxe_softc *sc) { device_t dev; int error, i, rid, rc; int msi_count, msi_required, msi_allocated; int msix_count, msix_required, msix_allocated; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); rc = 0; dev = sc->dev; msi_count = msi_required = msi_allocated = 0; msix_count = msix_required = msix_allocated = 0; /* Get the number of available MSI/MSI-X interrupts from the OS. */ if (sc->int_mode > 0) { if (sc->bxe_cap_flags & BXE_MSIX_CAPABLE_FLAG) msix_count = pci_msix_count(dev); if (sc->bxe_cap_flags & BXE_MSI_CAPABLE_FLAG) msi_count = pci_msi_count(dev); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): %d MSI and %d MSI-X vectors available.\n", __FUNCTION__, msi_count, msix_count); } /* Try allocating MSI-X interrupt resources. */ if ((sc->bxe_cap_flags & BXE_MSIX_CAPABLE_FLAG) && (sc->int_mode > 1) && (msix_count > 0) && (msix_count >= sc->num_queues)) { /* Ask for the necessary number of MSI-X vectors. */ if (sc->num_queues == 1) msix_allocated = msix_required = 2; else msix_allocated = msix_required = sc->num_queues + 1; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting %d MSI-X vectors.\n", __FUNCTION__, msix_required); /* BSD resource identifier */ rid = 1; error = pci_alloc_msix(dev, &msix_allocated); if (error == 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Required/Allocated (%d/%d) MSI-X vector(s).\n", __FUNCTION__, msix_required, msix_allocated); /* Make sure we got all the interrupts we asked for. */ if (msix_allocated >= msix_required) { sc->msix_count = msix_required; msi_count = 0; /* Allocate the MSI-X vectors. */ for (i = 0; i < msix_required; i++) { sc->bxe_msix_rid[i] = rid + i + BP_L_ID(sc); sc->bxe_msix_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->bxe_msix_rid[i], RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_msix_res[i] == NULL) { BXE_PRINTF( "%s(%d): Failed to map MSI-X[%d] vector!\n", __FILE__, __LINE__, (3)); rc = ENXIO; goto bxe_interrupt_alloc_exit; } } } else { DBPRINT(sc, BXE_WARN, "%s(): MSI-X allocation failed!\n", __FUNCTION__); /* Release any resources acquired. */ pci_release_msi(dev); sc->msix_count = msix_count = 0; /* We'll try MSI next. */ sc->int_mode = 1; } } } /* Try allocating MSI vector resources. */ if ((sc->bxe_cap_flags & BXE_MSI_CAPABLE_FLAG) && (sc->int_mode > 0) && (msi_count > 0) && (msi_count >= sc->num_queues)) { /* Ask for the necessary number of MSI vectors. */ if (sc->num_queues == 1) msi_required = msi_allocated = 1; else msi_required = msi_allocated = BXE_MSI_VECTOR_COUNT; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting %d MSI vectors.\n", __FUNCTION__, msi_required); rid = 1; error = pci_alloc_msi(dev, &msi_allocated); if (error == 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Required/Allocated (%d/%d) MSI vector(s).\n", __FUNCTION__, msi_required, msi_allocated); /* * Make sure we got all the vectors we asked for. * XXX * FreeBSD always gives 8 even if we ask for less. */ if (msi_required >= msi_allocated) { sc->msi_count = msi_required; /* Allocate the MSI vectors. */ for (i = 0; i < msi_required; i++) { sc->bxe_msi_rid[i] = i + rid; sc->bxe_msi_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->bxe_msi_rid[i], RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_msi_res[i] == NULL) { BXE_PRINTF( "%s(%d): Failed to map MSI vector (%d)!\n", __FILE__, __LINE__, (i)); rc = ENXIO; goto bxe_interrupt_alloc_exit; } } } } else { DBPRINT(sc, BXE_WARN, "%s(): MSI allocation failed!\n", __FUNCTION__); /* Release any resources acquired. */ pci_release_msi(dev); sc->msi_count = msi_count = 0; /* We'll try INTx next. */ sc->int_mode = 0; } } /* Try allocating INTx resources. */ if (sc->int_mode == 0) { sc->num_queues = 1; sc->multi_mode = ETH_RSS_MODE_DISABLED; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requesting legacy INTx interrupt.\n", __FUNCTION__); rid = 0; sc->bxe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); /* Report any IRQ allocation errors. */ if (sc->bxe_irq_res == NULL) { BXE_PRINTF("%s(%d): PCI map interrupt failed!\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_interrupt_alloc_exit; } sc->bxe_irq_rid = rid; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Actual: int_mode = %d, multi_mode = %d, num_queues = %d\n", __FUNCTION__, sc->int_mode, sc->multi_mode, sc->num_queues); bxe_interrupt_alloc_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); return (rc); } /* * This function releases taskqueues. * * Returns: * None */ static void bxe_interrupt_detach(struct bxe_softc *sc) { #ifdef BXE_TASK struct bxe_fastpath *fp; #endif device_t dev; int i; DBENTER(BXE_VERBOSE_UNLOAD); dev = sc->dev; #ifdef BXE_TASK /* Free the OS taskqueue resources. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->tq != NULL) { taskqueue_drain(fp->tq, &fp->task); taskqueue_free(fp->tq); } } if (sc->tq != NULL) { taskqueue_drain(sc->tq, &sc->task); taskqueue_free(sc->tq); } #endif /* Release interrupt resources. */ if (sc->msix_count > 0) { for (i = 0; i < sc->msix_count; i++) { if (sc->bxe_msix_tag[i] && sc->bxe_msix_res[i]) bus_teardown_intr(dev, sc->bxe_msix_res[i], sc->bxe_msix_tag[i]); } } else if (sc->msi_count > 0) { for (i = 0; i < sc->msi_count; i++) { if (sc->bxe_msi_tag[i] && sc->bxe_msi_res[i]) bus_teardown_intr(dev, sc->bxe_msi_res[i], sc->bxe_msi_tag[i]); } } else { if (sc->bxe_irq_tag != NULL) bus_teardown_intr(dev, sc->bxe_irq_res, sc->bxe_irq_tag); } DBEXIT(BXE_VERBOSE_UNLOAD); } /* * This function enables interrupts and attachs to the ISR. * * When using multiple MSI/MSI-X vectors the first vector * is used for slowpath operations while all remaining * vectors are used for fastpath operations. If only a * single MSI/MSI-X vector is used (SINGLE_ISR) then the * ISR must look for both slowpath and fastpath completions. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_interrupt_attach(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); rc = 0; #ifdef BXE_TASK /* Setup the slowpath deferred task queue. */ TASK_INIT(&sc->task, 0, bxe_task_sp, sc); sc->tq = taskqueue_create_fast("bxe_spq", M_NOWAIT, taskqueue_thread_enqueue, &sc->tq); taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s spq", device_get_nameunit(sc->dev)); #endif /* Setup interrupt handlers. */ if (sc->msix_count > 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling slowpath MSI-X[0] vector.\n",__FUNCTION__); /* * Setup the interrupt handler. Note that we pass the * driver instance to the interrupt handler for the * slowpath. */ rc = bus_setup_intr(sc->dev, sc->bxe_msix_res[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_sp, sc, &sc->bxe_msix_tag[0]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI-X[0] vector!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msix_res[0], sc->bxe_msix_tag[0], "sp"); #endif /* Now initialize the fastpath vectors. */ for (i = 0; i < (sc->num_queues); i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling MSI-X[%d] vector.\n", __FUNCTION__, i + 1); /* * Setup the interrupt handler. Note that we pass the * fastpath context to the interrupt handler in this * case. Also the first msix_res was used by the sp. */ rc = bus_setup_intr(sc->dev, sc->bxe_msix_res[i + 1], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_fp, fp, &sc->bxe_msix_tag[i + 1]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI-X[%d] vector!\n", __FILE__, __LINE__, (i + 1)); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msix_res[i + 1], sc->bxe_msix_tag[i + 1], "fp[%02d]", i); #endif /* Bind the fastpath instance to a CPU. */ if (sc->num_queues > 1) { bus_bind_intr(sc->dev, sc->bxe_msix_res[i + 1], i); } #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif fp->state = BXE_FP_STATE_IRQ; } } else if (sc->msi_count > 0) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling slowpath MSI[0] vector.\n", __FUNCTION__); /* * Setup the interrupt handler. Note that we pass the driver * instance to the interrupt handler for the slowpath. */ rc = bus_setup_intr(sc->dev,sc->bxe_msi_res[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_sp, sc, &sc->bxe_msi_tag[0]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI[0] vector!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msi_res[0], sc->bxe_msi_tag[0], "sp"); #endif /* Now initialize the fastpath vectors. */ for (i = 0; i < (sc->num_queues); i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling MSI[%d] vector.\n", __FUNCTION__, i + 1); /* * Setup the interrupt handler. Note that we pass the * fastpath context to the interrupt handler in this * case. */ rc = bus_setup_intr(sc->dev, sc->bxe_msi_res[i + 1], INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_fp, fp, &sc->bxe_msi_tag[i + 1]); if (rc) { BXE_PRINTF( "%s(%d): Failed to allocate MSI[%d] vector!\n", __FILE__, __LINE__, (i + 1)); goto bxe_interrupt_attach_exit; } #if __FreeBSD_version >= 800504 bus_describe_intr(sc->dev, sc->bxe_msi_res[i + 1], sc->bxe_msi_tag[i + 1], "fp[%02d]", i); #endif #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif } } else { #ifdef BXE_TASK fp = &sc->fp[0]; #endif DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Enabling INTx interrupts.\n", __FUNCTION__); /* * Setup the interrupt handler. Note that we pass the * driver instance to the interrupt handler which * will handle both the slowpath and fastpath. */ rc = bus_setup_intr(sc->dev,sc->bxe_irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, bxe_intr_legacy, sc, &sc->bxe_irq_tag); if (rc) { BXE_PRINTF("%s(%d): Failed to allocate interrupt!\n", __FILE__, __LINE__); goto bxe_interrupt_attach_exit; } #ifdef BXE_TASK TASK_INIT(&fp->task, 0, bxe_task_fp, fp); fp->tq = taskqueue_create_fast("bxe_fpq", M_NOWAIT, taskqueue_thread_enqueue, &fp->tq); taskqueue_start_threads(&fp->tq, 1, PI_NET, "%s fpq", device_get_nameunit(sc->dev)); #endif } bxe_interrupt_attach_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR); return (rc); } /* * PCI Capabilities Probe Function. * * Walks the PCI capabiites list for the device to find what features are * supported. These capabilites may be enabled/disabled by firmware so it's * best to walk the list rather than hard code any values. * * Returns: * None. */ static void bxe_probe_pci_caps(struct bxe_softc *sc) { device_t dev; uint32_t reg; uint16_t link_status; dev = sc->dev; DBENTER(BXE_EXTREME_LOAD); /* Check if PCI Power Management capability is enabled. */ if (pci_find_cap(dev, PCIY_PMG, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found PM capability at 0x%04X\n", __FUNCTION__, reg); sc->pm_cap = reg; } } /* Check if PCIe capability is enabled. */ if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { if (reg != 0) { link_status = pci_read_config(dev, reg + 0x12, 2); DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found PCIe capability at 0x%04X\n", __FUNCTION__, reg); /* Handle PCIe 2.0 workarounds for the 57710. */ if (CHIP_IS_E1(sc)) { /* Workaround for 57710 errata E4_57710_27462. */ sc->pcie_link_speed = (REG_RD(sc, 0x3d04) & (1 << 24)) ? 2 : 1; /* Workaround for 57710 errata E4_57710_27488. */ sc->pcie_link_width = (link_status >> 4) & 0x3f; if (sc->pcie_link_speed > 1) sc->pcie_link_width = ((link_status >> 4) & 0x3f) >> 1; } else { sc->pcie_link_speed = link_status & 0xf; sc->pcie_link_width = (link_status >> 4) & 0x3f; } sc->bxe_cap_flags |= BXE_PCIE_CAPABLE_FLAG; sc->pcie_cap = reg; } } /* Check if MSI capability is enabled. */ if (pci_find_cap(dev, PCIY_MSI, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found MSI capability at 0x%04X\n", __FUNCTION__, reg); sc->bxe_cap_flags |= BXE_MSI_CAPABLE_FLAG; } } /* Check if MSI-X capability is enabled. */ if (pci_find_cap(dev, PCIY_MSIX, ®) == 0) { if (reg != 0) { DBPRINT(sc, BXE_EXTREME_LOAD, "%s(): Found MSI-X capability at 0x%04X\n", __FUNCTION__, reg); sc->bxe_cap_flags |= BXE_MSIX_CAPABLE_FLAG; } } DBEXIT(BXE_EXTREME_LOAD); } /* * Setup firmware pointers for BCM57710. * * Returns: * None */ static void bxe_init_e1_firmware(struct bxe_softc *sc) { INIT_OPS(sc) = (struct raw_op *)init_ops_e1; INIT_DATA(sc) = (const uint32_t *)init_data_e1; INIT_OPS_OFFSETS(sc) = (const uint16_t *)init_ops_offsets_e1; INIT_TSEM_INT_TABLE_DATA(sc) = tsem_int_table_data_e1; INIT_TSEM_PRAM_DATA(sc) = tsem_pram_data_e1; INIT_USEM_INT_TABLE_DATA(sc) = usem_int_table_data_e1; INIT_USEM_PRAM_DATA(sc) = usem_pram_data_e1; INIT_XSEM_INT_TABLE_DATA(sc) = xsem_int_table_data_e1; INIT_XSEM_PRAM_DATA(sc) = xsem_pram_data_e1; INIT_CSEM_INT_TABLE_DATA(sc) = csem_int_table_data_e1; INIT_CSEM_PRAM_DATA(sc) = csem_pram_data_e1; } /* * Setup firmware pointers for BCM57711. * * Returns: * None */ static void bxe_init_e1h_firmware(struct bxe_softc *sc) { INIT_OPS(sc) = (struct raw_op *)init_ops_e1h; INIT_DATA(sc) = (const uint32_t *)init_data_e1h; INIT_OPS_OFFSETS(sc) = (const uint16_t *)init_ops_offsets_e1h; INIT_TSEM_INT_TABLE_DATA(sc) = tsem_int_table_data_e1h; INIT_TSEM_PRAM_DATA(sc) = tsem_pram_data_e1h; INIT_USEM_INT_TABLE_DATA(sc) = usem_int_table_data_e1h; INIT_USEM_PRAM_DATA(sc) = usem_pram_data_e1h; INIT_XSEM_INT_TABLE_DATA(sc) = xsem_int_table_data_e1h; INIT_XSEM_PRAM_DATA(sc) = xsem_pram_data_e1h; INIT_CSEM_INT_TABLE_DATA(sc) = csem_int_table_data_e1h; INIT_CSEM_PRAM_DATA(sc) = csem_pram_data_e1h; } /* * Sets up pointers for loading controller firmware. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_init_firmware(struct bxe_softc *sc) { int rc; rc = 0; if (CHIP_IS_E1(sc)) bxe_init_e1_firmware(sc); else if (CHIP_IS_E1H(sc)) bxe_init_e1h_firmware(sc); else { BXE_PRINTF("%s(%d): No firmware to support chip revision!\n", __FILE__, __LINE__); rc = ENXIO; } return (rc); } static void bxe_tunables_set(struct bxe_softc *sc) { /* * Get our starting point for interrupt mode/number of queues. * We will progressively step down from MSI-X to MSI to INTx * and reduce the number of receive queues as necessary to * match the system capabilities. */ sc->multi_mode = bxe_multi_mode; sc->int_mode = bxe_int_mode; sc->tso_enable = bxe_tso_enable; /* * Verify the Priority -> Receive Queue mappings. */ if (sc->int_mode > 0) { /* Multi-queue modes require MSI/MSI-X. */ switch (sc->multi_mode) { case ETH_RSS_MODE_DISABLED: /* No multi-queue mode requested. */ sc->num_queues = 1; break; case ETH_RSS_MODE_REGULAR: if (sc->int_mode > 1) { /* * Assume we can use MSI-X * (max of 16 receive queues). */ sc->num_queues = min((bxe_queue_count ? bxe_queue_count : mp_ncpus), MAX_CONTEXT); } else { /* * Assume we can use MSI * (max of 7 receive queues). */ sc->num_queues = min((bxe_queue_count ? bxe_queue_count : mp_ncpus), BXE_MSI_VECTOR_COUNT - 1); } break; default: BXE_PRINTF( "%s(%d): Unsupported multi_mode parameter (%d), " "disabling multi-queue support!\n", __FILE__, __LINE__, sc->multi_mode); sc->multi_mode = ETH_RSS_MODE_DISABLED; sc->num_queues = 1; break; } } else { /* User has forced INTx mode. */ sc->multi_mode = ETH_RSS_MODE_DISABLED; sc->num_queues = 1; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_INTR), "%s(): Requested: int_mode = %d, multi_mode = %d num_queues = %d\n", __FUNCTION__, sc->int_mode, sc->multi_mode, sc->num_queues); sc->stats_enable = TRUE; /* Select the host coalescing tick count values (limit values). */ if (bxe_tx_ticks > 100) { BXE_PRINTF("%s(%d): bxe_tx_ticks too large " "(%d), setting default value of 50.\n", __FILE__, __LINE__, bxe_tx_ticks); sc->tx_ticks = 50; } else sc->tx_ticks = bxe_tx_ticks; if (bxe_rx_ticks > 100) { BXE_PRINTF("%s(%d): bxe_rx_ticks too large " "(%d), setting default value of 25.\n", __FILE__, __LINE__, bxe_rx_ticks); sc->rx_ticks = 25; } else sc->rx_ticks = bxe_rx_ticks; /* Select the PCIe maximum read request size (MRRS). */ if (bxe_mrrs > 3) sc->mrrs = 3; else sc->mrrs = bxe_mrrs; /* Check for DCC support. */ if (bxe_dcc_enable == FALSE) sc->dcc_enable = FALSE; else sc->dcc_enable = TRUE; } /* * Allocates PCI resources from OS. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_pci_resources_alloc(struct bxe_softc *sc) { int rid, rc = 0; DBENTER(BXE_VERBOSE_LOAD); /* * Allocate PCI memory resources for BAR0. * This includes device registers and internal * processor memory. */ rid = PCIR_BAR(0); sc->bxe_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bxe_res == NULL) { BXE_PRINTF("%s(%d):PCI BAR0 memory allocation failed\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_pci_resources_alloc_exit; } /* Get OS resource handles for BAR0 memory. */ sc->bxe_btag = rman_get_bustag(sc->bxe_res); sc->bxe_bhandle = rman_get_bushandle(sc->bxe_res); sc->bxe_vhandle = (vm_offset_t) rman_get_virtual(sc->bxe_res); /* * Allocate PCI memory resources for BAR2. * Doorbell (DB) memory. */ rid = PCIR_BAR(2); sc->bxe_db_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->bxe_db_res == NULL) { BXE_PRINTF("%s(%d): PCI BAR2 memory allocation failed\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_pci_resources_alloc_exit; } /* Get OS resource handles for BAR2 memory. */ sc->bxe_db_btag = rman_get_bustag(sc->bxe_db_res); sc->bxe_db_bhandle = rman_get_bushandle(sc->bxe_db_res); sc->bxe_db_vhandle = (vm_offset_t) rman_get_virtual(sc->bxe_db_res); bxe_pci_resources_alloc_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Frees PCI resources allocated in bxe_pci_resources_alloc(). * * Returns: * None */ static void bxe_pci_resources_free(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_UNLOAD); /* Release the PCIe BAR0 mapped memory. */ if (sc->bxe_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->bxe_res); } /* Release the PCIe BAR2 (doorbell) mapped memory. */ if (sc->bxe_db_res != NULL) { bus_release_resource(sc->dev, SYS_RES_MEMORY, PCIR_BAR(2), sc->bxe_db_res); } DBENTER(BXE_VERBOSE_UNLOAD); } /* * Determines the media reported to the OS by examining * the installed PHY type. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_media_detect(struct bxe_softc *sc) { int rc; rc = 0; /* Identify supported media based on the PHY type. */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-CX4 media.\n", __FUNCTION__); sc->media = IFM_10G_CX4; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: /* Technically 10GBase-KR but report as 10GBase-SR*/ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-SR media.\n", __FUNCTION__); sc->media = IFM_10G_SR; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10Gb twinax media.\n", __FUNCTION__); sc->media = IFM_10G_TWINAX; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823: DBPRINT(sc, BXE_INFO_LOAD, "%s(): Found 10GBase-T media.\n", __FUNCTION__); sc->media = IFM_10G_T; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: default: sc->media = 0; rc = ENODEV; } return (rc); } /* * Device attach function. * * Allocates device resources, performs secondary chip identification, * resets and initializes the hardware, and initializes driver instance * variables. * * Returns: * 0 = Success, Positive value on failure. */ static int bxe_attach(device_t dev) { struct bxe_softc *sc; struct ifnet *ifp; int rc; sc = device_get_softc(dev); DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET); sc->dev = dev; sc->bxe_unit = device_get_unit(dev); sc->bxe_func = pci_get_function(dev); sc->bxe_flags = 0; sc->state = BXE_STATE_CLOSED; rc = 0; DBPRINT(sc, BXE_FATAL, "%s(): ************************\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): ** Debug mode enabled **\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): ************************\n", __FUNCTION__); DBPRINT(sc, BXE_FATAL, "%s(): sc vaddr = 0x%08X:%08X\n", __FUNCTION__, (uint32_t) U64_HI(sc), (uint32_t) U64_LO(sc)); /* Get the user configurable values for driver load. */ bxe_tunables_set(sc); bxe_mutexes_alloc(sc); /* Prepare tick routine. */ callout_init_mtx(&sc->bxe_tick_callout, &sc->bxe_core_mtx, 0); /* Enable bus master capability */ pci_enable_busmaster(dev); /* Enable PCI BAR mapped memory for register access. */ rc = bxe_pci_resources_alloc(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Error allocating PCI resources!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Put indirect address registers into a sane state. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); REG_WR(sc, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(sc) * 16, 0); REG_WR(sc, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(sc) * 16, 0); /* Get hardware info from shared memory and validate data. */ rc = bxe_hwinfo_function_get(sc); if (rc != 0) { DBPRINT(sc, BXE_WARN, "%s(): Failed to get hardware info!\n", __FUNCTION__); goto bxe_attach_fail; } /* Setup supported media options. */ rc = bxe_media_detect(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Unknown media (PHY) type!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Interface entrypoint for media type/status reporting. */ ifmedia_init(&sc->bxe_ifmedia, IFM_IMASK, bxe_ifmedia_upd, bxe_ifmedia_status); /* Default interface values. */ ifmedia_add(&sc->bxe_ifmedia, IFM_ETHER | sc->media | IFM_FDX, 0, NULL); ifmedia_add(&sc->bxe_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(&sc->bxe_ifmedia, IFM_ETHER | IFM_AUTO); sc->bxe_ifmedia.ifm_media = sc->bxe_ifmedia.ifm_cur->ifm_media; /* Setup firmware arrays (firmware load comes later). */ rc = bxe_init_firmware(sc); if (rc) { BXE_PRINTF("%s(%d): Error preparing firmware load!\n", __FILE__, __LINE__); goto bxe_attach_fail; } #ifdef BXE_DEBUG /* Allocate a memory buffer for grcdump output.*/ sc->grcdump_buffer = malloc(BXE_GRCDUMP_BUF_SIZE, M_TEMP, M_NOWAIT); if (sc->grcdump_buffer == NULL) { BXE_PRINTF("%s(%d): Failed to allocate grcdump memory " "buffer!\n", __FILE__, __LINE__); rc = ENOBUFS; } #endif /* Check that NVRAM contents are valid.*/ rc = bxe_nvram_test(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Failed NVRAM test!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Allocate the appropriate interrupts.*/ rc = bxe_interrupt_alloc(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Interrupt allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Useful for accessing unconfigured devices (i.e. factory diags).*/ if (nomcp) sc->bxe_flags |= BXE_NO_MCP_FLAG; /* If bootcode is not running only initialize port 0. */ if (nomcp && BP_PORT(sc)) { BXE_PRINTF( "%s(%d): Second device disabled (no bootcode), " "exiting...\n", __FILE__, __LINE__); rc = ENODEV; goto bxe_attach_fail; } /* Check if PXE/UNDI is still active and unload it. */ if (!NOMCP(sc)) bxe_undi_unload(sc); /* * Select the RX and TX ring sizes. The actual * ring size for TX is complicated by the fact * that a single TX frame may be broken up into * many buffer descriptors (tx_start_bd, * tx_parse_bd, tx_data_bd). In the best case, * there are always at least two BD's required * so we'll assume the best case here. */ sc->tx_ring_size = (USABLE_TX_BD >> 1); sc->rx_ring_size = USABLE_RX_BD; /* Assume receive IP/TCP/UDP checksum is enabled. */ /* ToDo: Change when IOCTL changes checksum offload? */ sc->rx_csum = 1; /* Disable WoL. */ sc->wol = 0; /* Assume a standard 1500 byte MTU size for mbuf allocations. */ sc->mbuf_alloc_size = MCLBYTES; /* Allocate DMA memory resources. */ rc = bxe_host_structures_alloc(sc->dev); if (rc != 0) { BXE_PRINTF("%s(%d): DMA memory allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Allocate a FreeBSD ifnet structure. */ ifp = sc->bxe_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { BXE_PRINTF("%s(%d): Interface allocation failed!\n", __FILE__, __LINE__); rc = ENXIO; goto bxe_attach_fail; } /* Initialize the FreeBSD ifnet interface. */ ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); /* Written by driver before attach, read-only afterwards. */ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* Driver entrypoints from the network interface. */ ifp->if_ioctl = bxe_ioctl; ifp->if_start = bxe_tx_start; #if __FreeBSD_version >= 800000 ifp->if_transmit = bxe_tx_mq_start; ifp->if_qflush = bxe_mq_flush; #endif #ifdef FreeBSD8_0 ifp->if_timer = 0; #endif ifp->if_init = bxe_init; ifp->if_hwassist = BXE_IF_HWASSIST; ifp->if_capabilities = BXE_IF_CAPABILITIES; /* TPA not enabled by default. */ ifp->if_capenable = BXE_IF_CAPABILITIES & ~IFCAP_LRO; ifp->if_baudrate = IF_Gbps(10UL); ifp->if_snd.ifq_drv_maxlen = sc->tx_ring_size; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); /* Attach to the Ethernet interface list. */ ether_ifattach(ifp, sc->link_params.mac_addr); /* Attach the interrupts to the interrupt handlers. */ rc = bxe_interrupt_attach(sc); if (rc != 0) { BXE_PRINTF("%s(%d): Interrupt allocation failed!\n", __FILE__, __LINE__); goto bxe_attach_fail; } /* Print important adapter info for the user. */ bxe_print_adapter_info(sc); /* Add the supported sysctls to the kernel. */ bxe_add_sysctls(sc); bxe_attach_fail: if (rc != 0) bxe_detach(dev); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET); return (rc); } /* * Supported link settings. * * Examines hardware configuration present in NVRAM and * determines the link settings that are supported between * the external PHY and the switch. * * Returns: * None. * * Side effects: * Sets sc->port.supported * Sets sc->link_params.phy_addr */ static void bxe_link_settings_supported(struct bxe_softc *sc, uint32_t switch_cfg) { uint32_t ext_phy_type; int port; DBENTER(BXE_VERBOSE_PHY); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): switch_cfg = 0x%08X\n", __FUNCTION__, switch_cfg); port = BP_PORT(sc); /* Get the link settings supported by the external PHY. */ switch (switch_cfg) { case SWITCH_CFG_1G: ext_phy_type = SERDES_EXT_PHY_TYPE(sc->link_params.ext_phy_config); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G switch w/ ext_phy_type = " "0x%08X\n", __FUNCTION__, ext_phy_type); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G Direct.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 1G 5482\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; default: BXE_PRINTF( "%s(%d): Bad NVRAM 1Gb PHY configuration data " "(ext_phy_config=0x%08X).\n", __FILE__, __LINE__, sc->link_params.ext_phy_config); goto bxe_link_settings_supported_exit; } sc->port.phy_addr = REG_RD(sc, NIG_REG_SERDES0_CTRL_PHY_ADDR + (port * 0x10)); DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): phy_addr = 0x%08X\n", __FUNCTION__, sc->port.phy_addr); break; case SWITCH_CFG_10G: ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); DBPRINT( sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ ext_phy_type = 0x%08X\n", __FUNCTION__, ext_phy_type); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ direct connect.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: DBPRINT(sc, BXE_VERBOSE_PHY, "ext_phy_type 0x%x (8072)\n",ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: DBPRINT(sc, BXE_VERBOSE_PHY,"ext_phy_type 0x%x (8073)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_2500baseX_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8705.\n",__FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8706.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ 8726.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: DBPRINT(sc, BXE_VERBOSE_PHY,"ext_phy_type 0x%x (8727)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): 10G switch w/ SFX7101.\n", __FUNCTION__); sc->port.supported |= (SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: DBPRINT(sc, BXE_VERBOSE_PHY, "ext_phy_type 0x%x (BCM8481)\n", ext_phy_type); sc->port.supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full | SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DBPRINT(sc, BXE_WARN, "%s(): 10G XGXS PHY failure detected.\n", __FUNCTION__); break; BXE_PRINTF( "%s(%d): Bad NVRAM 10Gb PHY configuration data " "(ext_phy_config=0x%08X).\n", __FILE__, __LINE__, sc->link_params.ext_phy_config); goto bxe_link_settings_supported_exit; } sc->port.phy_addr = REG_RD(sc, NIG_REG_XGXS0_CTRL_PHY_ADDR +(port * 0x18)); break; default: DBPRINT(sc, BXE_WARN, "%s(): BAD switch configuration " "(link_config = 0x%08X)\n", __FUNCTION__, sc->port.link_config); goto bxe_link_settings_supported_exit; } sc->link_params.phy_addr = sc->port.phy_addr; /* Mask out unsupported speeds according to NVRAM. */ if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF) == 0) sc->port.supported &= ~SUPPORTED_10baseT_Half; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) == 0) sc->port.supported &= ~SUPPORTED_10baseT_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF) == 0) sc->port.supported &= ~SUPPORTED_100baseT_Half; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL) == 0) sc->port.supported &= ~SUPPORTED_100baseT_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) == 0) sc->port.supported &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) == 0) sc->port.supported &= ~SUPPORTED_2500baseX_Full; if ((sc->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) == 0) sc->port.supported &= ~SUPPORTED_10000baseT_Full; DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): Supported link settings = 0x%b\n", __FUNCTION__, sc->port.supported, BXE_SUPPORTED_PRINTFB); bxe_link_settings_supported_exit: DBEXIT(BXE_VERBOSE_PHY); } /* * Requested link settings. * * Returns: * None. */ static void bxe_link_settings_requested(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); sc->link_params.req_duplex = MEDIUM_FULL_DUPLEX; switch (sc->port.link_config & PORT_FEATURE_LINK_SPEED_MASK) { case PORT_FEATURE_LINK_SPEED_AUTO: if (sc->port.supported & SUPPORTED_Autoneg) { sc->link_params.req_line_speed |= SPEED_AUTO_NEG; sc->port.advertising = sc->port.supported; } else { ext_phy_type = XGXS_EXT_PHY_TYPE( sc->link_params.ext_phy_config); if ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706)) { /* Force 10G, no autonegotiation. */ sc->link_params.req_line_speed = SPEED_10000; sc->port.advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE; break; } DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - Autoneg not supported!\n", __FUNCTION__, sc->port.link_config); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10M_FULL: if (sc->port.supported & SUPPORTED_10baseT_Full) { sc->link_params.req_line_speed = SPEED_10; sc->port.advertising = ADVERTISED_10baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10M_HALF: if (sc->port.supported & SUPPORTED_10baseT_Half) { sc->link_params.req_line_speed = SPEED_10; sc->link_params.req_duplex = MEDIUM_HALF_DUPLEX; sc->port.advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_100M_FULL: if (sc->port.supported & SUPPORTED_100baseT_Full) { sc->link_params.req_line_speed = SPEED_100; sc->port.advertising = ADVERTISED_100baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_100M_HALF: if (sc->port.supported & SUPPORTED_100baseT_Half) { sc->link_params.req_line_speed = SPEED_100; sc->link_params.req_duplex = MEDIUM_HALF_DUPLEX; sc->port.advertising = ADVERTISED_100baseT_Half | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_1G: if (sc->port.supported & SUPPORTED_1000baseT_Full) { sc->link_params.req_line_speed = SPEED_1000; sc->port.advertising = ADVERTISED_1000baseT_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_2_5G: if (sc->port.supported & SUPPORTED_2500baseX_Full) { sc->link_params.req_line_speed = SPEED_2500; sc->port.advertising = ADVERTISED_2500baseX_Full | ADVERTISED_TP; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; case PORT_FEATURE_LINK_SPEED_10G_CX4: case PORT_FEATURE_LINK_SPEED_10G_KX4: case PORT_FEATURE_LINK_SPEED_10G_KR: if (sc->port.supported & SUPPORTED_10000baseT_Full) { sc->link_params.req_line_speed = SPEED_10000; sc->port.advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE; } else { DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. Invalid " "link_config (0x%08X) - speed_cap_mask = 0x%08X\n", __FUNCTION__, sc->port.link_config, sc->link_params.speed_cap_mask); goto bxe_link_settings_requested_exit; } break; default: DBPRINT(sc, BXE_FATAL, "%s(): NVRAM config error. BAD link " "speed - link_config = 0x%08X\n", __FUNCTION__, sc->port.link_config); sc->link_params.req_line_speed = 0; sc->port.advertising = sc->port.supported; break; } DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): req_line_speed = %d, req_duplex = %d\n", __FUNCTION__, sc->link_params.req_line_speed, sc->link_params.req_duplex); sc->link_params.req_flow_ctrl = sc->port.link_config & PORT_FEATURE_FLOW_CONTROL_MASK; if ((sc->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) && !(sc->port.supported & SUPPORTED_Autoneg)) sc->link_params.req_flow_ctrl = FLOW_CTRL_NONE; DBPRINT(sc, BXE_VERBOSE_PHY, "%s(): req_flow_ctrl = 0x%08X, advertising = 0x%08X\n", __FUNCTION__, sc->link_params.req_flow_ctrl, sc->port.advertising); bxe_link_settings_requested_exit: DBEXIT(BXE_VERBOSE_PHY); } /* * Get function specific hardware configuration. * * Multiple function devices such as the BCM57711E have configuration * information that is specific to each PCIe function of the controller. * The number of PCIe functions is not necessarily the same as the number * of Ethernet ports supported by the device. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_function_get(struct bxe_softc *sc) { uint32_t mac_hi, mac_lo, val; int func, rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; func = BP_FUNC(sc); /* Get the common hardware configuration first. */ bxe_hwinfo_common_get(sc); /* Assume no outer VLAN/multi-function support. */ sc->e1hov = sc->e1hmf = 0; /* Get config info for mf enabled devices. */ if (CHIP_IS_E1H(sc)) { sc->mf_config[BP_E1HVN(sc)] = SHMEM_RD(sc, mf_cfg.func_mf_config[func].config); val = (SHMEM_RD(sc, mf_cfg.func_mf_config[func].e1hov_tag) & FUNC_MF_CFG_E1HOV_TAG_MASK); if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) { sc->e1hov = (uint16_t) val; sc->e1hmf = 1; } else { if (BP_E1HVN(sc)) { rc = EPERM; goto bxe_hwinfo_function_get_exit; } } } if (!NOMCP(sc)) { bxe_hwinfo_port_get(sc); sc->fw_seq = SHMEM_RD(sc, func_mb[func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK; } /* * Fetch the factory configured MAC address for multi function * devices. If this is not a multi-function device then the MAC * address was already read in the bxe_hwinfo_port_get() routine. * The MAC addresses used by the port are not the same as the MAC * addressed used by the function. */ if (IS_E1HMF(sc)) { mac_hi = SHMEM_RD(sc, mf_cfg.func_mf_config[func].mac_upper); mac_lo = SHMEM_RD(sc, mf_cfg.func_mf_config[func].mac_lower); if ((mac_lo == 0) && (mac_hi == 0)) { BXE_PRINTF("%s(%d): Invalid Ethernet address!\n", __FILE__, __LINE__); rc = ENODEV; } else { sc->link_params.mac_addr[0] = (u_char)(mac_hi >> 8); sc->link_params.mac_addr[1] = (u_char)(mac_hi); sc->link_params.mac_addr[2] = (u_char)(mac_lo >> 24); sc->link_params.mac_addr[3] = (u_char)(mac_lo >> 16); sc->link_params.mac_addr[4] = (u_char)(mac_lo >> 8); sc->link_params.mac_addr[5] = (u_char)(mac_lo); } } bxe_hwinfo_function_get_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Get port specific hardware configuration. * * Multiple port devices such as the BCM57710 have configuration * information that is specific to each Ethernet port of the * controller. This function reads that configuration * information from the bootcode's shared memory and saves it * for future use. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_port_get(struct bxe_softc *sc) { int i, port, rc; uint32_t val, mac_hi, mac_lo; DBENTER(BXE_VERBOSE_LOAD); rc = 0; port = BP_PORT(sc); sc->link_params.sc = sc; sc->link_params.port = port; /* Fetch several configuration values from bootcode shared memory. */ sc->link_params.lane_config = SHMEM_RD(sc, dev_info.port_hw_config[port].lane_config); sc->link_params.ext_phy_config = SHMEM_RD(sc, dev_info.port_hw_config[port].external_phy_config); if (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC) { sc->link_params.ext_phy_config &= ~PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; sc->link_params.ext_phy_config |= PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727; sc->link_params.feature_config_flags |= FEATURE_CONFIG_BCM8727_NOC; } sc->link_params.speed_cap_mask = SHMEM_RD(sc, dev_info.port_hw_config[port].speed_capability_mask); sc->port.link_config = SHMEM_RD(sc, dev_info.port_feature_config[port].link_config); /* Read the XGXS RX/TX preemphasis values. */ for (i = 0; i < 2; i++) { val = SHMEM_RD(sc, dev_info.port_hw_config[port].xgxs_config_rx[i<<1]); sc->link_params.xgxs_config_rx[i << 1] = ((val >> 16) & 0xffff); sc->link_params.xgxs_config_rx[(i << 1) + 1] = (val & 0xffff); val = SHMEM_RD(sc, dev_info.port_hw_config[port].xgxs_config_tx[i<<1]); sc->link_params.xgxs_config_tx[i << 1] = ((val >> 16) & 0xffff); sc->link_params.xgxs_config_tx[(i << 1) + 1] = (val & 0xffff); } /* Fetch the device configured link settings. */ sc->link_params.switch_cfg = sc->port.link_config & PORT_FEATURE_CONNECTED_SWITCH_MASK; bxe_link_settings_supported(sc, sc->link_params.switch_cfg); bxe_link_settings_requested(sc); mac_hi = SHMEM_RD(sc, dev_info.port_hw_config[port].mac_upper); mac_lo = SHMEM_RD(sc, dev_info.port_hw_config[port].mac_lower); if (mac_lo == 0 && mac_hi == 0) { BXE_PRINTF("%s(%d): No Ethernet address programmed on the " "controller!\n", __FILE__, __LINE__); rc = ENODEV; } else { sc->link_params.mac_addr[0] = (u_char)(mac_hi >> 8); sc->link_params.mac_addr[1] = (u_char)(mac_hi); sc->link_params.mac_addr[2] = (u_char)(mac_lo >> 24); sc->link_params.mac_addr[3] = (u_char)(mac_lo >> 16); sc->link_params.mac_addr[4] = (u_char)(mac_lo >> 8); sc->link_params.mac_addr[5] = (u_char)(mac_lo); } DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Get common hardware configuration. * * Multiple port devices such as the BCM57710 have configuration * information that is shared between all ports of the Ethernet * controller. This function reads that configuration * information from the bootcode's shared memory and saves it * for future use. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_hwinfo_common_get(struct bxe_softc *sc) { uint32_t val; int rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; /* Get the chip revision. */ sc->common.chip_id = sc->link_params.chip_id = ((REG_RD(sc, MISC_REG_CHIP_NUM) & 0xffff) << 16) | ((REG_RD(sc, MISC_REG_CHIP_REV) & 0x000f) << 12) | ((REG_RD(sc, MISC_REG_CHIP_METAL) & 0xff) << 4) | ((REG_RD(sc, MISC_REG_BOND_ID) & 0xf)); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): chip_id = 0x%08X.\n", __FUNCTION__, sc->common.chip_id); val = (REG_RD(sc, 0x2874) & 0x55); if ((sc->common.chip_id & 0x1) || (CHIP_IS_E1(sc) && val) || (CHIP_IS_E1H(sc) && (val == 0x55))) { sc->bxe_flags |= BXE_ONE_PORT_FLAG; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Single port device.\n", __FUNCTION__); } /* Identify enabled PCI capabilites (PCIe, MSI-X, etc.). */ bxe_probe_pci_caps(sc); /* Get the NVRAM size. */ val = REG_RD(sc, MCP_REG_MCPR_NVM_CFG4); sc->common.flash_size = (NVRAM_1MB_SIZE << (val & MCPR_NVM_CFG4_FLASH_SIZE)); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): flash_size = 0x%08x (%dKB)\n", __FUNCTION__, sc->common.flash_size,(sc->common.flash_size >> 10)); /* Find the shared memory base address. */ sc->common.shmem_base = sc->link_params.shmem_base = REG_RD(sc, MISC_REG_SHARED_MEM_ADDR); sc->common.shmem2_base = REG_RD(sc, MISC_REG_GENERIC_CR_0); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): shmem_base = 0x%08X\n", __FUNCTION__, sc->common.shmem_base); /* Make sure the shared memory address is valid. */ if (!sc->common.shmem_base || (sc->common.shmem_base < 0xA0000) || (sc->common.shmem_base > 0xC0000)) { BXE_PRINTF("%s(%d): MCP is not active!\n", __FILE__, __LINE__); /* ToDo: Remove the NOMCP support. */ sc->bxe_flags |= BXE_NO_MCP_FLAG; rc = ENODEV; goto bxe_hwinfo_common_get_exit; } /* Make sure the shared memory contents are valid. */ val = SHMEM_RD(sc, validity_map[BP_PORT(sc)]); if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) { BXE_PRINTF("%s(%d): Invalid NVRAM! Bad validity " "signature.\n", __FILE__, __LINE__); rc = ENODEV; goto bxe_hwinfo_common_get_exit; } /* Read the device configuration from shared memory. */ sc->common.hw_config = SHMEM_RD(sc, dev_info.shared_hw_config.config); sc->link_params.hw_led_mode = ((sc->common.hw_config & SHARED_HW_CFG_LED_MODE_MASK) >> SHARED_HW_CFG_LED_MODE_SHIFT); /* Check if we need to override the preemphasis values. */ sc->link_params.feature_config_flags = 0; val = SHMEM_RD(sc, dev_info.shared_feature_config.config); if (val & SHARED_FEAT_CFG_OVERRIDE_PREEMPHASIS_CFG_ENABLED) sc->link_params.feature_config_flags |= FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED; else sc->link_params.feature_config_flags &= ~FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED; /* In multifunction mode, we can't support WoL on a VN. */ if (BP_E1HVN(sc) == 0) { val = REG_RD(sc, PCICFG_OFFSET + PCICFG_PM_CAPABILITY); sc->bxe_flags |= (val & PCICFG_PM_CAPABILITY_PME_IN_D3_COLD) ? 0 : BXE_NO_WOL_FLAG; } else sc->bxe_flags |= BXE_NO_WOL_FLAG; DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): %sWoL capable\n", __FUNCTION__, (sc->bxe_flags & BXE_NO_WOL_FLAG) ? "Not " : ""); /* Check bootcode version */ sc->common.bc_ver = ((SHMEM_RD(sc, dev_info.bc_rev)) >> 8); if (sc->common.bc_ver < MIN_BXE_BC_VER) { BXE_PRINTF("%s(%d): Warning: This driver needs bootcode " "0x%08X but found 0x%08X, please upgrade!\n", __FILE__, __LINE__, MIN_BXE_BC_VER, sc->common.bc_ver); rc = ENODEV; goto bxe_hwinfo_common_get_exit; } bxe_hwinfo_common_get_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Remove traces of PXE boot by forcing UNDI driver unload. * * Returns: * None. */ static void bxe_undi_unload(struct bxe_softc *sc) { uint32_t reset_code, swap_en, swap_val, val; int func; DBENTER(BXE_VERBOSE_LOAD); /* Check if there is any driver already loaded */ val = REG_RD(sc, MISC_REG_UNPREPARED); if (val == 0x1) { /* Check if it is the UNDI driver. */ bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); val = REG_RD(sc, DORQ_REG_NORM_CID_OFST); if (val == 0x7) { reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; func = BP_FUNC(sc); DBPRINT(sc, BXE_WARN, "%s(): UNDI is active! Resetting the device.\n", __FUNCTION__); /* Clear the UNDI indication. */ REG_WR(sc, DORQ_REG_NORM_CID_OFST, 0); /* Try to unload UNDI on port 0. */ sc->bxe_func = 0; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); reset_code = bxe_fw_command(sc, reset_code); /* Check if UNDI is active on port 1. */ if (reset_code != FW_MSG_CODE_DRV_UNLOAD_COMMON) { /* Send "done" for previous unload. */ bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); /* Now unload on port 1. */ sc->bxe_func = 1; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; bxe_fw_command(sc, reset_code); } /* It's now safe to release the lock. */ bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); REG_WR(sc, (BP_PORT(sc) ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0), 0x1000); REG_WR(sc, (BP_PORT(sc) ? NIG_REG_LLH1_BRB1_DRV_MASK : NIG_REG_LLH0_BRB1_DRV_MASK), 0x0); REG_WR(sc, (BP_PORT(sc) ? NIG_REG_LLH1_BRB1_NOT_MCP : NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); /* Clear AEU. */ REG_WR(sc, (BP_PORT(sc) ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0), 0); DELAY(10000); /* Save NIG port swap information. */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_en = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); /* Reset the controller. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0xd3ffffff); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x00001403); /* Take the NIG out of reset and restore swap values.*/ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, MISC_REGISTERS_RESET_REG_1_RST_NIG); REG_WR(sc, NIG_REG_PORT_SWAP, swap_val); REG_WR(sc, NIG_REG_STRAP_OVERRIDE, swap_en); /* Send completion message to the MCP. */ bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); /* * Restore our function and firmware sequence counter. */ sc->bxe_func = func; sc->fw_seq = (SHMEM_RD(sc, func_mb[sc->bxe_func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); } else bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_UNDI); } DBEXIT(BXE_VERBOSE_LOAD); } /* * Device detach function. * * Stops the controller, resets the controller, and releases resources. * * Returns: * 0 on success, !0 = failure. */ static int bxe_detach(device_t dev) { struct bxe_softc *sc; struct ifnet *ifp; int rc; sc = device_get_softc(dev); DBENTER(BXE_INFO_UNLOAD); rc = 0; ifp = sc->bxe_ifp; if (ifp != NULL && ifp->if_vlantrunk != NULL) { BXE_PRINTF("%s(%d): Cannot detach while VLANs are in use.\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_detach_exit; } /* Stop and reset the controller if it was open. */ if (sc->state != BXE_STATE_CLOSED) { BXE_CORE_LOCK(sc); rc = bxe_stop_locked(sc, UNLOAD_CLOSE); BXE_CORE_UNLOCK(sc); } #ifdef BXE_DEBUG /* Free memory buffer for grcdump output.*/ if (sc->grcdump_buffer != NULL) free(sc->grcdump_buffer, M_TEMP); #endif /* Clean-up any remaining interrupt resources. */ bxe_interrupt_detach(sc); bxe_interrupt_free(sc); /* Release the network interface. */ if (ifp != NULL) ether_ifdetach(ifp); ifmedia_removeall(&sc->bxe_ifmedia); /* Release all remaining resources. */ bxe_release_resources(sc); /* Free all PCI resources. */ bxe_pci_resources_free(sc); pci_disable_busmaster(dev); bxe_mutexes_free(sc); bxe_detach_exit: DBEXIT(BXE_INFO_UNLOAD); return(0); } /* * Setup a leading connection for the controller. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_setup_leading(struct bxe_softc *sc) { int rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); DBPRINT(sc, BXE_VERBOSE_LOAD, "%s(): Setup leading connection " "on fp[00].\n", __FUNCTION__); /* Reset IGU state for the leading connection. */ bxe_ack_sb(sc, sc->fp[0].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* Post a PORT_SETUP ramrod and wait for completion. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_PORT_SETUP, 0, 0, 0, 0); /* Wait for the ramrod to complete on the leading connection. */ rc = bxe_wait_ramrod(sc, BXE_STATE_OPEN, 0, &(sc->state), 1); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); return (rc); } /* * Stop the leading connection on the controller. * * Returns: * None. */ static int bxe_stop_leading(struct bxe_softc *sc) { uint16_t dsb_sp_prod_idx; int rc, timeout; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Stop client connection " "on fp[00].\n", __FUNCTION__); /* Send the ETH_HALT ramrod. */ sc->fp[0].state = BXE_FP_STATE_HALTING; bxe_sp_post(sc,RAMROD_CMD_ID_ETH_HALT, 0, 0, sc->fp[0].cl_id, 0); /* Poll for the ETH_HALT ramrod on the leading connection. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_HALTED, 0, &(sc->fp[0].state), 1); if (rc) { DBPRINT(sc, BXE_FATAL, "%s(): Timeout waiting for " "STATE_HALTED ramrod completion!\n", __FUNCTION__); goto bxe_stop_leading_exit; } /* Get the default status block SP producer index. */ dsb_sp_prod_idx = *sc->dsb_sp_prod; /* After HALT we send PORT_DELETE ramrod. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_PORT_DEL, 0, 0, 0, 1); /* Be patient but don't wait forever. */ timeout = 500; while (dsb_sp_prod_idx == *sc->dsb_sp_prod) { if (timeout == 0) { DBPRINT(sc, BXE_FATAL, "%s(): Timeout waiting for " "PORT_DEL ramrod completion!\n", __FUNCTION__); rc = EBUSY; break; } timeout--; DELAY(1000); rmb(); } /* Update the adapter and connection states. */ sc->state = BXE_STATE_CLOSING_WAIT4_UNLOAD; sc->fp[0].state = BXE_FP_STATE_CLOSED; bxe_stop_leading_exit: return (rc); } /* * Setup a client connection when using multi-queue/RSS. * * Returns: * Nothing. */ static int bxe_setup_multi(struct bxe_softc *sc, int index) { struct bxe_fastpath *fp; int rc; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Setup client connection " "on fp[%02d].\n", __FUNCTION__, index); fp = &sc->fp[index]; /* Reset IGU state. */ bxe_ack_sb(sc, fp->sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* Post a CLIENT_SETUP ramrod. */ fp->state = BXE_FP_STATE_OPENING; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_CLIENT_SETUP, index, 0, fp->cl_id, 0); /* Wait for the ramrod to complete. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_OPEN, index, &fp->state, 1); return (rc); } /* * Stop a client connection. * * Stops an individual client connection on the device. Use * bxe_stop_leading() for the first/default connection. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_stop_multi(struct bxe_softc *sc, int index) { struct bxe_fastpath *fp; int rc; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD), "%s(): Stop client connection " "on fp[%02d].\n", __FUNCTION__, index); fp = &sc->fp[index]; /* Halt the client connection. */ fp->state = BXE_FP_STATE_HALTING; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_HALT, index, 0, fp->cl_id, 0); /* Wait for the HALT ramrod completion. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_HALTED, index, &fp->state, 1); if (rc){ BXE_PRINTF("%s(%d): fp[%02d] client ramrod halt failed!\n", __FILE__, __LINE__, index); goto bxe_stop_multi_exit; } /* Delete the CFC entry. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_CFC_DEL, index, 0, 0, 1); /* Poll for the DELETE ramrod completion. */ rc = bxe_wait_ramrod(sc, BXE_FP_STATE_CLOSED, index, &fp->state, 1); bxe_stop_multi_exit: return (rc); } /* * Hardware lock for shared, dual-port PHYs. * * Returns: * None. */ static void bxe_acquire_phy_lock(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); switch(ext_phy_type){ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_MDIO); break; default: break; } DBEXIT(BXE_VERBOSE_PHY); } /* * Hardware unlock for shared, dual-port PHYs. * * Returns: * None. */ static void bxe_release_phy_lock(struct bxe_softc *sc) { uint32_t ext_phy_type; DBENTER(BXE_VERBOSE_PHY); ext_phy_type = XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config); switch(ext_phy_type){ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_MDIO); break; default: break; } DBEXIT(BXE_VERBOSE_PHY); } /* * * Returns: * None. */ static void bxe__link_reset(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_PHY); if (!NOMCP(sc)) { bxe_acquire_phy_lock(sc); bxe_link_reset(&sc->link_params, &sc->link_vars, 1); bxe_release_phy_lock(sc); } else { DBPRINT(sc, BXE_WARN, "%s(): Bootcode is not running, not resetting link!\n", __FUNCTION__); } DBEXIT(BXE_VERBOSE_PHY); } /* * Stop the controller. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_stop_locked(struct bxe_softc *sc, int unload_mode) { struct ifnet *ifp; struct mac_configuration_cmd *config; struct bxe_fastpath *fp; uint32_t reset_code; uint32_t emac_base, val; uint8_t entry, *mac_addr; int count, i, port, rc; DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); ifp = sc->bxe_ifp; port = BP_PORT(sc), rc = reset_code = 0; BXE_CORE_LOCK_ASSERT(sc); /* Stop the periodic tick. */ callout_stop(&sc->bxe_tick_callout); sc->state = BXE_STATE_CLOSING_WAIT4_HALT; /* Prevent any further RX traffic. */ sc->rx_mode = BXE_RX_MODE_NONE; bxe_set_storm_rx_mode(sc); /* Tell the stack the driver is stopped and TX queue is full. */ if (ifp != NULL) ifp->if_drv_flags = 0; /* Tell the bootcode to stop watching for a heartbeat. */ SHMEM_WR(sc, func_mb[BP_FUNC(sc)].drv_pulse_mb, (DRV_PULSE_ALWAYS_ALIVE | sc->fw_drv_pulse_wr_seq)); /* Stop the statistics updates. */ bxe_stats_handle(sc, STATS_EVENT_STOP); /* Wait until all TX fastpath tasks have completed. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp == NULL || fp->tx_pkt_cons_sb == NULL) break; count = 1000; while (bxe_has_tx_work(fp)) { bxe_txeof(fp); if (count == 0) { BXE_PRINTF( "%s(%d): Timeout wating for fp[%02d] transmits to complete!\n", __FILE__, __LINE__, i); break; } count--; DELAY(1000); rmb(); } } /* Wait until all slowpath tasks have completed. */ count = 1000; while ((sc->spq_left != MAX_SPQ_PENDING) && count--) DELAY(1000); /* Disable Interrupts */ bxe_int_disable(sc); DELAY(1000); /* Clear the MAC addresses. */ if (CHIP_IS_E1(sc)) { config = BXE_SP(sc, mcast_config); bxe_set_mac_addr_e1(sc, 0); for (i = 0; i < config->hdr.length; i++) CAM_INVALIDATE(&config->config_table[i]); config->hdr.length = i; config->hdr.offset = BXE_MAX_MULTICAST * (1 + port); config->hdr.client_id = BP_CL_ID(sc); config->hdr.reserved1 = 0; bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mcast_config)), U64_LO(BXE_SP_MAPPING(sc, mcast_config)), 0); } else { REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 0); bxe_set_mac_addr_e1h(sc, 0); for (i = 0; i < MC_HASH_SIZE; i++) REG_WR(sc, MC_HASH_OFFSET(sc, i), 0); REG_WR(sc, MISC_REG_E1HMF_MODE, 0); } /* Determine if any WoL settings needed. */ if (unload_mode == UNLOAD_NORMAL) /* Driver initiatied WoL is disabled. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; else if (sc->bxe_flags & BXE_NO_WOL_FLAG) { /* Driver initiated WoL is disabled, use OOB WoL settings. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP; if (CHIP_IS_E1H(sc)) REG_WR(sc, MISC_REG_E1HMF_MODE, 0); } else if (sc->wol) { emac_base = BP_PORT(sc) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; mac_addr = sc->link_params.mac_addr; entry = (BP_E1HVN(sc) + 1) * 8; val = (mac_addr[0] << 8) | mac_addr[1]; EMAC_WR(sc, EMAC_REG_EMAC_MAC_MATCH + entry, val); val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | (mac_addr[4] << 8) | mac_addr[5]; EMAC_WR(sc, EMAC_REG_EMAC_MAC_MATCH + entry + 4, val); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_EN; } else { /* Prevent WoL. */ reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; } /* Stop all non-leading client connections. */ for (i = 1; i < sc->num_queues; i++) { if (bxe_stop_multi(sc, i)){ goto bxe_stop_locked_exit; } } /* Stop the leading client connection. */ rc = bxe_stop_leading(sc); DELAY(10000); bxe_stop_locked_exit: if (NOMCP(sc)) { DBPRINT(sc, BXE_INFO, "%s(): Old No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); load_count[0]--; load_count[1 + port]--; DBPRINT(sc, BXE_INFO, "%s(): New No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); if (load_count[0] == 0) reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; else if (load_count[1 + BP_PORT(sc)] == 0) reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT; else reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION; } else { /* Tell MCP driver unload is complete. */ reset_code = bxe_fw_command(sc, reset_code); } if ((reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) || (reset_code == FW_MSG_CODE_DRV_UNLOAD_PORT)) bxe__link_reset(sc); DELAY(10000); /* Reset the chip */ bxe_reset_chip(sc, reset_code); DELAY(10000); /* Report UNLOAD_DONE to MCP */ if (!NOMCP(sc)) bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); sc->port.pmf = 0; /* Free RX chains and buffers. */ bxe_clear_rx_chains(sc); /* Free TX chains and buffers. */ bxe_clear_tx_chains(sc); sc->state = BXE_STATE_CLOSED; bxe_ack_int(sc); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET |BXE_INFO_UNLOAD); return (rc); } /* * Device shutdown function. * * Stops and resets the controller. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_shutdown(device_t dev) { struct bxe_softc *sc; sc = device_get_softc(dev); DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); BXE_CORE_LOCK(sc); bxe_stop_locked(sc, UNLOAD_NORMAL); BXE_CORE_UNLOCK(sc); DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET | BXE_INFO_UNLOAD); return (0); } /* * Prints out link speed and duplex setting to console. * * Returns: * None. */ static void bxe_link_report(struct bxe_softc *sc) { uint32_t line_speed; uint16_t vn_max_rate; DBENTER(BXE_VERBOSE_PHY); if (sc->link_vars.link_up) { /* Report the link status change to OS. */ if (sc->state == BXE_STATE_OPEN) if_link_state_change(sc->bxe_ifp, LINK_STATE_UP); line_speed = sc->link_vars.line_speed; if (IS_E1HMF(sc)){ vn_max_rate = ((sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_MAX_BW_MASK) >> FUNC_MF_CFG_MAX_BW_SHIFT) * 100; if (vn_max_rate < line_speed) line_speed = vn_max_rate; } BXE_PRINTF("Link is up, %d Mbps, ", line_speed); if (sc->link_vars.duplex == MEDIUM_FULL_DUPLEX) printf("full duplex"); else printf("half duplex"); if (sc->link_vars.flow_ctrl) { if (sc->link_vars.flow_ctrl & FLOW_CTRL_RX) { printf(", receive "); if (sc->link_vars.flow_ctrl & FLOW_CTRL_TX) printf("& transmit "); } else printf(", transmit "); printf("flow control ON"); } printf("\n"); } else { /* Report the link down */ BXE_PRINTF("Link is down\n"); if_link_state_change(sc->bxe_ifp, LINK_STATE_DOWN); } DBEXIT(BXE_VERBOSE_PHY); } /* * * Returns: * None. */ static void bxe__link_status_update(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_PHY); if (sc->stats_enable == FALSE || sc->state != BXE_STATE_OPEN) return; bxe_link_status_update(&sc->link_params, &sc->link_vars); if (sc->link_vars.link_up) bxe_stats_handle(sc, STATS_EVENT_LINK_UP); else bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_read_mf_cfg(sc); /* Indicate link status. */ bxe_link_report(sc); DBEXIT(BXE_VERBOSE_PHY); } /* * Calculate flow control to advertise during autonegotiation. * * Returns: * None. */ static void bxe_calc_fc_adv(struct bxe_softc *sc) { DBENTER(BXE_EXTREME_PHY); switch (sc->link_vars.ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK) { case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE: sc->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH: sc->port.advertising |= (ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC: sc->port.advertising |= ADVERTISED_Asym_Pause; break; default: sc->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; } DBEXIT(BXE_EXTREME_PHY); } /* * * Returns: * */ static uint8_t bxe_initial_phy_init(struct bxe_softc *sc) { uint8_t rc; DBENTER(BXE_VERBOSE_PHY); rc = 0; if (!NOMCP(sc)) { /* * It is recommended to turn off RX flow control for 5771x * when using jumbo frames for better performance. */ if (!IS_E1HMF(sc) && (sc->mbuf_alloc_size > 5000)) sc->link_params.req_fc_auto_adv = FLOW_CTRL_TX; else sc->link_params.req_fc_auto_adv = FLOW_CTRL_BOTH; bxe_acquire_phy_lock(sc); rc = bxe_phy_init(&sc->link_params, &sc->link_vars); bxe_release_phy_lock(sc); bxe_calc_fc_adv(sc); if (sc->link_vars.link_up) { bxe_stats_handle(sc,STATS_EVENT_LINK_UP); bxe_link_report(sc); } } else { DBPRINT(sc, BXE_FATAL, "%s(): Bootcode is not running, " "not initializing link!\n", __FUNCTION__); rc = EINVAL; } DBEXIT(BXE_VERBOSE_PHY); return (rc); } #if __FreeBSD_version >= 800000 /* * Allocate buffer rings used for multiqueue. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_alloc_buf_rings(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD); rc = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp != NULL) { fp->br = buf_ring_alloc(BXE_BR_SIZE, M_DEVBUF, M_DONTWAIT, &fp->mtx); if (fp->br == NULL) { rc = ENOMEM; goto bxe_alloc_buf_rings_exit; } } else BXE_PRINTF("%s(%d): Bug!\n", __FILE__, __LINE__); } bxe_alloc_buf_rings_exit: DBEXIT(BXE_VERBOSE_LOAD); return (rc); } /* * Releases buffer rings used for multiqueue. * * Returns: * None */ static void bxe_free_buf_rings(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp != NULL) { if (fp->br != NULL) buf_ring_free(fp->br, M_DEVBUF); } } DBEXIT(BXE_VERBOSE_UNLOAD); } #endif /* * Handles controller initialization. * * Must be called from a locked routine. Since this code * may be called from the OS it does not provide a return * error value and must clean-up it's own mess. * * Returns: * Nothing. */ static void bxe_init_locked(struct bxe_softc *sc, int load_mode) { struct ifnet *ifp; uint32_t load_code; int error, i, port; DBENTER(BXE_INFO_LOAD | BXE_INFO_RESET); BXE_CORE_LOCK_ASSERT(sc); ifp = sc->bxe_ifp; /* Skip if we're in panic mode. */ if (sc->panic) { DBPRINT(sc, BXE_WARN, "%s(): Panic mode enabled, exiting!\n", __FUNCTION__); goto bxe_init_locked_exit; } /* Check if the driver is still running and bail out if it is. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { DBPRINT(sc, BXE_WARN, "%s(): Init called while driver is running!\n", __FUNCTION__); goto bxe_init_locked_exit; } /* * Send LOAD_REQUEST command to MCP. * The MCP will return the type of LOAD * the driver should perform. * - If it is the first port to be initialized * then all common blocks should be initialized. * - If it is not the first port to be initialized * then don't do the common block initialization. */ sc->state = BXE_STATE_OPENING_WAIT4_LOAD; if (NOMCP(sc)) { port = BP_PORT(sc); DBPRINT(sc, BXE_INFO, "%s(): Old No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); load_count[0]++; load_count[1 + port]++; DBPRINT(sc, BXE_INFO, "%s(): New No MCP load counts: %d, %d, %d\n", __FUNCTION__, load_count[0], load_count[1], load_count[2]); /* No MCP to tell us what to do. */ if (load_count[0] == 1) load_code = FW_MSG_CODE_DRV_LOAD_COMMON; else if (load_count[1 + port] == 1) load_code = FW_MSG_CODE_DRV_LOAD_PORT; else load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; } else { /* Ask the MCP what type of initialization we need to do. */ load_code = bxe_fw_command(sc, DRV_MSG_CODE_LOAD_REQ); if ((load_code == 0) || (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED)) { BXE_PRINTF("%s(%d): Bootcode refused load request.!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } } /* Keep track of whether we are controlling the port. */ if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) sc->port.pmf = 1; else sc->port.pmf = 0; /* Block any interrupts until we're ready. */ sc->intr_sem = 1; /* Initialize hardware. */ error = bxe_init_hw(sc, load_code); if (error != 0){ BXE_PRINTF("%s(%d): Hardware initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } /* Calculate and save the Ethernet MTU size. */ sc->port.ether_mtu = ifp->if_mtu + ETHER_HDR_LEN + (ETHER_VLAN_ENCAP_LEN * 2) + ETHER_CRC_LEN + 4; DBPRINT(sc, BXE_INFO, "%s(): Setting MTU = %d\n", __FUNCTION__, sc->port.ether_mtu); /* Setup the mbuf allocation size for RX frames. */ if (sc->port.ether_mtu <= MCLBYTES) sc->mbuf_alloc_size = MCLBYTES; else if (sc->port.ether_mtu <= PAGE_SIZE) sc->mbuf_alloc_size = PAGE_SIZE; else sc->mbuf_alloc_size = MJUM9BYTES; DBPRINT(sc, BXE_INFO, "%s(): mbuf_alloc_size = %d, " "max_frame_size = %d\n", __FUNCTION__, sc->mbuf_alloc_size, sc->port.ether_mtu); /* Setup NIC internals and enable interrupts. */ error = bxe_init_nic(sc, load_code); if (error != 0) { BXE_PRINTF("%s(%d): NIC initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) && (sc->common.shmem2_base)){ if (sc->dcc_enable == TRUE) { BXE_PRINTF("Enabing DCC support\n"); SHMEM2_WR(sc, dcc_support, (SHMEM_DCC_SUPPORT_DISABLE_ENABLE_PF_TLV | SHMEM_DCC_SUPPORT_BANDWIDTH_ALLOCATION_TLV)); } } #if __FreeBSD_version >= 800000 /* Allocate buffer rings for multiqueue operation. */ error = bxe_alloc_buf_rings(sc); if (error != 0) { BXE_PRINTF("%s(%d): Buffer ring initialization failed, " "aborting!\n", __FILE__, __LINE__); goto bxe_init_locked_failed1; } #endif /* Tell MCP that driver load is done. */ if (!NOMCP(sc)) { load_code = bxe_fw_command(sc, DRV_MSG_CODE_LOAD_DONE); if (!load_code) { BXE_PRINTF("%s(%d): Driver load failed! No MCP " "response to LOAD_DONE!\n", __FILE__, __LINE__); goto bxe_init_locked_failed2; } } sc->state = BXE_STATE_OPENING_WAIT4_PORT; /* Enable ISR for PORT_SETUP ramrod. */ sc->intr_sem = 0; /* Setup the leading connection for the controller. */ error = bxe_setup_leading(sc); if (error != 0) { DBPRINT(sc, BXE_FATAL, "%s(): Initial PORT_SETUP ramrod " "failed. State is not OPEN!\n", __FUNCTION__); goto bxe_init_locked_failed3; } if (CHIP_IS_E1H(sc)) { if (sc->mf_config[BP_E1HVN(sc)] & FUNC_MF_CFG_FUNC_DISABLED) { BXE_PRINTF("Multi-function mode is disabled\n"); /* sc->state = BXE_STATE_DISABLED; */ } /* Setup additional client connections for RSS/multi-queue */ if (sc->state == BXE_STATE_OPEN) { for (i = 1; i < sc->num_queues; i++) { if (bxe_setup_multi(sc, i)) { DBPRINT(sc, BXE_FATAL, "%s(): fp[%02d] CLIENT_SETUP ramrod failed! State not OPEN!\n", __FUNCTION__, i); goto bxe_init_locked_failed4; } } } } DELAY(5000); bxe_int_enable(sc); DELAY(5000); /* Initialize statistics. */ bxe_stats_init(sc); DELAY(1000); /* Load our MAC address. */ bcopy(IF_LLADDR(sc->bxe_ifp), sc->link_params.mac_addr, ETHER_ADDR_LEN); if (CHIP_IS_E1(sc)) bxe_set_mac_addr_e1(sc, 1); else bxe_set_mac_addr_e1h(sc, 1); DELAY(1000); /* Perform PHY initialization for the primary port. */ if (sc->port.pmf) bxe_initial_phy_init(sc); DELAY(1000); /* Start fastpath. */ switch (load_mode) { case LOAD_NORMAL: case LOAD_OPEN: /* Initialize the receive filters. */ bxe_set_rx_mode(sc); break; case LOAD_DIAG: /* Initialize the receive filters. */ bxe_set_rx_mode(sc); sc->state = BXE_STATE_DIAG; break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown load mode (%d)!\n", __FUNCTION__, load_mode); break; } if (!sc->port.pmf) bxe__link_status_update(sc); DELAY(1000); /* Tell the stack the driver is running. */ ifp->if_drv_flags = IFF_DRV_RUNNING; /* Schedule our periodic timer tick. */ callout_reset(&sc->bxe_tick_callout, hz, bxe_tick, sc); /* Everything went OK, go ahead and exit. */ goto bxe_init_locked_exit; bxe_init_locked_failed4: /* Try and gracefully shutdown the device because of a failure. */ for (i = 1; i < sc->num_queues; i++) bxe_stop_multi(sc, i); bxe_init_locked_failed3: bxe_stop_leading(sc); bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_init_locked_failed2: bxe_int_disable(sc); bxe_init_locked_failed1: if (!NOMCP(sc)) { bxe_fw_command(sc, DRV_MSG_CODE_LOAD_DONE); bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP); bxe_fw_command(sc, DRV_MSG_CODE_UNLOAD_DONE); } sc->port.pmf = 0; #if __FreeBSD_version >= 800000 bxe_free_buf_rings(sc); #endif DBPRINT(sc, BXE_WARN, "%s(): Initialization failed!\n", __FUNCTION__); bxe_init_locked_exit: DBEXIT(BXE_INFO_LOAD | BXE_INFO_RESET); } /* * Ramrod wait function. * * Waits for a ramrod command to complete. * * Returns: * 0 = Success, !0 = Failure */ static int bxe_wait_ramrod(struct bxe_softc *sc, int state, int idx, int *state_p, int poll) { int rc, timeout; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): %s for state 0x%08X on " "fp[%02d], currently 0x%08X.\n", __FUNCTION__, poll ? "Polling" : "Waiting", state, idx, *state_p); rc = 0; timeout = 5000; while (timeout) { /* Manually check for the completion. */ if (poll) { bxe_rxeof(sc->fp); /* * Some commands don't use the leading client * connection. */ if (idx) bxe_rxeof(&sc->fp[idx]); } /* State may be changed by bxe_sp_event(). */ mb(); if (*state_p == state) goto bxe_wait_ramrod_exit; timeout--; /* Pause 1ms before checking again. */ DELAY(1000); } /* We timed out polling for a completion. */ DBPRINT(sc, BXE_FATAL, "%s(): Timeout %s for state 0x%08X on fp[%02d]. " "Got 0x%x instead\n", __FUNCTION__, poll ? "polling" : "waiting", state, idx, *state_p); rc = EBUSY; bxe_wait_ramrod_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_RAMROD); return (rc); } /* * * */ static void bxe_write_dmae_phys_len(struct bxe_softc *sc, bus_addr_t phys_addr, uint32_t addr, uint32_t len) { int dmae_wr_max, offset; DBENTER(BXE_INSANE_REGS); dmae_wr_max = DMAE_LEN32_WR_MAX(sc); offset = 0; while (len > dmae_wr_max) { bxe_write_dmae(sc, phys_addr + offset, addr + offset, dmae_wr_max); offset += dmae_wr_max * 4; len -= dmae_wr_max; } bxe_write_dmae(sc, phys_addr + offset, addr + offset, len); DBEXIT(BXE_INSANE_REGS); } #define INIT_MEM_WR(block, reg, part, hw, data, reg_off, len) \ bxe_init_str_wr(sc, GRCBASE_##block + reg + reg_off * 4, data, len) /* * Write a block of data to a range of registers. * * Returns: * None. */ static void bxe_init_str_wr(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len) { uint32_t i; for (i = 0; i < len; i++) REG_WR(sc, addr + i * 4, data[i]); } /* * Write a block of data to a range of registers using indirect access. * * Returns: * None. */ static void bxe_init_ind_wr(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint16_t len) { uint32_t i; for (i = 0; i < len; i++) REG_WR_IND(sc, addr + i * 4, data[i]); } /* * * Returns: * None. */ static void bxe_write_big_buf(struct bxe_softc *sc, uint32_t addr, uint32_t len) { DBENTER(BXE_INSANE_REGS); #ifdef BXE_USE_DMAE if (sc->dmae_ready) bxe_write_dmae_phys_len(sc, sc->gz_dma.paddr, addr, len); else bxe_init_str_wr(sc, addr, sc->gz, len); #else bxe_init_str_wr(sc, addr, sc->gz, len); #endif DBEXIT(BXE_INSANE_REGS); } /* * Fill areas of device memory with the specified value. * * Generally used to clear a small area of device memory prior to writing * firmware to STORM memory or writing STORM firmware to device memory. * * Returns: * None. */ static void bxe_init_fill(struct bxe_softc *sc, uint32_t addr, int fill, uint32_t len) { uint32_t cur_len, i, leftovers, length; DBENTER(BXE_VERBOSE_LOAD); length = (((len * 4) > BXE_FW_BUF_SIZE) ? BXE_FW_BUF_SIZE : (len * 4)); leftovers = length / 4; memset(sc->gz, fill, length); for (i = 0; i < len; i += leftovers) { cur_len = min(leftovers, len - i); bxe_write_big_buf(sc, addr + i * 4, cur_len); } DBEXIT(BXE_VERBOSE_LOAD); } /* * * Returns: * None. */ static void bxe_init_wr_64(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len64) { uint64_t data64, *pdata; uint32_t buf_len32, cur_len, len; int i; DBENTER(BXE_INSANE_REGS); buf_len32 = BXE_FW_BUF_SIZE / 4; len = len64 * 2; /* 64 bit value is in a blob: first low DWORD, then high DWORD. */ data64 = HILO_U64((*(data + 1)), (*data)); len64 = min((uint32_t)(BXE_FW_BUF_SIZE / 8), len64); for (i = 0; i < len64; i++) { pdata = ((uint64_t *)(sc->gz)) + i; *pdata = data64; } for (i = 0; i < len; i += buf_len32) { cur_len = min(buf_len32, len - i); bxe_write_big_buf(sc, addr + i*4, cur_len); } DBEXIT(BXE_INSANE_REGS); } /* * There are different blobs for each PRAM section. In addition, each * blob write operation is divided into multiple, smaller write * operations in order to decrease the amount of physically contiguous * buffer memory needed. Thus, when we select a blob, the address may * be with some offset from the beginning of PRAM section. The same * holds for the INT_TABLE sections. */ #define IF_IS_INT_TABLE_ADDR(base, addr) \ if (((base) <= (addr)) && ((base) + 0x400 >= (addr))) #define IF_IS_PRAM_ADDR(base, addr) \ if (((base) <= (addr)) && ((base) + 0x40000 >= (addr))) /* * * Returns: * None. */ static const uint8_t * bxe_sel_blob(struct bxe_softc *sc, uint32_t addr, const uint8_t *data) { IF_IS_INT_TABLE_ADDR(TSEM_REG_INT_TABLE, addr) data = INIT_TSEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(CSEM_REG_INT_TABLE, addr) data = INIT_CSEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(USEM_REG_INT_TABLE, addr) data = INIT_USEM_INT_TABLE_DATA(sc); else IF_IS_INT_TABLE_ADDR(XSEM_REG_INT_TABLE, addr) data = INIT_XSEM_INT_TABLE_DATA(sc); else IF_IS_PRAM_ADDR(TSEM_REG_PRAM, addr) data = INIT_TSEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(CSEM_REG_PRAM, addr) data = INIT_CSEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(USEM_REG_PRAM, addr) data = INIT_USEM_PRAM_DATA(sc); else IF_IS_PRAM_ADDR(XSEM_REG_PRAM, addr) data = INIT_XSEM_PRAM_DATA(sc); return (data); } static void bxe_write_big_buf_wb(struct bxe_softc *sc, uint32_t addr, uint32_t len) { if (sc->dmae_ready) bxe_write_dmae_phys_len(sc, sc->gz_dma.paddr, addr, len); else bxe_init_ind_wr(sc, addr, sc->gz, len); } #define VIRT_WR_DMAE_LEN(sc, data, addr, len32, le32_swap) \ do { \ memcpy(sc->gz, data, (len32)*4); \ bxe_write_big_buf_wb(sc, addr, len32); \ } while (0) /* * * Returns: * None. */ static void bxe_init_wr_wb(struct bxe_softc *sc, uint32_t addr, const uint32_t *data, uint32_t len) { const uint32_t *old_data; DBENTER(BXE_INSANE_REGS); old_data = data; data = (const uint32_t *)bxe_sel_blob(sc, addr, (const uint8_t *)data); if (sc->dmae_ready) { if (old_data != data) VIRT_WR_DMAE_LEN(sc, data, addr, len, 1); else VIRT_WR_DMAE_LEN(sc, data, addr, len, 0); } else bxe_init_ind_wr(sc, addr, data, len); DBEXIT(BXE_INSANE_REGS); } static void bxe_init_wr_zp(struct bxe_softc *sc, uint32_t addr, uint32_t len, uint32_t blob_off) { BXE_PRINTF("%s(%d): Compressed FW is not supported yet. " "ERROR: address:0x%x len:0x%x blob_offset:0x%x\n", __FILE__, __LINE__, addr, len, blob_off); } /* * Initialize blocks of the device. * * This routine basically performs bulk register programming for different * blocks within the controller. The file bxe_init_values.h contains a * series of register access operations (read, write, fill, etc.) as well * as a BLOB of data to initialize multiple blocks within the controller. * Block initialization may be supported by all controllers or by specific * models only. * * Returns: * None. */ static void bxe_init_block(struct bxe_softc *sc, uint32_t block, uint32_t stage) { union init_op *op; const uint32_t *data, *data_base; uint32_t i, op_type, addr, len; uint16_t op_end, op_start; int hw_wr; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); op_start = INIT_OPS_OFFSETS(sc)[BLOCK_OPS_IDX(block, stage, STAGE_START)]; op_end = INIT_OPS_OFFSETS(sc)[BLOCK_OPS_IDX(block, stage, STAGE_END)]; /* If empty block */ if (op_start == op_end) return; hw_wr = OP_WR_ASIC; data_base = INIT_DATA(sc); for (i = op_start; i < op_end; i++) { op = (union init_op *)&(INIT_OPS(sc)[i]); op_type = op->str_wr.op; addr = op->str_wr.offset; len = op->str_wr.data_len; data = data_base + op->str_wr.data_off; /* HW/EMUL specific */ if ((op_type > OP_WB) && (op_type == hw_wr)) op_type = OP_WR; switch (op_type) { case OP_RD: REG_RD(sc, addr); break; case OP_WR: REG_WR(sc, addr, op->write.val); break; case OP_SW: bxe_init_str_wr(sc, addr, data, len); break; case OP_WB: bxe_init_wr_wb(sc, addr, data, len); break; case OP_SI: bxe_init_ind_wr(sc, addr, data, len); break; case OP_ZR: bxe_init_fill(sc, addr, 0, op->zero.len); break; case OP_ZP: bxe_init_wr_zp(sc, addr, len, op->str_wr.data_off); break; case OP_WR_64: bxe_init_wr_64(sc, addr, data, len); break; default: /* happens whenever an op is of a diff HW */ break; } } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Handles controller initialization when called from an unlocked routine. * ifconfig calls this function. * * Returns: * None. */ static void bxe_init(void *xsc) { struct bxe_softc *sc; sc = xsc; BXE_CORE_LOCK(sc); bxe_init_locked(sc, LOAD_NORMAL); BXE_CORE_UNLOCK(sc); } /* * Release all resources used by the driver. * * Releases all resources acquired by the driver including interrupts, * interrupt handler, interfaces, mutexes, and DMA memory. * * Returns: * None. */ static void bxe_release_resources(struct bxe_softc *sc) { device_t dev; DBENTER(BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); dev = sc->dev; /* Release the FreeBSD interface. */ if (sc->bxe_ifp != NULL) if_free(sc->bxe_ifp); /* Free the DMA resources. */ bxe_host_structures_free(sc); #if __FreeBSD_version >= 800000 /* Free multiqueue buffer rings. */ bxe_free_buf_rings(sc); #endif } /* * Indirect register write. * * Writes NetXtreme II registers using an index/data register pair in PCI * configuration space. Using this mechanism avoids issues with posted * writes but is much slower than memory-mapped I/O. * * Returns: * None. */ static void bxe_reg_wr_ind(struct bxe_softc *sc, uint32_t offset, uint32_t val) { DBPRINT(sc, BXE_INSANE_REGS, "%s(); offset = 0x%08X, val = 0x%08X\n", __FUNCTION__, offset, val); pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, offset, 4); pci_write_config(sc->dev, PCICFG_GRC_DATA, val, 4); /* Return to a safe address. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); } /* * Indirect register read. * * Reads NetXtreme II registers using an index/data register pair in PCI * configuration space. Using this mechanism avoids issues with posted * reads but is much slower than memory-mapped I/O. * * Returns: * The value of the register. */ static uint32_t bxe_reg_rd_ind(struct bxe_softc *sc, uint32_t offset) { uint32_t val; pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, offset, 4); val = pci_read_config(sc->dev, PCICFG_GRC_DATA, 4); /* Return to a safe address. */ pci_write_config(sc->dev, PCICFG_GRC_ADDRESS, PCICFG_VENDOR_ID_OFFSET, 4); DBPRINT(sc, BXE_INSANE_REGS, "%s(); offset = 0x%08X, val = 0x%08X\n", __FUNCTION__, offset, val); return (val); } static uint32_t dmae_reg_go_c[] = { DMAE_REG_GO_C0, DMAE_REG_GO_C1, DMAE_REG_GO_C2, DMAE_REG_GO_C3, DMAE_REG_GO_C4, DMAE_REG_GO_C5, DMAE_REG_GO_C6, DMAE_REG_GO_C7, DMAE_REG_GO_C8, DMAE_REG_GO_C9, DMAE_REG_GO_C10, DMAE_REG_GO_C11, DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15 }; /* * Copy DMAE command into memory and start the command. * * Returns: * None. */ static void bxe_post_dmae(struct bxe_softc *sc, struct dmae_command *dmae, int idx) { uint32_t cmd_offset; int i; cmd_offset = (DMAE_REG_CMD_MEM + sizeof(struct dmae_command) * idx); for (i = 0; i < (sizeof(struct dmae_command) / 4); i++) { REG_WR(sc, cmd_offset + i * 4, *(((uint32_t *)dmae) + i)); DBPRINT(sc, BXE_INSANE_REGS, "%s(): DMAE cmd[%d].%d : 0x%08X\n", __FUNCTION__, idx, i, cmd_offset + i * 4); } /* Kick off the command. */ REG_WR(sc, dmae_reg_go_c[idx], 1); } /* * Perform a DMAE write to device memory. * * Some of the registers on the 577XX controller are 128bits wide. It is * required that when accessing those registers that they be written * atomically and that no intervening bus acceses to the device occur. * This could be handled by a lock held across all driver instances for * the device or it can be handled by performing a DMA operation when * writing to the device. This code implements the latter. * * Returns: * None. */ void bxe_write_dmae(struct bxe_softc *sc, bus_addr_t dma_addr, uint32_t dst_addr, uint32_t len32) { struct dmae_command dmae; uint32_t *data, *wb_comp; int timeout; DBENTER(BXE_INSANE_REGS); DBPRINT(sc, BXE_EXTREME_REGS, "%s(): host addr = 0x%jX, device addr = 0x%08X, length = %d.\n", __FUNCTION__, (uintmax_t)dma_addr, dst_addr, (int)len32); wb_comp = BXE_SP(sc, wb_comp); /* Fall back to indirect access if DMAE is not ready. */ if (!sc->dmae_ready) { data = BXE_SP(sc, wb_data[0]); DBPRINT(sc, BXE_WARN, "%s(): DMAE not ready, " "using indirect.\n", __FUNCTION__); bxe_init_ind_wr(sc, dst_addr, data, len32); goto bxe_write_dmae_exit; } memset(&dmae, 0, sizeof(struct dmae_command)); dmae.opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae.src_addr_lo = U64_LO(dma_addr); dmae.src_addr_hi = U64_HI(dma_addr); dmae.dst_addr_lo = dst_addr >> 2; dmae.dst_addr_hi = 0; dmae.len = len32; dmae.comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_val = BXE_WB_COMP_VAL; BXE_DMAE_LOCK(sc); *wb_comp = 0; bxe_post_dmae(sc, &dmae, INIT_DMAE_C(sc)); DELAY(50); /* Wait up to 200ms. */ timeout = 4000; while (*wb_comp != BXE_WB_COMP_VAL) { if (!timeout) { DBPRINT(sc, BXE_FATAL, "%s(): DMAE timeout (dst_addr = 0x%08X, len = %d)!\n", __FUNCTION__, dst_addr, len32); break; } timeout--; DELAY(50); } BXE_DMAE_UNLOCK(sc); bxe_write_dmae_exit: DBEXIT(BXE_INSANE_REGS); } /* * Perform a DMAE read from to device memory. * * Some of the registers on the 577XX controller are 128bits wide. It is * required that when accessing those registers that they be read * atomically and that no intervening bus acceses to the device occur. * This could be handled by a lock held across all driver instances for * the device or it can be handled by performing a DMA operation when * reading from the device. This code implements the latter. * * Returns: * None. */ void bxe_read_dmae(struct bxe_softc *sc, uint32_t src_addr, uint32_t len32) { struct dmae_command dmae; uint32_t *data, *wb_comp; int i, timeout; DBENTER(BXE_INSANE_REGS); wb_comp = BXE_SP(sc, wb_comp); /* Fall back to indirect access if DMAE is not ready. */ if (!sc->dmae_ready) { data = BXE_SP(sc, wb_data[0]); DBPRINT(sc, BXE_WARN, "%s(): DMAE not ready, " "using indirect.\n", __FUNCTION__); for (i = 0; i < len32; i++) data[i] = bxe_reg_rd_ind(sc, src_addr + i * 4); goto bxe_read_dmae_exit; } memset(&dmae, 0, sizeof(struct dmae_command)); dmae.opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae.src_addr_lo = src_addr >> 2; dmae.src_addr_hi = 0; dmae.dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_data)); dmae.dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_data)); dmae.len = len32; dmae.comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, wb_comp)); dmae.comp_val = BXE_WB_COMP_VAL; BXE_DMAE_LOCK(sc); memset(BXE_SP(sc, wb_data[0]), 0, sizeof(uint32_t) * 4); *wb_comp = 0; bxe_post_dmae(sc, &dmae, INIT_DMAE_C(sc)); DELAY(50); timeout = 4000; while (*wb_comp != BXE_WB_COMP_VAL) { if (!timeout) { DBPRINT(sc, BXE_FATAL, "%s(): DMAE timeout (src_addr = 0x%08X, len = %d)!\n", __FUNCTION__, src_addr, len32); break; } timeout--; DELAY(50); } BXE_DMAE_UNLOCK(sc); bxe_read_dmae_exit: DBEXIT(BXE_INSANE_REGS); } /* * DMAE write wrapper. * * Returns: * None. */ static void bxe_wb_wr(struct bxe_softc *sc, int reg, uint32_t val_hi, uint32_t val_lo) { uint32_t wb_write[2]; wb_write[0] = val_hi; wb_write[1] = val_lo; REG_WR_DMAE(sc, reg, wb_write, 2); } /* * Poll a register waiting for a value. * * Returns: * The last read register value. */ static __inline uint32_t bxe_reg_poll(struct bxe_softc *sc, uint32_t reg, uint32_t expected, int ms, int wait) { uint32_t val; do { val = REG_RD(sc, reg); if (val == expected) break; ms -= wait; DELAY(wait * 1000); } while (ms > 0); return (val); } /* * Microcode assert display. * * This function walks through each STORM processor and prints out a * listing of all asserts currently in effect. Useful for post-mortem * debugging. * * Returns: * The number of asserts detected. */ static int bxe_mc_assert(struct bxe_softc *sc) { uint32_t row0, row1, row2, row3; char last_idx; int i, rc; DBENTER(BXE_VERBOSE_INTR); rc = 0; /* XSTORM */ last_idx = REG_RD8(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA XSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_XSTORM_INTMEM + XSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA XSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* TSTORM */ last_idx = REG_RD8(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA TSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_TSTORM_INTMEM + TSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA TSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* CSTORM */ last_idx = REG_RD8(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA CSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_CSTORM_INTMEM + CSTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA CSTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } /* USTORM */ last_idx = REG_RD8(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_INDEX_OFFSET); if (last_idx) DBPRINT(sc, BXE_FATAL, "DATA USTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); /* Print the asserts */ for (i = 0; i < STORM_ASSERT_ARRAY_SIZE; i++) { row0 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i)); row1 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 4); row2 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 8); row3 = REG_RD(sc, BAR_USTORM_INTMEM + USTORM_ASSERT_LIST_OFFSET(i) + 12); if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { DBPRINT(sc, BXE_FATAL, "DATA USTORM_ASSERT_INDEX %d = " "0x%08x 0x%08x 0x%08x 0x%08x\n", i, row3, row2, row1, row0); rc++; } else break; } DBEXIT(BXE_VERBOSE_INTR); return (rc); } /* * Perform a panic dump. * * Returns: * None */ static void bxe_panic_dump(struct bxe_softc *sc) { DBENTER(BXE_FATAL); sc->stats_state = STATS_STATE_DISABLED; BXE_PRINTF("---------- Begin crash dump ----------\n"); /* Idle check is run twice to verify the controller has stopped. */ bxe_idle_chk(sc); bxe_idle_chk(sc); bxe_mc_assert(sc); #ifdef BXE_DEBUG bxe_breakpoint(sc); #endif BXE_PRINTF("---------- End crash dump ----------\n"); DBEXIT(BXE_FATAL); } /* * Enables interrupt generation. * * Returns: * None. */ static void bxe_int_enable(struct bxe_softc *sc) { uint32_t hc_addr, val; int port; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); hc_addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; val = REG_RD(sc, hc_addr); if (sc->msix_count > 0) { if (sc->msix_count == 1) { /* Single interrupt, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI-X (SIMQ).\n", __FUNCTION__); /* Clear INTx. */ val &= ~HC_CONFIG_0_REG_INT_LINE_EN_0; /* Enable single ISR mode, MSI/MSI-X, and attention messages. */ val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } else { /* Multiple interrupts, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI-X (MIMQ).\n", __FUNCTION__); /* Clear single ISR mode and INTx. */ val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0); /* Enable MSI/MSI-X and attention messages. */ val |= (HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } } else if (sc->msi_count > 0) { if (sc->msi_count == 1) { /* Single interrupt, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for MSI (SIMQ).\n", __FUNCTION__); /* Clear INTx. */ val &= ~HC_CONFIG_0_REG_INT_LINE_EN_0; /* Enable single ISR mode, MSI/MSI-X, and attention * messages. */ val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } else { /* Multiple interrupts, multiple queues.*/ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for" "MSI (MIMQ).\n", __FUNCTION__); /* Clear single ISR mode and INTx. */ val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0); /* Enable MSI/MSI-X and attention messages. */ val |= (HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); } } else { /* Single interrupt, single queue. */ DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Setting host coalescing registers for INTA#.\n", __FUNCTION__); val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 | HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); REG_WR(sc, hc_addr, val); val &= ~HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0; } /* Write the interrupt mode to the host coalescing block. */ REG_WR(sc, hc_addr, val); if (CHIP_IS_E1H(sc)) { /* Init leading/trailing edge attention generation. */ if (IS_E1HMF(sc)) { val = (0xee0f | (1 << (BP_E1HVN(sc) + 4))); /* * Check if this driver instance is the port * master function. */ if (sc->port.pmf) /* Enable nig & GPIO3 attentions. */ val |= 0x1100; } else val = 0xffff; REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, val); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, val); } DBEXIT(BXE_VERBOSE_INTR); } /* * Disables interrupt generation. * * Returns: * None. */ static void bxe_int_disable(struct bxe_softc *sc) { uint32_t hc_addr, val; int port; DBENTER(BXE_VERBOSE_INTR | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); hc_addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; val = REG_RD(sc, hc_addr); val &= ~(HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 | HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); REG_WR(sc, hc_addr, val); if (REG_RD(sc, hc_addr)!= val) { DBPRINT(sc, BXE_WARN, "%s(): BUG! Returned value from IGU " "doesn't match value written (0x%08X).\n", __FUNCTION__, val); } DBEXIT(BXE_VERBOSE_INTR | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } #define BXE_CRC32_RESIDUAL 0xdebb20e3 /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_acquire_lock(struct bxe_softc *sc) { uint32_t val; int i, port, rc; DBENTER(BXE_VERBOSE_NVRAM); port = BP_PORT(sc); rc = 0; val = 0; /* Acquire the NVRAM lock. */ REG_WR(sc, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_SET1 << port)); for (i = 0; i < NVRAM_TIMEOUT_COUNT * 10; i++) { val = REG_RD(sc, MCP_REG_MCPR_NVM_SW_ARB); if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) break; DELAY(5); } if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) { DBPRINT(sc, BXE_WARN, "%s(): Cannot acquire NVRAM lock!\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_release_lock(struct bxe_softc *sc) { uint32_t val; int i, port, rc; DBENTER(BXE_VERBOSE_NVRAM); port = BP_PORT(sc); rc = 0; val = 0; /* Release the NVRAM lock. */ REG_WR(sc, MCP_REG_MCPR_NVM_SW_ARB, (MCPR_NVM_SW_ARB_ARB_REQ_CLR1 << port)); for (i = 0; i < NVRAM_TIMEOUT_COUNT * 10; i++) { val = REG_RD(sc, MCP_REG_MCPR_NVM_SW_ARB); if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) break; DELAY(5); } if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) { DBPRINT(sc, BXE_WARN, "%s(): Cannot release NVRAM lock!\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * None. */ static void bxe_nvram_enable_access(struct bxe_softc *sc) { uint32_t val; DBENTER(BXE_VERBOSE_NVRAM); val = REG_RD(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE); /* Enable both bits, even on read */ REG_WR(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE, (val | MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN)); DBEXIT(BXE_VERBOSE_NVRAM); } /* * Returns: * None. */ static void bxe_nvram_disable_access(struct bxe_softc *sc) { uint32_t val; DBENTER(BXE_VERBOSE_NVRAM); val = REG_RD(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE); /* Disable both bits, even after read. */ REG_WR(sc, MCP_REG_MCPR_NVM_ACCESS_ENABLE, (val & ~(MCPR_NVM_ACCESS_ENABLE_EN | MCPR_NVM_ACCESS_ENABLE_WR_EN))); DBEXIT(BXE_VERBOSE_NVRAM); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_read_dword(struct bxe_softc *sc, uint32_t offset, uint32_t *ret_val, uint32_t cmd_flags) { uint32_t val; int i, rc; DBENTER(BXE_INSANE_NVRAM); /* Build the command word. */ cmd_flags |= MCPR_NVM_COMMAND_DOIT; /* Need to clear DONE bit separately. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE); /* Address within the NVRAM to read. */ REG_WR(sc, MCP_REG_MCPR_NVM_ADDR, (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE)); /* Issue a read command. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, cmd_flags); /* Wait for completion. */ *ret_val = 0; rc = EBUSY; for (i = 0; i < NVRAM_TIMEOUT_COUNT; i++) { DELAY(5); val = REG_RD(sc, MCP_REG_MCPR_NVM_COMMAND); if (val & MCPR_NVM_COMMAND_DONE) { val = REG_RD(sc, MCP_REG_MCPR_NVM_READ); val = htobe32(val); *ret_val = val; rc = 0; break; } } DBPRINT(sc, BXE_INSANE_NVRAM, "%s(): Read 0x%08X from offset 0x%08X.\n", __FUNCTION__, *ret_val, offset); DBEXIT(BXE_INSANE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_read(struct bxe_softc *sc, uint32_t offset, uint8_t *ret_buf, int buf_size) { uint32_t cmd_flags, val; int rc; DBENTER(BXE_EXTREME_NVRAM); if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { DBPRINT(sc, BXE_WARN, "%s(): Unaligned address or invalid " "buffer for NVRAM read (offset = 0x%08X, buf_size = %d)!\n", __FUNCTION__, offset, buf_size); rc = EINVAL; goto bxe_nvram_read_exit; } if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Read extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_read_exit; } rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_read_exit; bxe_nvram_enable_access(sc); /* Read the first word(s). */ cmd_flags = MCPR_NVM_COMMAND_FIRST; while ((buf_size > sizeof(uint32_t)) && (rc == 0)) { rc = bxe_nvram_read_dword(sc, offset, &val, cmd_flags); memcpy(ret_buf, &val, 4); /* Advance to the next DWORD. */ offset += sizeof(uint32_t); ret_buf += sizeof(uint32_t); buf_size -= sizeof(uint32_t); cmd_flags = 0; } /* Read the final word. */ if (rc == 0) { cmd_flags |= MCPR_NVM_COMMAND_LAST; rc = bxe_nvram_read_dword(sc, offset, &val, cmd_flags); memcpy(ret_buf, &val, 4); } /* Disable access to NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_read_exit: DBEXIT(BXE_EXTREME_NVRAM); return (rc); } #ifdef BXE_NVRAM_WRITE_SUPPORT /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_write_dword(struct bxe_softc *sc, uint32_t offset, uint32_t val, uint32_t cmd_flags) { int i, rc; DBENTER(BXE_VERBOSE_NVRAM); /* Build the command word. */ cmd_flags |= MCPR_NVM_COMMAND_DOIT | MCPR_NVM_COMMAND_WR; /* Need to clear DONE bit separately. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, MCPR_NVM_COMMAND_DONE); /* Write the data. */ REG_WR(sc, MCP_REG_MCPR_NVM_WRITE, val); /* Address to write within the NVRAM. */ REG_WR(sc, MCP_REG_MCPR_NVM_ADDR, (offset & MCPR_NVM_ADDR_NVM_ADDR_VALUE)); /* Issue the write command. */ REG_WR(sc, MCP_REG_MCPR_NVM_COMMAND, cmd_flags); /* Wait for completion. */ rc = EBUSY; for (i = 0; i < NVRAM_TIMEOUT_COUNT; i++) { DELAY(5); val = REG_RD(sc, MCP_REG_MCPR_NVM_COMMAND); if (val & MCPR_NVM_COMMAND_DONE) { rc = 0; break; } } DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } #define BYTE_OFFSET(offset) (8 * (offset & 0x03)) /* * Returns: * */ static int bxe_nvram_write1(struct bxe_softc *sc, uint32_t offset, uint8_t *data_buf, int buf_size) { uint32_t align_offset, cmd_flags, val; int rc; DBENTER(BXE_VERBOSE_NVRAM); if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Write extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_write1_exit; } /* request access to nvram interface */ rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_write1_exit; /* Enable access to the NVRAM interface. */ bxe_nvram_enable_access(sc); cmd_flags = (MCPR_NVM_COMMAND_FIRST | MCPR_NVM_COMMAND_LAST); align_offset = (offset & ~0x03); rc = bxe_nvram_read_dword(sc, align_offset, &val, cmd_flags); if (rc == 0) { val &= ~(0xff << BYTE_OFFSET(offset)); val |= (*data_buf << BYTE_OFFSET(offset)); val = be32toh(val); rc = bxe_nvram_write_dword(sc, align_offset, val, cmd_flags); } /* Disable access to the NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_write1_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_write(struct bxe_softc *sc, uint32_t offset, uint8_t *data_buf, int buf_size) { uint32_t cmd_flags, val, written_so_far; int rc; rc = 0; if (buf_size == 1) return (bxe_nvram_write1(sc, offset, data_buf, buf_size)); if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { DBPRINT(sc, BXE_WARN, "%s(): Unaligned address or invalid " "buffer for NVRAM write " "(offset = 0x%08X, buf_size = %d)!\n", __FUNCTION__, offset, buf_size); rc = EINVAL; goto bxe_nvram_write_exit; } if (offset + buf_size > sc->common.flash_size) { DBPRINT(sc, BXE_WARN, "%s(): Write extends beyond the end of " "the NVRAM (offset (0x%08X) + buf_size (%d) > flash_size " "(0x%08X))!\n", __FUNCTION__, offset, buf_size, sc->common.flash_size); rc = EINVAL; goto bxe_nvram_write_exit; } /* Request access to NVRAM interface. */ rc = bxe_nvram_acquire_lock(sc); if (rc) goto bxe_nvram_write_exit; /* Enable access to the NVRAM interface. */ bxe_nvram_enable_access(sc); written_so_far = 0; cmd_flags = MCPR_NVM_COMMAND_FIRST; while ((written_so_far < buf_size) && (rc == 0)) { if (written_so_far == (buf_size - sizeof(uint32_t))) cmd_flags |= MCPR_NVM_COMMAND_LAST; else if (((offset + 4) % NVRAM_PAGE_SIZE) == 0) cmd_flags |= MCPR_NVM_COMMAND_LAST; else if ((offset % NVRAM_PAGE_SIZE) == 0) cmd_flags |= MCPR_NVM_COMMAND_FIRST; memcpy(&val, data_buf, 4); rc = bxe_nvram_write_dword(sc, offset, val, cmd_flags); /* Advance to the next DWORD. */ offset += sizeof(uint32_t); data_buf += sizeof(uint32_t); written_so_far += sizeof(uint32_t); cmd_flags = 0; } /* Disable access to the NVRAM interface. */ bxe_nvram_disable_access(sc); bxe_nvram_release_lock(sc); bxe_nvram_write_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } #endif /* * This function validates NVRAM content by reading spcific * regions and validating that the NVRAM checksum matches the * actual content. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_nvram_test(struct bxe_softc *sc) { static const struct { int offset; int size; } nvram_tbl[] = { { 0, 0x14 }, /* bootstrap area*/ { 0x14, 0xec }, /* directory area */ { 0x100, 0x350 }, /* manuf_info */ { 0x450, 0xf0 }, /* feature_info */ { 0x640, 0x64 }, /* upgrade_key_info */ { 0x708, 0x70 }, /* manuf_key_info */ { 0, 0 } }; uint32_t magic, csum, buf[0x350 / 4]; uint8_t *data; int i, rc; DBENTER(BXE_VERBOSE_NVRAM); data = (uint8_t *) buf; /* Read the DWORD at offset 0 in NVRAM. */ rc = bxe_nvram_read(sc, 0, data, 4); if (rc) { BXE_PRINTF("%s(%d): Error (%d) returned reading NVRAM!\n", __FILE__, __LINE__, rc); goto bxe_nvram_test_exit; } /* Make sure we found our magic value. */ magic = be32toh(buf[0]); if (magic != 0x669955aa) { BXE_PRINTF("%s(%d): Invalid magic value (0x%08x) found!\n", __FILE__, __LINE__, magic); rc = ENODEV; goto bxe_nvram_test_exit; } /* Read through each region in NVRAM and validate the checksum. */ for (i = 0; nvram_tbl[i].size; i++) { DBPRINT(sc, BXE_VERBOSE_NVRAM, "%s(): Testing NVRAM region %d, " "starting offset = %d, length = %d\n", __FUNCTION__, i, nvram_tbl[i].offset, nvram_tbl[i].size); rc = bxe_nvram_read(sc, nvram_tbl[i].offset, data, nvram_tbl[i].size); if (rc) { BXE_PRINTF("%s(%d): Error (%d) returned reading NVRAM " "region %d!\n", __FILE__, __LINE__, rc, i); goto bxe_nvram_test_exit; } csum = ether_crc32_le(data, nvram_tbl[i].size); if (csum != BXE_CRC32_RESIDUAL) { BXE_PRINTF("%s(%d): Checksum error (0x%08X) for NVRAM " "region %d!\n", __FILE__, __LINE__, csum, i); rc = ENODEV; goto bxe_nvram_test_exit; } } bxe_nvram_test_exit: DBEXIT(BXE_VERBOSE_NVRAM); return (rc); } /* * Acknowledge status block and modify interrupt mode. * * Returns: * None. */ static __inline void bxe_ack_sb(struct bxe_softc *sc, uint8_t sb_id, uint8_t storm, uint16_t index, uint8_t int_mode, uint8_t update) { struct igu_ack_register igu_ack; uint32_t hc_addr; hc_addr = (HC_REG_COMMAND_REG + BP_PORT(sc) * 32 + COMMAND_REG_INT_ACK); igu_ack.status_block_index = index; igu_ack.sb_id_and_flags = ((sb_id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) | (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) | (int_mode << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT)); rmb(); REG_WR(sc, hc_addr, (*(uint32_t *) &igu_ack)); wmb(); } /* * Update fastpath status block index. * * Returns: * 0 = Nu completes, 1 = TX completes, 2 = RX completes, * 3 = RX & TX completes */ static __inline uint16_t bxe_update_fpsb_idx(struct bxe_fastpath *fp) { struct host_status_block *fpsb; uint16_t rc; fpsb = fp->status_block; rc = 0; rmb(); /* Check for any CSTORM transmit completions. */ if (fp->fp_c_idx != le16toh(fpsb->c_status_block.status_block_index)) { fp->fp_c_idx = le16toh(fpsb->c_status_block.status_block_index); rc |= 0x1; } /* Check for any USTORM receive completions. */ if (fp->fp_u_idx != le16toh(fpsb->u_status_block.status_block_index)) { fp->fp_u_idx = le16toh(fpsb->u_status_block.status_block_index); rc |= 0x2; } return (rc); } /* * Acknowledge interrupt. * * Returns: * Interrupt value read from IGU. */ static uint16_t bxe_ack_int(struct bxe_softc *sc) { uint32_t hc_addr, result; hc_addr = HC_REG_COMMAND_REG + BP_PORT(sc) * 32 + COMMAND_REG_SIMD_MASK; result = REG_RD(sc, hc_addr); DBPRINT(sc, BXE_INSANE_INTR, "%s(): Read 0x%08X from HC addr 0x%08X\n", __FUNCTION__, result, hc_addr); return (result); } /* * Slowpath event handler. * * Checks that a ramrod completion occurs while the * controller is in the proper state. * * Returns: * None. */ static void bxe_sp_event(struct bxe_fastpath *fp, union eth_rx_cqe *rr_cqe) { struct bxe_softc *sc; int cid, command; sc = fp->sc; DBENTER(BXE_VERBOSE_RAMROD); cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data); command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): CID = %d, ramrod command = %d, " "device state = 0x%08X, fp[%02d].state = 0x%08X, type = %d\n", __FUNCTION__, cid, command, sc->state, fp->index, fp->state, rr_cqe->ramrod_cqe.ramrod_type); /* Free up an entry on the slowpath queue. */ sc->spq_left++; /* Handle ramrod commands that completed on a client connection. */ if (fp->index) { /* Check for a completion for the current state. */ switch (command | fp->state) { case (RAMROD_CMD_ID_ETH_CLIENT_SETUP | BXE_FP_STATE_OPENING): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] CLIENT_SETUP Ramrod.\n", __FUNCTION__, cid); fp->state = BXE_FP_STATE_OPEN; break; case (RAMROD_CMD_ID_ETH_HALT | BXE_FP_STATE_HALTING): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] ETH_HALT ramrod\n", __FUNCTION__, cid); fp->state = BXE_FP_STATE_HALTED; break; default: DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Unexpected microcode reply (%d) while " "in state 0x%04X!\n", __FUNCTION__, command, fp->state); } goto bxe_sp_event_exit; } /* Handle ramrod commands that completed on the leading connection. */ switch (command | sc->state) { case (RAMROD_CMD_ID_ETH_PORT_SETUP | BXE_STATE_OPENING_WAIT4_PORT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed PORT_SETUP ramrod.\n", __FUNCTION__); sc->state = BXE_STATE_OPEN; break; case (RAMROD_CMD_ID_ETH_HALT | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_HALT ramrod.\n", __FUNCTION__); sc->state = BXE_STATE_CLOSING_WAIT4_DELETE; fp->state = BXE_FP_STATE_HALTED; break; case (RAMROD_CMD_ID_ETH_CFC_DEL | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed fp[%02d] ETH_CFC_DEL ramrod.\n", __FUNCTION__, cid); sc->fp[cid].state = BXE_FP_STATE_CLOSED; break; case (RAMROD_CMD_ID_ETH_SET_MAC | BXE_STATE_OPEN): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_SET_MAC ramrod in STATE_OPEN state.\n", __FUNCTION__); break; case (RAMROD_CMD_ID_ETH_SET_MAC | BXE_STATE_CLOSING_WAIT4_HALT): DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): Completed ETH_SET_MAC ramrod in " "CLOSING_WAIT4_HALT state.\n", __FUNCTION__); break; default: DBPRINT(sc, BXE_FATAL, "%s(): Unexpected microcode reply (%d)! " "State is 0x%08X\n", __FUNCTION__, command, sc->state); } bxe_sp_event_exit: /* Force bxe_wait_ramrod() to see the change. */ mb(); DBEXIT(BXE_VERBOSE_RAMROD); } /* * Lock access to a hardware resource using controller arbitration * register. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_acquire_hw_lock(struct bxe_softc *sc, uint32_t resource) { uint32_t hw_lock_control_reg, lock_status, resource_bit; uint8_t func; int cnt, rc; DBENTER(BXE_VERBOSE_MISC); DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Locking resource 0x%08X\n", __FUNCTION__, resource); func = BP_FUNC(sc); resource_bit = 1 << resource; rc = 0; hw_lock_control_reg = ((func <= 5) ? (MISC_REG_DRIVER_CONTROL_1 + func * 8) : (MISC_REG_DRIVER_CONTROL_7 + (func - 6) * 8)); /* Validating that the resource is within range. */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { DBPRINT(sc, BXE_WARN, "%s(): Resource is out of range! " "resource(0x%08X) > HW_LOCK_MAX_RESOURCE_VALUE(0x%08X)\n", __FUNCTION__, resource, HW_LOCK_MAX_RESOURCE_VALUE); rc = EINVAL; goto bxe_acquire_hw_lock_exit; } /* Validating that the resource is not already taken. */ lock_status = REG_RD(sc, hw_lock_control_reg); if (lock_status & resource_bit) { DBPRINT(sc, BXE_WARN, "%s(): Failed to acquire lock! " "lock_status = 0x%08X, resource_bit = 0x%08X\n", __FUNCTION__, lock_status, resource_bit); rc = EEXIST; goto bxe_acquire_hw_lock_exit; } /* Try for 5 seconds every 5ms. */ for (cnt = 0; cnt < 1000; cnt++) { /* Try to acquire the lock. */ REG_WR(sc, hw_lock_control_reg + 4, resource_bit); lock_status = REG_RD(sc, hw_lock_control_reg); if (lock_status & resource_bit) goto bxe_acquire_hw_lock_exit; DELAY(5000); } DBPRINT(sc, BXE_WARN, "%s(): Timeout!\n", __FUNCTION__); rc = EAGAIN; bxe_acquire_hw_lock_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } /* * Unlock access to a hardware resource using controller arbitration * register. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_release_hw_lock(struct bxe_softc *sc, uint32_t resource) { uint32_t hw_lock_control_reg, lock_status, resource_bit; uint8_t func; int rc; DBENTER(BXE_VERBOSE_MISC); DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Unlocking resource 0x%08X\n", __FUNCTION__, resource); resource_bit = 1 << resource; func = BP_FUNC(sc); rc = 0; /* Validating that the resource is within range */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { DBPRINT(sc, BXE_WARN, "%s(): Resource is out of range! " "resource(0x%08X) > HW_LOCK_MAX_RESOURCE_VALUE(0x%08X)\n", __FUNCTION__, resource, HW_LOCK_MAX_RESOURCE_VALUE); rc = EINVAL; goto bxe_release_hw_lock_exit; } /* Find the register for the resource lock. */ hw_lock_control_reg = ((func <= 5) ? (MISC_REG_DRIVER_CONTROL_1 + func * 8) : (MISC_REG_DRIVER_CONTROL_7 + (func - 6) * 8)); /* Validating that the resource is currently taken */ lock_status = REG_RD(sc, hw_lock_control_reg); if (!(lock_status & resource_bit)) { DBPRINT(sc, BXE_WARN, "%s(): The resource is not currently " "locked! lock_status = 0x%08X, resource_bit = 0x%08X\n", __FUNCTION__, lock_status, resource_bit); rc = EFAULT; goto bxe_release_hw_lock_exit; } /* Free the hardware lock. */ REG_WR(sc, hw_lock_control_reg, resource_bit); bxe_release_hw_lock_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } int bxe_get_gpio(struct bxe_softc *sc, int gpio_num, uint8_t port) { uint32_t gpio_mask, gpio_reg; int gpio_port, gpio_shift, value; /* The GPIO should be swapped if swap register is set and active */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = 1 << gpio_shift; if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_WARN, "%s(): Invalid GPIO %d\n", __FUNCTION__, gpio_num); return (-EINVAL); } /* read GPIO value */ gpio_reg = REG_RD(sc, MISC_REG_GPIO); /* get the requested pin value */ if ((gpio_reg & gpio_mask) == gpio_mask) value = 1; else value = 0; DBPRINT(sc, BXE_VERBOSE_PHY, "pin %d value 0x%x\n", gpio_num, value); return (value); } /* * Sets the state of a General Purpose I/O (GPIO). * * Returns: * None. */ int bxe_set_gpio(struct bxe_softc *sc, int gpio_num, uint32_t mode, uint8_t port) { uint32_t gpio_reg, gpio_mask; int gpio_port, gpio_shift, rc; DBENTER(BXE_VERBOSE_MISC); /* The GPIO should be swapped if swap register is set and active. */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = (1 << gpio_shift); rc = 0; if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_FATAL, "%s(): Invalid GPIO (%d)!\n", __FUNCTION__, gpio_num); rc = EINVAL; goto bxe_set_gpio_exit; } /* Make sure no one else is trying to use the GPIO. */ rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't acquire GPIO lock!\n", __FUNCTION__); goto bxe_set_gpio_exit; } /* Read GPIO and mask all but the float bits. */ gpio_reg = (REG_RD(sc, MISC_REG_GPIO) & MISC_REGISTERS_GPIO_FLOAT); switch (mode) { case MISC_REGISTERS_GPIO_OUTPUT_LOW: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "output low\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_CLR_POS); break; case MISC_REGISTERS_GPIO_OUTPUT_HIGH: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "output high\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_SET_POS); break; case MISC_REGISTERS_GPIO_INPUT_HI_Z: DBPRINT(sc, BXE_VERBOSE, "%s(): Set GPIO %d (shift %d) -> " "input\n", __FUNCTION__, gpio_num, gpio_shift); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); break; default: DBPRINT(sc, BXE_FATAL, "%s(): Unknown GPIO mode (0x%08X)!\n", __FUNCTION__, mode); break; } REG_WR(sc, MISC_REG_GPIO, gpio_reg); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't release GPIO lock!\n", __FUNCTION__); } bxe_set_gpio_exit: DBEXIT(BXE_VERBOSE_MISC); return (rc); } int bxe_set_gpio_int(struct bxe_softc *sc, int gpio_num, uint32_t mode, uint8_t port) { uint32_t gpio_mask, gpio_reg; int gpio_port, gpio_shift; /* The GPIO should be swapped if swap register is set and active */ gpio_port = (REG_RD(sc, NIG_REG_PORT_SWAP) && REG_RD(sc, NIG_REG_STRAP_OVERRIDE)) ^ port; gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); gpio_mask = (1 << gpio_shift); if (gpio_num > MISC_REGISTERS_GPIO_3) { DBPRINT(sc, BXE_WARN, "%s(): Invalid GPIO %d\n", __FUNCTION__, gpio_num); return (-EINVAL); } bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); /* read GPIO int */ gpio_reg = REG_RD(sc, MISC_REG_GPIO_INT); switch (mode) { case MISC_REGISTERS_GPIO_INT_OUTPUT_CLR: DBPRINT(sc, BXE_VERBOSE_PHY, "Clear GPIO INT %d (shift %d) -> " "output low\n", gpio_num, gpio_shift); /* clear SET and set CLR */ gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS); break; case MISC_REGISTERS_GPIO_INT_OUTPUT_SET: DBPRINT(sc, BXE_VERBOSE_PHY, "Set GPIO INT %d (shift %d) -> " "output high\n", gpio_num, gpio_shift); /* clear CLR and set SET */ gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS); gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS); break; default: break; } REG_WR(sc, MISC_REG_GPIO_INT, gpio_reg); bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_GPIO); return (0); } /* * Sets the state of a Shared Purpose I/O (SPIO). * * Returns: * 0 = Success, !0 = Failure. */ int bxe_set_spio(struct bxe_softc *sc, int spio_num, uint32_t mode) { uint32_t spio_reg, spio_mask; int rc; rc = 0; spio_mask = 1 << spio_num; /* Validate the SPIO. */ if ((spio_num < MISC_REGISTERS_SPIO_4) || (spio_num > MISC_REGISTERS_SPIO_7)) { DBPRINT(sc, BXE_WARN, "%s(): Invalid SPIO (%d)!\n", __FUNCTION__, spio_num); rc = EINVAL; goto bxe_set_spio_exit; } rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_SPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't acquire SPIO lock!\n", __FUNCTION__); goto bxe_set_spio_exit; } /* Read SPIO and mask all but the float bits. */ spio_reg = (REG_RD(sc, MISC_REG_SPIO) & MISC_REGISTERS_SPIO_FLOAT); switch (mode) { case MISC_REGISTERS_SPIO_OUTPUT_LOW : DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "output low\n", __FUNCTION__, spio_num); spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_CLR_POS); break; case MISC_REGISTERS_SPIO_OUTPUT_HIGH : DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "output high\n", __FUNCTION__, spio_num); spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_SET_POS); break; case MISC_REGISTERS_SPIO_INPUT_HI_Z: DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Set SPIO %d -> " "input\n", __FUNCTION__, spio_num); spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown SPIO mode (0x%08X)!\n", __FUNCTION__, mode); break; } REG_WR(sc, MISC_REG_SPIO, spio_reg); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_SPIO); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Can't release SPIO lock!\n", __FUNCTION__); } bxe_set_spio_exit: return (rc); } /* * When the 57711E is operating in multi-function mode, the controller * must be configured to arbitrate TX between multiple VNICs. * * Returns: * None. */ static void bxe_init_port_minmax(struct bxe_softc *sc) { uint32_t fair_periodic_timeout_usec, r_param, t_fair; DBENTER(BXE_VERBOSE_MISC); r_param = sc->link_vars.line_speed / 8; memset(&(sc->cmng.rs_vars), 0, sizeof(struct rate_shaping_vars_per_port)); memset(&(sc->cmng.fair_vars), 0, sizeof(struct fairness_vars_per_port)); /* 100 usec in SDM ticks = 25 since each tick is 4 usec. */ sc->cmng.rs_vars.rs_periodic_timeout = RS_PERIODIC_TIMEOUT_USEC / 4; /* * This is the threshold below which no timer arming will occur. * We use a coefficient of 1, 25 so that the threshold is a * little bigger that real time to compensate for timer * in-accuracy. */ sc->cmng.rs_vars.rs_threshold = (RS_PERIODIC_TIMEOUT_USEC * r_param * 5) / 4; /* Resolution of fairness timer. */ fair_periodic_timeout_usec = QM_ARB_BYTES / r_param; /* For 10G it is 1000us, for 1G it is 10000us. */ t_fair = T_FAIR_COEF / sc->link_vars.line_speed; /* This is the threshold where we won't arm the timer anymore. */ sc->cmng.fair_vars.fair_threshold = QM_ARB_BYTES; /* * Multiply by 1e3/8 to get bytes/msec. We don't want the * credits to pass a credit of the T_FAIR*FAIR_MEM (algorithm * resolution) */ sc->cmng.fair_vars.upper_bound = r_param * t_fair * FAIR_MEM; /* Since each tick is 4 us. */ sc->cmng.fair_vars.fairness_timeout = fair_periodic_timeout_usec / 4; DBEXIT(BXE_VERBOSE_MISC); } /* * This function is called when a link interrupt is generated * and configures the controller for the new link state. * * Returns: * None. */ static void bxe_link_attn(struct bxe_softc *sc) { struct host_port_stats *pstats; uint32_t pause_enabled; int func, i, port, vn; DBENTER(BXE_VERBOSE_PHY); /* Make sure that we are synced with the current statistics. */ bxe_stats_handle(sc, STATS_EVENT_STOP); bxe_link_update(&sc->link_params, &sc->link_vars); if (sc->link_vars.link_up) { if (CHIP_IS_E1H(sc)) { port = BP_PORT(sc); pause_enabled = 0; if (sc->link_vars.flow_ctrl & FLOW_CTRL_TX) pause_enabled = 1; REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_PAUSE_ENABLED_OFFSET(port), pause_enabled); } if (sc->link_vars.mac_type == MAC_TYPE_BMAC) { pstats = BXE_SP(sc, port_stats); /* Reset old BMAC statistics. */ memset(&(pstats->mac_stx[0]), 0, sizeof(struct mac_stx)); } if ((sc->state == BXE_STATE_OPEN) || (sc->state == BXE_STATE_DISABLED)) bxe_stats_handle(sc, STATS_EVENT_LINK_UP); } /* Need additional handling for multi-function devices. */ if (IS_E1HMF(sc)) { port = BP_PORT(sc); if (sc->link_vars.link_up) { if (sc->dcc_enable == TRUE) { bxe_congestionmgmt(sc, TRUE); /* Store in internal memory. */ for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) { REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + (i*4), ((uint32_t *)(&sc->cmng))[i]); } } } for (vn = VN_0; vn < E1HVN_MAX; vn++) { /* Don't send an attention to ourselves. */ if (vn == BP_E1HVN(sc)) continue; func = ((vn << 1) | port); /* * Send an attention to other drivers on the same port. */ REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_0 + (LINK_SYNC_ATTENTION_BIT_FUNC_0 + func) * 4, 1); } } DBEXIT(BXE_VERBOSE_PHY); } /* * Sets the driver instance as the port management function (PMF). * * This is only used on "multi-function" capable devices such as the * 57711E and initializes the controller so that the PMF driver instance * can interact with other driver instances that may be operating on * the same Ethernet port. * * Returns: * None. */ static void bxe_pmf_update(struct bxe_softc *sc) { uint32_t val; int port; /* Record that this driver instance is managing the port. */ sc->port.pmf = 1; DBPRINT(sc, BXE_INFO, "%s(): Enabling this port as PMF.\n", __FUNCTION__); /* Enable NIG attention. */ port = BP_PORT(sc); val = (0xff0f | (1 << (BP_E1HVN(sc) + 4))); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, val); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, val); bxe_stats_handle(sc, STATS_EVENT_PMF); } /* 8073 Download definitions */ /* spi Parameters.*/ #define SPI_CTRL_1_L 0xC000 #define SPI_CTRL_1_H 0xC002 #define SPI_CTRL_2_L 0xC400 #define SPI_CTRL_2_H 0xC402 #define SPI_TXFIFO 0xD000 #define SPI_RXFIFO 0xD400 /* Input Command Messages.*/ /* * Write CPU/SPI Control Regs, followed by Count And CPU/SPI Controller * Reg add/data pairs. */ #define WR_CPU_CTRL_REGS 0x11 /* * Read CPU/SPI Control Regs, followed by Count and CPU/SPI Controller * Register Add. */ #define RD_CPU_CTRL_REGS 0xEE /* * Write CPU/SPI Control Regs Continously, followed by Count and * CPU/SPI Controller Reg addr and data's. */ #define WR_CPU_CTRL_FIFO 0x66 /* Output Command Messages.*/ #define DONE 0x4321 /* SPI Controller Commands (known As messages).*/ #define MSGTYPE_HWR 0x40 #define MSGTYPE_HRD 0x80 #define WRSR_OPCODE 0x01 #define WR_OPCODE 0x02 #define RD_OPCODE 0x03 #define WRDI_OPCODE 0x04 #define RDSR_OPCODE 0x05 #define WREN_OPCODE 0x06 #define WR_BLOCK_SIZE 0x40 /* Maximum 64 Bytes Writes.*/ /* * Post a slowpath command. * * A slowpath command is used to propogate a configuration change through * the controller in a controlled manner, allowing each STORM processor and * other H/W blocks to phase in the change. The commands sent on the * slowpath are referred to as ramrods. Depending on the ramrod used the * completion of the ramrod will occur in different ways. Here's a * breakdown of ramrods and how they complete: * * RAMROD_CMD_ID_ETH_PORT_SETUP * Used to setup the leading connection on a port. Completes on the * Receive Completion Queue (RCQ) of that port (typically fp[0]). * * RAMROD_CMD_ID_ETH_CLIENT_SETUP * Used to setup an additional connection on a port. Completes on the * RCQ of the multi-queue/RSS connection being initialized. * * RAMROD_CMD_ID_ETH_STAT_QUERY * Used to force the storm processors to update the statistics database * in host memory. This ramrod is send on the leading connection CID and * completes as an index increment of the CSTORM on the default status * block. * * RAMROD_CMD_ID_ETH_UPDATE * Used to update the state of the leading connection, usually to udpate * the RSS indirection table. Completes on the RCQ of the leading * connection. (Not currently used under FreeBSD until OS support becomes * available.) * * RAMROD_CMD_ID_ETH_HALT * Used when tearing down a connection prior to driver unload. Completes * on the RCQ of the multi-queue/RSS connection being torn down. Don't * use this on the leading connection. * * RAMROD_CMD_ID_ETH_SET_MAC * Sets the Unicast/Broadcast/Multicast used by the port. Completes on * the RCQ of the leading connection. * * RAMROD_CMD_ID_ETH_CFC_DEL * Used when tearing down a conneciton prior to driver unload. Completes * on the RCQ of the leading connection (since the current connection * has been completely removed from controller memory). * * RAMROD_CMD_ID_ETH_PORT_DEL * Used to tear down the leading connection prior to driver unload, * typically fp[0]. Completes as an index increment of the CSTORM on the * default status block. * * RAMROD_CMD_ID_ETH_FORWARD_SETUP * Used for connection offload. Completes on the RCQ of the multi-queue * RSS connection that is being offloaded. (Not currently used under * FreeBSD.) * * There can only be one command pending per function. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_sp_post(struct bxe_softc *sc, int command, int cid, uint32_t data_hi, uint32_t data_lo, int common) { int func, rc; DBRUNMSG((BXE_EXTREME_LOAD | BXE_EXTREME_RESET | BXE_EXTREME_UNLOAD | BXE_EXTREME_RAMROD), bxe_decode_ramrod_cmd(sc, command)); DBPRINT(sc, BXE_VERBOSE_RAMROD, "%s(): cid = %d, data_hi = 0x%08X, " "data_low = 0x%08X, remaining spq entries = %d\n", __FUNCTION__, cid, data_hi, data_lo, sc->spq_left); rc = 0; /* Skip all slowpath commands if the driver has panic'd. */ if (sc->panic) { rc = EIO; goto bxe_sp_post_exit; } BXE_SP_LOCK(sc); /* We are limited to 8 slowpath commands. */ if (!sc->spq_left) { BXE_PRINTF("%s(%d): Slowpath queue is full!\n", __FILE__, __LINE__); bxe_panic_dump(sc); rc = EBUSY; goto bxe_sp_post_exit; } /* Encode the CID with the command. */ sc->spq_prod_bd->hdr.conn_and_cmd_data = htole32(((command << SPE_HDR_CMD_ID_SHIFT) | HW_CID(sc, cid))); sc->spq_prod_bd->hdr.type = htole16(ETH_CONNECTION_TYPE); if (common) sc->spq_prod_bd->hdr.type |= htole16((1 << SPE_HDR_COMMON_RAMROD_SHIFT)); /* Point the hardware at the new configuration data. */ sc->spq_prod_bd->data.mac_config_addr.hi = htole32(data_hi); sc->spq_prod_bd->data.mac_config_addr.lo = htole32(data_lo); /* Reduce the number of available slots for slowpath commands. */ sc->spq_left--; /* Manage the end of the ring. */ if (sc->spq_prod_bd == sc->spq_last_bd) { sc->spq_prod_bd = sc->spq; sc->spq_prod_idx = 0; DBPRINT(sc, BXE_VERBOSE, "%s(): End of slowpath queue.\n", __FUNCTION__); } else { sc->spq_prod_bd++; sc->spq_prod_idx++; } func = BP_FUNC(sc); /* Kick off the slowpath command. */ REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), sc->spq_prod_idx); bxe_sp_post_exit: BXE_SP_UNLOCK(sc); return (rc); } /* * Acquire the MCP access lock. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_acquire_alr(struct bxe_softc *sc) { uint32_t val; int i, rc, retries; DBENTER(BXE_VERBOSE_MISC); rc = 0; retries = 100; /* Acquire lock using mcpr_access_lock SPLIT register. */ for (i = 0; i < retries * 10; i++) { val = 1UL << 31; REG_WR(sc, GRCBASE_MCP + 0x9c, val); val = REG_RD(sc, GRCBASE_MCP + 0x9c); if (val & (1L << 31)) break; DELAY(5000); } if (!(val & (1L << 31))) { DBPRINT(sc, BXE_WARN, "%s(): Cannot acquire MCP split access lock.\n", __FUNCTION__); rc = EBUSY; } DBEXIT(BXE_VERBOSE_MISC); return (rc); } /* * Release the MCP access lock. * * Returns: * None. */ static void bxe_release_alr(struct bxe_softc* sc) { DBENTER(BXE_VERBOSE_MISC); REG_WR(sc, GRCBASE_MCP + 0x9c, 0); DBEXIT(BXE_VERBOSE_MISC); } /* * Update driver's copies of the values in the host default status block. * * Returns: * Bitmap indicating changes to the block. */ static __inline uint16_t bxe_update_dsb_idx(struct bxe_softc *sc) { struct host_def_status_block *dsb; uint16_t rc; rc = 0; dsb = sc->def_sb; /* Read memory barrier since block is written by hardware. */ rmb(); if (sc->def_att_idx != le16toh(dsb->atten_status_block.attn_bits_index)) { sc->def_att_idx = le16toh(dsb->atten_status_block.attn_bits_index); rc |= 0x1; } if (sc->def_c_idx != le16toh(dsb->c_def_status_block.status_block_index)) { sc->def_c_idx = le16toh(dsb->c_def_status_block.status_block_index); rc |= 0x2; } if (sc->def_u_idx != le16toh(dsb->u_def_status_block.status_block_index)) { sc->def_u_idx = le16toh(dsb->u_def_status_block.status_block_index); rc |= 0x4; } if (sc->def_x_idx != le16toh(dsb->x_def_status_block.status_block_index)) { sc->def_x_idx = le16toh(dsb->x_def_status_block.status_block_index); rc |= 0x8; } if (sc->def_t_idx != le16toh(dsb->t_def_status_block.status_block_index)) { sc->def_t_idx = le16toh(dsb->t_def_status_block.status_block_index); rc |= 0x10; } return (rc); } /* * Handle any attentions that have been newly asserted. * * Returns: * None */ static void bxe_attn_int_asserted(struct bxe_softc *sc, uint32_t asserted) { uint32_t aeu_addr, hc_addr, nig_int_mask_addr; uint32_t aeu_mask, nig_mask; int port, rc; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); hc_addr = (HC_REG_COMMAND_REG + port * 32 + COMMAND_REG_ATTN_BITS_SET); aeu_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; nig_int_mask_addr = port ? NIG_REG_MASK_INTERRUPT_PORT1 : NIG_REG_MASK_INTERRUPT_PORT0; nig_mask = 0; if (sc->attn_state & asserted) BXE_PRINTF("%s(%d): IGU attention ERROR!\n", __FILE__, __LINE__); rc = bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Failed to acquire attention lock for port %d!\n", __FUNCTION__, port); goto bxe_attn_int_asserted_exit; } aeu_mask = REG_RD(sc, aeu_addr); DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): aeu_mask = 0x%08X, newly asserted = 0x%08X\n", __FUNCTION__, aeu_mask, asserted); aeu_mask &= ~(asserted & 0xff); DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): new mask = 0x%08X\n", __FUNCTION__, aeu_mask); REG_WR(sc, aeu_addr, aeu_mask); rc = bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); if (rc) { DBPRINT(sc, BXE_WARN, "%s(): Failed to release attention lock!\n", __FUNCTION__); goto bxe_attn_int_asserted_exit; } DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); sc->attn_state |= asserted; DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): new attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); if (asserted & ATTN_HARD_WIRED_MASK) { if (asserted & ATTN_NIG_FOR_FUNC) { bxe_acquire_phy_lock(sc); /* Save NIG interrupt mask. */ nig_mask = REG_RD(sc, nig_int_mask_addr); REG_WR(sc, nig_int_mask_addr, 0); bxe_link_attn(sc); } if (asserted & ATTN_SW_TIMER_4_FUNC) DBPRINT(sc, BXE_WARN, "%s(): ATTN_SW_TIMER_4_FUNC!\n", __FUNCTION__); if (asserted & GPIO_2_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_2_FUNC!\n", __FUNCTION__); if (asserted & GPIO_3_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_3_FUNC!\n", __FUNCTION__); if (asserted & GPIO_4_FUNC) DBPRINT(sc, BXE_WARN, "%s(): GPIO_4_FUNC!\n", __FUNCTION__); if (port == 0) { if (asserted & ATTN_GENERAL_ATTN_1) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_1!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_1, 0x0); } if (asserted & ATTN_GENERAL_ATTN_2) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_2!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_2, 0x0); } if (asserted & ATTN_GENERAL_ATTN_3) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_3!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_3, 0x0); } } else { if (asserted & ATTN_GENERAL_ATTN_4) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_4!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_4, 0x0); } if (asserted & ATTN_GENERAL_ATTN_5) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_5!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_5, 0x0); } if (asserted & ATTN_GENERAL_ATTN_6) { DBPRINT(sc, BXE_WARN, "%s(): ATTN_GENERAL_ATTN_6!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_6, 0x0); } } } DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): Writing 0x%08X to HC addr 0x%08X\n", __FUNCTION__, asserted, hc_addr); REG_WR(sc, hc_addr, asserted); /* Now set back the NIG mask. */ if (asserted & ATTN_NIG_FOR_FUNC) { REG_WR(sc, nig_int_mask_addr, nig_mask); bxe_release_phy_lock(sc); } bxe_attn_int_asserted_exit: DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted0(struct bxe_softc *sc, uint32_t attn) { uint32_t val, swap_val, swap_override; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; /* Handle SPIO5 attention. */ if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { val = REG_RD(sc, reg_offset); val &= ~AEU_INPUTS_ATTN_BITS_SPIO5; REG_WR(sc, reg_offset, val); DBPRINT(sc, BXE_FATAL, "%s(): SPIO5 H/W attention!\n", __FUNCTION__); /* Fan failure attention */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* * SPIO5 is used on A1022G boards to indicate * fan failure. Shutdown the controller and * associated PHY to avoid damage. */ /* Low power mode is controled by GPIO 2. */ bxe_set_gpio(sc, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); /* PHY reset is controled by GPIO 1. */ bxe_set_gpio(sc, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* * The PHY reset is controlled by GPIO 1. * Fake the port number to cancel the swap done in * set_gpio(). */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_override = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); port = (swap_val && swap_override) ^ 1; bxe_set_gpio(sc, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); break; default: break; } /* Mark the failure. */ sc->link_params.ext_phy_config &= ~PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; sc->link_params.ext_phy_config |= PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE; SHMEM_WR(sc, dev_info.port_hw_config[port].external_phy_config, sc->link_params.ext_phy_config); /* Log the failure */ BXE_PRINTF("A fan failure has caused the driver to " "shutdown the device to prevent permanent damage.\n"); } if (attn & (AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 | AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1)) { bxe_acquire_phy_lock(sc); bxe_handle_module_detect_int(&sc->link_params); bxe_release_phy_lock(sc); } /* Checking for an assert on the zero block */ if (attn & HW_INTERRUT_ASSERT_SET_0) { val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_0); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention " "(set0 = 0x%08X)!\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_0)); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted1(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); if (attn & AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT) { val = REG_RD(sc, DORQ_REG_DORQ_INT_STS_CLR); DBPRINT(sc, BXE_FATAL, "%s(): Doorbell hardware attention (0x%08X).\n", __FUNCTION__, val); /* DORQ discard attention */ if (val & 0x2) DBPRINT(sc, BXE_FATAL, "%s(): FATAL doorbell queue error!\n", __FUNCTION__); } if (attn & HW_INTERRUT_ASSERT_SET_1) { port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_1 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_1; val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_1); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention " "(set1 = 0x%08X)!\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_1)); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted2(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int port, reg_offset; DBENTER(BXE_VERBOSE_INTR); if (attn & AEU_INPUTS_ATTN_BITS_CFC_HW_INTERRUPT) { val = REG_RD(sc, CFC_REG_CFC_INT_STS_CLR); DBPRINT(sc, BXE_FATAL, "%s(): CFC hardware attention (0x%08X).\n", __FUNCTION__, val); /* CFC error attention. */ if (val & 0x2) DBPRINT(sc, BXE_FATAL, "%s(): FATAL CFC error!\n", __FUNCTION__); } if (attn & AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT) { val = REG_RD(sc, PXP_REG_PXP_INT_STS_CLR_0); DBPRINT(sc, BXE_FATAL, "%s(): PXP hardware attention (0x%08X).\n", __FUNCTION__, val); /* RQ_USDMDP_FIFO_OVERFLOW */ if (val & 0x18000) DBPRINT(sc, BXE_FATAL, "%s(): FATAL PXP error!\n", __FUNCTION__); } if (attn & HW_INTERRUT_ASSERT_SET_2) { port = BP_PORT(sc); reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_2 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_2; val = REG_RD(sc, reg_offset); val &= ~(attn & HW_INTERRUT_ASSERT_SET_2); REG_WR(sc, reg_offset, val); BXE_PRINTF("%s(%d): FATAL hardware block attention (set2 = " "0x%08X)! port=%d, val written=0x%x attn=0x%x\n", __FILE__, __LINE__, (attn & (uint32_t)HW_INTERRUT_ASSERT_SET_2), port, val, attn); bxe_panic_dump(sc); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static __inline void bxe_attn_int_deasserted3(struct bxe_softc *sc, uint32_t attn) { uint32_t val; int func; DBENTER(BXE_VERBOSE_INTR); if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) { /* Look for any port assertions. */ if (attn & BXE_PMF_LINK_ASSERT) { /* * We received a message from the driver instance * that is managing the Ethernet port (link up/down). * Go ahead and handle it. */ func = BP_FUNC(sc); DBPRINT(sc, BXE_INFO, "%s(): Received link attention from PMF.\n", __FUNCTION__); /* Clear the attention. */ REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_12 + func * 4, 0); sc->mf_config[BP_E1HVN(sc)] = SHMEM_RD(sc, mf_cfg.func_mf_config[(sc->bxe_func & 1)].config); val = SHMEM_RD(sc, func_mb[func].drv_status); if (sc->dcc_enable == TRUE) { if (val & DRV_STATUS_DCC_EVENT_MASK) bxe_dcc_event(sc, val & DRV_STATUS_DCC_EVENT_MASK); } bxe__link_status_update(sc); if ((sc->port.pmf == 0) && (val & DRV_STATUS_PMF)) bxe_pmf_update(sc); /* Look for any microcode assertions. */ } else if (attn & BXE_MC_ASSERT_BITS) { DBPRINT(sc, BXE_FATAL, "%s(): Microcode assert!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_10, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_9, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_8, 0); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_7, 0); bxe_panic_dump(sc); /* Look for any bootcode assertions. */ } else if (attn & BXE_MCP_ASSERT) { DBPRINT(sc, BXE_FATAL, "%s(): Bootcode assert!\n", __FUNCTION__); REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_11, 0); DBRUN(bxe_dump_fw(sc)); } else DBPRINT(sc, BXE_FATAL, "%s(): Unknown hardware assertion " "(attn = 0x%08X)!\n", __FUNCTION__, attn); } /* Look for any hardware latched attentions. */ if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) { DBPRINT(sc, BXE_FATAL, "%s(): Latched attention 0x%08X (masked)!\n", __FUNCTION__, attn); /* Check if a GRC register access timeout occurred. */ if (attn & BXE_GRC_TIMEOUT) { val = CHIP_IS_E1H(sc) ? REG_RD(sc, MISC_REG_GRC_TIMEOUT_ATTN) : 0; DBPRINT(sc, BXE_WARN, "%s(): GRC timeout for register 0x%08X!\n", __FUNCTION__, val); } /* Check if a GRC reserved register was accessed. */ if (attn & BXE_GRC_RSV) { val = CHIP_IS_E1H(sc) ? REG_RD(sc, MISC_REG_GRC_RSV_ATTN) : 0; DBPRINT(sc, BXE_WARN, "%s(): GRC register 0x%08X is reserved!\n", __FUNCTION__, val); } REG_WR(sc, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff); } DBEXIT(BXE_VERBOSE_INTR); } /* * Handle any attentions that have been newly deasserted. * * Returns: * None */ static void bxe_attn_int_deasserted(struct bxe_softc *sc, uint32_t deasserted) { struct attn_route attn; struct attn_route group_mask; uint32_t val, reg_addr, aeu_mask; int index, port; DBENTER(BXE_VERBOSE_INTR); /* * Need to take HW lock because MCP or other port might also try * to handle this event. */ bxe_acquire_alr(sc); port = BP_PORT(sc); /* Get the current attention signal bits. */ attn.sig[0] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_1_FUNC_0 + port * 4); attn.sig[1] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port * 4); attn.sig[2] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port * 4); attn.sig[3] = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port * 4); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): attention = 0x%08X 0x%08X 0x%08X 0x%08X\n", __FUNCTION__, attn.sig[0], attn.sig[1], attn.sig[2], attn.sig[3]); /* * Compare the current attention bits to each attention group * to see if anyone has registered this attention. */ for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { if (deasserted & (1 << index)) { group_mask = sc->attn_group[index]; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): group[%02d] = 0x%08X 0x%08X 0x%08x 0X%08x\n", __FUNCTION__, index, group_mask.sig[0], group_mask.sig[1], group_mask.sig[2], group_mask.sig[3]); /* Handle any registered attentions. */ bxe_attn_int_deasserted3(sc, attn.sig[3] & group_mask.sig[3]); bxe_attn_int_deasserted1(sc, attn.sig[1] & group_mask.sig[1]); bxe_attn_int_deasserted2(sc, attn.sig[2] & group_mask.sig[2]); bxe_attn_int_deasserted0(sc, attn.sig[0] & group_mask.sig[0]); if ((attn.sig[0] & group_mask.sig[0] & HW_PRTY_ASSERT_SET_0) || (attn.sig[1] & group_mask.sig[1] & HW_PRTY_ASSERT_SET_1) || (attn.sig[2] & group_mask.sig[2] & HW_PRTY_ASSERT_SET_2)) BXE_PRINTF("%s(%d): FATAL hardware block " "parity attention!\n", __FILE__, __LINE__); } } bxe_release_alr(sc); reg_addr = (HC_REG_COMMAND_REG + port * 32 + COMMAND_REG_ATTN_BITS_CLR); val = ~deasserted; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): About to mask 0x%08X at HC addr 0x%08X\n", __FUNCTION__, deasserted, reg_addr); REG_WR(sc, reg_addr, val); if (~sc->attn_state & deasserted) DBPRINT(sc, BXE_FATAL, "%s(): IGU Bug!\n", __FUNCTION__); reg_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; bxe_acquire_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); aeu_mask = REG_RD(sc, reg_addr); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): Current aeu_mask = 0x%08X, newly deasserted = 0x%08X\n", __FUNCTION__, aeu_mask, deasserted); aeu_mask |= (deasserted & 0xff); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): New aeu_mask = 0x%08X\n", __FUNCTION__, aeu_mask); REG_WR(sc, reg_addr, aeu_mask); bxe_release_hw_lock(sc, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port); DBPRINT(sc, BXE_EXTREME_INTR, "%s(): Current attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); sc->attn_state &= ~deasserted; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): New attn_state = 0x%08X\n", __FUNCTION__, sc->attn_state); DBEXIT(BXE_VERBOSE_INTR); } /* * Handle interrupts caused by internal attentions (everything else other * than RX, TX, and link state changes). * * Returns: * None */ static void bxe_attn_int(struct bxe_softc* sc) { uint32_t attn_ack, attn_bits, attn_state; uint32_t asserted, deasserted; DBENTER(BXE_VERBOSE_INTR); attn_bits = le32toh(sc->def_sb->atten_status_block.attn_bits); attn_ack = le32toh(sc->def_sb->atten_status_block.attn_bits_ack); attn_state = sc->attn_state; asserted = attn_bits & ~attn_ack & ~attn_state; deasserted = ~attn_bits & attn_ack & attn_state; /* Make sure we're in a sane state. */ if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) BXE_PRINTF("%s(%d): Bad attention state!\n", __FILE__, __LINE__); /* Handle any attentions that are newly asserted. */ if (asserted) { DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X, attn_bits = 0x%08X, " "attn_ack = 0x%08X, asserted = 0x%08X\n", __FUNCTION__, attn_state, attn_bits, attn_ack, asserted); bxe_attn_int_asserted(sc, asserted); } /* Handle any attentions that are newly deasserted. */ if (deasserted) { DBPRINT(sc, BXE_VERBOSE_INTR, "%s(): attn_state = 0x%08X, attn_bits = 0x%08X, " "attn_ack = 0x%08X, deasserted = 0x%08X\n", __FUNCTION__, attn_state, attn_bits, attn_ack, deasserted); bxe_attn_int_deasserted(sc, deasserted); } DBEXIT(BXE_VERBOSE_INTR); } /* sum[hi:lo] += add[hi:lo] */ #define ADD_64(s_hi, a_hi, s_lo, a_lo) do { \ s_lo += a_lo; \ s_hi += a_hi + ((s_lo < a_lo) ? 1 : 0); \ } while (0) /* Subtraction = minuend -= subtrahend */ #define SUB_64(m_hi, s_hi, m_lo, s_lo) \ do { \ DIFF_64(m_hi, m_hi, s_hi, m_lo, m_lo, s_lo); \ } while (0) /* difference = minuend - subtrahend */ #define DIFF_64(d_hi, m_hi, s_hi, d_lo, m_lo, s_lo) do { \ if (m_lo < s_lo) { \ /* underflow */ \ d_hi = m_hi - s_hi; \ if (d_hi > 0) { \ /* we can 'loan' 1 */ \ d_hi--; \ d_lo = m_lo + (UINT_MAX - s_lo) + 1; \ } else { \ /* m_hi <= s_hi */ \ d_hi = 0; \ d_lo = 0; \ } \ } else { \ /* m_lo >= s_lo */ \ if (m_hi < s_hi) { \ d_hi = 0; \ d_lo = 0; \ } else { \ /* m_hi >= s_hi */ \ d_hi = m_hi - s_hi; \ d_lo = m_lo - s_lo; \ } \ } \ } while (0) #define UPDATE_STAT64(s, t) do { \ DIFF_64(diff.hi, new->s##_hi, pstats->mac_stx[0].t##_hi,\ diff.lo, new->s##_lo, pstats->mac_stx[0].t##_lo); \ pstats->mac_stx[0].t##_hi = new->s##_hi; \ pstats->mac_stx[0].t##_lo = new->s##_lo; \ ADD_64(pstats->mac_stx[1].t##_hi, diff.hi, \ pstats->mac_stx[1].t##_lo, diff.lo); \ } while (0) #define UPDATE_STAT64_NIG(s, t) do { \ DIFF_64(diff.hi, new->s##_hi, old->s##_hi, \ diff.lo, new->s##_lo, old->s##_lo); \ ADD_64(estats->t##_hi, diff.hi, \ estats->t##_lo, diff.lo); \ } while (0) /* sum[hi:lo] += add */ #define ADD_EXTEND_64(s_hi, s_lo, a) do { \ s_lo += a; \ s_hi += (s_lo < a) ? 1 : 0; \ } while (0) #define UPDATE_EXTEND_STAT(s) do { \ ADD_EXTEND_64(pstats->mac_stx[1].s##_hi, \ pstats->mac_stx[1].s##_lo, new->s); \ } while (0) #define UPDATE_EXTEND_TSTAT(s, t) do { \ diff = (tclient->s) - (old_tclient->s); \ old_tclient->s = (tclient->s); \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define UPDATE_EXTEND_XSTAT(s, t) do { \ diff = xclient->s - old_xclient->s; \ old_xclient->s = xclient->s; \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define UPDATE_EXTEND_USTAT(s, t) do { \ diff = uclient->s - old_uclient->s; \ old_uclient->s = uclient->s; \ ADD_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #define SUB_EXTEND_64(m_hi, m_lo, s)do { \ SUB_64(m_hi, 0, m_lo, s); \ } while (0) #define SUB_EXTEND_USTAT(s, t)do { \ diff = (uclient->s) - (old_uclient->s); \ SUB_EXTEND_64(qstats->t##_hi, qstats->t##_lo, diff); \ } while (0) #ifdef __i386__ #define BITS_PER_LONG 32 #else #define BITS_PER_LONG 64 #endif static __inline long bxe_hilo(uint32_t *hiref) { uint32_t lo; lo = *(hiref + 1); #if (BITS_PER_LONG == 64) uint32_t hi = *hiref; return (HILO_U64(hi, lo)); #else return (lo); #endif } /* * Request the STORM statistics by posting a slowpath ramrod. * * Returns: * None. */ static void bxe_stats_storm_post(struct bxe_softc *sc) { struct eth_query_ramrod_data ramrod_data = {0}; int i, rc; DBENTER(BXE_INSANE_STATS); if (!sc->stats_pending) { ramrod_data.drv_counter = sc->stats_counter++; ramrod_data.collect_port = sc->port.pmf ? 1 : 0; for (i = 0; i < sc->num_queues; i++) ramrod_data.ctr_id_vector |= (1 << sc->fp[i].cl_id); rc = bxe_sp_post(sc, RAMROD_CMD_ID_ETH_STAT_QUERY, 0, ((uint32_t *)&ramrod_data)[1], ((uint32_t *)&ramrod_data)[0], 0); if (rc == 0) { /* Stats ramrod has it's own slot on the SPQ. */ sc->spq_left++; sc->stats_pending = 1; } } DBEXIT(BXE_INSANE_STATS); } /* * Setup the adrress used by the driver to report port-based statistics * back to the controller. * * Returns: * None. */ static void bxe_stats_port_base_init(struct bxe_softc *sc) { uint32_t *stats_comp; struct dmae_command *dmae; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->port.port_stx) { BXE_PRINTF("%s(%d): Invalid statistcs port setup!\n", __FILE__, __LINE__); goto bxe_stats_port_base_init_exit; } stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; /* DMA the address of the drivers port statistics block. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_port_base_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Setup the adrress used by the driver to report function-based statistics * back to the controller. * * Returns: * None. */ static void bxe_stats_func_base_init(struct bxe_softc *sc) { int port, func; int vn, vn_max; uint32_t func_stx; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistcs function setup!\n", __FILE__, __LINE__); goto bxe_stats_func_base_init_exit; } port = BP_PORT(sc); func_stx = sc->func_stx; vn_max = IS_E1HMF(sc) ? E1HVN_MAX : E1VN_MAX; /* Initialize each function individually. */ for (vn = VN_0; vn < vn_max; vn++) { func = 2 * vn + port; sc->func_stx = SHMEM_RD(sc, func_mb[func].fw_mb_param); bxe_stats_func_init(sc); bxe_stats_hw_post(sc); bxe_stats_comp(sc); } sc->func_stx = func_stx; bxe_stats_func_base_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * DMA the function-based statistics to the controller. * * Returns: * None. */ static void bxe_stats_func_base_update(struct bxe_softc *sc) { uint32_t *stats_comp; struct dmae_command *dmae; DBENTER(BXE_VERBOSE_STATS); /* Only the port management function (PMF) does this work. */ if ((sc->port.pmf == 0) || !sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistcs function update!\n", __FILE__, __LINE__); goto bxe_stats_func_base_update_exit; } dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; memset(dmae, 0, sizeof(struct dmae_command)); /* DMA the function statistics from the driver to the H/W. */ dmae->opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = sc->func_stx >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats_base)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats_base)); dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_func_base_update_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Initialize statistics. * * Returns: * Nothing. */ static void bxe_stats_init(struct bxe_softc *sc) { struct bxe_fastpath *fp; int func, i, port; DBENTER(BXE_VERBOSE_STATS); if (sc->stats_enable == FALSE) goto bxe_stats_init_exit; port = BP_PORT(sc); func = BP_FUNC(sc); sc->executer_idx = 0; sc->stats_counter = 0; sc->stats_pending = 0; /* Fetch the offset of port & function statistics in shared memory. */ if (NOMCP(sc)){ sc->port.port_stx = 0; sc->func_stx = 0; } else{ sc->port.port_stx = SHMEM_RD(sc, port_mb[port].port_stx); sc->func_stx = SHMEM_RD(sc, func_mb[func].fw_mb_param); } DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): sc->port.port_stx = 0x%08X\n", __FUNCTION__, sc->port.port_stx); DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): sc->func_stx = 0x%08X\n", __FUNCTION__, sc->func_stx); /* Port statistics. */ memset(&(sc->port.old_nig_stats), 0, sizeof(struct nig_stats)); sc->port.old_nig_stats.brb_discard = REG_RD(sc, NIG_REG_STAT0_BRB_DISCARD + port * 0x38); sc->port.old_nig_stats.brb_truncate = REG_RD(sc, NIG_REG_STAT0_BRB_TRUNCATE + port * 0x38); REG_RD_DMAE(sc, NIG_REG_STAT0_EGRESS_MAC_PKT0 + port * 0x50, &(sc->port.old_nig_stats.egress_mac_pkt0_lo), 2); REG_RD_DMAE(sc, NIG_REG_STAT0_EGRESS_MAC_PKT1 + port * 0x50, &(sc->port.old_nig_stats.egress_mac_pkt1_lo), 2); /* Function statistics. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Clear all per-queue statistics. */ memset(&fp->old_tclient, 0, sizeof(struct tstorm_per_client_stats)); memset(&fp->old_uclient, 0, sizeof(struct ustorm_per_client_stats)); memset(&fp->old_xclient, 0, sizeof(struct xstorm_per_client_stats)); memset(&fp->eth_q_stats, 0, sizeof(struct bxe_q_stats)); } /* ToDo: Clear any driver specific statistics? */ sc->stats_state = STATS_STATE_DISABLED; if (sc->port.pmf == 1) { /* Init port & function stats if we're PMF. */ if (sc->port.port_stx) bxe_stats_port_base_init(sc); if (sc->func_stx) bxe_stats_func_base_init(sc); } else if (sc->func_stx) /* Update function stats if we're not PMF. */ bxe_stats_func_base_update(sc); bxe_stats_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * * Returns: * None. */ static void bxe_stats_hw_post(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t *stats_comp; int loader_idx; DBENTER(BXE_INSANE_STATS); dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); *stats_comp = DMAE_COMP_VAL; if (sc->executer_idx) { loader_idx = PMF_DMAE_C(sc); memset(dmae, 0, sizeof(struct dmae_command)); dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, dmae[0])); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, dmae[0])); dmae->dst_addr_lo = (DMAE_REG_CMD_MEM + sizeof(struct dmae_command) * (loader_idx + 1)) >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct dmae_command) >> 2; if (CHIP_IS_E1(sc)) dmae->len--; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx + 1] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; *stats_comp = 0; bxe_post_dmae(sc, dmae, loader_idx); } else if (sc->func_stx) { *stats_comp = 0; bxe_post_dmae(sc, dmae, INIT_DMAE_C(sc)); } DBEXIT(BXE_INSANE_STATS); } /* * Delay routine which polls for the DMA engine to complete. * * Returns: * 0 = Failure, !0 = Success */ static int bxe_stats_comp(struct bxe_softc *sc) { uint32_t *stats_comp; int cnt; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); cnt = 10; while (*stats_comp != DMAE_COMP_VAL) { if (!cnt) { BXE_PRINTF("%s(%d): Timeout waiting for statistics " "completions.\n", __FILE__, __LINE__); break; } cnt--; DELAY(1000); } DBEXIT(BXE_VERBOSE_STATS); /* ToDo: Shouldn't this return the value of cnt? */ return (1); } /* * DMA port statistcs from controller to driver. * * Returns: * None. */ static void bxe_stats_pmf_update(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t opcode, *stats_comp; int loader_idx; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); loader_idx = PMF_DMAE_C(sc); /* We shouldn't be here if any of the following are false. */ if (!IS_E1HMF(sc) || (sc->port.pmf == 0) || !sc->port.port_stx) { BXE_PRINTF("%s(%d): Statistics bug!\n", __FILE__, __LINE__); goto bxe_stats_pmf_update_exit; } sc->executer_idx = 0; /* Instruct DMA engine to copy port statistics from H/W to driver. */ opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_GRC); dmae->src_addr_lo = sc->port.port_stx >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->len = DMAE_LEN32_RD_MAX; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = (sc->port.port_stx >> 2) + DMAE_LEN32_RD_MAX; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats) + DMAE_LEN32_RD_MAX * 4); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats) + DMAE_LEN32_RD_MAX * 4); dmae->len = (sizeof(struct host_port_stats) >> 2) - DMAE_LEN32_RD_MAX; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; /* Start the DMA and wait for the result. */ *stats_comp = 0; bxe_stats_hw_post(sc); bxe_stats_comp(sc); bxe_stats_pmf_update_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Prepare the DMAE parameters required for all statistics. * * This function should only be called by the driver instance * that is designated as the port management function (PMF). * * Returns: * None. */ static void bxe_stats_port_init(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t mac_addr, opcode, *stats_comp; int loader_idx, port, vn; DBENTER(BXE_VERBOSE_STATS); port = BP_PORT(sc); vn = BP_E1HVN(sc); loader_idx = PMF_DMAE_C(sc); stats_comp = BXE_SP(sc, stats_comp); /* Only the port management function (PMF) does this work. */ if (!sc->link_vars.link_up || (sc->port.pmf == 0)) { BXE_PRINTF("%s(%d): Invalid statistics port setup!\n", __FILE__, __LINE__); goto bxe_stats_port_init_exit; } sc->executer_idx = 0; /* The same opcde is used for multiple DMA operations. */ opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); /* Setup the DMA for port statistics. */ if (sc->port.port_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } /* Setup the DMA for function statistics. */ if (sc->func_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } /* Setup statistics reporting for the MAC. */ opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); if (sc->link_vars.mac_type == MAC_TYPE_BMAC) { /* Enable statistics for the 10Gb BMAC. */ mac_addr = (port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM); /* Setup BMAC TX statistics (TX_STAT_GTPKT .. TX_STAT_GTBYT). */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats)); dmae->len = (8 + BIGMAC_REGISTER_TX_STAT_GTBYT - BIGMAC_REGISTER_TX_STAT_GTPKT) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup BMAC RX statistcs (RX_STAT_GR64 .. RX_STAT_GRIPJ). */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + BIGMAC_REGISTER_RX_STAT_GR64) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct bmac_stats, rx_stat_gr64_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct bmac_stats, rx_stat_gr64_lo)); dmae->len = (8 + BIGMAC_REGISTER_RX_STAT_GRIPJ - BIGMAC_REGISTER_RX_STAT_GR64) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else if (sc->link_vars.mac_type == MAC_TYPE_EMAC) { /* Enable statistics for the 1Gb EMAC. */ mac_addr = (port ? GRCBASE_EMAC1 : GRCBASE_EMAC0); /* Setup EMAC RX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_RX_STAT_AC) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats)); dmae->len = EMAC_REG_EMAC_RX_STAT_AC_COUNT; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup additional EMAC RX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_RX_STAT_AC_28) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, rx_stat_falsecarriererrors)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, rx_stat_falsecarriererrors)); dmae->len = 1; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; /* Setup EMAC TX statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (mac_addr + EMAC_REG_EMAC_TX_STAT_AC) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, tx_stat_ifhcoutoctets)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, mac_stats) + offsetof(struct emac_stats, tx_stat_ifhcoutoctets)); dmae->len = EMAC_REG_EMAC_TX_STAT_AC_COUNT; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else { DBPRINT(sc, BXE_WARN, "%s(): Undefined MAC type.\n", __FUNCTION__); } /* Enable NIG statistics. */ dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (port ? NIG_REG_STAT1_BRB_DISCARD : NIG_REG_STAT0_BRB_DISCARD) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats)); dmae->len = (sizeof(struct nig_stats) - 4 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = opcode; dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT0 : NIG_REG_STAT0_EGRESS_MAC_PKT0) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt0_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt0_lo)); dmae->len = (2 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (vn << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = (port ? NIG_REG_STAT1_EGRESS_MAC_PKT1 : NIG_REG_STAT0_EGRESS_MAC_PKT1) >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt1_lo)); dmae->dst_addr_hi = U64_HI(BXE_SP_MAPPING(sc, nig_stats) + offsetof(struct nig_stats, egress_mac_pkt1_lo)); dmae->len = (2 * sizeof(uint32_t)) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; /* Clear the statistics completion value. */ *stats_comp = 0; bxe_stats_port_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Prepare the DMAE parameters required for function statistics. * * This function is called by all driver instances. * * Returns: * None. */ static void bxe_stats_func_init(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t *stats_comp; DBENTER(BXE_VERBOSE_STATS); if (!sc->func_stx) { BXE_PRINTF("%s(%d): Invalid statistics function setup!\n", __FILE__, __LINE__); goto bxe_stats_func_init_exit; } dmae = &sc->stats_dmae; stats_comp = BXE_SP(sc, stats_comp); sc->executer_idx = 0; memset(dmae, 0, sizeof(struct dmae_command)); /* Setup the DMA for function statistics. */ dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; bxe_stats_func_init_exit: DBEXIT(BXE_VERBOSE_STATS); } /* * Starts a statistics update DMA and waits for completion. * * Returns: * None. */ static void bxe_stats_start(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); if (sc->port.pmf == 1) bxe_stats_port_init(sc); else if (sc->func_stx) bxe_stats_func_init(sc); bxe_stats_hw_post(sc); bxe_stats_storm_post(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_pmf_start(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); bxe_stats_comp(sc); bxe_stats_pmf_update(sc); bxe_stats_start(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_restart(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); bxe_stats_comp(sc); bxe_stats_start(sc); DBEXIT(BXE_VERBOSE_STATS); } /* * Update the Big MAC (10Gb BMAC) statistics. * * Returns: * None. */ static void bxe_stats_bmac_update(struct bxe_softc *sc) { struct bmac_stats *new; struct host_port_stats *pstats; struct bxe_port_stats *estats; struct regpair diff; DBENTER(BXE_INSANE_STATS); new = BXE_SP(sc, mac_stats.bmac_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; UPDATE_STAT64(rx_stat_grerb, rx_stat_ifhcinbadoctets); UPDATE_STAT64(rx_stat_grfcs, rx_stat_dot3statsfcserrors); UPDATE_STAT64(rx_stat_grund, rx_stat_etherstatsundersizepkts); UPDATE_STAT64(rx_stat_grovr, rx_stat_dot3statsframestoolong); UPDATE_STAT64(rx_stat_grfrg, rx_stat_etherstatsfragments); UPDATE_STAT64(rx_stat_grjbr, rx_stat_etherstatsjabbers); UPDATE_STAT64(rx_stat_grxcf, rx_stat_maccontrolframesreceived); UPDATE_STAT64(rx_stat_grxpf, rx_stat_xoffstateentered); UPDATE_STAT64(rx_stat_grxpf, rx_stat_bmac_xpf); UPDATE_STAT64(tx_stat_gtxpf, tx_stat_outxoffsent); UPDATE_STAT64(tx_stat_gtxpf, tx_stat_flowcontroldone); UPDATE_STAT64(tx_stat_gt64, tx_stat_etherstatspkts64octets); UPDATE_STAT64(tx_stat_gt127, tx_stat_etherstatspkts65octetsto127octets); UPDATE_STAT64(tx_stat_gt255, tx_stat_etherstatspkts128octetsto255octets); UPDATE_STAT64(tx_stat_gt511, tx_stat_etherstatspkts256octetsto511octets); UPDATE_STAT64(tx_stat_gt1023, tx_stat_etherstatspkts512octetsto1023octets); UPDATE_STAT64(tx_stat_gt1518, tx_stat_etherstatspkts1024octetsto1522octets); UPDATE_STAT64(tx_stat_gt2047, tx_stat_bmac_2047); UPDATE_STAT64(tx_stat_gt4095, tx_stat_bmac_4095); UPDATE_STAT64(tx_stat_gt9216, tx_stat_bmac_9216); UPDATE_STAT64(tx_stat_gt16383, tx_stat_bmac_16383); UPDATE_STAT64(tx_stat_gterr, tx_stat_dot3statsinternalmactransmiterrors); UPDATE_STAT64(tx_stat_gtufl, tx_stat_bmac_ufl); estats->pause_frames_received_hi = pstats->mac_stx[1].rx_stat_bmac_xpf_hi; estats->pause_frames_received_lo = pstats->mac_stx[1].rx_stat_bmac_xpf_lo; estats->pause_frames_sent_hi = pstats->mac_stx[1].tx_stat_outxoffsent_hi; estats->pause_frames_sent_lo = pstats->mac_stx[1].tx_stat_outxoffsent_lo; DBEXIT(BXE_INSANE_STATS); } /* * Update the Ethernet MAC (1Gb EMAC) statistics. * * Returns: * None. */ static void bxe_stats_emac_update(struct bxe_softc *sc) { struct emac_stats *new; struct host_port_stats *pstats; struct bxe_port_stats *estats; DBENTER(BXE_INSANE_STATS); new = BXE_SP(sc, mac_stats.emac_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; UPDATE_EXTEND_STAT(rx_stat_ifhcinbadoctets); UPDATE_EXTEND_STAT(tx_stat_ifhcoutbadoctets); UPDATE_EXTEND_STAT(rx_stat_dot3statsfcserrors); UPDATE_EXTEND_STAT(rx_stat_dot3statsalignmenterrors); UPDATE_EXTEND_STAT(rx_stat_dot3statscarriersenseerrors); UPDATE_EXTEND_STAT(rx_stat_falsecarriererrors); UPDATE_EXTEND_STAT(rx_stat_etherstatsundersizepkts); UPDATE_EXTEND_STAT(rx_stat_dot3statsframestoolong); UPDATE_EXTEND_STAT(rx_stat_etherstatsfragments); UPDATE_EXTEND_STAT(rx_stat_etherstatsjabbers); UPDATE_EXTEND_STAT(rx_stat_maccontrolframesreceived); UPDATE_EXTEND_STAT(rx_stat_xoffstateentered); UPDATE_EXTEND_STAT(rx_stat_xonpauseframesreceived); UPDATE_EXTEND_STAT(rx_stat_xoffpauseframesreceived); UPDATE_EXTEND_STAT(tx_stat_outxonsent); UPDATE_EXTEND_STAT(tx_stat_outxoffsent); UPDATE_EXTEND_STAT(tx_stat_flowcontroldone); UPDATE_EXTEND_STAT(tx_stat_etherstatscollisions); UPDATE_EXTEND_STAT(tx_stat_dot3statssinglecollisionframes); UPDATE_EXTEND_STAT(tx_stat_dot3statsmultiplecollisionframes); UPDATE_EXTEND_STAT(tx_stat_dot3statsdeferredtransmissions); UPDATE_EXTEND_STAT(tx_stat_dot3statsexcessivecollisions); UPDATE_EXTEND_STAT(tx_stat_dot3statslatecollisions); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts64octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts65octetsto127octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts128octetsto255octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts256octetsto511octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts512octetsto1023octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspkts1024octetsto1522octets); UPDATE_EXTEND_STAT(tx_stat_etherstatspktsover1522octets); UPDATE_EXTEND_STAT(tx_stat_dot3statsinternalmactransmiterrors); estats->pause_frames_received_hi = pstats->mac_stx[1].rx_stat_xonpauseframesreceived_hi; estats->pause_frames_received_lo = pstats->mac_stx[1].rx_stat_xonpauseframesreceived_lo; ADD_64(estats->pause_frames_received_hi, pstats->mac_stx[1].rx_stat_xoffpauseframesreceived_hi, estats->pause_frames_received_lo, pstats->mac_stx[1].rx_stat_xoffpauseframesreceived_lo); estats->pause_frames_sent_hi = pstats->mac_stx[1].tx_stat_outxonsent_hi; estats->pause_frames_sent_lo = pstats->mac_stx[1].tx_stat_outxonsent_lo; ADD_64(estats->pause_frames_sent_hi, pstats->mac_stx[1].tx_stat_outxoffsent_hi, estats->pause_frames_sent_lo, pstats->mac_stx[1].tx_stat_outxoffsent_lo); DBEXIT(BXE_INSANE_STATS); } /* * Returns: * 0 = Success, !0 = Failure. */ static int bxe_stats_hw_update(struct bxe_softc *sc) { struct nig_stats *new, *old; struct host_port_stats *pstats; struct bxe_port_stats *estats; struct regpair diff; uint32_t nig_timer_max; int rc; DBENTER(BXE_INSANE_STATS); rc = 0; new = BXE_SP(sc, nig_stats); old = &(sc->port.old_nig_stats); pstats = BXE_SP(sc, port_stats); estats = &sc->eth_stats; /* Update statistics for the active MAC. */ if (sc->link_vars.mac_type == MAC_TYPE_BMAC) bxe_stats_bmac_update(sc); else if (sc->link_vars.mac_type == MAC_TYPE_EMAC) bxe_stats_emac_update(sc); else { DBPRINT(sc, BXE_WARN, "%s(): Statistics updated by DMAE but no MAC is active!\n", __FUNCTION__); rc = EINVAL; goto bxe_stats_hw_update_exit; } /* Now update the hardware (NIG) statistics. */ ADD_EXTEND_64(pstats->brb_drop_hi, pstats->brb_drop_lo, new->brb_discard - old->brb_discard); ADD_EXTEND_64(estats->brb_truncate_hi, estats->brb_truncate_lo, new->brb_truncate - old->brb_truncate); UPDATE_STAT64_NIG(egress_mac_pkt0, etherstatspkts1024octetsto1522octets); UPDATE_STAT64_NIG(egress_mac_pkt1, etherstatspktsover1522octets); memcpy(old, new, sizeof(struct nig_stats)); memcpy(&(estats->rx_stat_ifhcinbadoctets_hi), &(pstats->mac_stx[1]), sizeof(struct mac_stx)); estats->brb_drop_hi = pstats->brb_drop_hi; estats->brb_drop_lo = pstats->brb_drop_lo; pstats->host_port_stats_start = ++pstats->host_port_stats_end; if (!NOMCP(sc)) { nig_timer_max = SHMEM_RD(sc, port_mb[BP_PORT(sc)].stat_nig_timer); if (nig_timer_max != estats->nig_timer_max) { estats->nig_timer_max = nig_timer_max; DBPRINT(sc, BXE_WARN, "%s(): NIG timer reached max value (%u)!\n", __FUNCTION__, estats->nig_timer_max); } } bxe_stats_hw_update_exit: DBEXIT(BXE_INSANE_STATS); return (rc); } /* * Returns: * 0 = Success, !0 = Failure. */ // DRC - Done static int bxe_stats_storm_update(struct bxe_softc *sc) { int rc, i, cl_id; struct eth_stats_query *stats; struct bxe_port_stats *estats; struct host_func_stats *fstats; struct bxe_q_stats *qstats; struct tstorm_per_port_stats *tport; struct tstorm_per_client_stats *tclient; struct ustorm_per_client_stats *uclient; struct xstorm_per_client_stats *xclient; struct tstorm_per_client_stats *old_tclient; struct ustorm_per_client_stats *old_uclient; struct xstorm_per_client_stats *old_xclient; struct bxe_fastpath * fp; uint32_t diff; DBENTER(BXE_INSANE_STATS); rc = 0; diff = 0; stats = BXE_SP(sc, fw_stats); tport = &stats->tstorm_common.port_statistics; fstats = BXE_SP(sc, func_stats); memcpy(&(fstats->total_bytes_received_hi), &(BXE_SP(sc, func_stats_base)->total_bytes_received_hi), sizeof(struct host_func_stats) - 2 * sizeof(uint32_t)); estats = &sc->eth_stats; estats->no_buff_discard_hi = 0; estats->no_buff_discard_lo = 0; estats->error_bytes_received_hi = 0; estats->error_bytes_received_lo = 0; estats->etherstatsoverrsizepkts_hi = 0; estats->etherstatsoverrsizepkts_lo = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; cl_id = fp->cl_id; tclient = &stats->tstorm_common.client_statistics[cl_id]; old_tclient = &fp->old_tclient; uclient = &stats->ustorm_common.client_statistics[cl_id]; old_uclient = &fp->old_uclient; xclient = &stats->xstorm_common.client_statistics[cl_id]; old_xclient = &fp->old_xclient; qstats = &fp->eth_q_stats; /* Are TSTORM statistics valid? */ if ((uint16_t)(le16toh(tclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by TSTORM " "(tstorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, tclient->stats_counter, sc->stats_counter); rc = 1; goto bxe_stats_storm_update_exit; } /* Are USTORM statistics valid? */ if ((uint16_t)(le16toh(uclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by USTORM " "(ustorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, uclient->stats_counter, sc->stats_counter); rc = 2; goto bxe_stats_storm_update_exit; } /* Are XSTORM statistics valid? */ if ((uint16_t)(le16toh(xclient->stats_counter) + 1) != sc->stats_counter) { DBPRINT(sc, BXE_WARN, "%s(): Stats not updated by XSTORM " "(xstorm counter (%d) != stats_counter (%d))!\n", __FUNCTION__, xclient->stats_counter, sc->stats_counter); rc = 3; goto bxe_stats_storm_update_exit; } qstats->total_bytes_received_hi = (tclient->rcv_broadcast_bytes.hi); qstats->total_bytes_received_lo = le32toh(tclient->rcv_broadcast_bytes.lo); ADD_64(qstats->total_bytes_received_hi, le32toh(tclient->rcv_multicast_bytes.hi), qstats->total_bytes_received_lo, le32toh(tclient->rcv_multicast_bytes.lo)); ADD_64(qstats->total_bytes_received_hi, le32toh(tclient->rcv_unicast_bytes.hi), qstats->total_bytes_received_lo, le32toh(tclient->rcv_unicast_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->bcast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->bcast_no_buff_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->mcast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->mcast_no_buff_bytes.lo)); SUB_64(qstats->total_bytes_received_hi, le32toh(uclient->ucast_no_buff_bytes.hi), qstats->total_bytes_received_lo, le32toh(uclient->ucast_no_buff_bytes.lo)); qstats->valid_bytes_received_hi = qstats->total_bytes_received_hi; qstats->valid_bytes_received_lo = qstats->total_bytes_received_lo; qstats->error_bytes_received_hi = le32toh(tclient->rcv_error_bytes.hi); qstats->error_bytes_received_lo = le32toh(tclient->rcv_error_bytes.lo); ADD_64(qstats->total_bytes_received_hi, qstats->error_bytes_received_hi, qstats->total_bytes_received_lo, qstats->error_bytes_received_lo); UPDATE_EXTEND_TSTAT(rcv_unicast_pkts, total_unicast_packets_received); UPDATE_EXTEND_TSTAT(rcv_multicast_pkts, total_multicast_packets_received); UPDATE_EXTEND_TSTAT(rcv_broadcast_pkts, total_broadcast_packets_received); UPDATE_EXTEND_TSTAT(packets_too_big_discard, etherstatsoverrsizepkts); UPDATE_EXTEND_TSTAT(no_buff_discard, no_buff_discard); SUB_EXTEND_USTAT(ucast_no_buff_pkts, total_unicast_packets_received); SUB_EXTEND_USTAT(mcast_no_buff_pkts, total_multicast_packets_received); SUB_EXTEND_USTAT(bcast_no_buff_pkts, total_broadcast_packets_received); UPDATE_EXTEND_USTAT(ucast_no_buff_pkts, no_buff_discard); UPDATE_EXTEND_USTAT(mcast_no_buff_pkts, no_buff_discard); UPDATE_EXTEND_USTAT(bcast_no_buff_pkts, no_buff_discard); qstats->total_bytes_transmitted_hi = le32toh(xclient->unicast_bytes_sent.hi); qstats->total_bytes_transmitted_lo = le32toh(xclient->unicast_bytes_sent.lo); ADD_64(qstats->total_bytes_transmitted_hi, le32toh(xclient->multicast_bytes_sent.hi), qstats->total_bytes_transmitted_lo, le32toh(xclient->multicast_bytes_sent.lo)); ADD_64(qstats->total_bytes_transmitted_hi, le32toh(xclient->broadcast_bytes_sent.hi), qstats->total_bytes_transmitted_lo, le32toh(xclient->broadcast_bytes_sent.lo)); UPDATE_EXTEND_XSTAT(unicast_pkts_sent, total_unicast_packets_transmitted); UPDATE_EXTEND_XSTAT(multicast_pkts_sent, total_multicast_packets_transmitted); UPDATE_EXTEND_XSTAT(broadcast_pkts_sent, total_broadcast_packets_transmitted); old_tclient->checksum_discard = tclient->checksum_discard; old_tclient->ttl0_discard = tclient->ttl0_discard; ADD_64(fstats->total_bytes_received_hi, qstats->total_bytes_received_hi, fstats->total_bytes_received_lo, qstats->total_bytes_received_lo); ADD_64(fstats->total_bytes_transmitted_hi, qstats->total_bytes_transmitted_hi, fstats->total_bytes_transmitted_lo, qstats->total_bytes_transmitted_lo); ADD_64(fstats->total_unicast_packets_received_hi, qstats->total_unicast_packets_received_hi, fstats->total_unicast_packets_received_lo, qstats->total_unicast_packets_received_lo); ADD_64(fstats->total_multicast_packets_received_hi, qstats->total_multicast_packets_received_hi, fstats->total_multicast_packets_received_lo, qstats->total_multicast_packets_received_lo); ADD_64(fstats->total_broadcast_packets_received_hi, qstats->total_broadcast_packets_received_hi, fstats->total_broadcast_packets_received_lo, qstats->total_broadcast_packets_received_lo); ADD_64(fstats->total_unicast_packets_transmitted_hi, qstats->total_unicast_packets_transmitted_hi, fstats->total_unicast_packets_transmitted_lo, qstats->total_unicast_packets_transmitted_lo); ADD_64(fstats->total_multicast_packets_transmitted_hi, qstats->total_multicast_packets_transmitted_hi, fstats->total_multicast_packets_transmitted_lo, qstats->total_multicast_packets_transmitted_lo); ADD_64(fstats->total_broadcast_packets_transmitted_hi, qstats->total_broadcast_packets_transmitted_hi, fstats->total_broadcast_packets_transmitted_lo, qstats->total_broadcast_packets_transmitted_lo); ADD_64(fstats->valid_bytes_received_hi, qstats->valid_bytes_received_hi, fstats->valid_bytes_received_lo, qstats->valid_bytes_received_lo); ADD_64(estats->error_bytes_received_hi, qstats->error_bytes_received_hi, estats->error_bytes_received_lo, qstats->error_bytes_received_lo); ADD_64(estats->etherstatsoverrsizepkts_hi, qstats->etherstatsoverrsizepkts_hi, estats->etherstatsoverrsizepkts_lo, qstats->etherstatsoverrsizepkts_lo); ADD_64(estats->no_buff_discard_hi, qstats->no_buff_discard_hi, estats->no_buff_discard_lo, qstats->no_buff_discard_lo); } ADD_64(fstats->total_bytes_received_hi, estats->rx_stat_ifhcinbadoctets_hi, fstats->total_bytes_received_lo, estats->rx_stat_ifhcinbadoctets_lo); memcpy(estats, &(fstats->total_bytes_received_hi), sizeof(struct host_func_stats) - 2 * sizeof(uint32_t)); ADD_64(estats->etherstatsoverrsizepkts_hi, estats->rx_stat_dot3statsframestoolong_hi, estats->etherstatsoverrsizepkts_lo, estats->rx_stat_dot3statsframestoolong_lo); ADD_64(estats->error_bytes_received_hi, estats->rx_stat_ifhcinbadoctets_hi, estats->error_bytes_received_lo, estats->rx_stat_ifhcinbadoctets_lo); if (sc->port.pmf) { estats->mac_filter_discard = le32toh(tport->mac_filter_discard); estats->xxoverflow_discard = le32toh(tport->xxoverflow_discard); estats->brb_truncate_discard = le32toh(tport->brb_truncate_discard); estats->mac_discard = le32toh(tport->mac_discard); } fstats->host_func_stats_start = ++fstats->host_func_stats_end; sc->stats_pending = 0; bxe_stats_storm_update_exit: DBEXIT(BXE_INSANE_STATS); return (rc); } /* * Copy the controller maintained statistics over to the OS. * * Returns: * None. */ static void bxe_stats_net_update(struct bxe_softc *sc) { struct tstorm_per_client_stats *old_tclient; struct bxe_port_stats *estats; struct ifnet *ifp; DBENTER(BXE_INSANE_STATS); old_tclient = &sc->fp[0].old_tclient; estats = &sc->eth_stats; ifp = sc->bxe_ifp; /* * Update the OS interface statistics from * the hardware statistics. */ ifp->if_collisions = (u_long) estats->tx_stat_dot3statssinglecollisionframes_lo + (u_long) estats->tx_stat_dot3statsmultiplecollisionframes_lo + (u_long) estats->tx_stat_dot3statslatecollisions_lo + (u_long) estats->tx_stat_dot3statsexcessivecollisions_lo; ifp->if_ierrors = (u_long) old_tclient->checksum_discard + (u_long) estats->no_buff_discard_lo + (u_long) estats->mac_discard + (u_long) estats->rx_stat_etherstatsundersizepkts_lo + (u_long) estats->brb_drop_lo + (u_long) estats->brb_truncate_discard + (u_long) estats->rx_stat_dot3statsfcserrors_lo + (u_long) estats->rx_stat_dot3statsalignmenterrors_lo + (u_long) estats->xxoverflow_discard; ifp->if_oerrors = (u_long) estats->tx_stat_dot3statslatecollisions_lo + (u_long) estats->tx_stat_dot3statsexcessivecollisions_lo + (u_long) estats->tx_stat_dot3statsinternalmactransmiterrors_lo; ifp->if_ipackets = bxe_hilo(&estats->total_unicast_packets_received_hi) + bxe_hilo(&estats->total_multicast_packets_received_hi) + bxe_hilo(&estats->total_broadcast_packets_received_hi); ifp->if_opackets = bxe_hilo(&estats->total_unicast_packets_transmitted_hi) + bxe_hilo(&estats->total_multicast_packets_transmitted_hi) + bxe_hilo(&estats->total_broadcast_packets_transmitted_hi); DBEXIT(BXE_INSANE_STATS); } /* * * Returns: * None. */ static void bxe_stats_update(struct bxe_softc *sc) { uint32_t *stats_comp; int update; DBENTER(BXE_INSANE_STATS); stats_comp = BXE_SP(sc, stats_comp); update = 0; /* Make sure the statistics DMAE update has completed. */ if (*stats_comp != DMAE_COMP_VAL) goto bxe_stats_update_exit; /* Check for any hardware statistics updates. */ if (sc->port.pmf == 1) update = (bxe_stats_hw_update(sc) == 0); /* Check for any STORM statistics updates. */ update |= (bxe_stats_storm_update(sc) == 0); /* If we got updated hardware statistics then update the OS. */ if (update) bxe_stats_net_update(sc); else { /* Check if any statistics updates are pending. */ if (sc->stats_pending) { /* The update hasn't completed, keep waiting. */ sc->stats_pending++; /* Have we been waiting for too long? */ if (sc->stats_pending >= 3) { BXE_PRINTF( "%s(%d): Failed to get statistics after " "3 tries!\n", __FILE__, __LINE__); bxe_panic_dump(sc); goto bxe_stats_update_exit; } } } /* Kickoff the next statistics request. */ bxe_stats_hw_post(sc); bxe_stats_storm_post(sc); bxe_stats_update_exit: DBEXIT(BXE_INSANE_STATS); } /* * * Returns: * None. */ static void bxe_stats_port_stop(struct bxe_softc *sc) { struct dmae_command *dmae; uint32_t opcode, *stats_comp; int loader_idx; DBENTER(BXE_VERBOSE_STATS); stats_comp = BXE_SP(sc, stats_comp); loader_idx = PMF_DMAE_C(sc); sc->executer_idx = 0; opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_C_ENABLE | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | #ifdef __BIG_ENDIAN DMAE_CMD_ENDIANITY_B_DW_SWAP | #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif (BP_PORT(sc) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | (BP_E1HVN(sc) << DMAE_CMD_E1HVN_SHIFT)); if (sc->port.port_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); if (sc->func_stx) dmae->opcode = (opcode | DMAE_CMD_C_DST_GRC); else dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, port_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, port_stats)); dmae->dst_addr_lo = sc->port.port_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_port_stats) >> 2; if (sc->func_stx) { dmae->comp_addr_lo = dmae_reg_go_c[loader_idx] >> 2; dmae->comp_addr_hi = 0; dmae->comp_val = 1; } else { dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; } } if (sc->func_stx) { dmae = BXE_SP(sc, dmae[sc->executer_idx++]); dmae->opcode = (opcode | DMAE_CMD_C_DST_PCI); dmae->src_addr_lo = U64_LO(BXE_SP_MAPPING(sc, func_stats)); dmae->src_addr_hi = U64_HI(BXE_SP_MAPPING(sc, func_stats)); dmae->dst_addr_lo = sc->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = sizeof(struct host_func_stats) >> 2; dmae->comp_addr_lo = U64_LO(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_addr_hi = U64_HI(BXE_SP_MAPPING(sc, stats_comp)); dmae->comp_val = DMAE_COMP_VAL; *stats_comp = 0; } DBEXIT(BXE_VERBOSE_STATS); } /* * Returns: * None. */ static void bxe_stats_stop(struct bxe_softc *sc) { int update; DBENTER(BXE_VERBOSE_STATS); update = 0; /* Wait for any pending completions. */ bxe_stats_comp(sc); if (sc->port.pmf == 1) update = (bxe_stats_hw_update(sc) == 0); update |= (bxe_stats_storm_update(sc) == 0); if (update) { bxe_stats_net_update(sc); if (sc->port.pmf == 1) bxe_stats_port_stop(sc); bxe_stats_hw_post(sc); bxe_stats_comp(sc); } DBEXIT(BXE_VERBOSE_STATS); } /* * A dummy function to fill in the statistics state transition table. * * Returns: * None. */ static void bxe_stats_do_nothing(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_STATS); DBEXIT(BXE_VERBOSE_STATS); } static const struct { void (*action)(struct bxe_softc *sc); enum bxe_stats_state next_state; } bxe_stats_stm[STATS_STATE_MAX][STATS_EVENT_MAX] = { /* State Event */ { /* DISABLED PMF */ {bxe_stats_pmf_update, STATS_STATE_DISABLED}, /* LINK_UP */ {bxe_stats_start, STATS_STATE_ENABLED}, /* UPDATE */ {bxe_stats_do_nothing, STATS_STATE_DISABLED}, /* STOP */ {bxe_stats_do_nothing, STATS_STATE_DISABLED} }, { /* ENABLED PMF */ {bxe_stats_pmf_start, STATS_STATE_ENABLED}, /* LINK_UP */ {bxe_stats_restart, STATS_STATE_ENABLED}, /* UPDATE */ {bxe_stats_update, STATS_STATE_ENABLED}, /* STOP */ {bxe_stats_stop, STATS_STATE_DISABLED} } }; /* * Move to the next state of the statistics state machine. * * Returns: * None. */ static void bxe_stats_handle(struct bxe_softc *sc, enum bxe_stats_event event) { enum bxe_stats_state state; DBENTER(BXE_EXTREME_STATS); state = sc->stats_state; #ifdef BXE_DEBUG if (event != STATS_EVENT_UPDATE) DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): Current state = %d, event = %d.\n", __FUNCTION__, state, event); #endif bxe_stats_stm[state][event].action(sc); sc->stats_state = bxe_stats_stm[state][event].next_state; #ifdef BXE_DEBUG if (event != STATS_EVENT_UPDATE) DBPRINT(sc, BXE_VERBOSE_STATS, "%s(): New state = %d.\n", __FUNCTION__, sc->stats_state); #endif DBEXIT(BXE_EXTREME_STATS); } /* * bxe_chktso_window() * Checks to ensure the 13 bd sliding window is >= MSS for TSO. * Check that (13 total bds - 3bds) = 10 bd window >= MSS. * The window: 3 bds are = 1 (for headers BD) + 2 (for PBD and last BD) * The headers comes in a seperate bd in FreeBSD. So 13-3=10. * * Returns: * 0 if OK to send, 1 if packet needs further defragmentation. */ static int bxe_chktso_window(struct bxe_softc* sc, int nsegs, bus_dma_segment_t *segs, struct mbuf *m0) { uint32_t num_wnds, wnd_size, wnd_sum; int32_t frag_idx, wnd_idx; unsigned short lso_mss; int defrag; defrag = 0; wnd_sum = 0; wnd_size = 10; num_wnds = nsegs - wnd_size; lso_mss = htole16(m0->m_pkthdr.tso_segsz); /* * Total Header lengths Eth+IP+TCP in 1st FreeBSD mbuf so * calculate the first window sum of data skip the first * assuming it is the header in FreeBSD. */ for (frag_idx = 1; (frag_idx <= wnd_size); frag_idx++) wnd_sum += htole16(segs[frag_idx].ds_len); /* Chk the first 10 bd window size */ if (wnd_sum < lso_mss) return (defrag = 1); /* Run through the windows */ for (wnd_idx = 0; wnd_idx < num_wnds; wnd_idx++, frag_idx++) { /* Subtract the 1st mbuf->m_len of the last wndw(-header). */ wnd_sum -= htole16(segs[wnd_idx+1].ds_len); /* Add the next mbuf len to the len of our new window. */ wnd_sum += htole16(segs[frag_idx].ds_len); if (wnd_sum < lso_mss) { defrag = 1; break; } } return (defrag); } /* * Encapsultes an mbuf cluster into the tx_bd chain structure and * makes the memory visible to the controller. * * If an mbuf is submitted to this routine and cannot be given to the * controller (e.g. it has too many fragments) then the function may free * the mbuf and return to the caller. * * Returns: * 0 = Success, !0 = Failure * Note the side effect that an mbuf may be freed if it causes a problem. */ static int bxe_tx_encap(struct bxe_fastpath *fp, struct mbuf **m_head) { bus_dma_segment_t segs[32]; bus_dmamap_t map; struct mbuf *m0; struct eth_tx_parse_bd *tx_parse_bd; struct eth_tx_bd *tx_data_bd; struct eth_tx_bd *tx_total_pkt_size_bd; struct eth_tx_start_bd *tx_start_bd; uint16_t etype, sw_tx_bd_prod, sw_pkt_prod, total_pkt_size; // uint16_t bd_index, pkt_index; uint8_t mac_type; int i, defragged, e_hlen, error, nsegs, rc, nbds, vlan_off, ovlan; struct bxe_softc *sc; sc = fp->sc; DBENTER(BXE_VERBOSE_SEND); DBRUN(M_ASSERTPKTHDR(*m_head)); m0 = *m_head; rc = defragged = nbds = ovlan = vlan_off = total_pkt_size = 0; tx_start_bd = NULL; tx_data_bd = NULL; tx_parse_bd = NULL; tx_total_pkt_size_bd = NULL; /* Get the H/W pointer (0 to 65535) for packets and BD's. */ sw_pkt_prod = fp->tx_pkt_prod; sw_tx_bd_prod = fp->tx_bd_prod; /* Create the S/W index (0 to MAX_TX_BD) for packets and BD's. */ // pkt_index = TX_BD(sw_pkt_prod); // bd_index = TX_BD(sw_tx_bd_prod); mac_type = UNICAST_ADDRESS; /* Map the mbuf into the next open DMAable memory. */ map = fp->tx_mbuf_map[TX_BD(sw_pkt_prod)]; error = bus_dmamap_load_mbuf_sg(fp->tx_mbuf_tag, map, m0, segs, &nsegs, BUS_DMA_NOWAIT); /* Handle any mapping errors. */ if(__predict_false(error != 0)){ fp->tx_dma_mapping_failure++; if (error == ENOMEM) { /* Resource issue, try again later. */ rc = ENOMEM; } else if (error == EFBIG) { /* Possibly recoverable with defragmentation. */ fp->mbuf_defrag_attempts++; m0 = m_defrag(*m_head, M_DONTWAIT); if (m0 == NULL) { fp->mbuf_defrag_failures++; rc = ENOBUFS; } else { /* Defrag successful, try mapping again.*/ *m_head = m0; error = bus_dmamap_load_mbuf_sg( fp->tx_mbuf_tag, map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error) { fp->tx_dma_mapping_failure++; rc = error; } } } else { /* Unknown, unrecoverable mapping error. */ DBPRINT(sc, BXE_WARN_SEND, "%s(): Unknown TX mapping error! " "rc = %d.\n", __FUNCTION__, error); DBRUN(bxe_dump_mbuf(sc, m0)); rc = error; } goto bxe_tx_encap_continue; } /* Make sure there's enough room in the send queue. */ if (__predict_false((nsegs + 2) > (USABLE_TX_BD - fp->tx_bd_used))) { /* Recoverable, try again later. */ fp->tx_hw_queue_full++; bus_dmamap_unload(fp->tx_mbuf_tag, map); rc = ENOMEM; goto bxe_tx_encap_continue; } /* Capture the current H/W TX chain high watermark. */ if (__predict_false(fp->tx_hw_max_queue_depth < fp->tx_bd_used)) fp->tx_hw_max_queue_depth = fp->tx_bd_used; /* Now make sure it fits in the packet window. */ if (__predict_false(nsegs > 12)) { /* * The mbuf may be to big for the controller * to handle. If the frame is a TSO frame * we'll need to do an additional check. */ if(m0->m_pkthdr.csum_flags & CSUM_TSO){ if (bxe_chktso_window(sc,nsegs,segs,m0) == 0) /* OK to send. */ goto bxe_tx_encap_continue; else fp->tx_window_violation_tso++; } else fp->tx_window_violation_std++; /* No sense trying to defrag again, we'll drop the frame. */ if (defragged > 0) rc = ENODEV; } bxe_tx_encap_continue: /* Check for errors */ if (rc){ if(rc == ENOMEM){ /* Recoverable try again later */ }else{ fp->tx_soft_errors++; fp->tx_mbuf_alloc--; m_freem(*m_head); *m_head = NULL; } goto bxe_tx_encap_exit; } /* Save the mbuf and mapping. */ fp->tx_mbuf_ptr[TX_BD(sw_pkt_prod)] = m0; fp->tx_mbuf_map[TX_BD(sw_pkt_prod)] = map; /* Set flag according to packet type (UNICAST_ADDRESS is default). */ if (m0->m_flags & M_BCAST) mac_type = BROADCAST_ADDRESS; else if (m0->m_flags & M_MCAST) mac_type = MULTICAST_ADDRESS; /* Prepare the first transmit (Start) BD for the mbuf. */ tx_start_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].start_bd; tx_start_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr)); tx_start_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr)); tx_start_bd->nbytes = htole16(segs[0].ds_len); total_pkt_size += tx_start_bd->nbytes; tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD; tx_start_bd->general_data = (mac_type << ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT); tx_start_bd->general_data |= (1 << ETH_TX_START_BD_HDR_NBDS_SHIFT); /* All frames have at least Start BD + Parsing BD. */ nbds = nsegs + 1; tx_start_bd->nbd = htole16(nbds); if (m0->m_flags & M_VLANTAG) { tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_VLAN_TAG; tx_start_bd->vlan = htole16(m0->m_pkthdr.ether_vtag); } else /* * In cases where the VLAN tag is not used the firmware * expects to see a packet counter in the VLAN tag field * Failure to do so will cause an assertion which will * stop the controller. */ tx_start_bd->vlan = htole16(fp->tx_pkt_prod); /* * Add a parsing BD from the chain. The parsing BD is always added, * however, it is only used for TSO & chksum. */ sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); tx_parse_bd = (struct eth_tx_parse_bd *) &fp->tx_chain[TX_BD(sw_tx_bd_prod)].parse_bd; memset(tx_parse_bd, 0, sizeof(struct eth_tx_parse_bd)); /* Gather all info about the packet and add to tx_parse_bd */ if (m0->m_pkthdr.csum_flags) { struct ether_vlan_header *eh; struct ip *ip = NULL; struct tcphdr *th = NULL; uint16_t flags = 0; struct udphdr *uh = NULL; /* Map Ethernet header to find type & header length. */ eh = mtod(m0, struct ether_vlan_header *); /* Handle VLAN encapsulation if present. */ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { etype = ntohs(eh->evl_proto); e_hlen = ETHER_HDR_LEN + vlan_off; } else { etype = ntohs(eh->evl_encap_proto); e_hlen = ETHER_HDR_LEN; } /* Set the Ethernet header length in 16 bit words. */ tx_parse_bd->global_data = (e_hlen + ovlan) >> 1; tx_parse_bd->global_data |= ((m0->m_flags & M_VLANTAG) << ETH_TX_PARSE_BD_LLC_SNAP_EN_SHIFT); switch (etype) { case ETHERTYPE_IP: /* If mbuf len < 20bytes, IP header is in next mbuf. */ if (m0->m_len < sizeof(struct ip)) ip = (struct ip *) m0->m_next->m_data; else ip = (struct ip *) (m0->m_data + e_hlen); /* Calculate IP header length (16 bit words). */ tx_parse_bd->ip_hlen = (ip->ip_hl << 1); /* Calculate enet + IP header length (16 bit words). */ tx_parse_bd->total_hlen = tx_parse_bd->ip_hlen + (e_hlen >> 1); if (m0->m_pkthdr.csum_flags & CSUM_IP) { fp->tx_offload_frames_csum_ip++; flags |= ETH_TX_BD_FLAGS_IP_CSUM; } /* Handle any checksums requested by the stack. */ if ((m0->m_pkthdr.csum_flags & CSUM_TCP)|| (m0->m_pkthdr.csum_flags & CSUM_TSO)){ /* Get the TCP header. */ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); /* Add the TCP checksum offload flag. */ flags |= ETH_TX_BD_FLAGS_L4_CSUM; fp->tx_offload_frames_csum_tcp++; /* Update the enet + IP + TCP header length. */ tx_parse_bd->total_hlen += (uint16_t)(th->th_off << 1); /* Get the pseudo header checksum. */ tx_parse_bd->tcp_pseudo_csum = ntohs(th->th_sum); } else if (m0->m_pkthdr.csum_flags & CSUM_UDP) { /* * The hardware doesn't actually support UDP * checksum offload but we can fake it by * doing TCP checksum offload and factoring * out the extra bytes that are different * between the TCP header and the UDP header. * * Calculation will begin 10 bytes before the * actual start of the UDP header. To work * around this we need to calculate the * checksum of the 10 bytes before the UDP * header and factor that out of the UDP * pseudo header checksum before asking the * H/W to calculate the full UDP checksum. */ uint16_t tmp_csum; uint32_t *tmp_uh; /* This value is 10. */ uint8_t fix = (uint8_t) (offsetof(struct tcphdr, th_sum) - (int) offsetof(struct udphdr, uh_sum)); /* * Add the TCP checksum offload flag for * UDP frames too.* */ flags |= ETH_TX_BD_FLAGS_L4_CSUM; fp->tx_offload_frames_csum_udp++; tx_parse_bd->global_data |= ETH_TX_PARSE_BD_UDP_CS_FLG; /* Get a pointer to the UDP header. */ uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); /* Set pointer 10 bytes before UDP header. */ tmp_uh = (uint32_t *)((uint8_t *)uh - fix); /* * Calculate a pseudo header checksum over * the 10 bytes before the UDP header. */ tmp_csum = in_pseudo(ntohl(*tmp_uh), ntohl(*(tmp_uh + 1)), ntohl((*(tmp_uh + 2)) & 0x0000FFFF)); /* Update the enet + IP + UDP header length. */ tx_parse_bd->total_hlen += (sizeof(struct udphdr) >> 1); tx_parse_bd->tcp_pseudo_csum = ~in_addword(uh->uh_sum, ~tmp_csum); } /* Update the offload flags. */ tx_start_bd->bd_flags.as_bitfield |= flags; break; case ETHERTYPE_IPV6: fp->tx_unsupported_tso_request_ipv6++; /* ToDo: Add IPv6 support. */ break; default: fp->tx_unsupported_tso_request_not_tcp++; /* ToDo - How to handle this error? */ } /* Setup the Parsing BD with TSO specific info */ if (m0->m_pkthdr.csum_flags & CSUM_TSO) { uint16_t hdr_len = tx_parse_bd->total_hlen << 1; tx_start_bd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_SW_LSO; fp->tx_offload_frames_tso++; /* ToDo: Does this really help? */ if (__predict_false(tx_start_bd->nbytes > hdr_len)) { fp->tx_header_splits++; /* * Split the first BD into 2 BDs to make the * firmwares job easy... */ tx_start_bd->nbd++; DBPRINT(sc, BXE_EXTREME_SEND, "%s(): TSO split headr size is %d (%x:%x) nbds %d\n", __FUNCTION__, tx_start_bd->nbytes, tx_start_bd->addr_hi, tx_start_bd->addr_lo, nbds); sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); /* New transmit BD (after the tx_parse_bd). */ tx_data_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].reg_bd; tx_data_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr + hdr_len)); tx_data_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr + hdr_len)); tx_data_bd->nbytes = htole16(segs[0].ds_len) - hdr_len; if (tx_total_pkt_size_bd == NULL) tx_total_pkt_size_bd = tx_data_bd; } /* * The controller needs the following info for TSO: * MSS, tcp_send_seq, ip_id, and tcp_pseudo_csum. */ tx_parse_bd->lso_mss = htole16(m0->m_pkthdr.tso_segsz); tx_parse_bd->tcp_send_seq = ntohl(th->th_seq); tx_parse_bd->tcp_flags = th->th_flags; tx_parse_bd->ip_id = ntohs(ip->ip_id); tx_parse_bd->tcp_pseudo_csum = ntohs(in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP))); tx_parse_bd->global_data |= ETH_TX_PARSE_BD_PSEUDO_CS_WITHOUT_LEN; } } /* Prepare remaining BDs. Start_tx_bd contains first seg (frag). */ for (i = 1; i < nsegs ; i++) { sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); tx_data_bd = &fp->tx_chain[TX_BD(sw_tx_bd_prod)].reg_bd; tx_data_bd->addr_lo = htole32(U64_LO(segs[i].ds_addr)); tx_data_bd->addr_hi = htole32(U64_HI(segs[i].ds_addr)); tx_data_bd->nbytes = htole16(segs[i].ds_len); if (tx_total_pkt_size_bd == NULL) tx_total_pkt_size_bd = tx_data_bd; total_pkt_size += tx_data_bd->nbytes; } if(tx_total_pkt_size_bd != NULL) tx_total_pkt_size_bd->total_pkt_bytes = total_pkt_size; /* Update TX BD producer index value for next TX */ sw_tx_bd_prod = NEXT_TX_BD(sw_tx_bd_prod); /* Update the used TX BD counter. */ fp->tx_bd_used += nbds; /* * If the chain of tx_bd's describing this frame * is adjacent to or spans an eth_tx_next_bd element * then we need to increment the nbds value. */ if(TX_IDX(sw_tx_bd_prod) < nbds) nbds++; /* Don't allow reordering of writes for nbd and packets. */ mb(); fp->tx_db.data.prod += nbds; /* Producer points to the next free tx_bd at this point. */ fp->tx_pkt_prod++; fp->tx_bd_prod = sw_tx_bd_prod; DOORBELL(sc, fp->index, fp->tx_db.raw); fp->tx_pkts++; /* Prevent speculative reads from getting ahead of the status block. */ bus_space_barrier(sc->bxe_btag, sc->bxe_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); /* Prevent speculative reads from getting ahead of the doorbell. */ bus_space_barrier(sc->bxe_db_btag, sc->bxe_db_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); bxe_tx_encap_exit: DBEXIT(BXE_VERBOSE_SEND); return (rc); } /* * Legacy (non-RSS) dispatch routine. * * Returns: * Nothing. */ static void bxe_tx_start(struct ifnet *ifp) { struct bxe_softc *sc; struct bxe_fastpath *fp; sc = ifp->if_softc; DBENTER(BXE_EXTREME_SEND); /* Exit if the transmit queue is full or link down. */ if (((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) || !sc->link_vars.link_up) { DBPRINT(sc, BXE_WARN, "%s(): No link or TX queue full, ignoring " "transmit request.\n", __FUNCTION__); goto bxe_tx_start_exit; } /* Set the TX queue for the frame. */ fp = &sc->fp[0]; BXE_FP_LOCK(fp); bxe_tx_start_locked(ifp, fp); BXE_FP_UNLOCK(fp); bxe_tx_start_exit: DBEXIT(BXE_EXTREME_SEND); } /* * Legacy (non-RSS) transmit routine. * * Returns: * Nothing. */ static void bxe_tx_start_locked(struct ifnet *ifp, struct bxe_fastpath *fp) { struct bxe_softc *sc; struct mbuf *m = NULL; int tx_count = 0; sc = fp->sc; DBENTER(BXE_EXTREME_SEND); BXE_FP_LOCK_ASSERT(fp); /* Keep adding entries while there are frames to send. */ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { /* Check for any frames to send. */ IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (__predict_false(m == NULL)) break; /* The transmit mbuf now belongs to us, keep track of it. */ fp->tx_mbuf_alloc++; /* * Pack the data into the transmit ring. If we * don't have room, place the mbuf back at the * head of the TX queue, set the OACTIVE flag, * and wait for the NIC to drain the chain. */ if (__predict_false(bxe_tx_encap(fp, &m))) { fp->tx_encap_failures++; /* Very Bad Frames(tm) may have been dropped. */ if (m != NULL) { /* * Mark the TX queue as full and return * the frame. */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; IFQ_DRV_PREPEND(&ifp->if_snd, m); fp->tx_mbuf_alloc--; fp->tx_queue_xoff++; } else { } /* Stop looking for more work. */ break; } /* The transmit frame was enqueued successfully. */ tx_count++; /* Send a copy of the frame to any BPF listeners. */ BPF_MTAP(ifp, m); } /* No TX packets were dequeued. */ if (tx_count > 0) /* Reset the TX watchdog timeout timer. */ fp->watchdog_timer = BXE_TX_TIMEOUT; DBEXIT(BXE_EXTREME_SEND); } #if __FreeBSD_version >= 800000 /* * Multiqueue (RSS) dispatch routine. * * Returns: * 0 if transmit succeeds, !0 otherwise. */ static int bxe_tx_mq_start(struct ifnet *ifp, struct mbuf *m) { struct bxe_softc *sc; struct bxe_fastpath *fp; int fp_index, rc; sc = ifp->if_softc; DBENTER(BXE_EXTREME_SEND); fp_index = 0; /* If using flow ID, assign the TX queue based on the flow ID. */ if ((m->m_flags & M_FLOWID) != 0) fp_index = m->m_pkthdr.flowid % sc->num_queues; /* Select the fastpath TX queue for the frame. */ fp = &sc->fp[fp_index]; /* Skip H/W enqueue if transmit queue is full or link down. */ if (((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) || !sc->link_vars.link_up) { /* Stash the mbuf if we can. */ rc = drbr_enqueue(ifp, fp->br, m); goto bxe_tx_mq_start_exit; } BXE_FP_LOCK(fp); rc = bxe_tx_mq_start_locked(ifp, fp, m); BXE_FP_UNLOCK(fp); bxe_tx_mq_start_exit: DBEXIT(BXE_EXTREME_SEND); return (rc); } /* * Multiqueue (TSS) transmit routine. This routine is responsible * for adding a frame to the hardware's transmit queue. * * Returns: * 0 if transmit succeeds, !0 otherwise. */ static int bxe_tx_mq_start_locked(struct ifnet *ifp, struct bxe_fastpath *fp, struct mbuf *m) { struct bxe_softc *sc; struct mbuf *next; int depth, rc, tx_count; sc = fp->sc; DBENTER(BXE_EXTREME_SEND); rc = tx_count = 0; /* Fetch the depth of the driver queue. */ depth = drbr_inuse(ifp, fp->br); if (depth > fp->tx_max_drbr_queue_depth) fp->tx_max_drbr_queue_depth = depth; BXE_FP_LOCK_ASSERT(fp); if (m == NULL) { /* No new work, check for pending frames. */ next = drbr_dequeue(ifp, fp->br); } else if (drbr_needs_enqueue(ifp, fp->br)) { /* Both new and pending work, maintain packet order. */ rc = drbr_enqueue(ifp, fp->br, m); if (rc != 0) { fp->tx_soft_errors++; goto bxe_tx_mq_start_locked_exit; } next = drbr_dequeue(ifp, fp->br); } else /* New work only, nothing pending. */ next = m; /* Keep adding entries while there are frames to send. */ while (next != NULL) { /* The transmit mbuf now belongs to us, keep track of it. */ fp->tx_mbuf_alloc++; /* * Pack the data into the transmit ring. If we * don't have room, place the mbuf back at the * head of the TX queue, set the OACTIVE flag, * and wait for the NIC to drain the chain. */ rc = bxe_tx_encap(fp, &next); if (__predict_false(rc != 0)) { fp->tx_encap_failures++; /* Very Bad Frames(tm) may have been dropped. */ if (next != NULL) { /* * Mark the TX queue as full and save * the frame. */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; fp->tx_frame_deferred++; /* This may reorder frame. */ rc = drbr_enqueue(ifp, fp->br, next); fp->tx_mbuf_alloc--; } /* Stop looking for more work. */ break; } /* The transmit frame was enqueued successfully. */ tx_count++; /* Send a copy of the frame to any BPF listeners. */ BPF_MTAP(ifp, next); /* Handle any completions if we're running low. */ if (fp->tx_bd_used >= BXE_TX_CLEANUP_THRESHOLD) bxe_txeof(fp); /* Close TX since there's so little room left. */ if (fp->tx_bd_used >= BXE_TX_CLEANUP_THRESHOLD) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; break; } next = drbr_dequeue(ifp, fp->br); } /* No TX packets were dequeued. */ if (tx_count > 0) /* Reset the TX watchdog timeout timer. */ fp->watchdog_timer = BXE_TX_TIMEOUT; bxe_tx_mq_start_locked_exit: DBEXIT(BXE_EXTREME_SEND); return (rc); } static void bxe_mq_flush(struct ifnet *ifp) { struct bxe_softc *sc; struct bxe_fastpath *fp; struct mbuf *m; int i; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->br != NULL) { DBPRINT(sc, BXE_VERBOSE_UNLOAD, "%s(): Clearing fp[%02d]...\n", __FUNCTION__, fp->index); BXE_FP_LOCK(fp); while ((m = buf_ring_dequeue_sc(fp->br)) != NULL) m_freem(m); BXE_FP_UNLOCK(fp); } } if_qflush(ifp); DBEXIT(BXE_VERBOSE_UNLOAD); } #endif /* FreeBSD_version >= 800000 */ /* * Handles any IOCTL calls from the operating system. * * Returns: * 0 for success, positive value for failure. */ static int bxe_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct bxe_softc *sc; struct ifreq *ifr; int error, mask, reinit; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_MISC); ifr = (struct ifreq *)data; error = 0; reinit = 0; switch (command) { case SIOCSIFMTU: /* Set the MTU. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFMTU\n", __FUNCTION__); /* Check that the MTU setting is supported. */ if ((ifr->ifr_mtu < BXE_MIN_MTU) || (ifr->ifr_mtu > BXE_JUMBO_MTU)) { error = EINVAL; break; } BXE_CORE_LOCK(sc); ifp->if_mtu = ifr->ifr_mtu; BXE_CORE_UNLOCK(sc); reinit = 1; break; case SIOCSIFFLAGS: /* Toggle the interface state up or down. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFFLAGS\n", __FUNCTION__); BXE_CORE_LOCK(sc); /* Check if the interface is up. */ if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* Set promiscuous/multicast flags. */ bxe_set_rx_mode(sc); } else { /* Start the HW */ bxe_init_locked(sc, LOAD_NORMAL); } } else { /* Bring down the interface. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) bxe_stop_locked(sc, UNLOAD_NORMAL); } BXE_CORE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* Add/Delete multicast addresses. */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCADDMULTI/SIOCDELMULTI\n", __FUNCTION__); BXE_CORE_LOCK(sc); /* Check if the interface is up. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) /* Set receive mode flags. */ bxe_set_rx_mode(sc); BXE_CORE_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: /* Set/Get Interface media */ DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFMEDIA/SIOCGIFMEDIA\n", __FUNCTION__); error = ifmedia_ioctl(ifp, ifr, &sc->bxe_ifmedia, command); break; case SIOCSIFCAP: /* Set interface capability */ /* Find out which capabilities have changed. */ mask = ifr->ifr_reqcap ^ ifp->if_capenable; DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Received SIOCSIFCAP (mask = 0x%08X)\n", __FUNCTION__, (uint32_t)mask); BXE_CORE_LOCK(sc); /* Toggle the LRO capabilites enable flag. */ if (mask & IFCAP_LRO) { ifp->if_capenable ^= IFCAP_LRO; sc->bxe_flags ^= BXE_TPA_ENABLE_FLAG; DBPRINT(sc, BXE_INFO_MISC, "%s(): Toggling LRO (bxe_flags = " "0x%08X).\n", __FUNCTION__, sc->bxe_flags); /* LRO requires different buffer setup. */ reinit = 1; } /* Toggle the TX checksum capabilites enable flag. */ if (mask & IFCAP_TXCSUM) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_TXCSUM.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_TXCSUM; if (IFCAP_TXCSUM & ifp->if_capenable) ifp->if_hwassist = BXE_IF_HWASSIST; else ifp->if_hwassist = 0; } /* Toggle the RX checksum capabilities enable flag. */ if (mask & IFCAP_RXCSUM) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_RXCSUM.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_RXCSUM; if (IFCAP_RXCSUM & ifp->if_capenable) ifp->if_hwassist = BXE_IF_HWASSIST; else ifp->if_hwassist = 0; } /* Toggle VLAN_MTU capabilities enable flag. */ if (mask & IFCAP_VLAN_MTU) { /* ToDo: Is this really true? */ BXE_PRINTF("%s(%d): Changing VLAN_MTU not supported.\n", __FILE__, __LINE__); error = EINVAL; } /* Toggle VLANHWTAG capabilities enabled flag. */ if (mask & IFCAP_VLAN_HWTAGGING) { /* ToDo: Is this really true? */ BXE_PRINTF( "%s(%d): Changing VLAN_HWTAGGING not supported!\n", __FILE__, __LINE__); error = EINVAL; } /* Toggle TSO4 capabilities enabled flag. */ if (mask & IFCAP_TSO4) { DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): Toggling IFCAP_TSO4.\n", __FUNCTION__); ifp->if_capenable ^= IFCAP_TSO4; } /* Toggle TSO6 capabilities enabled flag. */ if (mask & IFCAP_TSO6) { /* ToDo: Add TSO6 support. */ BXE_PRINTF( "%s(%d): Changing TSO6 not supported!\n", __FILE__, __LINE__); } BXE_CORE_UNLOCK(sc); /* * ToDo: Look into supporting: * VLAN_HWFILTER * VLAN_HWCSUM * VLAN_HWTSO * POLLING * WOL[_UCAST|_MCAST|_MAGIC] * */ break; default: /* We don't know how to handle the IOCTL, pass it on. */ error = ether_ioctl(ifp, command, data); break; } /* Restart the controller with the new capabilities. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && (reinit != 0)) { BXE_CORE_LOCK(sc); bxe_stop_locked(sc, UNLOAD_NORMAL); bxe_init_locked(sc, LOAD_NORMAL); BXE_CORE_UNLOCK(sc); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_MISC); return (error); } /* * Gets the current value of the RX Completion Consumer index * from the fastpath status block, updates it as necessary if * it is pointing to a "Next Page" entry, and returns it to the * caller. * * Returns: * The adjusted value of *fp->rx_cons_sb. */ static __inline uint16_t bxe_rx_cq_cons(struct bxe_fastpath *fp) { volatile uint16_t rx_cq_cons_sb = 0; rmb(); rx_cq_cons_sb = (volatile uint16_t) le16toh(*fp->rx_cq_cons_sb); /* * It is valid for the hardware's copy of the completion * consumer index to be pointing at a "Next Page" entry in * the completion chain but the driver prefers to assume * that it is pointing at the next available CQE so we * need to adjust the value accordingly. */ if ((rx_cq_cons_sb & USABLE_RCQ_ENTRIES_PER_PAGE) == USABLE_RCQ_ENTRIES_PER_PAGE) rx_cq_cons_sb++; return (rx_cq_cons_sb); } static __inline int bxe_has_tx_work(struct bxe_fastpath *fp) { rmb(); return (((fp->tx_pkt_prod != le16toh(*fp->tx_pkt_cons_sb)) || \ (fp->tx_pkt_prod != fp->tx_pkt_cons))); } /* * Checks if there are any received frames to process on the * completion queue. * * Returns: * 0 = No received frames pending, !0 = Received frames * pending */ static __inline int bxe_has_rx_work(struct bxe_fastpath *fp) { rmb(); return (bxe_rx_cq_cons(fp) != fp->rx_cq_cons); } /* * Slowpath task entry point. * * Returns: * None */ static void bxe_task_sp(void *xsc, int pending) { struct bxe_softc *sc; uint32_t sp_status; sc = xsc; DBPRINT(sc, BXE_EXTREME_INTR, "%s(): pending = %d.\n", __FUNCTION__, pending); /* Check for the source of the interrupt. */ sp_status = bxe_update_dsb_idx(sc); /* Handle any hardware attentions. */ if (sp_status & 0x1) { bxe_attn_int(sc); sp_status &= ~0x1; } /* CSTORM event asserted (query_stats, port delete ramrod, etc.). */ if (sp_status & 0x2) { sc->stats_pending = 0; sp_status &= ~0x2; } /* Check for other weirdness. */ if (sp_status != 0) { DBPRINT(sc, BXE_WARN, "%s(): Unexpected slowpath interrupt " "(sp_status = 0x%04X)!\n", __FUNCTION__, sp_status); } /* Acknowledge the xSTORM tags and enable slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, ATTENTION_ID, le16toh(sc->def_att_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, USTORM_ID, le16toh(sc->def_u_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, CSTORM_ID, le16toh(sc->def_c_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, XSTORM_ID, le16toh(sc->def_x_idx), IGU_INT_NOP, 1); bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, le16toh(sc->def_t_idx), IGU_INT_ENABLE, 1); } /* * Legacy interrupt entry point. * * Verifies that the controller generated the interrupt and * then calls a separate routine to handle the various * interrupt causes: link, RX, and TX. * * Returns: * None */ static void bxe_intr_legacy(void *xsc) { struct bxe_softc *sc; struct bxe_fastpath *fp; uint32_t mask, fp_status; sc = xsc; fp = &sc->fp[0]; /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_legacy_exit; /* Bail out if the interrupt wasn't generated by our hardware. */ fp_status = bxe_ack_int(sc); if (fp_status == 0) goto bxe_intr_legacy_exit; /* Handle the fastpath interrupt. */ /* * sb_id = 0 for ustorm, 1 for cstorm. * The bits returned from ack_int() are 0-15, * bit 0=attention status block * bit 1=fast path status block * A mask of 0x2 or more = tx/rx event * A mask of 1 = slow path event */ mask = (0x2 << fp->sb_id); DBPRINT(sc, BXE_INSANE_INTR, "%s(): fp_status = 0x%08X, mask = " "0x%08X\n", __FUNCTION__, fp_status, mask); /* CSTORM event means fastpath completion. */ if (fp_status & mask) { /* This interrupt must be ours, disable further interrupts. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK taskqueue_enqueue(fp->tq, &fp->task); #else bxe_task_fp((void *)fp, 0); #endif /* Clear this event from the status flags. */ fp_status &= ~mask; } /* Handle all slow path interrupts and attentions */ if (fp_status & 0x1) { /* Acknowledge and disable further slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK /* Schedule the slowpath task. */ taskqueue_enqueue(sc->tq, &sc->task); #else bxe_task_sp(xsc, 0); #endif /* Clear this event from the status flags. */ fp_status &= ~0x1; } #ifdef BXE_DEBUG if (fp_status) { DBPRINT(sc, BXE_WARN, "%s(): Unexpected fastpath status (fp_status = 0x%08X)!\n", __FUNCTION__, fp_status); } #endif DBEXIT(BXE_EXTREME_INTR); bxe_intr_legacy_exit: return; } /* * Slowpath interrupt entry point. * * Acknowledge the interrupt and schedule a slowpath task. * * Returns: * None */ static void bxe_intr_sp(void *xsc) { struct bxe_softc *sc; sc = xsc; DBPRINT(sc, BXE_INSANE_INTR, "%s(%d): Slowpath interrupt.\n", __FUNCTION__, curcpu); /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_sp_exit; /* Acknowledge and disable further slowpath interrupts. */ bxe_ack_sb(sc, DEF_SB_ID, TSTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK /* Schedule the slowpath task. */ taskqueue_enqueue(sc->tq, &sc->task); #else bxe_task_sp(xsc, 0); #endif bxe_intr_sp_exit: return; } /* * Fastpath interrupt entry point. * * Acknowledge the interrupt and schedule a fastpath task. * * Returns: * None */ static void bxe_intr_fp (void *xfp) { struct bxe_fastpath *fp; struct bxe_softc *sc; fp = xfp; sc = fp->sc; DBPRINT(sc, BXE_INSANE_INTR, "%s(%d): fp[%02d].sb_id = %d interrupt.\n", __FUNCTION__, curcpu, fp->index, fp->sb_id); /* Don't handle any interrupts if we're not ready. */ if (__predict_false(sc->intr_sem != 0)) goto bxe_intr_fp_exit; /* Disable further interrupts. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BXE_TASK taskqueue_enqueue(fp->tq, &fp->task); #else bxe_task_fp (xfp, 0); #endif bxe_intr_fp_exit: return; } /* * Fastpath task entry point. * * Handle any pending transmit or receive events. * * Returns: * None */ static void bxe_task_fp (void *xfp, int pending) { struct bxe_fastpath *fp; struct bxe_softc *sc; fp = xfp; sc = fp->sc; DBPRINT(sc, BXE_EXTREME_INTR, "%s(%d): Fastpath task on fp[%02d]" ".sb_id = %d\n", __FUNCTION__, curcpu, fp->index, fp->sb_id); /* Update the fast path indices */ bxe_update_fpsb_idx(fp); /* Service any completed TX frames. */ if (bxe_has_tx_work(fp)) { BXE_FP_LOCK(fp); bxe_txeof(fp); BXE_FP_UNLOCK(fp); } /* Service any completed RX frames. */ rmb(); bxe_rxeof(fp); /* Acknowledge the fastpath status block indices. */ bxe_ack_sb(sc, fp->sb_id, USTORM_ID, fp->fp_u_idx, IGU_INT_NOP, 1); bxe_ack_sb(sc, fp->sb_id, CSTORM_ID, fp->fp_c_idx, IGU_INT_ENABLE, 1); } /* * Clears the fastpath (per-queue) status block. * * Returns: * None */ static void bxe_zero_sb(struct bxe_softc *sc, int sb_id) { int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); port = BP_PORT(sc); /* "CSTORM" */ bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), 0, CSTORM_SB_STATUS_BLOCK_U_SIZE / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id), 0, CSTORM_SB_STATUS_BLOCK_C_SIZE / 4); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Initialize the fastpath (per queue) status block. * * Returns: * None */ static void bxe_init_sb(struct bxe_softc *sc, struct host_status_block *sb, bus_addr_t mapping, int sb_id) { uint64_t section; int func, index, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); port = BP_PORT(sc); func = BP_FUNC(sc); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Initializing sb_id = %d on port %d, function %d.\n", __FUNCTION__, sb_id, port, func); /* Setup the USTORM status block. */ section = ((uint64_t)mapping) + offsetof(struct host_status_block, u_status_block); sb->u_status_block.status_block_id = sb_id; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + FP_USB_FUNC_OFF + CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), func); for (index = 0; index < HC_USTORM_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, index), 0x1); /* Setup the CSTORM status block. */ section = ((uint64_t)mapping) + offsetof(struct host_status_block, c_status_block); sb->c_status_block.status_block_id = sb_id; /* Write the status block address to CSTORM. Order is important! */ REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HOST_SB_ADDR_C_OFFSET(port, sb_id), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_SB_HOST_SB_ADDR_C_OFFSET(port, sb_id)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + FP_CSB_FUNC_OFF + CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id), func); for (index = 0; index < HC_CSTORM_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, index), 0x1); /* Enable interrupts. */ bxe_ack_sb(sc, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Clears the default status block. * * Returns: * None */ static void bxe_zero_def_sb(struct bxe_softc *sc) { int func; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Clearing default status block on function %d.\n", __FUNCTION__, func); /* Fill the STORM's copy of the default status block with 0. */ bxe_init_fill(sc, TSEM_REG_FAST_MEMORY + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, sizeof(struct tstorm_def_status_block) / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_DEF_SB_HOST_STATUS_BLOCK_U_OFFSET(func), 0, sizeof(struct cstorm_def_status_block_u) / 4); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY + CSTORM_DEF_SB_HOST_STATUS_BLOCK_C_OFFSET(func), 0, sizeof(struct cstorm_def_status_block_c) / 4); bxe_init_fill(sc, XSEM_REG_FAST_MEMORY + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, sizeof(struct xstorm_def_status_block) / 4); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Initialize default status block. * * Returns: * None */ static void bxe_init_def_sb(struct bxe_softc *sc, struct host_def_status_block *def_sb, bus_addr_t mapping, int sb_id) { uint64_t section; int func, index, port, reg_offset, val; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): Initializing default status block on port %d, function %d.\n", __FUNCTION__, port, func); /* Setup the default status block (DSB). */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, atten_status_block); def_sb->atten_status_block.status_block_id = sb_id; sc->attn_state = 0; sc->def_att_idx = 0; /* * Read routing configuration for attn signal * output of groups. Currently, only groups * 0 through 3 are wired. */ reg_offset = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { sc->attn_group[index].sig[0] = REG_RD(sc, reg_offset + 0x10 * index); sc->attn_group[index].sig[1] = REG_RD(sc, reg_offset + 0x10 * index + 0x4); sc->attn_group[index].sig[2] = REG_RD(sc, reg_offset + 0x10 * index + 0x8); sc->attn_group[index].sig[3] = REG_RD(sc, reg_offset + 0x10 * index + 0xc); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR), "%s(): attn_group[%d] = 0x%08X 0x%08X 0x%08x 0X%08x\n", __FUNCTION__, index, sc->attn_group[index].sig[0], sc->attn_group[index].sig[1], sc->attn_group[index].sig[2], sc->attn_group[index].sig[3]); } reg_offset = port ? HC_REG_ATTN_MSG1_ADDR_L : HC_REG_ATTN_MSG0_ADDR_L; REG_WR(sc, reg_offset, U64_LO(section)); REG_WR(sc, reg_offset + 4, U64_HI(section)); reg_offset = port ? HC_REG_ATTN_NUM_P1 : HC_REG_ATTN_NUM_P0; val = REG_RD(sc, reg_offset); val |= sb_id; REG_WR(sc, reg_offset, val); /* USTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, u_def_status_block); def_sb->u_def_status_block.status_block_id = sb_id; sc->def_u_idx = 0; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HOST_SB_ADDR_U_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_DEF_SB_HOST_SB_ADDR_U_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + DEF_USB_FUNC_OFF + CSTORM_DEF_SB_HOST_STATUS_BLOCK_U_OFFSET(func), func); for (index = 0; index < HC_USTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HC_DISABLE_U_OFFSET(func, index), 1); /* CSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, c_def_status_block); def_sb->c_def_status_block.status_block_id = sb_id; sc->def_c_idx = 0; REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HOST_SB_ADDR_C_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_CSTORM_INTMEM + ((CSTORM_DEF_SB_HOST_SB_ADDR_C_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_CSTORM_INTMEM + DEF_CSB_FUNC_OFF + CSTORM_DEF_SB_HOST_STATUS_BLOCK_C_OFFSET(func), func); for (index = 0; index < HC_CSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_DEF_SB_HC_DISABLE_C_OFFSET(func, index), 1); /* TSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, t_def_status_block); def_sb->t_def_status_block.status_block_id = sb_id; sc->def_t_idx = 0; REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_TSTORM_INTMEM + ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_TSTORM_INTMEM + DEF_TSB_FUNC_OFF + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); for (index = 0; index < HC_TSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_TSTORM_INTMEM + TSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* XSTORM */ section = ((uint64_t)mapping) + offsetof(struct host_def_status_block, x_def_status_block); def_sb->x_def_status_block.status_block_id = sb_id; sc->def_x_idx = 0; REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(sc, BAR_XSTORM_INTMEM + ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); REG_WR8(sc, BAR_XSTORM_INTMEM + DEF_XSB_FUNC_OFF + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); for (index = 0; index < HC_XSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(sc, BAR_XSTORM_INTMEM + XSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); sc->stats_pending = 0; sc->set_mac_pending = 0; bxe_ack_sb(sc, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_INTR); } /* * Update interrupt coalescing parameters. * * Returns: * None */ static void bxe_update_coalesce(struct bxe_softc *sc) { int i, port, sb_id; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); port = BP_PORT(sc); /* Cycle through each fastpath queue and set the coalescing values. */ for (i = 0; i < sc->num_queues; i++) { sb_id = sc->fp[i].sb_id; /* Receive interrupt coalescing is done on USTORM. */ REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_TIMEOUT_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), sc->rx_ticks / (BXE_BTR * 4)); REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, U_SB_ETH_RX_CQ_INDEX), (sc->rx_ticks / (BXE_BTR * 4)) ? 0 : 1); /* Transmit interrupt coalescing is done on CSTORM. */ REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), sc->tx_ticks / (BXE_BTR * 4)); REG_WR16(sc, BAR_CSTORM_INTMEM + CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id, C_SB_ETH_TX_CQ_INDEX), (sc->tx_ticks / (BXE_BTR * 4)) ? 0 : 1); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Allocate an mbuf and assign it to the TPA pool. * * Returns: * 0 = Success, !0 = Failure * * Modifies: * fp->tpa_mbuf_ptr[queue] * fp->tpa_mbuf_map[queue] * fp->tpa_mbuf_segs[queue] */ static int bxe_alloc_tpa_mbuf(struct bxe_fastpath *fp, int queue) { struct bxe_softc *sc; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_TPA); rc = 0; DBRUNIF((fp->disable_tpa == TRUE), BXE_PRINTF("%s(): fp[%02d] TPA disabled!\n", __FUNCTION__, fp->index)); #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_tpa_alloc_failed++; rc = ENOMEM; goto bxe_alloc_tpa_mbuf_exit; } #endif /* Allocate the new TPA mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->mbuf_alloc_size); if (__predict_false(m == NULL)) { fp->mbuf_tpa_alloc_failed++; rc = ENOBUFS; goto bxe_alloc_tpa_mbuf_exit; } DBRUN(fp->tpa_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = sc->mbuf_alloc_size; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_tpa_mapping_failed++; m_freem(m); DBRUN(fp->tpa_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_tpa_mbuf_exit; } #endif /* Map the TPA mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_tpa_mapping_failed++; m_free(m); DBRUN(fp->tpa_mbuf_alloc--); goto bxe_alloc_tpa_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Release any existing TPA mbuf mapping. */ if (fp->tpa_mbuf_map[queue] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue]); } /* Save the mbuf and mapping info for the TPA mbuf. */ map = fp->tpa_mbuf_map[queue]; fp->tpa_mbuf_map[queue] = fp->tpa_mbuf_spare_map; fp->tpa_mbuf_spare_map = map; bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[queue], BUS_DMASYNC_PREREAD); fp->tpa_mbuf_ptr[queue] = m; fp->tpa_mbuf_segs[queue] = segs[0]; bxe_alloc_tpa_mbuf_exit: DBEXIT(BXE_INSANE_TPA); return (rc); } /* * Allocate mbufs for a fastpath TPA pool. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->tpa_state[] * fp->disable_tpa */ static int bxe_fill_tpa_pool(struct bxe_fastpath *fp) { struct bxe_softc *sc; int max_agg_queues, queue, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; if (!TPA_ENABLED(sc)) { fp->disable_tpa = TRUE; goto bxe_fill_tpa_pool_exit; } max_agg_queues = CHIP_IS_E1(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1 : ETH_MAX_AGGREGATION_QUEUES_E1H; /* Assume the fill operation worked. */ fp->disable_tpa = FALSE; /* Fill the TPA pool. */ for (queue = 0; queue < max_agg_queues; queue++) { rc = bxe_alloc_tpa_mbuf(fp, queue); if (rc != 0) { BXE_PRINTF( "%s(%d): fp[%02d] TPA disabled!\n", __FILE__, __LINE__, fp->index); fp->disable_tpa = TRUE; break; } fp->tpa_state[queue] = BXE_TPA_STATE_STOP; } bxe_fill_tpa_pool_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all mbufs from a fastpath TPA pool. * * Returns: * None * * Modifies: * fp->tpa_mbuf_ptr[] * fp->tpa_mbuf_map[] * fp->tpa_mbuf_alloc */ static void bxe_free_tpa_pool(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i, max_agg_queues; sc = fp->sc; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_UNLOAD | BXE_INSANE_TPA); if (fp->rx_mbuf_tag == NULL) goto bxe_free_tpa_pool_exit; max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; /* Release all mbufs and and all DMA maps in the TPA pool. */ for (i = 0; i < max_agg_queues; i++) { if (fp->tpa_mbuf_map[i] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->tpa_mbuf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->tpa_mbuf_map[i]); } if (fp->tpa_mbuf_ptr[i] != NULL) { m_freem(fp->tpa_mbuf_ptr[i]); DBRUN(fp->tpa_mbuf_alloc--); fp->tpa_mbuf_ptr[i] = NULL; } } bxe_free_tpa_pool_exit: DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_UNLOAD | BXE_INSANE_TPA); } /* * Allocate an mbuf and assign it to the receive scatter gather chain. * The caller must take care to save a copy of the existing mbuf in the * SG mbuf chain. * * Returns: * 0 = Success, !0= Failure. * * Modifies: * fp->sg_chain[index] * fp->rx_sge_buf_ptr[index] * fp->rx_sge_buf_map[index] * fp->rx_sge_spare_map */ static int bxe_alloc_rx_sge_mbuf(struct bxe_fastpath *fp, uint16_t index) { struct bxe_softc *sc; struct eth_rx_sge *sge; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_TPA); rc = 0; #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_sge_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } #endif /* Allocate a new SGE mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, SGE_PAGE_SIZE); if (__predict_false(m == NULL)) { fp->mbuf_sge_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } DBRUN(fp->sge_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = SGE_PAGE_SIZE; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_sge_mapping_failed++; m_freem(m); DBRUN(fp->sge_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_rx_sge_mbuf_exit; } #endif /* Map the SGE mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_sge_buf_tag, fp->rx_sge_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_sge_mapping_failed++; m_freem(m); DBRUN(fp->sge_mbuf_alloc--); goto bxe_alloc_rx_sge_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Unload any existing SGE mbuf mapping. */ if (fp->rx_sge_buf_map[index] != NULL) { bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index]); } /* Add the new SGE mbuf to the SGE ring. */ map = fp->rx_sge_buf_map[index]; fp->rx_sge_buf_map[index] = fp->rx_sge_spare_map; fp->rx_sge_spare_map = map; bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[index], BUS_DMASYNC_PREREAD); fp->rx_sge_buf_ptr[index] = m; sge = &fp->sg_chain[index]; sge->addr_hi = htole32(U64_HI(segs[0].ds_addr)); sge->addr_lo = htole32(U64_LO(segs[0].ds_addr)); bxe_alloc_rx_sge_mbuf_exit: DBEXIT(BXE_INSANE_TPA); return (rc); } /* * Allocate mbufs for a SGE chain. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->disable_tpa * fp->rx_sge_prod */ static int bxe_fill_sg_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint16_t index; int i, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; if (!TPA_ENABLED(sc)) { fp->disable_tpa = TRUE; goto bxe_fill_sg_chain_exit; } /* Assume the fill operation works. */ fp->disable_tpa = FALSE; /* Fill the RX SGE chain. */ index = 0; for (i = 0; i < USABLE_RX_SGE; i++) { rc = bxe_alloc_rx_sge_mbuf(fp, index); if (rc != 0) { BXE_PRINTF( "%s(%d): fp[%02d] SGE memory allocation failure!\n", __FILE__, __LINE__, fp->index); index = 0; fp->disable_tpa = TRUE; break; } index = NEXT_SGE_IDX(index); } /* Update the driver's copy of the RX SGE producer index. */ fp->rx_sge_prod = index; bxe_fill_sg_chain_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all elements from the receive scatter gather chain. * * Returns: * None * * Modifies: * fp->rx_sge_buf_ptr[] * fp->rx_sge_buf_map[] * fp->sge_mbuf_alloc */ static void bxe_free_sg_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i; sc = fp->sc; DBENTER(BXE_INSANE_TPA); if (fp->rx_sge_buf_tag == NULL) goto bxe_free_sg_chain_exit; /* Free all mbufs and unload all maps. */ for (i = 0; i < TOTAL_RX_SGE; i++) { /* Free the map and the mbuf if they're allocated. */ if (fp->rx_sge_buf_map[i] != NULL) { bus_dmamap_sync(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_sge_buf_tag, fp->rx_sge_buf_map[i]); } if (fp->rx_sge_buf_ptr[i] != NULL) { m_freem(fp->rx_sge_buf_ptr[i]); DBRUN(fp->sge_mbuf_alloc--); fp->rx_sge_buf_ptr[i] = NULL; } } bxe_free_sg_chain_exit: DBEXIT(BXE_INSANE_TPA); } /* * Allocate an mbuf, if necessary, and add it to the receive chain. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_alloc_rx_bd_mbuf(struct bxe_fastpath *fp, uint16_t index) { struct bxe_softc *sc; struct eth_rx_bd *rx_bd; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct mbuf *m; int nsegs, rc; sc = fp->sc; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_RESET | BXE_INSANE_RECV); rc = 0; #ifdef BXE_DEBUG /* Simulate an mbuf allocation failure. */ if (DB_RANDOMTRUE(bxe_debug_mbuf_allocation_failure)) { sc->debug_sim_mbuf_alloc_failed++; fp->mbuf_rx_bd_alloc_failed++; rc = ENOMEM; goto bxe_alloc_rx_bd_mbuf_exit; } #endif /* Allocate the new RX BD mbuf. */ m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, sc->mbuf_alloc_size); if (__predict_false(m == NULL)) { fp->mbuf_rx_bd_alloc_failed++; rc = ENOBUFS; goto bxe_alloc_rx_bd_mbuf_exit; } DBRUN(fp->rx_mbuf_alloc++); /* Initialize the mbuf buffer length. */ m->m_pkthdr.len = m->m_len = sc->mbuf_alloc_size; #ifdef BXE_DEBUG /* Simulate an mbuf mapping failure. */ if (DB_RANDOMTRUE(bxe_debug_dma_map_addr_failure)) { sc->debug_sim_mbuf_map_failed++; fp->mbuf_rx_bd_mapping_failed++; m_freem(m); DBRUN(fp->rx_mbuf_alloc--); rc = ENOMEM; goto bxe_alloc_rx_bd_mbuf_exit; } #endif /* Map the TPA mbuf into non-paged pool. */ rc = bus_dmamap_load_mbuf_sg(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (__predict_false(rc != 0)) { fp->mbuf_rx_bd_mapping_failed++; m_freem(m); DBRUN(fp->rx_mbuf_alloc--); goto bxe_alloc_rx_bd_mbuf_exit; } /* All mubfs must map to a single segment. */ KASSERT(nsegs == 1, ("%s(): Too many segments (%d) returned!", __FUNCTION__, nsegs)); /* Release any existing RX BD mbuf mapping. */ if (fp->rx_mbuf_map[index] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[index], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_map[index]); } /* Save the mbuf and mapping info. */ map = fp->rx_mbuf_map[index]; fp->rx_mbuf_map[index] = fp->rx_mbuf_spare_map; fp->rx_mbuf_spare_map = map; bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[index], BUS_DMASYNC_PREREAD); fp->rx_mbuf_ptr[index] = m; rx_bd = &fp->rx_chain[index]; rx_bd->addr_hi = htole32(U64_HI(segs[0].ds_addr)); rx_bd->addr_lo = htole32(U64_LO(segs[0].ds_addr)); bxe_alloc_rx_bd_mbuf_exit: DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_RESET | BXE_INSANE_RECV); return (rc); } /* * Allocate mbufs for a receive chain. * * Returns: * 0 = Success, !0 = Failure. * * Modifies: * fp->rx_bd_prod */ static int bxe_fill_rx_bd_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint16_t index; int i, rc; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = index = 0; /* Allocate buffers for all the RX BDs in RX BD Chain. */ for (i = 0; i < USABLE_RX_BD; i++) { rc = bxe_alloc_rx_bd_mbuf(fp, index); if (rc != 0) { BXE_PRINTF( "%s(%d): Memory allocation failure! Cannot fill fp[%02d] RX chain.\n", __FILE__, __LINE__, fp->index); index = 0; break; } index = NEXT_RX_BD(index); } fp->rx_bd_prod = index; DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free all buffers from the receive chain. * * Returns: * None * * Modifies: * fp->rx_mbuf_ptr[] * fp->rx_mbuf_map[] * fp->rx_mbuf_alloc */ static void bxe_free_rx_bd_chain(struct bxe_fastpath *fp) { struct bxe_softc *sc; int i; sc = fp->sc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (fp->rx_mbuf_tag == NULL) goto bxe_free_rx_bd_chain_exit; /* Free all mbufs and unload all maps. */ for (i = 0; i < TOTAL_RX_BD; i++) { if (fp->rx_mbuf_map[i] != NULL) { bus_dmamap_sync(fp->rx_mbuf_tag, fp->rx_mbuf_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_map[i]); } if (fp->rx_mbuf_ptr[i] != NULL) { m_freem(fp->rx_mbuf_ptr[i]); DBRUN(fp->rx_mbuf_alloc--); fp->rx_mbuf_ptr[i] = NULL; } } bxe_free_rx_bd_chain_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Setup mutexes used by the driver. * * Returns: * None. */ static void bxe_mutexes_alloc(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_LOAD); BXE_CORE_LOCK_INIT(sc, device_get_nameunit(sc->dev)); BXE_SP_LOCK_INIT(sc, "bxe_sp_lock"); BXE_DMAE_LOCK_INIT(sc, "bxe_dmae_lock"); BXE_PHY_LOCK_INIT(sc, "bxe_phy_lock"); BXE_FWMB_LOCK_INIT(sc, "bxe_fwmb_lock"); BXE_PRINT_LOCK_INIT(sc, "bxe_print_lock"); /* Allocate one mutex for each fastpath structure. */ for (i = 0; i < sc->num_queues; i++ ) { fp = &sc->fp[i]; /* Allocate per fastpath mutexes. */ snprintf(fp->mtx_name, sizeof(fp->mtx_name), "%s:fp[%02d]", device_get_nameunit(sc->dev), fp->index); mtx_init(&fp->mtx, fp->mtx_name, NULL, MTX_DEF); } DBEXIT(BXE_VERBOSE_LOAD); } /* * Free mutexes used by the driver. * * Returns: * None. */ static void bxe_mutexes_free(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_UNLOAD); for (i = 0; i < sc->num_queues; i++ ) { fp = &sc->fp[i]; /* Release per fastpath mutexes. */ if (mtx_initialized(&fp->mtx)) mtx_destroy(&fp->mtx); } BXE_PRINT_LOCK_DESTROY(sc); BXE_FWMB_LOCK_DESTROY(sc); BXE_PHY_LOCK_DESTROY(sc); BXE_DMAE_LOCK_DESTROY(sc); BXE_SP_LOCK_DESTROY(sc); BXE_CORE_LOCK_DESTROY(sc); DBEXIT(BXE_VERBOSE_UNLOAD); } /* * Free memory and clear the RX data structures. * * Returns: * Nothing. */ static void bxe_clear_rx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; DBENTER(BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Free all RX buffers. */ bxe_free_rx_bd_chain(fp); bxe_free_tpa_pool(fp); bxe_free_sg_chain(fp); /* Check if any mbufs lost in the process. */ DBRUNIF((fp->tpa_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] TPA pool!\n", __FUNCTION__, fp->tpa_mbuf_alloc, fp->index)); DBRUNIF((fp->sge_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] SGE chain!\n", __FUNCTION__, fp->sge_mbuf_alloc, fp->index)); DBRUNIF((fp->rx_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] RX chain!\n", __FUNCTION__, fp->rx_mbuf_alloc, fp->index)); } DBEXIT(BXE_VERBOSE_RESET); } /* * Initialize the receive rings. * * Returns: * None. */ static int bxe_init_rx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int func, i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); rc = 0; func = BP_FUNC(sc); /* Allocate memory for RX and CQ chains. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing fp[%02d] RX chain.\n", __FUNCTION__, i); fp->rx_bd_cons = fp->rx_bd_prod = 0; fp->rx_cq_cons = fp->rx_cq_prod = 0; /* Pointer to status block's CQ consumer index. */ fp->rx_cq_cons_sb = &fp->status_block-> u_status_block.index_values[HC_INDEX_U_ETH_RX_CQ_CONS]; /* Pointer to status block's receive consumer index. */ fp->rx_bd_cons_sb = &fp->status_block-> u_status_block.index_values[HC_INDEX_U_ETH_RX_BD_CONS]; fp->rx_cq_prod = TOTAL_RCQ_ENTRIES; fp->rx_pkts = fp->rx_tpa_pkts = fp->rx_soft_errors = 0; /* Allocate memory for the receive chain. */ rc = bxe_fill_rx_bd_chain(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Allocate memory for TPA pool. */ rc = bxe_fill_tpa_pool(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Allocate memory for scatter-gather chain. */ rc = bxe_fill_sg_chain(fp); if (rc != 0) goto bxe_init_rx_chains_exit; /* Prepare the receive BD and CQ buffers for DMA access. */ bus_dmamap_sync(fp->rx_dma.tag, fp->rx_dma.map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(fp->rcq_dma.tag, fp->rcq_dma.map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * Tell the controller that we have rx_bd's and CQE's * available. Warning! this will generate an interrupt * (to the TSTORM). This must only be done when the * controller is initialized. */ bxe_update_rx_prod(sc, fp, fp->rx_bd_prod, fp->rx_cq_prod, fp->rx_sge_prod); /* ToDo - Move to dma_alloc(). */ /* * Tell controller where the receive CQ * chains start in physical memory. */ if (i == 0) { REG_WR(sc, BAR_USTORM_INTMEM + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(func), U64_LO(fp->rcq_dma.paddr)); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(func) + 4, U64_HI(fp->rcq_dma.paddr)); } } bxe_init_rx_chains_exit: /* Release memory if an error occurred. */ if (rc != 0) bxe_clear_rx_chains(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Free memory and clear the TX data structures. * * Returns: * Nothing. */ static void bxe_clear_tx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j; DBENTER(BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Free all mbufs and unload all maps. */ if (fp->tx_mbuf_tag) { for (j = 0; j < TOTAL_TX_BD; j++) { if (fp->tx_mbuf_ptr[j] != NULL) { bus_dmamap_sync(fp->tx_mbuf_tag, fp->tx_mbuf_map[j], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); m_freem(fp->tx_mbuf_ptr[j]); fp->tx_mbuf_alloc--; fp->tx_mbuf_ptr[j] = NULL; } } } /* Check if we lost any mbufs in the process. */ DBRUNIF((fp->tx_mbuf_alloc), DBPRINT(sc, BXE_FATAL, "%s(): Memory leak! Lost %d mbufs from fp[%02d] TX chain!\n", __FUNCTION__, fp->tx_mbuf_alloc, fp->index)); } DBEXIT(BXE_VERBOSE_RESET); } /* * Initialize the transmit chain. * * Returns: * None. */ static void bxe_init_tx_chains(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Initialize transmit doorbell. */ fp->tx_db.data.header.header = DOORBELL_HDR_DB_TYPE; fp->tx_db.data.zero_fill1 = 0; fp->tx_db.data.prod = 0; /* Initialize tranmsit producer/consumer indices. */ fp->tx_pkt_prod = fp->tx_pkt_cons = 0; fp->tx_bd_prod = fp->tx_bd_cons = 0; fp->tx_bd_used = 0; /* Pointer to TX packet consumer in status block. */ fp->tx_pkt_cons_sb = &fp->status_block->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX]; /* Soft TX counters. */ fp->tx_pkts = 0; fp->tx_soft_errors = 0; fp->tx_offload_frames_csum_ip = 0; fp->tx_offload_frames_csum_tcp = 0; fp->tx_offload_frames_csum_udp = 0; fp->tx_offload_frames_tso = 0; fp->tx_header_splits = 0; fp->tx_encap_failures = 0; fp->tx_hw_queue_full = 0; fp->tx_hw_max_queue_depth = 0; fp->tx_dma_mapping_failure = 0; fp->tx_max_drbr_queue_depth = 0; fp->tx_window_violation_std = 0; fp->tx_window_violation_tso = 0; fp->tx_unsupported_tso_request_ipv6 = 0; fp->tx_unsupported_tso_request_not_tcp = 0; fp->tx_chain_lost_mbuf = 0; fp->tx_frame_deferred = 0; fp->tx_queue_xoff = 0; /* Clear all TX mbuf pointers. */ for (j = 0; j < TOTAL_TX_BD; j++) { fp->tx_mbuf_ptr[j] = NULL; } } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize the slowpath ring. * * Returns: * None. */ static void bxe_init_sp_ring(struct bxe_softc *sc) { int func; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); bzero((char *)sc->slowpath, BXE_SLOWPATH_SZ); /* When the producer equals the consumer the chain is empty. */ sc->spq_left = MAX_SPQ_PENDING; sc->spq_prod_idx = 0; sc->dsb_sp_prod = BXE_SP_DSB_INDEX; sc->spq_prod_bd = sc->spq; sc->spq_last_bd = sc->spq_prod_bd + MAX_SP_DESC_CNT; /* Tell the controller the address of the slowpath ring. */ REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func), U64_LO(sc->spq_dma.paddr)); REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func) + 4, U64_HI(sc->spq_dma.paddr)); REG_WR(sc, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(func), sc->spq_prod_idx); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize STORM processor context. * * Returns: * None. */ static void bxe_init_context(struct bxe_softc *sc) { struct eth_context *context; struct bxe_fastpath *fp; uint8_t sb_id; uint8_t cl_id; int i; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); for (i = 0; i < sc->num_queues; i++) { context = BXE_SP(sc, context[i].eth); fp = &sc->fp[i]; sb_id = fp->sb_id; cl_id = fp->cl_id; /* Update the USTORM context. */ context->ustorm_st_context.common.sb_index_numbers = BXE_RX_SB_INDEX_NUM; context->ustorm_st_context.common.clientId = cl_id; context->ustorm_st_context.common.status_block_id = sb_id; /* Enable packet alignment/pad and statistics. */ context->ustorm_st_context.common.flags = USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT; if (sc->stats_enable == TRUE) context->ustorm_st_context.common.flags |= USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_STATISTICS; context->ustorm_st_context.common.statistics_counter_id=cl_id; /* * Set packet alignment boundary. * (Must be >= 4 (i.e. 16 bytes).) */ context->ustorm_st_context.common.mc_alignment_log_size = 8; /* Set the size of the receive buffers. */ context->ustorm_st_context.common.bd_buff_size = sc->mbuf_alloc_size; /* Set the address of the receive chain base page. */ context->ustorm_st_context.common.bd_page_base_hi = U64_HI(fp->rx_dma.paddr); context->ustorm_st_context.common.bd_page_base_lo = U64_LO(fp->rx_dma.paddr); if (TPA_ENABLED(sc) && (fp->disable_tpa == FALSE)) { /* Enable TPA and SGE chain support. */ context->ustorm_st_context.common.flags |= USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA; /* Set the size of the SGE buffer. */ context->ustorm_st_context.common.sge_buff_size = (uint16_t) (SGE_PAGE_SIZE * PAGES_PER_SGE); /* Set the address of the SGE chain base page. */ context->ustorm_st_context.common.sge_page_base_hi = U64_HI(fp->sg_dma.paddr); context->ustorm_st_context.common.sge_page_base_lo = U64_LO(fp->sg_dma.paddr); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): MTU = %d\n", __FUNCTION__, (int) sc->bxe_ifp->if_mtu); /* Describe MTU to SGE alignment. */ context->ustorm_st_context.common.max_sges_for_packet = SGE_PAGE_ALIGN(sc->bxe_ifp->if_mtu) >> SGE_PAGE_SHIFT; context->ustorm_st_context.common.max_sges_for_packet = ((context->ustorm_st_context.common. max_sges_for_packet + PAGES_PER_SGE - 1) & (~(PAGES_PER_SGE - 1))) >> PAGES_PER_SGE_SHIFT; DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): max_sges_for_packet = %d\n", __FUNCTION__, context->ustorm_st_context.common.max_sges_for_packet); } /* Update USTORM context. */ context->ustorm_ag_context.cdu_usage = CDU_RSRVD_VALUE_TYPE_A(HW_CID(sc, i), CDU_REGION_NUMBER_UCM_AG, ETH_CONNECTION_TYPE); /* Update XSTORM context. */ context->xstorm_ag_context.cdu_reserved = CDU_RSRVD_VALUE_TYPE_A(HW_CID(sc, i), CDU_REGION_NUMBER_XCM_AG, ETH_CONNECTION_TYPE); /* Set the address of the transmit chain base page. */ context->xstorm_st_context.tx_bd_page_base_hi = U64_HI(fp->tx_dma.paddr); context->xstorm_st_context.tx_bd_page_base_lo = U64_LO(fp->tx_dma.paddr); /* Enable XSTORM statistics. */ context->xstorm_st_context.statistics_data = (cl_id | XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE); /* Update CSTORM status block configuration. */ context->cstorm_st_context.sb_index_number = C_SB_ETH_TX_CQ_INDEX; context->cstorm_st_context.status_block_id = sb_id; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize indirection table. * * Returns: * None. */ static void bxe_init_ind_table(struct bxe_softc *sc) { int func, i; func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (sc->multi_mode == ETH_RSS_MODE_DISABLED) return; /* Initialize the indirection table. */ for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++) REG_WR8(sc, BAR_TSTORM_INTMEM + TSTORM_INDIRECTION_TABLE_OFFSET(func) + i, sc->fp->cl_id + (i % sc->num_queues)); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Set client configuration. * * Returns: * None. */ static void bxe_set_client_config(struct bxe_softc *sc) { struct tstorm_eth_client_config tstorm_client = {0}; int i, port; port = BP_PORT(sc); DBENTER(BXE_VERBOSE_MISC); tstorm_client.mtu = sc->bxe_ifp->if_mtu; /* ETHERMTU */ tstorm_client.config_flags = (TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE | TSTORM_ETH_CLIENT_CONFIG_E1HOV_REM_ENABLE); /* Unconditionally enable VLAN tag stripping. */ if (sc->rx_mode) { tstorm_client.config_flags |= TSTORM_ETH_CLIENT_CONFIG_VLAN_REM_ENABLE; DBPRINT(sc, BXE_VERBOSE, "%s(): VLAN tag stripping enabled.\n", __FUNCTION__); } /* Initialize the receive mode for each receive queue. */ for (i = 0; i < sc->num_queues; i++) { tstorm_client.statistics_counter_id = sc->fp[i].cl_id; REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_CLIENT_CONFIG_OFFSET(port, sc->fp[i].cl_id), ((uint32_t *) &tstorm_client)[0]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_CLIENT_CONFIG_OFFSET(port, sc->fp[i].cl_id) + 4, ((uint32_t *) &tstorm_client)[1]); } DBEXIT(BXE_VERBOSE_MISC); } /* * Set receive mode. * * Programs the MAC according to the type of unicast/broadcast/multicast * packets it should receive. * * Returns: * None. */ static void bxe_set_storm_rx_mode(struct bxe_softc *sc) { struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0}; uint32_t llh_mask; int mode, mask; int func, i , port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); mode = sc->rx_mode; mask = 1 << BP_L_ID(sc); func = BP_FUNC(sc); port = BP_PORT(sc); /* All but management unicast packets should pass to the host as well */ llh_mask = NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_BRCST | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_MLCST | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_VLAN | NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_NO_VLAN; /* Set the individual accept/drop flags based on the receive mode. */ switch (mode) { case BXE_RX_MODE_NONE: /* Drop everything. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_NONE for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.ucast_drop_all = mask; tstorm_mac_filter.mcast_drop_all = mask; tstorm_mac_filter.bcast_drop_all = mask; break; case BXE_RX_MODE_NORMAL: /* Accept all broadcast frames. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_NORMAL for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.bcast_accept_all = mask; break; case BXE_RX_MODE_ALLMULTI: /* Accept all broadcast and multicast frames. */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_ALLMULTI for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.mcast_accept_all = mask; tstorm_mac_filter.bcast_accept_all = mask; break; case BXE_RX_MODE_PROMISC: /* Accept all frames (promiscuous mode). */ DBPRINT(sc, BXE_VERBOSE, "%s(): Setting RX_MODE_PROMISC for function %d.\n", __FUNCTION__, func); tstorm_mac_filter.ucast_accept_all = mask; tstorm_mac_filter.mcast_accept_all = mask; tstorm_mac_filter.bcast_accept_all = mask; llh_mask |= NIG_LLH0_BRB1_DRV_MASK_REG_LLH0_BRB1_DRV_MASK_UNCST; break; default: BXE_PRINTF( "%s(%d): Tried to set unknown receive mode (0x%08X)!\n", __FILE__, __LINE__, mode); } REG_WR(sc, port ? NIG_REG_LLH1_BRB1_DRV_MASK : NIG_REG_LLH0_BRB1_DRV_MASK, llh_mask); /* Write the RX mode filter to the TSTORM. */ for (i = 0; i < sizeof(struct tstorm_eth_mac_filter_config) / 4; i++) REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_MAC_FILTER_CONFIG_OFFSET(func) + (i * 4), ((uint32_t *) &tstorm_mac_filter)[i]); if (mode != BXE_RX_MODE_NONE) bxe_set_client_config(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize common internal resources. (Applies to both ports and * functions.) * * Returns: * Nothing. */ static void bxe_init_internal_common(struct bxe_softc *sc) { int i; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* * Zero this manually as its initialization is currently not * handled through block initialization. */ for (i = 0; i < (USTORM_AGG_DATA_SIZE >> 2); i++) REG_WR(sc, BAR_USTORM_INTMEM + USTORM_AGG_DATA_OFFSET + i * 4, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize port specific internal resources. * * Returns: * Nothing. */ static void bxe_init_internal_port(struct bxe_softc *sc) { int port = BP_PORT(sc); port = BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Port %d internal initialization.\n", __FUNCTION__, port); /* * Each SDM timer tick is 4us. Configure host coalescing * basic timer resolution (BTR) to 12us (3 * 4us). */ REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_HC_BTR_U_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_HC_BTR_C_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_HC_BTR_OFFSET(port), BXE_BTR); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_HC_BTR_OFFSET(port), BXE_BTR); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize function specific internal resources. * * Returns: * Nothing. */ static void bxe_init_internal_func(struct bxe_softc *sc) { struct tstorm_eth_function_common_config tstorm_config = {0}; struct stats_indication_flags stats_flags = {0}; struct ustorm_eth_rx_pause_data_e1h rx_pause = {0}; struct bxe_fastpath *fp; struct eth_rx_cqe_next_page *nextpg; uint32_t offset, size; uint16_t max_agg_size; uint8_t cl_id; int func, i, j, port; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Port %d, function %d internal initialization.\n", __FUNCTION__, port, func); /* * Configure which fields the controller looks at when * distributing incoming frames for RSS/multi-queue operation. */ if (sc->num_queues > 1) { tstorm_config.config_flags = MULTI_FLAGS(sc); tstorm_config.rss_result_mask = MULTI_MASK; } /* Enable TPA if needed */ if (TPA_ENABLED(sc)) tstorm_config.config_flags |= TSTORM_ETH_FUNCTION_COMMON_CONFIG_ENABLE_TPA; if (IS_E1HMF(sc)) tstorm_config.config_flags |= TSTORM_ETH_FUNCTION_COMMON_CONFIG_E1HOV_IN_CAM; tstorm_config.leading_client_id = BP_L_ID(sc); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(func), (*(uint32_t *)&tstorm_config)); /* Don't receive anything until the link is up. */ sc->rx_mode = BXE_RX_MODE_NONE; sc->rx_mode_cl_mask = (1 << BP_L_ID(sc)); bxe_set_storm_rx_mode(sc); for (i = 0; i < sc->num_queues; i++) { cl_id = sc->fp[i].cl_id; /* Reset XSTORM per client statistics. */ size = sizeof(struct xstorm_per_client_stats) / 4; offset = BAR_XSTORM_INTMEM + XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset +(j * 4), 0); /* Reset TSTORM per client statistics. */ size = sizeof(struct tstorm_per_client_stats) / 4; offset = BAR_TSTORM_INTMEM + TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset + (j * 4), 0); /* Reset USTORM per client statistics. */ size = sizeof(struct ustorm_per_client_stats) / 4; offset = BAR_USTORM_INTMEM + USTORM_PER_COUNTER_ID_STATS_OFFSET(port, cl_id); for (j = 0; j < size; j++) REG_WR(sc, offset + (j * 4), 0); } /* Initialize statistics related context. */ stats_flags.collect_eth = 1; REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(func), ((uint32_t *)&stats_flags)[0]); REG_WR(sc, BAR_CSTORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(func) + 4, ((uint32_t *)&stats_flags)[1]); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_TSTORM_INTMEM + TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_STATS_QUERY_ADDR_OFFSET(func), U64_LO(BXE_SP_MAPPING(sc, fw_stats))); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_ETH_STATS_QUERY_ADDR_OFFSET(func) + 4, U64_HI(BXE_SP_MAPPING(sc, fw_stats))); /* Additional initialization for 57711/57711E. */ if (CHIP_IS_E1H(sc)) { REG_WR8(sc, BAR_XSTORM_INTMEM + XSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_TSTORM_INTMEM + TSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_CSTORM_INTMEM + CSTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); REG_WR8(sc, BAR_USTORM_INTMEM + USTORM_FUNCTION_MODE_OFFSET, IS_E1HMF(sc)); /* Set the outer VLAN tag. */ REG_WR16(sc, BAR_XSTORM_INTMEM + XSTORM_E1HOV_OFFSET(func), sc->e1hov); } /* Init completion queue mapping and TPA aggregation size. */ max_agg_size = min((uint32_t)(sc->mbuf_alloc_size + (8 * BCM_PAGE_SIZE * PAGES_PER_SGE)), (uint32_t)0xffff); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): max_agg_size = 0x%08X\n", __FUNCTION__, max_agg_size); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; nextpg = (struct eth_rx_cqe_next_page *) &fp->rcq_chain[USABLE_RCQ_ENTRIES_PER_PAGE]; /* Program the completion queue address. */ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_BASE_OFFSET(port, fp->cl_id), U64_LO(fp->rcq_dma.paddr)); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_BASE_OFFSET(port, fp->cl_id) + 4, U64_HI(fp->rcq_dma.paddr)); /* Program the first CQ next page address. */ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_NEXT_OFFSET(port, fp->cl_id), nextpg->addr_lo); REG_WR(sc, BAR_USTORM_INTMEM + USTORM_CQE_PAGE_NEXT_OFFSET(port, fp->cl_id) + 4, nextpg->addr_hi); /* Set the maximum TPA aggregation size. */ REG_WR16(sc, BAR_USTORM_INTMEM + USTORM_MAX_AGG_SIZE_OFFSET(port, fp->cl_id), max_agg_size); } /* Configure lossless flow control. */ if (CHIP_IS_E1H(sc)) { rx_pause.bd_thr_low = 250; rx_pause.cqe_thr_low = 250; rx_pause.cos = 1; rx_pause.sge_thr_low = 0; rx_pause.bd_thr_high = 350; rx_pause.cqe_thr_high = 350; rx_pause.sge_thr_high = 0; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (fp->disable_tpa == FALSE) { rx_pause.sge_thr_low = 150; rx_pause.sge_thr_high = 250; } offset = BAR_USTORM_INTMEM + USTORM_ETH_RING_PAUSE_DATA_OFFSET(port, fp->cl_id); for (j = 0; j < sizeof(struct ustorm_eth_rx_pause_data_e1h) / 4; j++) REG_WR(sc, offset + (j * 4), ((uint32_t *)&rx_pause)[j]); } } memset(&(sc->cmng), 0, sizeof(struct cmng_struct_per_port)); if (IS_E1HMF(sc)) { /* * During init there is no active link. * Until link is up, assume link rate @ 10Gbps */ bxe_read_mf_cfg(sc); if (!sc->vn_wsum) DBPRINT(sc, BXE_VERBOSE_MISC, "%s(): All MIN values are zeroes, " "fairness will be disabled.\n", __FUNCTION__); } /* Store it to internal memory */ if (sc->port.pmf) { for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) REG_WR(sc, BAR_XSTORM_INTMEM + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i * 4, ((uint32_t *)(&sc->cmng))[i]); } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Initialize internal resources. * * Returns: * Nothing. */ static void bxe_init_internal(struct bxe_softc *sc, uint32_t load_code) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); switch (load_code) { case FW_MSG_CODE_DRV_LOAD_COMMON: bxe_init_internal_common(sc); /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_PORT: bxe_init_internal_port(sc); /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_FUNCTION: bxe_init_internal_func(sc); break; default: BXE_PRINTF( "%s(%d): Unknown load_code (0x%08X) from MCP!\n", __FILE__, __LINE__, load_code); break; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Perform driver instance specific initialization. * * Returns: * None */ static int bxe_init_nic(struct bxe_softc *sc, uint32_t load_code) { struct bxe_fastpath *fp; int i, rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Intialize fastpath structures and the status block. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; fp->disable_tpa = TRUE; bzero((char *)fp->status_block, BXE_STATUS_BLK_SZ); fp->fp_u_idx = 0; fp->fp_c_idx = 0; /* Set a pointer back to the driver instance. */ fp->sc = sc; /* Set the fastpath starting state as closed. */ fp->state = BXE_FP_STATE_CLOSED; /* Self-reference to this fastpath's instance. */ fp->index = i; /* Set the client ID beginning with the leading id. */ fp->cl_id = BP_L_ID(sc) + i; /* Set the status block ID for this fastpath instance. */ fp->sb_id = fp->cl_id; DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): fp[%02d]: cl_id = %d, sb_id = %d\n", __FUNCTION__, fp->index, fp->cl_id, fp->sb_id); /* Initialize the fastpath status block. */ bxe_init_sb(sc, fp->status_block, fp->sb_dma.paddr, fp->sb_id); bxe_update_fpsb_idx(fp); } rmb(); bzero((char *)sc->def_sb, BXE_DEF_STATUS_BLK_SZ); /* Initialize the Default Status Block. */ bxe_init_def_sb(sc, sc->def_sb, sc->def_sb_dma.paddr, DEF_SB_ID); bxe_update_dsb_idx(sc); /* Initialize the coalescence parameters. */ bxe_update_coalesce(sc); /* Initialize receive chains. */ rc = bxe_init_rx_chains(sc); if (rc != 0) { goto bxe_init_nic_exit; } /* Initialize the Transmit BD Chain. */ bxe_init_tx_chains(sc); /* Initialize the Slow Path Chain. */ bxe_init_sp_ring(sc); /* Initialize STORM processor context/configuration. */ bxe_init_context(sc); /* Initialize the Context. */ bxe_init_internal(sc, load_code); /* Enable indirection table for multi-queue operation. */ bxe_init_ind_table(sc); mb(); /* Disable the interrupts from device until init is complete.*/ bxe_int_disable(sc); bxe_init_nic_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Send a loopback packet through the Network Interface Glue (NIG) block. * * Returns: * None. */ static void bxe_lb_pckt(struct bxe_softc *sc) { #ifdef BXE_USE_DMAE uint32_t wb_write[3]; #endif DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Ethernet source and destination addresses. */ #ifdef BXE_USE_DMAE wb_write[0] = 0x55555555; wb_write[1] = 0x55555555; wb_write[2] = 0x20; /* SOP */ REG_WR_DMAE(sc, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); #else REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 8, 0x20); #endif /* NON-IP protocol. */ #ifdef BXE_USE_DMAE wb_write[0] = 0x09000000; wb_write[1] = 0x55555555; wb_write[2] = 0x10; /* EOP */ REG_WR_DMAE(sc, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); #else REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB, 0x09000000); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); REG_WR_IND(sc, NIG_REG_DEBUG_PACKET_LB + 8, 0x10); #endif DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Perform an internal memory test. * * Some internal memories are not accessible through the PCIe interface so * we send some debug packets for the test. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_int_mem_test(struct bxe_softc *sc) { uint32_t val; int count, i, rc; rc = 0; val = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Perform a single debug packet test. */ /* Disable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x0); REG_WR(sc, TCM_REG_PRS_IFEN, 0x0); REG_WR(sc, CFC_REG_DEBUG0, 0x1); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x0); /* Write 0 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0); /* Send an Ethernet packet. */ bxe_lb_pckt(sc); /* Wait until NIG register shows 1 packet of size 0x10. */ count = 1000; while (count) { bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); if (val == 0x10) break; DELAY(10000); count--; } if (val != 0x10) { DBPRINT(sc, BXE_FATAL, "%s(): NIG loopback test 1 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 1; goto bxe_int_mem_test_exit; } /* Wait until PRS register shows 1 packet */ count = 1000; while (count) { val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val == 1) break; DELAY(10000); count--; } if (val != 0x1) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 1 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 2; goto bxe_int_mem_test_exit; } /* Reset and init BRB, PRS. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x3); DELAY(50000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x3); DELAY(50000); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); /* Perform the test again, this time with 10 packets. */ /* Disable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x0); REG_WR(sc, TCM_REG_PRS_IFEN, 0x0); REG_WR(sc, CFC_REG_DEBUG0, 0x1); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x0); /* Write 0 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x0); /* Send 10 Ethernet packets. */ for (i = 0; i < 10; i++) bxe_lb_pckt(sc); /* Wait until NIG shows 10 + 1 packets of size 11 * 0x10 = 0xb0. */ count = 1000; while (count) { bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); if (val == 0xb0) break; DELAY(10000); count--; } if (val != 0xb0) { DBPRINT(sc, BXE_FATAL, "%s(): NIG loopback test 2 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 3; goto bxe_int_mem_test_exit; } /* Wait until PRS register shows 2 packets. */ val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val != 2) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 2 timeout (val = 0x%x)!\n", __FUNCTION__, val); rc = 4; goto bxe_int_mem_test_exit; } /* Write 1 to parser credits for CFC search request. */ REG_WR(sc, PRS_REG_CFC_SEARCH_INITIAL_CREDIT, 0x1); /* Wait until PRS register shows 3 packets. */ DELAY(10000); /* Wait until NIG register shows 1 packet of size 0x10. */ val = REG_RD(sc, PRS_REG_NUM_OF_PACKETS); if (val != 3) { DBPRINT(sc, BXE_FATAL, "%s(): PRS loopback test 3 timeout (val = 0x%08X)!\n", __FUNCTION__, val); rc = 5; goto bxe_int_mem_test_exit; } /* Clear NIG end-of-packet FIFO. */ for (i = 0; i < 11; i++) REG_RD(sc, NIG_REG_INGRESS_EOP_LB_FIFO); val = REG_RD(sc, NIG_REG_INGRESS_EOP_LB_EMPTY); if (val != 1) { DBPRINT(sc, BXE_INFO, "%s(): Unable to clear NIG!\n", __FUNCTION__); rc = 6; goto bxe_int_mem_test_exit; } /* Reset and init BRB, PRS, NIG. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03); DELAY(50000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03); DELAY(50000); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); /* Set NIC mode. */ REG_WR(sc, PRS_REG_NIC_MODE, 1); /* Enable inputs of parser neighbor blocks. */ REG_WR(sc, TSDM_REG_ENABLE_IN1, 0x7fffffff); REG_WR(sc, TCM_REG_PRS_IFEN, 0x1); REG_WR(sc, CFC_REG_DEBUG0, 0x0); REG_WR(sc, NIG_REG_PRS_REQ_IN_EN, 0x1); bxe_int_mem_test_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Enable attentions from various blocks. * * Returns: * None. */ static void bxe_enable_blocks_attention(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); REG_WR(sc, PXP_REG_PXP_INT_MASK_0, 0); REG_WR(sc, PXP_REG_PXP_INT_MASK_1, 0); REG_WR(sc, DORQ_REG_DORQ_INT_MASK, 0); REG_WR(sc, CFC_REG_CFC_INT_MASK, 0); REG_WR(sc, QM_REG_QM_INT_MASK, 0); REG_WR(sc, TM_REG_TM_INT_MASK, 0); REG_WR(sc, XSDM_REG_XSDM_INT_MASK_0, 0); REG_WR(sc, XSDM_REG_XSDM_INT_MASK_1, 0); REG_WR(sc, XCM_REG_XCM_INT_MASK, 0); REG_WR(sc, USDM_REG_USDM_INT_MASK_0, 0); REG_WR(sc, USDM_REG_USDM_INT_MASK_1, 0); REG_WR(sc, UCM_REG_UCM_INT_MASK, 0); REG_WR(sc, GRCBASE_UPB + PB_REG_PB_INT_MASK, 0); REG_WR(sc, CSDM_REG_CSDM_INT_MASK_0, 0); REG_WR(sc, CSDM_REG_CSDM_INT_MASK_1, 0); REG_WR(sc, CCM_REG_CCM_INT_MASK, 0); REG_WR(sc, PXP2_REG_PXP2_INT_MASK_0, 0x480000); REG_WR(sc, TSDM_REG_TSDM_INT_MASK_0, 0); REG_WR(sc, TSDM_REG_TSDM_INT_MASK_1, 0); REG_WR(sc, TCM_REG_TCM_INT_MASK, 0); REG_WR(sc, CDU_REG_CDU_INT_MASK, 0); REG_WR(sc, DMAE_REG_DMAE_INT_MASK, 0); REG_WR(sc, PBF_REG_PBF_INT_MASK, 0X18); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * PXP Arbiter */ /* * This code configures the PCI read/write arbiter * which implements a weighted round robin * between the virtual queues in the chip. * * The values were derived for each PCI max payload and max request size. * since max payload and max request size are only known at run time, * this is done as a separate init stage. */ #define NUM_WR_Q 13 #define NUM_RD_Q 29 #define MAX_RD_ORD 3 #define MAX_WR_ORD 2 /* Configuration for one arbiter queue. */ struct arb_line { int l; int add; int ubound; }; /* Derived configuration for each read queue for each max request size. */ static const struct arb_line read_arb_data[NUM_RD_Q][MAX_RD_ORD + 1] = { /* 1 */ { {8, 64, 25}, {16, 64, 25}, {32, 64, 25}, {64, 64, 41} }, { {4, 8, 4}, {4, 8, 4}, {4, 8, 4}, {4, 8, 4} }, { {4, 3, 3}, {4, 3, 3}, {4, 3, 3}, {4, 3, 3} }, { {8, 3, 6}, {16, 3, 11}, {16, 3, 11}, {16, 3, 11} }, { {8, 64, 25}, {16, 64, 25}, {32, 64, 25}, {64, 64, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {64, 3, 41} }, /* 10 */{ {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 64, 6}, {16, 64, 11}, {32, 64, 21}, {32, 64, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, /* 20 */{ {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 3, 6}, {16, 3, 11}, {32, 3, 21}, {32, 3, 21} }, { {8, 64, 25}, {16, 64, 41}, {32, 64, 81}, {64, 64, 120} } }; /* Derived configuration for each write queue for each max request size. */ static const struct arb_line write_arb_data[NUM_WR_Q][MAX_WR_ORD + 1] = { /* 1 */ { {4, 6, 3}, {4, 6, 3}, {4, 6, 3} }, { {4, 2, 3}, {4, 2, 3}, {4, 2, 3} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 2, 6}, {16, 2, 11}, {32, 2, 21} }, { {8, 64, 25}, {16, 64, 25}, {32, 64, 25} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, { {8, 2, 6}, {16, 2, 11}, {16, 2, 11} }, /* 10 */{ {8, 9, 6}, {16, 9, 11}, {32, 9, 21} }, { {8, 47, 19}, {16, 47, 19}, {32, 47, 21} }, { {8, 9, 6}, {16, 9, 11}, {16, 9, 11} }, { {8, 64, 25}, {16, 64, 41}, {32, 64, 81} } }; /* Register addresses for read queues. */ static const struct arb_line read_arb_addr[NUM_RD_Q-1] = { /* 1 */ {PXP2_REG_RQ_BW_RD_L0, PXP2_REG_RQ_BW_RD_ADD0, PXP2_REG_RQ_BW_RD_UBOUND0}, {PXP2_REG_PSWRQ_BW_L1, PXP2_REG_PSWRQ_BW_ADD1, PXP2_REG_PSWRQ_BW_UB1}, {PXP2_REG_PSWRQ_BW_L2, PXP2_REG_PSWRQ_BW_ADD2, PXP2_REG_PSWRQ_BW_UB2}, {PXP2_REG_PSWRQ_BW_L3, PXP2_REG_PSWRQ_BW_ADD3, PXP2_REG_PSWRQ_BW_UB3}, {PXP2_REG_RQ_BW_RD_L4, PXP2_REG_RQ_BW_RD_ADD4, PXP2_REG_RQ_BW_RD_UBOUND4}, {PXP2_REG_RQ_BW_RD_L5, PXP2_REG_RQ_BW_RD_ADD5, PXP2_REG_RQ_BW_RD_UBOUND5}, {PXP2_REG_PSWRQ_BW_L6, PXP2_REG_PSWRQ_BW_ADD6, PXP2_REG_PSWRQ_BW_UB6}, {PXP2_REG_PSWRQ_BW_L7, PXP2_REG_PSWRQ_BW_ADD7, PXP2_REG_PSWRQ_BW_UB7}, {PXP2_REG_PSWRQ_BW_L8, PXP2_REG_PSWRQ_BW_ADD8, PXP2_REG_PSWRQ_BW_UB8}, /* 10 */{PXP2_REG_PSWRQ_BW_L9, PXP2_REG_PSWRQ_BW_ADD9, PXP2_REG_PSWRQ_BW_UB9}, {PXP2_REG_PSWRQ_BW_L10, PXP2_REG_PSWRQ_BW_ADD10, PXP2_REG_PSWRQ_BW_UB10}, {PXP2_REG_PSWRQ_BW_L11, PXP2_REG_PSWRQ_BW_ADD11, PXP2_REG_PSWRQ_BW_UB11}, {PXP2_REG_RQ_BW_RD_L12, PXP2_REG_RQ_BW_RD_ADD12, PXP2_REG_RQ_BW_RD_UBOUND12}, {PXP2_REG_RQ_BW_RD_L13, PXP2_REG_RQ_BW_RD_ADD13, PXP2_REG_RQ_BW_RD_UBOUND13}, {PXP2_REG_RQ_BW_RD_L14, PXP2_REG_RQ_BW_RD_ADD14, PXP2_REG_RQ_BW_RD_UBOUND14}, {PXP2_REG_RQ_BW_RD_L15, PXP2_REG_RQ_BW_RD_ADD15, PXP2_REG_RQ_BW_RD_UBOUND15}, {PXP2_REG_RQ_BW_RD_L16, PXP2_REG_RQ_BW_RD_ADD16, PXP2_REG_RQ_BW_RD_UBOUND16}, {PXP2_REG_RQ_BW_RD_L17, PXP2_REG_RQ_BW_RD_ADD17, PXP2_REG_RQ_BW_RD_UBOUND17}, {PXP2_REG_RQ_BW_RD_L18, PXP2_REG_RQ_BW_RD_ADD18, PXP2_REG_RQ_BW_RD_UBOUND18}, /* 20 */{PXP2_REG_RQ_BW_RD_L19, PXP2_REG_RQ_BW_RD_ADD19, PXP2_REG_RQ_BW_RD_UBOUND19}, {PXP2_REG_RQ_BW_RD_L20, PXP2_REG_RQ_BW_RD_ADD20, PXP2_REG_RQ_BW_RD_UBOUND20}, {PXP2_REG_RQ_BW_RD_L22, PXP2_REG_RQ_BW_RD_ADD22, PXP2_REG_RQ_BW_RD_UBOUND22}, {PXP2_REG_RQ_BW_RD_L23, PXP2_REG_RQ_BW_RD_ADD23, PXP2_REG_RQ_BW_RD_UBOUND23}, {PXP2_REG_RQ_BW_RD_L24, PXP2_REG_RQ_BW_RD_ADD24, PXP2_REG_RQ_BW_RD_UBOUND24}, {PXP2_REG_RQ_BW_RD_L25, PXP2_REG_RQ_BW_RD_ADD25, PXP2_REG_RQ_BW_RD_UBOUND25}, {PXP2_REG_RQ_BW_RD_L26, PXP2_REG_RQ_BW_RD_ADD26, PXP2_REG_RQ_BW_RD_UBOUND26}, {PXP2_REG_RQ_BW_RD_L27, PXP2_REG_RQ_BW_RD_ADD27, PXP2_REG_RQ_BW_RD_UBOUND27}, {PXP2_REG_PSWRQ_BW_L28, PXP2_REG_PSWRQ_BW_ADD28, PXP2_REG_PSWRQ_BW_UB28} }; /* Register addresses for write queues. */ static const struct arb_line write_arb_addr[NUM_WR_Q-1] = { /* 1 */ {PXP2_REG_PSWRQ_BW_L1, PXP2_REG_PSWRQ_BW_ADD1, PXP2_REG_PSWRQ_BW_UB1}, {PXP2_REG_PSWRQ_BW_L2, PXP2_REG_PSWRQ_BW_ADD2, PXP2_REG_PSWRQ_BW_UB2}, {PXP2_REG_PSWRQ_BW_L3, PXP2_REG_PSWRQ_BW_ADD3, PXP2_REG_PSWRQ_BW_UB3}, {PXP2_REG_PSWRQ_BW_L6, PXP2_REG_PSWRQ_BW_ADD6, PXP2_REG_PSWRQ_BW_UB6}, {PXP2_REG_PSWRQ_BW_L7, PXP2_REG_PSWRQ_BW_ADD7, PXP2_REG_PSWRQ_BW_UB7}, {PXP2_REG_PSWRQ_BW_L8, PXP2_REG_PSWRQ_BW_ADD8, PXP2_REG_PSWRQ_BW_UB8}, {PXP2_REG_PSWRQ_BW_L9, PXP2_REG_PSWRQ_BW_ADD9, PXP2_REG_PSWRQ_BW_UB9}, {PXP2_REG_PSWRQ_BW_L10, PXP2_REG_PSWRQ_BW_ADD10, PXP2_REG_PSWRQ_BW_UB10}, {PXP2_REG_PSWRQ_BW_L11, PXP2_REG_PSWRQ_BW_ADD11, PXP2_REG_PSWRQ_BW_UB11}, /* 10 */{PXP2_REG_PSWRQ_BW_L28, PXP2_REG_PSWRQ_BW_ADD28, PXP2_REG_PSWRQ_BW_UB28}, {PXP2_REG_RQ_BW_WR_L29, PXP2_REG_RQ_BW_WR_ADD29, PXP2_REG_RQ_BW_WR_UBOUND29}, {PXP2_REG_RQ_BW_WR_L30, PXP2_REG_RQ_BW_WR_ADD30, PXP2_REG_RQ_BW_WR_UBOUND30} }; static void bxe_init_pxp_arb(struct bxe_softc *sc, int r_order, int w_order) { uint32_t val, i; if (r_order > MAX_RD_ORD) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read order of %d order adjusted to %d\n", __FUNCTION__, r_order, MAX_RD_ORD); r_order = MAX_RD_ORD; } if (w_order > MAX_WR_ORD) { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Write order of %d order adjusted to %d\n", __FUNCTION__, w_order, MAX_WR_ORD); w_order = MAX_WR_ORD; } DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read order %d, write order %d\n", __FUNCTION__, r_order, w_order); for (i = 0; i < NUM_RD_Q - 1; i++) { REG_WR(sc, read_arb_addr[i].l, read_arb_data[i][r_order].l); REG_WR(sc, read_arb_addr[i].add, read_arb_data[i][r_order].add); REG_WR(sc, read_arb_addr[i].ubound, read_arb_data[i][r_order].ubound); } for (i = 0; i < NUM_WR_Q - 1; i++) { if ((write_arb_addr[i].l == PXP2_REG_RQ_BW_WR_L29) || (write_arb_addr[i].l == PXP2_REG_RQ_BW_WR_L30)) { REG_WR(sc, write_arb_addr[i].l, write_arb_data[i][w_order].l); REG_WR(sc, write_arb_addr[i].add, write_arb_data[i][w_order].add); REG_WR(sc, write_arb_addr[i].ubound, write_arb_data[i][w_order].ubound); } else { val = REG_RD(sc, write_arb_addr[i].l); REG_WR(sc, write_arb_addr[i].l, val | (write_arb_data[i][w_order].l << 10)); val = REG_RD(sc, write_arb_addr[i].add); REG_WR(sc, write_arb_addr[i].add, val | (write_arb_data[i][w_order].add << 10)); val = REG_RD(sc, write_arb_addr[i].ubound); REG_WR(sc, write_arb_addr[i].ubound, val | (write_arb_data[i][w_order].ubound << 7)); } } val = write_arb_data[NUM_WR_Q - 1][w_order].add; val += write_arb_data[NUM_WR_Q - 1][w_order].ubound << 10; val += write_arb_data[NUM_WR_Q - 1][w_order].l << 17; REG_WR(sc, PXP2_REG_PSWRQ_BW_RD, val); val = read_arb_data[NUM_RD_Q - 1][r_order].add; val += read_arb_data[NUM_RD_Q - 1][r_order].ubound << 10; val += read_arb_data[NUM_RD_Q - 1][r_order].l << 17; REG_WR(sc, PXP2_REG_PSWRQ_BW_WR, val); REG_WR(sc, PXP2_REG_RQ_WR_MBS0, w_order); REG_WR(sc, PXP2_REG_RQ_WR_MBS1, w_order); REG_WR(sc, PXP2_REG_RQ_RD_MBS0, r_order); REG_WR(sc, PXP2_REG_RQ_RD_MBS1, r_order); if (r_order == MAX_RD_ORD) REG_WR(sc, PXP2_REG_RQ_PDR_LIMIT, 0xe00); REG_WR(sc, PXP2_REG_WR_USDMDP_TH, (0x18 << w_order)); if (CHIP_IS_E1H(sc)) { /* MPS w_order optimal TH presently TH * 128 0 0 2 * 256 1 1 3 * >=512 2 2 3 */ val = ((w_order == 0) ? 2 : 3); REG_WR(sc, PXP2_REG_WR_HC_MPS, val); REG_WR(sc, PXP2_REG_WR_USDM_MPS, val); REG_WR(sc, PXP2_REG_WR_CSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_TSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_XSDM_MPS, val); REG_WR(sc, PXP2_REG_WR_QM_MPS, val); REG_WR(sc, PXP2_REG_WR_TM_MPS, val); REG_WR(sc, PXP2_REG_WR_SRC_MPS, val); REG_WR(sc, PXP2_REG_WR_DBG_MPS, val); REG_WR(sc, PXP2_REG_WR_DMAE_MPS, 2); /* DMAE is special */ REG_WR(sc, PXP2_REG_WR_CDU_MPS, val); } } static void bxe_init_pxp(struct bxe_softc *sc) { uint16_t devctl; int r_order, w_order; devctl = pci_read_config(sc->dev, sc->pcie_cap + PCI_EXP_DEVCTL, 2); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Read 0x%x from devctl\n", __FUNCTION__, devctl); w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); if (sc->mrrs == -1) r_order = ((devctl & PCI_EXP_DEVCTL_READRQ) >> 12); else { DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Force MRRS read order to %d\n", __FUNCTION__, sc->mrrs); r_order = sc->mrrs; } bxe_init_pxp_arb(sc, r_order, w_order); } static void bxe_setup_fan_failure_detection(struct bxe_softc *sc) { uint32_t phy_type, val; int is_required, port; is_required = 0; if (NOMCP(sc)) return; val = SHMEM_RD(sc, dev_info.shared_hw_config.config2) & SHARED_HW_CFG_FAN_FAILURE_MASK; if (val == SHARED_HW_CFG_FAN_FAILURE_ENABLED) is_required = 1; /* * The fan failure mechanism is usually related to the PHY type since * the power consumption of the board is affected by the PHY. Currently, * fan is required for most designs with SFX7101, BCM8727 and BCM8481. */ else if (val == SHARED_HW_CFG_FAN_FAILURE_PHY_TYPE) for (port = PORT_0; port < PORT_MAX; port++) { phy_type = SHMEM_RD(sc, dev_info.port_hw_config[port].external_phy_config) & PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK; is_required |= ((phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101) || (phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) || (phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481)); } if (is_required == 0) return; /* Fan failure is indicated by SPIO 5. */ bxe_set_spio(sc, MISC_REGISTERS_SPIO_5, MISC_REGISTERS_SPIO_INPUT_HI_Z); /* Set to active low mode. */ val = REG_RD(sc, MISC_REG_SPIO_INT); val |= ((1 << MISC_REGISTERS_SPIO_5) << MISC_REGISTERS_SPIO_INT_OLD_SET_POS); REG_WR(sc, MISC_REG_SPIO_INT, val); /* Enable interrupt to signal the IGU. */ val = REG_RD(sc, MISC_REG_SPIO_EVENT_EN); val |= (1 << MISC_REGISTERS_SPIO_5); REG_WR(sc, MISC_REG_SPIO_EVENT_EN, val); } /* * Common initialization. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_common(struct bxe_softc *sc) { uint32_t val; int i, rc; rc = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); /* Reset all blocks within the chip except the BMAC. */ bxe_reset_common(sc); DELAY(30000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc); DELAY(30000); bxe_init_block(sc, MISC_BLOCK, COMMON_STAGE); if (CHIP_IS_E1H(sc)) REG_WR(sc, MISC_REG_E1HMF_MODE, IS_E1HMF(sc)); REG_WR(sc, MISC_REG_LCPLL_CTRL_REG_2, 0x100); DELAY(30000); REG_WR(sc, MISC_REG_LCPLL_CTRL_REG_2, 0x0); bxe_init_block(sc, PXP_BLOCK, COMMON_STAGE); if (CHIP_IS_E1(sc)) { /* * Enable HW interrupt from PXP on USDM overflow * bit 16 on INT_MASK_0. */ REG_WR(sc, PXP_REG_PXP_INT_MASK_0, 0); } bxe_init_block(sc, PXP2_BLOCK, COMMON_STAGE); bxe_init_pxp(sc); #ifdef __BIG_ENDIAN REG_WR(sc, PXP2_REG_RQ_QM_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_TM_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_SRC_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_CDU_ENDIAN_M, 1); REG_WR(sc, PXP2_REG_RQ_DBG_ENDIAN_M, 1); /* Make sure this value is 0. */ REG_WR(sc, PXP2_REG_RQ_HC_ENDIAN_M, 0); REG_WR(sc, PXP2_REG_RD_QM_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_TM_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_SRC_SWAP_MODE, 1); REG_WR(sc, PXP2_REG_RD_CDURD_SWAP_MODE, 1); #endif REG_WR(sc, PXP2_REG_RQ_CDU_P_SIZE, 2); /* Let the HW do it's magic ... */ DELAY(100000); /* Finish the PXP initialization. */ val = REG_RD(sc, PXP2_REG_RQ_CFG_DONE); if (val != 1) { BXE_PRINTF("%s(%d): PXP2 CFG failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = REG_RD(sc, PXP2_REG_RD_INIT_DONE); if (val != 1) { BXE_PRINTF("%s(%d): PXP2 RD_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } REG_WR(sc, PXP2_REG_RQ_DISABLE_INPUTS, 0); REG_WR(sc, PXP2_REG_RD_DISABLE_INPUTS, 0); bxe_init_block(sc, DMAE_BLOCK, COMMON_STAGE); sc->dmae_ready = 1; bxe_init_fill(sc, TSEM_REG_PRAM, 0, 8); bxe_init_block(sc, TCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, UCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CCM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XCM_BLOCK, COMMON_STAGE); bxe_read_dmae(sc, XSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, CSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, TSEM_REG_PASSIVE_BUFFER, 3); bxe_read_dmae(sc, USEM_REG_PASSIVE_BUFFER, 3); bxe_init_block(sc, QM_BLOCK, COMMON_STAGE); /* Soft reset pulse. */ REG_WR(sc, QM_REG_SOFT_RESET, 1); REG_WR(sc, QM_REG_SOFT_RESET, 0); bxe_init_block(sc, DQ_BLOCK, COMMON_STAGE); REG_WR(sc, DORQ_REG_DPM_CID_OFST, BCM_PAGE_SHIFT); REG_WR(sc, DORQ_REG_DORQ_INT_MASK, 0); bxe_init_block(sc, BRB1_BLOCK, COMMON_STAGE); bxe_init_block(sc, PRS_BLOCK, COMMON_STAGE); REG_WR(sc, PRS_REG_A_PRSU_20, 0xf); if (CHIP_IS_E1H(sc)) REG_WR(sc, PRS_REG_E1HOV_MODE, IS_E1HMF(sc)); bxe_init_block(sc, TSDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CSDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, USDM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XSDM_BLOCK, COMMON_STAGE); /* Clear STORM processor memory. */ bxe_init_fill(sc, TSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, USEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, CSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_fill(sc, XSEM_REG_FAST_MEMORY, 0, STORM_INTMEM_SIZE(sc)); bxe_init_block(sc, TSEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, USEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, CSEM_BLOCK, COMMON_STAGE); bxe_init_block(sc, XSEM_BLOCK, COMMON_STAGE); /* Sync semi rtc. */ REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x80000000); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x80000000); bxe_init_block(sc, UPB_BLOCK, COMMON_STAGE); bxe_init_block(sc, XPB_BLOCK, COMMON_STAGE); bxe_init_block(sc, PBF_BLOCK, COMMON_STAGE); REG_WR(sc, SRC_REG_SOFT_RST, 1); /* Setup RSS/multi-queue hasking keys. */ for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) REG_WR(sc, i, 0xc0cac01a); bxe_init_block(sc, SRCH_BLOCK, COMMON_STAGE); REG_WR(sc, SRC_REG_SOFT_RST, 0); /* Make sure the cdu_context structure has the right size. */ if (sizeof(union cdu_context) != 1024) { BXE_PRINTF("%s(%d): Invalid size for context (%ld != 1024)!\n", __FILE__, __LINE__, (long)sizeof(union cdu_context)); rc = EBUSY; goto bxe_init_common_exit; } bxe_init_block(sc, CDU_BLOCK, COMMON_STAGE); /* * val = (num_context_in_page << 24) + * (context_waste_size << 12) + * context_line_size. */ val = (4 << 24) + (0 << 12) + 1024; REG_WR(sc, CDU_REG_CDU_GLOBAL_PARAMS, val); bxe_init_block(sc, CFC_BLOCK, COMMON_STAGE); REG_WR(sc, CFC_REG_INIT_REG, 0x7FF); /* Enable context validation interrupt from CFC. */ REG_WR(sc, CFC_REG_CFC_INT_MASK, 0); /* Set the thresholds to prevent CFC/CDU race. */ REG_WR(sc, CFC_REG_DEBUG0, 0x20020000); bxe_init_block(sc, HC_BLOCK, COMMON_STAGE); bxe_init_block(sc, MISC_AEU_BLOCK, COMMON_STAGE); bxe_init_block(sc, PXPCS_BLOCK, COMMON_STAGE); /* Clear PCIe block debug status bits. */ REG_WR(sc, 0x2814, 0xffffffff); REG_WR(sc, 0x3820, 0xffffffff); bxe_init_block(sc, EMAC0_BLOCK, COMMON_STAGE); bxe_init_block(sc, EMAC1_BLOCK, COMMON_STAGE); bxe_init_block(sc, DBU_BLOCK, COMMON_STAGE); bxe_init_block(sc, DBG_BLOCK, COMMON_STAGE); bxe_init_block(sc, NIG_BLOCK, COMMON_STAGE); if (CHIP_IS_E1H(sc)) { REG_WR(sc, NIG_REG_LLH_MF_MODE, IS_E1HMF(sc)); REG_WR(sc, NIG_REG_LLH_E1HOV_MODE, IS_E1HOV(sc)); } /* Finish CFC initialization. */ val = bxe_reg_poll(sc, CFC_REG_LL_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC LL_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = bxe_reg_poll(sc, CFC_REG_AC_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC AC_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } val = bxe_reg_poll(sc, CFC_REG_CAM_INIT_DONE, 1, 100, 10); if (val != 1) { BXE_PRINTF("%s(%d): CFC CAM_INIT failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } REG_WR(sc, CFC_REG_DEBUG0, 0); /* Read NIG statistic and check for first load since powerup. */ bxe_read_dmae(sc, NIG_REG_STAT2_BRB_OCTET, 2); val = *BXE_SP(sc, wb_data[0]); /* Do internal memory self test only after a full power cycle. */ if ((CHIP_IS_E1(sc)) && (val == 0) && bxe_int_mem_test(sc)) { BXE_PRINTF("%s(%d): Internal memory self-test failed!\n", __FILE__, __LINE__); rc = EBUSY; goto bxe_init_common_exit; } /* Handle any board specific initialization. */ switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: break; default: break; } bxe_setup_fan_failure_detection(sc); /* Clear PXP2 attentions. */ REG_RD(sc, PXP2_REG_PXP2_INT_STS_CLR_0); bxe_enable_blocks_attention(sc); if (!NOMCP(sc)) { bxe_acquire_phy_lock(sc); bxe_common_init_phy(sc, sc->common.shmem_base); bxe_release_phy_lock(sc); } else BXE_PRINTF( "%s(%d): Bootcode is missing - cannot initialize PHY!\n", __FILE__, __LINE__); bxe_init_common_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Port initialization. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_port(struct bxe_softc *sc) { uint32_t val, low, high; uint32_t swap_val, swap_override, aeu_gpio_mask, offset; uint32_t reg_addr; int init_stage, port; port = BP_PORT(sc); init_stage = port ? PORT1_STAGE : PORT0_STAGE; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing port %d.\n", __FUNCTION__, port); REG_WR(sc, NIG_REG_MASK_INTERRUPT_PORT0 + port * 4, 0); bxe_init_block(sc, PXP_BLOCK, init_stage); bxe_init_block(sc, PXP2_BLOCK, init_stage); bxe_init_block(sc, TCM_BLOCK, init_stage); bxe_init_block(sc, UCM_BLOCK, init_stage); bxe_init_block(sc, CCM_BLOCK, init_stage); bxe_init_block(sc, XCM_BLOCK, init_stage); bxe_init_block(sc, DQ_BLOCK, init_stage); bxe_init_block(sc, BRB1_BLOCK, init_stage); /* Determine the pause threshold for the BRB */ if (IS_E1HMF(sc)) low = (sc->bxe_flags & BXE_ONE_PORT_FLAG) ? 160 : 246; else if (sc->bxe_ifp->if_mtu > 4096) { if (sc->bxe_flags & BXE_ONE_PORT_FLAG) low = 160; else { val = sc->bxe_ifp->if_mtu; /* (24*1024 + val*4)/256 */ low = 96 + (val/64) + ((val % 64) ? 1 : 0); } } else low = (sc->bxe_flags & BXE_ONE_PORT_FLAG) ? 80 : 160; high = low + 56; /* 14 * 1024 / 256 */ REG_WR(sc, BRB1_REG_PAUSE_LOW_THRESHOLD_0 + port * 4, low); REG_WR(sc, BRB1_REG_PAUSE_HIGH_THRESHOLD_0 + port * 4, high); /* Port PRS comes here. */ bxe_init_block(sc, PRS_BLOCK, init_stage); bxe_init_block(sc, TSDM_BLOCK, init_stage); bxe_init_block(sc, CSDM_BLOCK, init_stage); bxe_init_block(sc, USDM_BLOCK, init_stage); bxe_init_block(sc, XSDM_BLOCK, init_stage); bxe_init_block(sc, TSEM_BLOCK, init_stage); bxe_init_block(sc, USEM_BLOCK, init_stage); bxe_init_block(sc, CSEM_BLOCK, init_stage); bxe_init_block(sc, XSEM_BLOCK, init_stage); bxe_init_block(sc, UPB_BLOCK, init_stage); bxe_init_block(sc, XPB_BLOCK, init_stage); bxe_init_block(sc, PBF_BLOCK, init_stage); /* Configure PBF to work without pause for MTU = 9000. */ REG_WR(sc, PBF_REG_P0_PAUSE_ENABLE + port * 4, 0); /* Update threshold. */ REG_WR(sc, PBF_REG_P0_ARB_THRSH + port * 4, (9040/16)); /* Update initial credit. */ REG_WR(sc, PBF_REG_P0_INIT_CRD + port * 4, (9040/16) + 553 - 22); /* Probe changes. */ REG_WR(sc, PBF_REG_INIT_P0 + port * 4, 1); DELAY(5000); REG_WR(sc, PBF_REG_INIT_P0 + port * 4, 0); bxe_init_block(sc, CDU_BLOCK, init_stage); bxe_init_block(sc, CFC_BLOCK, init_stage); if (CHIP_IS_E1(sc)) { REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); } bxe_init_block(sc, HC_BLOCK, init_stage); bxe_init_block(sc, MISC_AEU_BLOCK, init_stage); /* * init aeu_mask_attn_func_0/1: * - SF mode: bits 3-7 are masked. only bits 0-2 are in use * - MF mode: bit 3 is masked. bits 0-2 are in use as in SF * bits 4-7 are used for "per vn group attention" */ REG_WR(sc, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port * 4, (IS_E1HMF(sc) ? 0xF7 : 0x7)); bxe_init_block(sc, PXPCS_BLOCK, init_stage); bxe_init_block(sc, EMAC0_BLOCK, init_stage); bxe_init_block(sc, EMAC1_BLOCK, init_stage); bxe_init_block(sc, DBU_BLOCK, init_stage); bxe_init_block(sc, DBG_BLOCK, init_stage); bxe_init_block(sc, NIG_BLOCK, init_stage); REG_WR(sc, NIG_REG_XGXS_SERDES0_MODE_SEL + port * 4, 1); if (CHIP_IS_E1H(sc)) { /* Enable outer VLAN support if required. */ REG_WR(sc, NIG_REG_LLH0_BRB1_DRV_MASK_MF + port * 4, (IS_E1HOV(sc) ? 0x1 : 0x2)); } REG_WR(sc, NIG_REG_LLFC_ENABLE_0 + port * 4, 0); REG_WR(sc, NIG_REG_LLFC_OUT_EN_0 + port * 4, 0); REG_WR(sc, NIG_REG_PAUSE_ENABLE_0 + port * 4, 1); bxe_init_block(sc, MCP_BLOCK, init_stage); bxe_init_block(sc, DMAE_BLOCK, init_stage); switch (XGXS_EXT_PHY_TYPE(sc->link_params.ext_phy_config)) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: bxe_set_gpio(sc, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INPUT_HI_Z, port); /* * The GPIO should be swapped if the swap register is * set and active. */ swap_val = REG_RD(sc, NIG_REG_PORT_SWAP); swap_override = REG_RD(sc, NIG_REG_STRAP_OVERRIDE); /* Select function upon port-swap configuration. */ if (port == 0) { offset = MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; aeu_gpio_mask = (swap_val && swap_override) ? AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1 : AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0; } else { offset = MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0; aeu_gpio_mask = (swap_val && swap_override) ? AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 : AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1; } val = REG_RD(sc, offset); /* Add GPIO3 to group. */ val |= aeu_gpio_mask; REG_WR(sc, offset, val); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: /* Add SPIO 5 to group 0. */ reg_addr = port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0; val = REG_RD(sc, reg_addr); val |= AEU_INPUTS_ATTN_BITS_SPIO5; REG_WR(sc, reg_addr, val); break; default: break; } bxe__link_reset(sc); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (0); } #define ILT_PER_FUNC (768/2) #define FUNC_ILT_BASE(func) (func * ILT_PER_FUNC) /* * The phys address is shifted right 12 bits and has an added 1=valid * bit added to the 53rd bit (bit 52) then since this is a wide * register(TM) we split it into two 32 bit writes. */ #define ONCHIP_ADDR1(x) ((uint32_t)(((uint64_t)x >> 12) & 0xFFFFFFFF)) #define ONCHIP_ADDR2(x) ((uint32_t)((1 << 20) | ((uint64_t)x >> 44))) #define PXP_ONE_ILT(x) (((x) << 10) | x) #define PXP_ILT_RANGE(f, l) (((l) << 10) | f) #define CNIC_ILT_LINES 0 /* * ILT write. * * Returns: * None. */ static void bxe_ilt_wr(struct bxe_softc *sc, uint32_t index, bus_addr_t addr) { int reg; DBENTER(BXE_INSANE_LOAD | BXE_INSANE_RESET); if (CHIP_IS_E1H(sc)) reg = PXP2_REG_RQ_ONCHIP_AT_B0 + index * 8; else reg = PXP2_REG_RQ_ONCHIP_AT + index * 8; bxe_wb_wr(sc, reg, ONCHIP_ADDR1(addr), ONCHIP_ADDR2(addr)); DBEXIT(BXE_INSANE_LOAD | BXE_INSANE_RESET); } /* * Initialize a function. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_init_func(struct bxe_softc *sc) { uint32_t addr, val; int func, i, port; port = BP_PORT(sc); func = BP_FUNC(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBPRINT(sc, (BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET), "%s(): Initializing port %d, function %d.\n", __FUNCTION__, port, func); /* Set MSI reconfigure capability. */ addr = (port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0); val = REG_RD(sc, addr); val |= HC_CONFIG_0_REG_MSI_ATTN_EN_0; REG_WR(sc, addr, val); i = FUNC_ILT_BASE(func); bxe_ilt_wr(sc, i, BXE_SP_MAPPING(sc, context)); if (CHIP_IS_E1H(sc)) { REG_WR(sc, PXP2_REG_RQ_CDU_FIRST_ILT, i); REG_WR(sc, PXP2_REG_RQ_CDU_LAST_ILT, i + CNIC_ILT_LINES); } else /* E1 */ REG_WR(sc, PXP2_REG_PSWRQ_CDU0_L2P + func * 4, PXP_ILT_RANGE(i, i + CNIC_ILT_LINES)); if (CHIP_IS_E1H(sc)) { bxe_init_block(sc, MISC_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, TCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, UCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, CCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, XCM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, TSEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, USEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, CSEM_BLOCK, FUNC0_STAGE + func); bxe_init_block(sc, XSEM_BLOCK, FUNC0_STAGE + func); REG_WR(sc, NIG_REG_LLH0_FUNC_EN + port * 8, 1); REG_WR(sc, NIG_REG_LLH0_FUNC_VLAN_ID + port * 8, sc->e1hov); } /* Host Coalescing initialization per function. */ if (CHIP_IS_E1H(sc)) { REG_WR(sc, MISC_REG_AEU_GENERAL_ATTN_12 + func * 4, 0); REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); } bxe_init_block(sc, HC_BLOCK, FUNC0_STAGE + func); /* Reset PCIe block debug values. */ REG_WR(sc, 0x2114, 0xffffffff); REG_WR(sc, 0x2120, 0xffffffff); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (0); } /* * * Returns: * 0 = Failure, !0 = Failure. */ static int bxe_init_hw(struct bxe_softc *sc, uint32_t load_code) { int func, i, rc; rc = 0; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); sc->dmae_ready = 0; switch (load_code) { case FW_MSG_CODE_DRV_LOAD_COMMON: rc = bxe_init_common(sc); if (rc) goto bxe_init_hw_exit; /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_PORT: sc->dmae_ready = 1; rc = bxe_init_port(sc); if (rc) goto bxe_init_hw_exit; /* FALLTHROUGH */ case FW_MSG_CODE_DRV_LOAD_FUNCTION: sc->dmae_ready = 1; rc = bxe_init_func(sc); if (rc) goto bxe_init_hw_exit; break; default: DBPRINT(sc, BXE_WARN, "%s(): Unknown load_code (0x%08X) from MCP!\n", __FUNCTION__, load_code); break; } /* Fetch additional config data if the bootcode is running. */ if (!NOMCP(sc)) { func = BP_FUNC(sc); /* Fetch the pulse sequence number. */ sc->fw_drv_pulse_wr_seq = (SHMEM_RD(sc, func_mb[func].drv_pulse_mb) & DRV_PULSE_SEQ_MASK); } /* Clear the default status block. */ bxe_zero_def_sb(sc); for (i = 0; i < sc->num_queues; i++) bxe_zero_sb(sc, BP_L_ID(sc) + i); bxe_init_hw_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Send a firmware command and wait for the response. * * Post a command to shared memory for the bootcode running on the MCP and * stall until the bootcode responds or a timeout occurs. * * Returns: * 0 = Failure, otherwise firmware response code (FW_MSG_CODE_*). */ static int bxe_fw_command(struct bxe_softc *sc, uint32_t command) { uint32_t cnt, rc, seq; int func; func = BP_FUNC(sc); seq = ++sc->fw_seq; rc = 0; cnt = 1; DBRUNMSG(BXE_VERBOSE, bxe_decode_mb_msgs(sc, (command | seq), 0)); BXE_FWMB_LOCK(sc); /* Write the command to the shared memory mailbox. */ SHMEM_WR(sc, func_mb[func].drv_mb_header, (command | seq)); /* Wait up to 2 seconds for a response. */ do { /* Wait 10ms for a response. */ DELAY(10000); /* Pickup the response. */ rc = SHMEM_RD(sc, func_mb[func].fw_mb_header); } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 400)); DBRUNMSG(BXE_VERBOSE, bxe_decode_mb_msgs(sc, 0, rc)); /* Make sure we read the right response. */ if (seq == (rc & FW_MSG_SEQ_NUMBER_MASK )) rc &= FW_MSG_CODE_MASK; else { BXE_PRINTF("%s(%d): Bootcode failed to respond!\n", __FILE__, __LINE__); DBRUN(bxe_dump_fw(sc)); rc = 0; } BXE_FWMB_UNLOCK(sc); return (rc); } /* * Allocate a block of memory and map it for DMA. No partial * completions allowed, release any resources acquired if we * can't acquire all resources. * * Returns: * 0 = Success, !0 = Failure * * Modifies: * dma->paddr * dma->vaddr * dma->tag * dma->map * dma->size * */ static int bxe_dma_malloc(struct bxe_softc *sc, bus_size_t size, struct bxe_dma *dma, int mapflags, const char *msg) { int rc; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); DBRUNIF(dma->size > 0, BXE_PRINTF("%s(): Called for %s with size > 0 (%05d)!\n", __FUNCTION__, msg, (int) dma->size)); rc = bus_dma_tag_create( sc->parent_tag, /* parent */ BCM_PAGE_SIZE, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, NULL, /* filter f(), arg */ size, /* max size for this tag */ 1, /* # of discontinuities */ size, /* max seg size */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lock f(), arg */ &dma->tag); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dma_tag_create() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_create; } rc = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, BUS_DMA_NOWAIT, &dma->map); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dmamem_alloc() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_alloc; } rc = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, bxe_dma_map_addr, &dma->paddr, mapflags | BUS_DMA_NOWAIT); if (rc != 0) { BXE_PRINTF("%s(%d): bus_dmamap_load() " "failed (rc = %d) for %s!\n", __FILE__, __LINE__, rc, msg); goto bxe_dma_malloc_fail_load; } dma->size = size; DBPRINT(sc, BXE_VERBOSE, "%s(): size=%06d, vaddr=0x%p, " "paddr=0x%jX - %s\n", __FUNCTION__, (int) dma->size, dma->vaddr, (uintmax_t) dma->paddr, msg); goto bxe_dma_malloc_exit; bxe_dma_malloc_fail_load: bus_dmamem_free(dma->tag, dma->vaddr, dma->map); bxe_dma_malloc_fail_alloc: bus_dma_tag_destroy(dma->tag); dma->vaddr = NULL; bxe_dma_malloc_fail_create: dma->map = NULL; dma->tag = NULL; dma->size = 0; bxe_dma_malloc_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); return (rc); } /* * Release a block of DMA memory associated tag/map. * * Returns: * None */ static void bxe_dma_free(struct bxe_softc *sc, struct bxe_dma *dma) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_UNLOAD); if (dma->size > 0) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, dma->vaddr, dma->map); bus_dma_tag_destroy(dma->tag); dma->size = 0; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_UNLOAD); } /* * Free any DMA memory owned by the driver. * * Scans through each data structre that requires DMA memory and frees * the memory if allocated. * * Returns: * Nothing. */ static void bxe_host_structures_free(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i, j, max_agg_queues; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; if (sc->parent_tag == NULL) goto bxe_host_structures_free_exit; for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* Trust no one! */ if (fp == NULL) break; /* Status block. */ bxe_dma_free(sc, &fp->sb_dma); /* TX chain. */ bxe_dma_free(sc, &fp->tx_dma); fp->tx_chain = NULL; /* RX chain */ bxe_dma_free(sc, &fp->rx_dma); fp->rx_chain = NULL; /* RCQ chain */ bxe_dma_free(sc, &fp->rcq_dma); fp->rcq_chain = NULL; /* SG chain */ bxe_dma_free(sc, &fp->sg_dma); fp->sg_chain = NULL; /* Unload and destroy the TX mbuf maps. */ if (fp->tx_mbuf_tag != NULL) { for (j = 0; j < TOTAL_TX_BD; j++) { if (fp->tx_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); bus_dmamap_destroy( fp->tx_mbuf_tag, fp->tx_mbuf_map[j]); } } bus_dma_tag_destroy(fp->tx_mbuf_tag); } /* Unload and destroy the TPA pool mbuf maps. */ if (fp->rx_mbuf_tag != NULL) { if (fp->tpa_mbuf_spare_map != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->tpa_mbuf_spare_map); } for (j = 0; j < max_agg_queues; j++) { if (fp->tpa_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->tpa_mbuf_map[j]); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->tpa_mbuf_map[j]); } } } /* Unload and destroy the SGE Buf maps. */ if (fp->rx_sge_buf_tag != NULL) { if (fp->rx_sge_spare_map != NULL) { bus_dmamap_unload( fp->rx_sge_buf_tag, fp->rx_sge_spare_map); bus_dmamap_destroy( fp->rx_sge_buf_tag, fp->rx_sge_spare_map); } for (j = 0; j < TOTAL_RX_SGE; j++) { if (fp->rx_sge_buf_map[j] != NULL) { bus_dmamap_unload( fp->rx_sge_buf_tag, fp->rx_sge_buf_map[j]); bus_dmamap_destroy( fp->rx_sge_buf_tag, fp->rx_sge_buf_map[j]); } } bus_dma_tag_destroy(fp->rx_sge_buf_tag); } /* Unload and destroy the RX mbuf maps. */ if (fp->rx_mbuf_tag != NULL) { if (fp->rx_mbuf_spare_map != NULL) { bus_dmamap_unload(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map); bus_dmamap_destroy(fp->rx_mbuf_tag, fp->rx_mbuf_spare_map); } for (j = 0; j < TOTAL_RX_BD; j++) { if (fp->rx_mbuf_map[j] != NULL) { bus_dmamap_unload( fp->rx_mbuf_tag, fp->rx_mbuf_map[j]); bus_dmamap_destroy( fp->rx_mbuf_tag, fp->rx_mbuf_map[j]); } } bus_dma_tag_destroy(fp->rx_mbuf_tag); } } /* Destroy the default status block */ bxe_dma_free(sc, &sc->def_sb_dma); sc->def_sb = NULL; /* Destroy the statistics block */ bxe_dma_free(sc, &sc->stats_dma); sc->stats = NULL; /* Destroy the slowpath block. */ bxe_dma_free(sc, &sc->slowpath_dma); sc->slowpath = NULL; /* Destroy the slowpath queue. */ bxe_dma_free(sc, &sc->spq_dma); sc->spq = NULL; /* Destroy the slowpath queue. */ bxe_dma_free(sc, &sc->gz_dma); sc->gz = NULL; free(sc->strm, M_DEVBUF); sc->strm = NULL; bxe_host_structures_free_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Get DMA memory from the OS. * * Validates that the OS has provided DMA buffers in response to a * bus_dmamap_load call and saves the physical address of those buffers. * When the callback is used the OS will return 0 for the mapping function * (bus_dmamap_load) so we use the value of map_arg->maxsegs to pass any * failures back to the caller. * * Returns: * Nothing. */ static void bxe_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *busaddr; busaddr = arg; /* Check for an error and signal the caller that an error occurred. */ if (error) { printf( "bxe %s(%d): DMA mapping error (error = %d, nseg = %d)!\n", __FILE__, __LINE__, error, nseg); *busaddr = 0; return; } *busaddr = segs->ds_addr; } /* * Allocate any non-paged DMA memory needed by the driver. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_host_structures_alloc(device_t dev) { struct bxe_softc *sc; struct bxe_fastpath *fp; int rc; bus_addr_t busaddr; bus_size_t max_size, max_seg_size; int i, j, max_segments; sc = device_get_softc(dev); DBENTER(BXE_VERBOSE_RESET); rc = 0; int max_agg_queues = CHIP_IS_E1H(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1H : ETH_MAX_AGGREGATION_QUEUES_E1; /* * Allocate the parent bus DMA tag appropriate for PCI. */ - rc = bus_dma_tag_create(NULL, /* parent tag */ + rc = bus_dma_tag_create( + bus_get_dma_tag(dev), /* PCI parent tag */ 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ MAXBSIZE, /* max map for this tag */ BUS_SPACE_UNRESTRICTED, /* # of discontinuities */ BUS_SPACE_MAXSIZE_32BIT, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &sc->parent_tag); /* dma tag */ if (rc != 0) { BXE_PRINTF("%s(%d): Could not allocate parent DMA tag!\n", __FILE__, __LINE__); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Allocate DMA memory for each fastpath structure. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; /* * Allocate status block* */ rc = bxe_dma_malloc(sc, BXE_STATUS_BLK_SZ, &fp->sb_dma, BUS_DMA_NOWAIT, "fp status block"); /* ToDo: Only using 32 bytes out of 4KB allocation! */ if (rc != 0) goto bxe_host_structures_alloc_exit; fp->status_block = (struct host_status_block *) fp->sb_dma.vaddr; /* * Allocate TX chain. */ rc = bxe_dma_malloc(sc, BXE_TX_CHAIN_PAGE_SZ * NUM_TX_PAGES, &fp->tx_dma, BUS_DMA_NOWAIT, "tx chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->tx_chain = (union eth_tx_bd_types *) fp->tx_dma.vaddr; /* Link the TX chain pages. */ for (j = 1; j <= NUM_TX_PAGES; j++) { struct eth_tx_next_bd *tx_n_bd = &fp->tx_chain[TOTAL_TX_BD_PER_PAGE * j - 1].next_bd; busaddr = fp->tx_dma.paddr + BCM_PAGE_SIZE * (j % NUM_TX_PAGES); tx_n_bd->addr_hi = htole32(U64_HI(busaddr)); tx_n_bd->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate RX chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RX_PAGES, &fp->rx_dma, BUS_DMA_NOWAIT, "rx chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->rx_chain = (struct eth_rx_bd *) fp->rx_dma.vaddr; /* Link the RX chain pages. */ for (j = 1; j <= NUM_RX_PAGES; j++) { struct eth_rx_bd *rx_bd = &fp->rx_chain[TOTAL_RX_BD_PER_PAGE * j - 2]; busaddr = fp->rx_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RX_PAGES); rx_bd->addr_hi = htole32(U64_HI(busaddr)); rx_bd->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate CQ chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RCQ_PAGES, &fp->rcq_dma, BUS_DMA_NOWAIT, "rcq chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->rcq_chain = (union eth_rx_cqe *) fp->rcq_dma.vaddr; /* Link the CQ chain pages. */ for (j = 1; j <= NUM_RCQ_PAGES; j++) { struct eth_rx_cqe_next_page *nextpg = (struct eth_rx_cqe_next_page *) &fp->rcq_chain[TOTAL_RCQ_ENTRIES_PER_PAGE * j - 1]; busaddr = fp->rcq_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RCQ_PAGES); nextpg->addr_hi = htole32(U64_HI(busaddr)); nextpg->addr_lo = htole32(U64_LO(busaddr)); } /* * Allocate SG chain. */ rc = bxe_dma_malloc(sc, BXE_RX_CHAIN_PAGE_SZ * NUM_RX_SGE_PAGES, &fp->sg_dma, BUS_DMA_NOWAIT, "sg chain pages"); if (rc != 0) goto bxe_host_structures_alloc_exit; fp->sg_chain = (struct eth_rx_sge *) fp->sg_dma.vaddr; /* Link the SG chain pages. */ for (j = 1; j <= NUM_RX_SGE_PAGES; j++) { struct eth_rx_sge *nextpg = &fp->sg_chain[TOTAL_RX_SGE_PER_PAGE * j - 2]; busaddr = fp->sg_dma.paddr + BCM_PAGE_SIZE * (j % NUM_RX_SGE_PAGES); nextpg->addr_hi = htole32(U64_HI(busaddr)); nextpg->addr_lo = htole32(U64_LO(busaddr)); } /* * Check required size before mapping to conserve resources. */ if (sc->tso_enable == TRUE) { max_size = BXE_TSO_MAX_SIZE; max_segments = BXE_TSO_MAX_SEGMENTS; max_seg_size = BXE_TSO_MAX_SEG_SIZE; } else { max_size = MCLBYTES * BXE_MAX_SEGMENTS; max_segments = BXE_MAX_SEGMENTS; max_seg_size = MCLBYTES; } /* Create a DMA tag for TX mbufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ max_size, /* max map for this tag */ max_segments, /* # of discontinuities */ max_seg_size, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &fp->tx_mbuf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%d] " "TX mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for each the TX mbuf cluster(ext buf). */ for (j = 0; j < TOTAL_TX_BD; j++) { if (bus_dmamap_create(fp->tx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tx_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tx_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* * Create a DMA tag for RX mbufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, /* alignment for segs */ BXE_DMA_BOUNDARY, /* cannot cross */ BUS_SPACE_MAXADDR, /* restricted low */ BUS_SPACE_MAXADDR, /* restricted hi */ NULL, /* filter f() */ NULL, /* filter f() arg */ MJUM9BYTES, /* max map for this tag */ 1, /* # of discontinuities */ MJUM9BYTES, /* max seg size */ 0, /* flags */ NULL, /* lock f() */ NULL, /* lock f() arg */ &fp->rx_mbuf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%02d] " "RX mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for the RX mbuf clusters. */ if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->rx_mbuf_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_mbuf_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < TOTAL_RX_BD; j++) { if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->rx_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* * Create a DMA tag for RX SGE bufs. */ if (bus_dma_tag_create(sc->parent_tag, 1, BXE_DMA_BOUNDARY, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL, NULL, &fp->rx_sge_buf_tag)) { BXE_PRINTF( "%s(%d): Could not allocate fp[%02d] " "RX SGE mbuf DMA tag!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } /* Create DMA maps for the SGE mbuf clusters. */ if (bus_dmamap_create(fp->rx_sge_buf_tag, BUS_DMA_NOWAIT, &fp->rx_sge_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_sge_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < TOTAL_RX_SGE; j++) { if (bus_dmamap_create(fp->rx_sge_buf_tag, BUS_DMA_NOWAIT, &fp->rx_sge_buf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "rx_sge_buf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } /* Create DMA maps for the TPA pool mbufs. */ if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tpa_mbuf_spare_map)) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tpa_mbuf_spare_map DMA map!\n", __FILE__, __LINE__, i); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } for (j = 0; j < max_agg_queues; j++) { if (bus_dmamap_create(fp->rx_mbuf_tag, BUS_DMA_NOWAIT, &fp->tpa_mbuf_map[j])) { BXE_PRINTF( "%s(%d): Unable to create fp[%02d]." "tpa_mbuf_map[%d] DMA map!\n", __FILE__, __LINE__, i, j); rc = ENOMEM; goto bxe_host_structures_alloc_exit; } } bxe_init_sge_ring_bit_mask(fp); } /* * Allocate default status block. */ rc = bxe_dma_malloc(sc, BXE_DEF_STATUS_BLK_SZ, &sc->def_sb_dma, BUS_DMA_NOWAIT, "default status block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->def_sb = (struct host_def_status_block *) sc->def_sb_dma.vaddr; /* * Allocate statistics block. */ rc = bxe_dma_malloc(sc, BXE_STATS_BLK_SZ, &sc->stats_dma, BUS_DMA_NOWAIT, "statistics block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->stats = (struct statistics_block *) sc->stats_dma.vaddr; /* * Allocate slowpath block. */ rc = bxe_dma_malloc(sc, BXE_SLOWPATH_SZ, &sc->slowpath_dma, BUS_DMA_NOWAIT, "slowpath block"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->slowpath = (struct bxe_slowpath *) sc->slowpath_dma.vaddr; /* * Allocate slowpath queue. */ rc = bxe_dma_malloc(sc, BXE_SPQ_SZ, &sc->spq_dma, BUS_DMA_NOWAIT, "slowpath queue"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->spq = (struct eth_spe *) sc->spq_dma.vaddr; /* * Allocate firmware decompression buffer. */ rc = bxe_dma_malloc(sc, BXE_FW_BUF_SIZE, &sc->gz_dma, BUS_DMA_NOWAIT, "gunzip buffer"); if (rc != 0) goto bxe_host_structures_alloc_exit; sc->gz = sc->gz_dma.vaddr; if (sc->strm == NULL) { goto bxe_host_structures_alloc_exit; } sc->strm = malloc(sizeof(*sc->strm), M_DEVBUF, M_NOWAIT); bxe_host_structures_alloc_exit: DBEXIT(BXE_VERBOSE_RESET); return (rc); } /* * Program the MAC address for 57710 controllers. * * Returns: * Nothing. */ static void bxe_set_mac_addr_e1(struct bxe_softc *sc, int set) { struct mac_configuration_cmd *config; struct mac_configuration_entry *config_table; uint8_t *eaddr; int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); config = BXE_SP(sc, mac_config); port = BP_PORT(sc); /* * CAM allocation: * Port 0 Unicast Addresses: 32 Perfect Match Filters (31-0) * Port 1 Unicast Addresses: 32 Perfect Match Filters (63-32) * Port 0 Multicast Addresses: 128 Hashes (127-64) * Port 1 Multicast Addresses: 128 Hashes (191-128) */ config->hdr.length = 2; config->hdr.offset = port ? 32 : 0; config->hdr.client_id = BP_CL_ID(sc); config->hdr.reserved1 = 0; /* Program the primary MAC address. */ config_table = &config->config_table[0]; eaddr = sc->link_params.mac_addr; config_table->cam_entry.msb_mac_addr = eaddr[0] << 8 | eaddr[1]; config_table->cam_entry.middle_mac_addr = eaddr[2] << 8 | eaddr[3]; config_table->cam_entry.lsb_mac_addr = eaddr[4] << 8 | eaddr[5]; config_table->cam_entry.flags = htole16(port); if (set) config_table->target_table_entry.flags = 0; else CAM_INVALIDATE(config_table); config_table->target_table_entry.vlan_id = 0; DBPRINT(sc, BXE_VERBOSE, "%s(): %s MAC (%04x:%04x:%04x)\n", __FUNCTION__, (set ? "Setting" : "Clearing"), config_table->cam_entry.msb_mac_addr, config_table->cam_entry.middle_mac_addr, config_table->cam_entry.lsb_mac_addr); /* Program the broadcast MAC address. */ config_table = &config->config_table[1]; config_table->cam_entry.msb_mac_addr = 0xffff; config_table->cam_entry.middle_mac_addr = 0xffff; config_table->cam_entry.lsb_mac_addr = 0xffff; config_table->cam_entry.flags = htole16(port); if (set) config_table->target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; else CAM_INVALIDATE(config_table); config_table->target_table_entry.vlan_id = 0; /* Post the command to slow path queue. */ bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mac_config)), U64_LO(BXE_SP_MAPPING(sc, mac_config)), 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Program the MAC address for 57711/57711E controllers. * * Returns: * Nothing. */ static void bxe_set_mac_addr_e1h(struct bxe_softc *sc, int set) { struct mac_configuration_cmd_e1h *config; struct mac_configuration_entry_e1h *config_table; uint8_t *eaddr; int func, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); config = (struct mac_configuration_cmd_e1h *)BXE_SP(sc, mac_config); port = BP_PORT(sc); func = BP_FUNC(sc); if (set && (sc->state != BXE_STATE_OPEN)) { DBPRINT(sc, BXE_VERBOSE, "%s(): Can't set E1H MAC in state 0x%08X!\n", __FUNCTION__, sc->state); goto bxe_set_mac_addr_e1h_exit; } /* * CAM allocation: * Function 0-7 Unicast Addresses: 8 Perfect Match Filters * Multicast Addresses: 20 + FUNC * 20, 20 each (???) */ config->hdr.length = 1; config->hdr.offset = func; config->hdr.client_id = 0xff; config->hdr.reserved1 = 0; /* Program the primary MAC address. */ config_table = &config->config_table[0]; eaddr = sc->link_params.mac_addr; config_table->msb_mac_addr = eaddr[0] << 8 | eaddr[1]; config_table->middle_mac_addr = eaddr[2] << 8 | eaddr[3]; config_table->lsb_mac_addr = eaddr[4] << 8 | eaddr[5]; config_table->clients_bit_vector = htole32(1 << sc->fp->cl_id); config_table->vlan_id = 0; config_table->e1hov_id = htole16(sc->e1hov); if (set) config_table->flags = port; else config_table->flags = MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE; DBPRINT(sc, BXE_VERBOSE, "%s(): %s MAC (%04x:%04x:%04x), E1HOV = %d, CLID = %d\n", __FUNCTION__, (set ? "Setting" : "Clearing"), config_table->msb_mac_addr, config_table->middle_mac_addr, config_table->lsb_mac_addr, sc->e1hov, BP_L_ID(sc)); bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mac_config)), U64_LO(BXE_SP_MAPPING(sc, mac_config)), 0); bxe_set_mac_addr_e1h_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Programs the various packet receive modes (broadcast and multicast). * * Returns: * Nothing. */ static void bxe_set_rx_mode(struct bxe_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; struct mac_configuration_cmd *config; struct mac_configuration_entry *config_table; uint32_t mc_filter[MC_HASH_SIZE]; uint8_t *maddr; uint32_t crc, bit, regidx, rx_mode; int i, old, offset, port; BXE_CORE_LOCK_ASSERT(sc); rx_mode = BXE_RX_MODE_NORMAL; port = BP_PORT(sc); DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); if (sc->state != BXE_STATE_OPEN) { DBPRINT(sc, BXE_WARN, "%s(): State (0x%08X) is not open!\n", __FUNCTION__, sc->state); goto bxe_set_rx_mode_exit; } ifp = sc->bxe_ifp; /* * Check for promiscuous, all multicast, or selected * multicast address filtering. */ if (ifp->if_flags & IFF_PROMISC) { /* Enable promiscuous mode. */ rx_mode = BXE_RX_MODE_PROMISC; } else if (ifp->if_flags & IFF_ALLMULTI || ifp->if_amcount > BXE_MAX_MULTICAST) { /* Enable all multicast addresses. */ rx_mode = BXE_RX_MODE_ALLMULTI; } else { /* Enable selective multicast mode. */ if (CHIP_IS_E1(sc)) { i = 0; config = BXE_SP(sc, mcast_config); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; maddr = (uint8_t *)LLADDR( (struct sockaddr_dl *)ifma->ifma_addr); config_table = &config->config_table[i]; config_table->cam_entry.msb_mac_addr = maddr[0] << 8 | maddr[1]; config_table->cam_entry.middle_mac_addr = maddr[2] << 8 | maddr[3]; config_table->cam_entry.lsb_mac_addr = maddr[4] << 8 | maddr[5]; config_table->cam_entry.flags = htole16(port); config_table->target_table_entry.flags = 0; config_table->target_table_entry. clients_bit_vector = htole32(1 << BP_L_ID(sc)); config_table->target_table_entry.vlan_id = 0; i++; DBPRINT(sc, BXE_INFO, "%s(): Setting MCAST[%d] (%04X:%04X:%04X)\n", __FUNCTION__, i, config_table->cam_entry.msb_mac_addr, config_table->cam_entry.middle_mac_addr, config_table->cam_entry.lsb_mac_addr); } if_maddr_runlock(ifp); old = config->hdr.length; /* Invalidate any extra MC entries in the CAM. */ if (old > i) { for (; i < old; i++) { config_table = &config->config_table[i]; if (CAM_IS_INVALID(config_table)) break; /* Invalidate */ CAM_INVALIDATE(config_table); } } offset = BXE_MAX_MULTICAST * (1 + port); config->hdr.length = i; config->hdr.offset = offset; config->hdr.client_id = sc->fp->cl_id; config->hdr.reserved1 = 0; wmb(); bxe_sp_post(sc, RAMROD_CMD_ID_ETH_SET_MAC, 0, U64_HI(BXE_SP_MAPPING(sc, mcast_config)), U64_LO(BXE_SP_MAPPING(sc, mcast_config)), 0); } else { /* E1H */ /* Accept one or more multicasts */ memset(mc_filter, 0, 4 * MC_HASH_SIZE); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_le(ifma->ifma_addr->sa_data, ETHER_ADDR_LEN); bit = (crc >> 24) & 0xff; regidx = bit >> 5; bit &= 0x1f; mc_filter[regidx] |= (1 << bit); } if_maddr_runlock(ifp); for (i = 0; i < MC_HASH_SIZE; i++) REG_WR(sc, MC_HASH_OFFSET(sc, i), mc_filter[i]); } } DBPRINT(sc, BXE_VERBOSE, "%s(): Enabling new receive mode: 0x%08X\n", __FUNCTION__, rx_mode); sc->rx_mode = rx_mode; bxe_set_storm_rx_mode(sc); bxe_set_rx_mode_exit: DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET); } /* * Function specific controller reset. * * Returns: * Nothing. */ static void bxe_reset_func(struct bxe_softc *sc) { int base, func, i, port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); func = BP_FUNC(sc); /* Configure IGU. */ REG_WR(sc, HC_REG_LEADING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_TRAILING_EDGE_0 + port * 8, 0); REG_WR(sc, HC_REG_CONFIG_0 + (port * 4), 0x1000); /* Clear ILT. */ base = FUNC_ILT_BASE(func); for (i = base; i < base + ILT_PER_FUNC; i++) bxe_ilt_wr(sc, i, 0); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Port specific controller reset. * * Returns: * Nothing. */ static void bxe_reset_port(struct bxe_softc *sc) { uint32_t val; int port; DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); port = BP_PORT(sc); REG_WR(sc, NIG_REG_MASK_INTERRUPT_PORT0 + port * 4, 0); /* Do not receive packets to BRB. */ REG_WR(sc, NIG_REG_LLH0_BRB1_DRV_MASK + port * 4, 0x0); /* Do not direct receive packets that are not for MCP to the BRB. */ REG_WR(sc, port ? NIG_REG_LLH1_BRB1_NOT_MCP : NIG_REG_LLH0_BRB1_NOT_MCP, 0x0); /* Configure AEU. */ REG_WR(sc, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port * 4, 0); DELAY(100000); /* Check for BRB port occupancy. */ val = REG_RD(sc, BRB1_REG_PORT_NUM_OCC_BLOCKS_0 + port * 4); if (val) DBPRINT(sc, BXE_VERBOSE, "%s(): BRB1 is not empty (%d blocks are occupied)!\n", __FUNCTION__, val); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Common controller reset. * * Returns: * Nothing. */ static void bxe_reset_common(struct bxe_softc *sc) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0xd3ffff7f); REG_WR(sc, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Reset the controller. * * Returns: * Nothing. */ static void bxe_reset_chip(struct bxe_softc *sc, uint32_t reset_code) { DBENTER(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); switch (reset_code) { case FW_MSG_CODE_DRV_UNLOAD_COMMON: bxe_reset_port(sc); bxe_reset_func(sc); bxe_reset_common(sc); break; case FW_MSG_CODE_DRV_UNLOAD_PORT: bxe_reset_port(sc); bxe_reset_func(sc); break; case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: bxe_reset_func(sc); break; default: BXE_PRINTF("%s(%d): Unknown reset code (0x%08X) from MCP!\n", __FILE__, __LINE__, reset_code); break; } DBEXIT(BXE_VERBOSE_LOAD | BXE_VERBOSE_RESET | BXE_VERBOSE_UNLOAD); } /* * Called by the OS to set media options (link, speed, etc.) * when the user specifies "ifconfig bxe media XXX" or * "ifconfig bxe mediaopt XXX". * * Returns: * 0 = Success, !0 = Failure */ static int bxe_ifmedia_upd(struct ifnet *ifp) { struct bxe_softc *sc; struct ifmedia *ifm; int rc; sc = ifp->if_softc; DBENTER(BXE_VERBOSE_PHY); ifm = &sc->bxe_ifmedia; rc = 0; /* We only support Ethernet media type. */ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) { rc = EINVAL; goto bxe_ifmedia_upd_exit; } switch (IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: /* ToDo: What to do here? */ /* Doing nothing translates to success here. */ break; case IFM_10G_CX4: /* Fall-through */ case IFM_10G_SR: /* Fall-through */ case IFM_10G_T: /* Fall-through */ case IFM_10G_TWINAX: /* Fall-through */ default: /* We don't support channging the media type. */ DBPRINT(sc, BXE_WARN, "%s(): Invalid media type!\n", __FUNCTION__); rc = EINVAL; } bxe_ifmedia_upd_exit: DBENTER(BXE_VERBOSE_PHY); return (rc); } /* * Called by the OS to report current media status * (link, speed, etc.). * * Returns: * Nothing. */ static void bxe_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) { struct bxe_softc *sc; sc = ifp->if_softc; DBENTER(BXE_EXTREME_LOAD | BXE_EXTREME_RESET); /* Report link down if the driver isn't running. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ifmr->ifm_active |= IFM_NONE; goto bxe_ifmedia_status_exit; } /* Setup the default interface info. */ ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; if (sc->link_vars.link_up) ifmr->ifm_status |= IFM_ACTIVE; else { ifmr->ifm_active |= IFM_NONE; goto bxe_ifmedia_status_exit; } ifmr->ifm_active |= sc->media; if (sc->link_vars.duplex == MEDIUM_FULL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; bxe_ifmedia_status_exit: DBEXIT(BXE_EXTREME_LOAD | BXE_EXTREME_RESET); } /* * Update last maximum scatter gather entry. * * Returns: * None. */ static __inline void bxe_update_last_max_sge(struct bxe_fastpath *fp, uint16_t index) { uint16_t last_max; last_max = fp->last_max_sge; if (SUB_S16(index, last_max) > 0) fp->last_max_sge = index; } /* * Clear scatter gather mask next elements. * * Returns: * None */ static void bxe_clear_sge_mask_next_elems(struct bxe_fastpath *fp) { int i, index, j; for (i = 0; i < NUM_RX_SGE_PAGES; i++) { index = i * TOTAL_RX_SGE_PER_PAGE + USABLE_RX_SGE_PER_PAGE; for (j = 0; j < 2; j++) { SGE_MASK_CLEAR_BIT(fp, index); index++; } } } /* * Update SGE producer. * * Returns: * None. */ static void bxe_update_sge_prod(struct bxe_fastpath *fp, struct eth_fast_path_rx_cqe *fp_cqe) { struct bxe_softc *sc; uint16_t delta, first_elem, last_max, last_elem, sge_len; int i; sc = fp->sc; DBENTER(BXE_EXTREME_RECV); delta = 0; sge_len = SGE_PAGE_ALIGN(le16toh(fp_cqe->pkt_len) - le16toh(fp_cqe->len_on_bd)) >> SGE_PAGE_SHIFT; if (!sge_len) goto bxe_update_sge_prod_exit; /* First mark all used pages. */ for (i = 0; i < sge_len; i++) SGE_MASK_CLEAR_BIT(fp, RX_SGE(le16toh(fp_cqe->sgl[i]))); /* Assume that the last SGE index is the biggest. */ bxe_update_last_max_sge(fp, le16toh(fp_cqe->sgl[sge_len - 1])); last_max = RX_SGE(fp->last_max_sge); last_elem = last_max >> RX_SGE_MASK_ELEM_SHIFT; first_elem = RX_SGE(fp->rx_sge_prod) >> RX_SGE_MASK_ELEM_SHIFT; /* If ring is not full. */ if (last_elem + 1 != first_elem) last_elem++; /* Now update the producer index. */ for (i = first_elem; i != last_elem; i = NEXT_SGE_MASK_ELEM(i)) { if (fp->rx_sge_mask[i]) break; fp->rx_sge_mask[i] = RX_SGE_MASK_ELEM_ONE_MASK; delta += RX_SGE_MASK_ELEM_SZ; } if (delta > 0) { fp->rx_sge_prod += delta; /* clear page-end entries */ bxe_clear_sge_mask_next_elems(fp); } bxe_update_sge_prod_exit: DBEXIT(BXE_EXTREME_RECV); } /* * Initialize scatter gather ring bitmask. * * Each entry in the SGE is associated with an aggregation in process. * Since there is no guarantee that all Ethernet frames associated with * a partciular TCP flow will arrive at the adapter and be placed into * the SGE chain contiguously, we maintain a bitmask for each SGE element * that identifies which aggregation an Ethernet frame belongs to. * * Returns: * None */ static __inline void bxe_init_sge_ring_bit_mask(struct bxe_fastpath *fp) { /* Set the mask to all 1s, it's faster to compare to 0 than to 0xf. */ memset(fp->rx_sge_mask, 0xff, (TOTAL_RX_SGE >> RX_SGE_MASK_ELEM_SHIFT) * sizeof(uint64_t)); /* * The SGE chain is formatted just like the RX chain. * The last two elements are reserved as a "next page pointer" * to the next page of SGE elements. Clear the last two * elements in each SGE chain page since they will never be * used to track an aggregation. */ bxe_clear_sge_mask_next_elems(fp); } /* * The current mbuf is part of an aggregation. Swap the mbuf into the TPA * aggregation queue, swap an empty mbuf back onto the receive chain, and * mark the current aggregation queue as in-progress. * * Returns: * None. */ static void bxe_tpa_start(struct bxe_fastpath *fp, uint16_t queue, uint16_t cons, uint16_t prod) { struct bxe_softc *sc; struct mbuf *m_temp; struct eth_rx_bd *rx_bd; bus_dmamap_t map_temp; int max_agg_queues; sc = fp->sc; DBENTER(BXE_INSANE_RECV | BXE_INSANE_TPA); DBPRINT(sc, BXE_EXTREME_TPA, "%s(): fp[%02d].tpa[%02d], cons=0x%04X, prod=0x%04X\n", __FUNCTION__, fp->index, queue, cons, prod); max_agg_queues = CHIP_IS_E1(sc) ? ETH_MAX_AGGREGATION_QUEUES_E1 : ETH_MAX_AGGREGATION_QUEUES_E1H; DBRUNIF((queue > max_agg_queues), BXE_PRINTF("%s(): fp[%02d] illegal aggregation (%d > %d)!\n", __FUNCTION__, fp->index, queue, max_agg_queues)); DBRUNIF((fp->tpa_state[queue] != BXE_TPA_STATE_STOP), BXE_PRINTF("%s(): Starting aggregation on " "fp[%02d].tpa[%02d] even though queue is not in the " "TPA_STOP state!\n", __FUNCTION__, fp->index, queue)); /* Remove the existing mbuf and mapping from the TPA pool. */ m_temp = fp->tpa_mbuf_ptr[queue]; map_temp = fp->tpa_mbuf_map[queue]; /* Only the paranoid survive! */ if(m_temp == NULL) { BXE_PRINTF("%s(%d): fp[%02d].tpa[%02d] not allocated!\n", __FILE__, __LINE__, fp->index, queue); /* ToDo: Additional error handling! */ goto bxe_tpa_start_exit; } /* Move received mbuf and mapping to TPA pool. */ fp->tpa_mbuf_ptr[queue] = fp->rx_mbuf_ptr[cons]; fp->tpa_mbuf_map[queue] = fp->rx_mbuf_map[cons]; /* Place the TPA bin into the START state. */ fp->tpa_state[queue] = BXE_TPA_STATE_START; DBRUN(fp->tpa_queue_used |= (1 << queue)); /* Get the rx_bd for the next open entry on the receive chain. */ rx_bd = &fp->rx_chain[prod]; /* Update the rx_bd with the empty mbuf from the TPA pool. */ rx_bd->addr_hi = htole32(U64_HI(fp->tpa_mbuf_segs[queue].ds_addr)); rx_bd->addr_lo = htole32(U64_LO(fp->tpa_mbuf_segs[queue].ds_addr)); fp->rx_mbuf_ptr[prod] = m_temp; fp->rx_mbuf_map[prod] = map_temp; bxe_tpa_start_exit: DBEXIT(BXE_INSANE_RECV | BXE_INSANE_TPA); } /* * When a TPA aggregation is completed, loop through the individual mbufs * of the aggregation, combining them into a single mbuf which will be sent * up the stack. Refill all freed SGEs with mbufs as we go along. * * Returns: * 0 = Success, !0 = Failure. */ static int bxe_fill_frag_mbuf(struct bxe_softc *sc, struct bxe_fastpath *fp, struct mbuf *m, struct eth_fast_path_rx_cqe *fp_cqe, uint16_t cqe_idx) { struct mbuf *m_frag; uint32_t frag_len, frag_size, pages, i; uint16_t sge_idx, len_on_bd; int j, rc; DBENTER(BXE_EXTREME_RECV | BXE_EXTREME_TPA); rc = 0; len_on_bd = le16toh(fp_cqe->len_on_bd); frag_size = le16toh(fp_cqe->pkt_len) - len_on_bd; pages = SGE_PAGE_ALIGN(frag_size) >> SGE_PAGE_SHIFT; DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): len_on_bd=%d, frag_size=%d, pages=%d\n", __FUNCTION__, len_on_bd, frag_size, pages); /* Make sure the aggregated frame is not too big to handle. */ if (pages > 8 * PAGES_PER_SGE) { DBPRINT(sc, BXE_FATAL, "%s(): fp[%02d].rx_sge[0x%04X] has too many pages (%d)!\n", __FUNCTION__, fp->index, cqe_idx, pages); DBPRINT(sc, BXE_FATAL, "%s(): fp_cqe->pkt_len = %d fp_cqe->len_on_bd = %d\n", __FUNCTION__, le16toh(fp_cqe->pkt_len), len_on_bd); bxe_panic_dump(sc); rc = EINVAL; goto bxe_fill_frag_mbuf_exit; } /* * Scan through the scatter gather list, pulling individual * mbufs into a single mbuf for the host stack. */ for (i = 0, j = 0; i < pages; i += PAGES_PER_SGE, j++) { sge_idx = RX_SGE(le16toh(fp_cqe->sgl[j])); /* * Firmware gives the indices of the SGE as if the ring is an * array (meaning that the "next" element will consume 2 * indices). */ frag_len = min(frag_size, (uint32_t)(BCM_PAGE_SIZE * PAGES_PER_SGE)); DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): i=%d, j=%d, frag_size=%d, frag_len=%d\n", __FUNCTION__, i, j, frag_size, frag_len); m_frag = fp->rx_sge_buf_ptr[sge_idx]; /* Allocate a new mbuf for the SGE. */ rc = bxe_alloc_rx_sge_mbuf(fp, sge_idx); if (rc) { /* * Leave all remaining SGEs in the ring. */ goto bxe_fill_frag_mbuf_exit; } /* Update the fragment its length. */ m_frag->m_len = frag_len; /* Concatenate the fragment to the head mbuf. */ m_cat(m, m_frag); DBRUN(fp->sge_mbuf_alloc--); /* Update TPA mbuf size and remaining fragment size. */ m->m_pkthdr.len += frag_len; frag_size -= frag_len; } bxe_fill_frag_mbuf_exit: DBPRINT(sc, BXE_VERBOSE_TPA, "%s(): frag_size=%d\n", __FUNCTION__, frag_size); DBEXIT(BXE_EXTREME_RECV | BXE_EXTREME_TPA); return (rc); } /* * The aggregation on the current TPA queue has completed. Pull the * individual mbuf fragments together into a single mbuf, perform all * necessary checksum calculations, and send the resuting mbuf to the stack. * * Returns: * None. */ static void bxe_tpa_stop(struct bxe_softc *sc, struct bxe_fastpath *fp, uint16_t queue, int pad, int len, union eth_rx_cqe *cqe, uint16_t cqe_idx) { struct mbuf *m; struct ifnet *ifp; int rc; DBENTER(BXE_INSANE_RECV | BXE_INSANE_TPA); DBPRINT(sc, (BXE_EXTREME_RECV | BXE_EXTREME_TPA), "%s(): fp[%02d].tpa[%02d], len=%d, pad=%d\n", __FUNCTION__, fp->index, queue, len, pad); rc = 0; ifp = sc->bxe_ifp; m = fp->tpa_mbuf_ptr[queue]; /* Allocate a replacement before modifying existing mbuf. */ rc = bxe_alloc_tpa_mbuf(fp, queue); if (rc) { /* Drop the frame and log a soft error. */ fp->rx_soft_errors++; goto bxe_tpa_stop_exit; } /* We have a replacement, fixup the current mbuf. */ m_adj(m, pad); m->m_pkthdr.len = m->m_len = len; /* Mark the checksums valid (taken care of by firmware). */ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; /* Aggregate all of the SGEs into a single mbuf. */ rc = bxe_fill_frag_mbuf(sc, fp, m, &cqe->fast_path_cqe, cqe_idx); if (rc) { /* Drop the packet and log an error. */ fp->rx_soft_errors++; m_freem(m); } else { /* Find VLAN tag and send frame up to the stack. */ if ((le16toh(cqe->fast_path_cqe.pars_flags.flags) & PARSING_FLAGS_VLAN)) { m->m_pkthdr.ether_vtag = cqe->fast_path_cqe.vlan_tag; m->m_flags |= M_VLANTAG; } /* Assign packet to the appropriate interface. */ m->m_pkthdr.rcvif = ifp; /* Update packet statistics. */ fp->rx_tpa_pkts++; ifp->if_ipackets++; /* ToDo: Any potential locking issues here? */ /* Pass the frame to the stack. */ (*ifp->if_input)(ifp, m); } /* We passed mbuf up the stack or dropped the frame. */ DBRUN(fp->tpa_mbuf_alloc--); bxe_tpa_stop_exit: fp->tpa_state[queue] = BXE_TPA_STATE_STOP; DBRUN(fp->tpa_queue_used &= ~(1 << queue)); DBEXIT(BXE_INSANE_RECV | BXE_INSANE_TPA); } /* * Notify the controller that the RX producer indices have been updated for * a fastpath connection by writing them to the controller. * * Returns: * None */ static __inline void bxe_update_rx_prod(struct bxe_softc *sc, struct bxe_fastpath *fp, uint16_t bd_prod, uint16_t cqe_prod, uint16_t sge_prod) { volatile struct ustorm_eth_rx_producers rx_prods = {0}; int i; /* Update producers. */ rx_prods.bd_prod = bd_prod; rx_prods.cqe_prod = cqe_prod; rx_prods.sge_prod = sge_prod; wmb(); for (i = 0; i < sizeof(struct ustorm_eth_rx_producers) / 4; i++){ REG_WR(sc, BAR_USTORM_INTMEM + USTORM_RX_PRODS_OFFSET(BP_PORT(sc), fp->cl_id) + i * 4, ((volatile uint32_t *) &rx_prods)[i]); } DBPRINT(sc, BXE_EXTREME_RECV, "%s(%d): Wrote fp[%02d] bd_prod = 0x%04X, " "cqe_prod = 0x%04X, sge_prod = 0x%04X\n", __FUNCTION__, curcpu, fp->index, bd_prod, cqe_prod, sge_prod); } /* * Processes received frames. * * Returns: * Nothing. */ static void bxe_rxeof(struct bxe_fastpath *fp) { struct bxe_softc *sc; struct ifnet *ifp; uint16_t rx_bd_cons, rx_bd_cons_idx; uint16_t rx_bd_prod, rx_bd_prod_idx; uint16_t rx_cq_cons, rx_cq_cons_idx; uint16_t rx_cq_prod, rx_cq_cons_sb; unsigned long rx_pkts = 0; int rc; sc = fp->sc; ifp = sc->bxe_ifp; DBENTER(BXE_EXTREME_RECV); /* Get the status block's view of the RX completion consumer index. */ rx_cq_cons_sb = bxe_rx_cq_cons(fp); /* * Get working copies of the driver's view of the * RX indices. These are 16 bit values that are * expected to increment from 0 to 65535 and then * wrap-around to 0 again. */ rx_bd_cons = fp->rx_bd_cons; rx_bd_prod = fp->rx_bd_prod; rx_cq_cons = fp->rx_cq_cons; rx_cq_prod = fp->rx_cq_prod; DBPRINT(sc, (BXE_EXTREME_RECV), "%s(%d): BEFORE: fp[%02d], rx_bd_cons = 0x%04X, rx_bd_prod = 0x%04X, " "rx_cq_cons_sw = 0x%04X, rx_cq_prod_sw = 0x%04X\n", __FUNCTION__, curcpu, fp->index, rx_bd_cons, rx_bd_prod, rx_cq_cons, rx_cq_prod); /* * Memory barrier to prevent speculative reads of the RX buffer * from getting ahead of the index in the status block. */ rmb(); /* * Scan through the receive chain as long * as there is work to do. */ while (rx_cq_cons != rx_cq_cons_sb) { struct mbuf *m; union eth_rx_cqe *cqe; uint8_t cqe_fp_flags; uint16_t len, pad; /* * Convert the 16 bit indices used by hardware * into array indices used by the driver. */ rx_cq_cons_idx = RCQ_ENTRY(rx_cq_cons); rx_bd_prod_idx = RX_BD(rx_bd_prod); rx_bd_cons_idx = RX_BD(rx_bd_cons); wmb(); /* Fetch the completion queue entry (i.e. cookie). */ cqe = (union eth_rx_cqe *) &fp->rcq_chain[rx_cq_cons_idx]; cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; /* Sanity check the cookie flags. */ if (__predict_false(cqe_fp_flags == 0)) { fp->rx_null_cqe_flags++; DBRUN(bxe_dump_cqe(fp, rx_cq_cons_idx, cqe)); /* ToDo: What error handling can be done here? */ } /* Check the CQE type for slowpath or fastpath completion. */ if (__predict_false(CQE_TYPE(cqe_fp_flags) == RX_ETH_CQE_TYPE_ETH_RAMROD)) { /* This is a slowpath completion. */ bxe_sp_event(fp, cqe); goto bxe_rxeof_next_cqe; } else { /* This is a fastpath completion. */ /* Get the length and pad information from the CQE. */ len = le16toh(cqe->fast_path_cqe.pkt_len); pad = cqe->fast_path_cqe.placement_offset; /* Check if the completion is for TPA. */ if ((fp->disable_tpa == FALSE) && (TPA_TYPE(cqe_fp_flags) != (TPA_TYPE_START | TPA_TYPE_END))) { uint16_t queue = cqe->fast_path_cqe.queue_index; /* * No need to worry about error flags in * the frame as the firmware has already * managed that for us when aggregating * the frames. */ /* Check if TPA aggregation has started. */ if (TPA_TYPE(cqe_fp_flags) == TPA_TYPE_START) { bxe_tpa_start(fp, queue, rx_bd_cons_idx, rx_bd_prod_idx); goto bxe_rxeof_next_rx; } /* Check if TPA aggregation has completed. */ if (TPA_TYPE(cqe_fp_flags) == TPA_TYPE_END) { DBRUNIF(!BXE_RX_SUM_FIX(cqe), DBPRINT(sc, BXE_FATAL, "%s(): STOP on non-TCP data.\n", __FUNCTION__)); /* * This is the size of the linear * data on this mbuf. */ len = le16toh(cqe->fast_path_cqe.len_on_bd); /* * Stop the aggregation and pass * the frame up. */ bxe_tpa_stop(sc, fp, queue, pad, len, cqe, rx_cq_cons_idx); bxe_update_sge_prod(fp, &cqe->fast_path_cqe); goto bxe_rxeof_next_cqe; } } m = fp->rx_mbuf_ptr[rx_bd_cons_idx]; /* Allocate a replacement before modifying existing mbuf. */ rc = bxe_alloc_rx_bd_mbuf(fp, rx_bd_prod_idx); if (rc) { /* Drop the frame and log a soft error. */ fp->rx_soft_errors++; goto bxe_rxeof_next_rx; } /* Check if the received frame has any errors. */ if (__predict_false(cqe_fp_flags & ETH_RX_ERROR_FLAGS)) { DBPRINT(sc, BXE_WARN , "%s(): fp[%02d].cqe[0x%04X] has errors " "(0x%08X)!\n", __FUNCTION__, fp->index, rx_cq_cons, cqe_fp_flags); fp->rx_soft_errors++; goto bxe_rxeof_next_rx; } /* We have a replacement, fixup the current mbuf. */ m_adj(m, pad); m->m_pkthdr.len = m->m_len = len; /* Assign packet to the appropriate interface. */ m->m_pkthdr.rcvif = ifp; /* Assume no hardware checksum complated. */ m->m_pkthdr.csum_flags = 0; /* Validate checksum if offload enabled. */ if (ifp->if_capenable & IFCAP_RXCSUM) { /* Check whether IP checksummed or not. */ if (sc->rx_csum && !(cqe->fast_path_cqe.status_flags & ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG)) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (__predict_false(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG)) { DBPRINT(sc, BXE_WARN_SEND, "%s(): Invalid IP checksum!\n", __FUNCTION__); } else m->m_pkthdr.csum_flags |= CSUM_IP_VALID; } /* Check for a valid TCP/UDP frame. */ if (sc->rx_csum && !(cqe->fast_path_cqe.status_flags & ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)) { /* Check for a good TCP/UDP checksum. */ if (__predict_false(cqe_fp_flags & ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG)) { DBPRINT(sc, BXE_VERBOSE_RECV, "%s(): Invalid TCP/UDP checksum!\n", __FUNCTION__); } else { m->m_pkthdr.csum_data = 0xFFFF; m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); } } } /* * If we received a packet with a vlan tag, * attach that information to the packet. */ if (cqe->fast_path_cqe.pars_flags.flags & PARSING_FLAGS_VLAN) { m->m_pkthdr.ether_vtag = cqe->fast_path_cqe.vlan_tag; m->m_flags |= M_VLANTAG; } #if __FreeBSD_version >= 800000 /* Tell OS what RSS queue was used for this flow. */ m->m_pkthdr.flowid = fp->index; m->m_flags |= M_FLOWID; #endif /* Last chance to check for problems. */ DBRUN(bxe_validate_rx_packet(fp, rx_cq_cons, cqe, m)); /* Update packet statistics. */ ifp->if_ipackets++; rx_pkts++; /* ToDo: Any potential locking issues here? */ /* Pass the frame to the stack. */ (*ifp->if_input)(ifp, m); DBRUN(fp->rx_mbuf_alloc--); } bxe_rxeof_next_rx: rx_bd_prod = NEXT_RX_BD(rx_bd_prod); rx_bd_cons = NEXT_RX_BD(rx_bd_cons); bxe_rxeof_next_cqe: rx_cq_prod = NEXT_RCQ_IDX(rx_cq_prod); rx_cq_cons = NEXT_RCQ_IDX(rx_cq_cons); /* * Memory barrier to prevent speculative reads of the RX buffer * from getting ahead of the index in the status block. */ rmb(); } /* Update driver copy of the fastpath indices. */ fp->rx_bd_cons = rx_bd_cons; fp->rx_bd_prod = rx_bd_prod; fp->rx_cq_cons = rx_cq_cons; fp->rx_cq_prod = rx_cq_prod; DBPRINT(sc, (BXE_EXTREME_RECV), "%s(%d): AFTER: fp[%02d], rx_bd_cons = 0x%04X, rx_bd_prod = 0x%04X, " "rx_cq_cons_sw = 0x%04X, rx_cq_prod_sw = 0x%04X\n", __FUNCTION__, curcpu, fp->index, rx_bd_cons, rx_bd_prod, rx_cq_cons, rx_cq_prod); /* Update producers */ bxe_update_rx_prod(sc, fp, fp->rx_bd_prod, fp->rx_cq_prod, fp->rx_sge_prod); bus_space_barrier(sc->bxe_btag, sc->bxe_bhandle, 0, 0, BUS_SPACE_BARRIER_READ); fp->rx_pkts += rx_pkts; DBEXIT(BXE_EXTREME_RECV); } /* * Processes transmit completions. * * Returns: * Nothing. */ static void bxe_txeof(struct bxe_fastpath *fp) { struct bxe_softc *sc; struct ifnet *ifp; struct eth_tx_start_bd *txbd; uint16_t hw_pkt_cons, sw_pkt_cons, sw_tx_bd_cons; uint16_t bd_index, pkt_index, nbds; int i; sc = fp->sc; ifp = sc->bxe_ifp; DBENTER(BXE_EXTREME_SEND); /* Get the hardware's view of the TX packet consumer index. */ hw_pkt_cons = le16toh(*fp->tx_pkt_cons_sb); sw_pkt_cons = fp->tx_pkt_cons; sw_tx_bd_cons = fp->tx_bd_cons; /* Cycle through any completed TX chain page entries. */ while (sw_pkt_cons != hw_pkt_cons) { bd_index = TX_BD(sw_tx_bd_cons); pkt_index = TX_BD(sw_pkt_cons); txbd = &fp->tx_chain[bd_index].start_bd; nbds = txbd->nbd; /* Free the completed frame's mbuf. */ if (__predict_true(fp->tx_mbuf_ptr[pkt_index] != NULL)) { /* Unmap the mbuf from non-paged memory. */ bus_dmamap_unload(fp->tx_mbuf_tag, fp->tx_mbuf_map[pkt_index]); /* Return the mbuf to the system. */ m_freem(fp->tx_mbuf_ptr[pkt_index]); fp->tx_mbuf_alloc--; fp->tx_mbuf_ptr[pkt_index] = NULL; fp->opackets++; } else { fp->tx_chain_lost_mbuf++; } /* Updated packet consumer value. */ sw_pkt_cons++; /* Skip over the remaining used buffer descriptors. */ fp->tx_bd_used -= nbds; for (i = 0; i < nbds; i++) sw_tx_bd_cons = NEXT_TX_BD(sw_tx_bd_cons); /* Check for new work since we started. */ hw_pkt_cons = le16toh(*fp->tx_pkt_cons_sb); rmb(); } /* Enable new transmits if we've made enough room. */ if (fp->tx_bd_used < BXE_TX_CLEANUP_THRESHOLD) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (fp->tx_bd_used == 0) { /* * Clear the watchdog timer if we've emptied * the TX chain. */ fp->watchdog_timer = 0; } else { /* * Reset the watchdog timer if we still have * transmits pending. */ fp->watchdog_timer = BXE_TX_TIMEOUT; } } /* Save our indices. */ fp->tx_pkt_cons = sw_pkt_cons; fp->tx_bd_cons = sw_tx_bd_cons; DBEXIT(BXE_EXTREME_SEND); } /* * Transmit timeout handler. * * Returns: * 0 = No timeout, !0 = timeout occurred. */ static int bxe_watchdog(struct bxe_fastpath *fp) { struct bxe_softc *sc; int rc = 0; sc = fp->sc; DBENTER(BXE_INSANE_SEND); BXE_FP_LOCK(fp); if (fp->watchdog_timer == 0 || --fp->watchdog_timer) { rc = EINVAL; BXE_FP_UNLOCK(fp); goto bxe_watchdog_exit; } BXE_FP_UNLOCK(fp); BXE_PRINTF("TX watchdog timeout occurred on fp[%02d], " "resetting!\n", fp->index); /* DBRUNLV(BXE_FATAL, bxe_breakpoint(sc)); */ BXE_CORE_LOCK(sc); /* Mark the interface as down. */ sc->bxe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; bxe_stop_locked(sc, UNLOAD_NORMAL); DELAY(10000); bxe_init_locked(sc, LOAD_OPEN); BXE_CORE_UNLOCK(sc); bxe_watchdog_exit: DBEXIT(BXE_INSANE_SEND); return (rc); } /* * The periodic timer tick routine. * * This code only runs when the interface is up. * * Returns: * None */ static void bxe_tick(void *xsc) { struct bxe_softc *sc; struct bxe_fastpath *fp; #if 0 /* Re-enable at a later time. */ uint32_t drv_pulse, mcp_pulse; #endif int i, func; sc = xsc; DBENTER(BXE_INSANE_MISC); /* Check for TX timeouts on any fastpath. */ for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; if (bxe_watchdog(fp) != 0) break; } func = BP_FUNC(sc); /* Schedule the next tick. */ callout_reset(&sc->bxe_tick_callout, hz, bxe_tick, sc); #if 0 if (!NOMCP(sc)) { func = BP_FUNC(sc); ++sc->fw_drv_pulse_wr_seq; sc->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK; /* Let the MCP know we're alive. */ drv_pulse = sc->fw_drv_pulse_wr_seq; SHMEM_WR(sc, func_mb[func].drv_pulse_mb, drv_pulse); /* Check if the MCP is still alive. */ mcp_pulse = (SHMEM_RD(sc, func_mb[func].mcp_pulse_mb) & MCP_PULSE_SEQ_MASK); /* * The delta between driver pulse and MCP response should be 1 * (before MCP response) or 0 (after MCP response). */ if ((drv_pulse != mcp_pulse) && (drv_pulse != ((mcp_pulse + 1) & MCP_PULSE_SEQ_MASK))) { /* Someone's in cardiac arrest. */ DBPRINT(sc, BXE_WARN, "%s(): drv_pulse (0x%x) != mcp_pulse (0x%x)\n", __FUNCTION__, drv_pulse, mcp_pulse); } } #endif if ((sc->state == BXE_STATE_OPEN) || (sc->state == BXE_STATE_DISABLED)) bxe_stats_handle(sc, STATS_EVENT_UPDATE); } #ifdef BXE_DEBUG /* * Allows the driver state to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_driver_state(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, i, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { bxe_dump_driver_state(sc); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; bxe_dump_fp_state(fp); } bxe_dump_status_block(sc); } return (error); } /* * Allows the hardware state to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_hw_state(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) bxe_dump_hw_state(sc); return (error); } /* * Allows the MCP firmware to be dumped through the sysctl interface. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_fw(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) bxe_dump_fw(sc); return (error); } /* * Provides a sysctl interface to allow dumping the RX completion chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_rx_cq_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_rx_cq_chain(fp, 0, TOTAL_RCQ_ENTRIES); } return (error); } /* * Provides a sysctl interface to allow dumping the RX chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_rx_bd_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_rx_bd_chain(fp, 0, TOTAL_RX_BD); } return (error); } /* * Provides a sysctl interface to allow dumping the TX chain. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_dump_tx_chain(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; struct bxe_fastpath *fp; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if ((result >= 0) && (result < sc->num_queues)) { fp = &sc->fp[result]; bxe_dump_tx_chain(fp, 0, TOTAL_TX_BD); } return (error); } /* * Provides a sysctl interface to allow reading arbitrary registers in the * device. DO NOT ENABLE ON PRODUCTION SYSTEMS! * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_reg_read(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; uint32_t result, val; int error; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || (req->newptr == NULL)) return (error); val = REG_RD(sc, result); BXE_PRINTF("reg 0x%08X = 0x%08X\n", result, val); return (error); } /* * Provides a sysctl interface to allow generating a grcdump. * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_grcdump(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; sc = (struct bxe_softc *)arg1; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { /* Generate a grcdump and log the contents.*/ bxe_grcdump(sc, 1); } else { /* Generate a grcdump and don't log the contents. */ bxe_grcdump(sc, 0); } return (error); } /* * Provides a sysctl interface to forcing the driver to dump state and * enter the debugger. DO NOT ENABLE ON PRODUCTION SYSTEMS! * * Returns: * 0 for success, positive value for failure. */ static int bxe_sysctl_breakpoint(SYSCTL_HANDLER_ARGS) { struct bxe_softc *sc; int error, result; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { sc = (struct bxe_softc *)arg1; bxe_breakpoint(sc); } return (error); } #endif /* * Adds any sysctl parameters for tuning or debugging purposes. * * Returns: * None. */ static void bxe_add_sysctls(struct bxe_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); struct sysctl_oid_list *children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); struct bxe_port_stats *estats = &sc->eth_stats; SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_received_hi", CTLFLAG_RD, &estats->total_bytes_received_hi, 0, "Total bytes received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_received_lo", CTLFLAG_RD, &estats->total_bytes_received_lo, 0, "Total bytes received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_valid_bytes_received_hi", CTLFLAG_RD, &estats->valid_bytes_received_hi, 0, "Valid bytes received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_valid_bytes_received_lo", CTLFLAG_RD, &estats->valid_bytes_received_lo, 0, "Valid bytes received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_received_hi", CTLFLAG_RD, &estats->total_unicast_packets_received_hi, 0, "Total unicast packets received (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_received_lo", CTLFLAG_RD, &estats->total_unicast_packets_received_lo, 0, "Total unicast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_transmitted_hi", CTLFLAG_RD, &estats->total_bytes_transmitted_hi, 0, "Total bytes transmitted (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_bytes_transmitted_lo", CTLFLAG_RD, &estats->total_bytes_transmitted_lo, 0, "Total bytes transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_transmitted_hi", CTLFLAG_RD, &estats->total_unicast_packets_transmitted_hi, 0, "Total unicast packets transmitted (hi)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_unicast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_unicast_packets_transmitted_lo, 0, "Total unicast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_broadcast_packets_received_lo", CTLFLAG_RD, &estats->total_broadcast_packets_received_lo, 0, "Total broadcast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_broadcast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_broadcast_packets_transmitted_lo, 0, "Total broadcast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_multicast_packets_received_lo", CTLFLAG_RD, &estats->total_multicast_packets_received_lo, 0, "Total multicast packets received (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "estats_total_multicast_packets_transmitted_lo", CTLFLAG_RD, &estats->total_multicast_packets_transmitted_lo, 0, "Total multicast packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "tx_stat_etherstatspkts64octets_hi", CTLFLAG_RD, &estats->tx_stat_etherstatspkts64octets_hi, 0, "Total 64 byte packets transmitted (hi)"); /* ToDo: Fix for 64 bit access. */ SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "tx_stat_etherstatspkts64octets_lo", CTLFLAG_RD, &estats->tx_stat_etherstatspkts64octets_lo, 0, "Total 64 byte packets transmitted (lo)"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "driver_xoff", CTLFLAG_RD, &estats->driver_xoff, 0, "Driver transmit queue full count"); SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, "tx_start_called_with_link_down", CTLFLAG_RD, &sc->tx_start_called_with_link_down, "TX start routine called while link down count"); SYSCTL_ADD_ULONG(ctx, children, OID_AUTO, "tx_start_called_with_queue_full", CTLFLAG_RD, &sc->tx_start_called_with_queue_full, "TX start routine called with queue full count"); /* ToDo: Add more statistics here. */ #ifdef BXE_DEBUG SYSCTL_ADD_INT(ctx, children, OID_AUTO, "bxe_debug", CTLFLAG_RW, &bxe_debug, 0, "Debug message level flag"); #endif do { #define QUEUE_NAME_LEN 32 char namebuf[QUEUE_NAME_LEN]; struct sysctl_oid *queue_node; struct sysctl_oid_list *queue_list; for (int i = 0; i < sc->num_queues; i++) { struct bxe_fastpath *fp = &sc->fp[i]; snprintf(namebuf, QUEUE_NAME_LEN, "fp[%02d]", i); queue_node = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, namebuf, CTLFLAG_RD, NULL, "Queue Name"); queue_list = SYSCTL_CHILDREN(queue_node); /* * Receive related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_pkts", CTLFLAG_RD, &fp->rx_pkts, "Received packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_tpa_pkts", CTLFLAG_RD, &fp->rx_tpa_pkts, "Received TPA packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_null_cqe_flags", CTLFLAG_RD, &fp->rx_null_cqe_flags, "CQEs with NULL flags count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_soft_errors", CTLFLAG_RD, &fp->rx_soft_errors, "Received frames dropped by driver count"); /* * Transmit related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_pkts", CTLFLAG_RD, &fp->tx_pkts, "Transmitted packets"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_soft_errors", CTLFLAG_RD, &fp->tx_soft_errors, "Transmit frames dropped by driver count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_ip", CTLFLAG_RD, &fp->tx_offload_frames_csum_ip, "IP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_tcp", CTLFLAG_RD, &fp->tx_offload_frames_csum_tcp, "TCP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_csum_udp", CTLFLAG_RD, &fp->tx_offload_frames_csum_udp, "UDP checksum offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_offload_frames_tso", CTLFLAG_RD, &fp->tx_offload_frames_tso, "TSO offload frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_header_splits", CTLFLAG_RD, &fp->tx_header_splits, "TSO frame header/data split count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_encap_failures", CTLFLAG_RD, &fp->tx_encap_failures, "TX encapsulation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_hw_queue_full", CTLFLAG_RD, &fp->tx_hw_queue_full, "TX H/W queue too full to add a frame count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_hw_max_queue_depth", CTLFLAG_RD, &fp->tx_hw_max_queue_depth, "TX H/W maximum queue depth count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_dma_mapping_failure", CTLFLAG_RD, &fp->tx_dma_mapping_failure, "TX DMA mapping failure"); SYSCTL_ADD_INT(ctx, queue_list, OID_AUTO, "tx_max_drbr_queue_depth", CTLFLAG_RD, &fp->tx_max_drbr_queue_depth, 0, "TX S/W queue maximum depth"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_window_violation_std", CTLFLAG_RD, &fp->tx_window_violation_std, "Standard frame TX BD window violation count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_window_violation_tso", CTLFLAG_RD, &fp->tx_window_violation_tso, "TSO frame TX BD window violation count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_unsupported_tso_request_ipv6", CTLFLAG_RD, &fp->tx_unsupported_tso_request_ipv6, "TSO frames with unsupported IPv6 protocol count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_unsupported_tso_request_not_tcp", CTLFLAG_RD, &fp->tx_unsupported_tso_request_not_tcp, "TSO frames with unsupported protocol count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_chain_lost_mbuf", CTLFLAG_RD, &fp->tx_chain_lost_mbuf, "Mbufs lost on TX chain count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_frame_deferred", CTLFLAG_RD, &fp->tx_frame_deferred, "TX frame deferred from H/W queue to S/W queue count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_queue_xoff", CTLFLAG_RD, &fp->tx_queue_xoff, "TX queue full count"); /* * Memory related fastpath statistics.* */ SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_rx_bd_alloc_failed", CTLFLAG_RD, &fp->mbuf_rx_bd_alloc_failed, "RX BD mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_rx_bd_mapping_failed", CTLFLAG_RD, &fp->mbuf_rx_bd_mapping_failed, "RX BD mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_tpa_alloc_failed", CTLFLAG_RD, &fp->mbuf_tpa_alloc_failed, "TPA mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_tpa_mapping_failed", CTLFLAG_RD, &fp->mbuf_tpa_mapping_failed, "TPA mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_sge_alloc_failed", CTLFLAG_RD, &fp->mbuf_sge_alloc_failed, "SGE mbuf allocation failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_sge_mapping_failed", CTLFLAG_RD, &fp->mbuf_sge_mapping_failed, "SGE mbuf mapping failure count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_defrag_attempts", CTLFLAG_RD, &fp->mbuf_defrag_attempts, "Mbuf defrag attempt count"); SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "mbuf_defrag_failures", CTLFLAG_RD, &fp->mbuf_defrag_failures, "Mbuf defrag failure count"); } } while (0); #ifdef BXE_DEBUG SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "driver_state", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_driver_state, "I", "Drive state information"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "hw_state", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_hw_state, "I", "Hardware state information"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_fw", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_fw, "I", "Dump MCP firmware"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_rx_bd_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_rx_bd_chain, "I", "Dump rx_bd chain"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_rx_cq_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_rx_cq_chain, "I", "Dump cqe chain"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "dump_tx_chain", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_dump_tx_chain, "I", "Dump tx_bd chain"); /* * Generates a GRCdump (run sysctl dev.bxe.0.grcdump=0 * before accessing buffer below). */ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "grcdump", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_grcdump, "I", "Initiate a grcdump operation"); /* * Hidden sysctl. * Use "sysctl -b dev.bxe.0.grcdump_buffer > buf.bin". */ SYSCTL_ADD_OPAQUE(ctx, children, OID_AUTO, "grcdump_buffer", CTLFLAG_RD | CTLFLAG_SKIP, sc->grcdump_buffer, BXE_GRCDUMP_BUF_SIZE, "IU", "Access grcdump buffer"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "breakpoint", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_breakpoint, "I", "Driver breakpoint"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "reg_read", CTLTYPE_INT | CTLFLAG_RW, (void *)sc, 0, bxe_sysctl_reg_read, "I", "Register read"); #endif /* BXE_DEBUG */ } /* * BXE Debug Routines */ #ifdef BXE_DEBUG /* * Writes out the header for the debug dump buffer. * * Returns: * None. * * Modifies: * index */ static void bxe_dump_debug_header(struct bxe_softc *sc, uint32_t *index) { struct hd_param hd_param_cu = {0}; uint32_t *buf; buf = sc->grcdump_buffer; if (CHIP_IS_E1H(sc)) hd_param_cu = hd_param_e1h; else hd_param_cu = hd_param_e1; buf[(*index)++] = hd_param_cu.time_stamp; buf[(*index)++] = hd_param_cu.diag_ver; buf[(*index)++] = hd_param_cu.grc_dump_ver; buf[(*index)++] = REG_RD_IND(sc, XSTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, TSTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, USTORM_WAITP_ADDRESS); buf[(*index)++] = REG_RD_IND(sc, CSTORM_WAITP_ADDRESS); /* The size of the header is stored at the first DWORD. */ buf[0] = (*index) - 1; } /* * Writes to the controller to prepare it for a dump. * * Returns: * None. * * Modifies: * None. */ static void bxe_dump_debug_writes(struct bxe_softc *sc) { uint32_t write_val; write_val = 1; /* Halt the STORMs to get a consistent device state. */ REG_WR_IND(sc, XSTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, TSTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, USTORM_WAITP_ADDRESS, write_val); REG_WR_IND(sc, CSTORM_WAITP_ADDRESS, write_val); if (CHIP_IS_E1H(sc)) REG_WR_IND(sc, TSTORM_CAM_MODE, write_val); } /* * Cycles through the required register reads and dumps them * to the debug buffer. * * Returns: * None. * * Modifies: * index */ static void bxe_dump_debug_reg_read(struct bxe_softc *sc, uint32_t *index) { preg_addr preg_addrs; uint32_t regs_count, *buf; uint32_t i, reg_addrs_index; buf = sc->grcdump_buffer; preg_addrs = NULL; /* Read different registers for different controllers. */ if (CHIP_IS_E1H(sc)) { regs_count = regs_count_e1h; preg_addrs = ®_addrs_e1h[0]; } else { regs_count = regs_count_e1; preg_addrs = ®_addrs_e1[0]; } /* ToDo: Add a buffer size check. */ for (reg_addrs_index = 0; reg_addrs_index < regs_count; reg_addrs_index++) { for (i = 0; i < preg_addrs[reg_addrs_index].size; i++) { buf[(*index)++] = REG_RD_IND(sc, preg_addrs[reg_addrs_index].addr + (i * 4)); } } } /* * Cycles through the required wide register reads and dumps them * to the debug buffer. * * Returns: * None. */ static void bxe_dump_debug_reg_wread(struct bxe_softc *sc, uint32_t *index) { pwreg_addr pwreg_addrs; uint32_t reg_addrs_index, reg_add_read, reg_add_count; uint32_t *buf, cam_index, wregs_count; buf = sc->grcdump_buffer; pwreg_addrs = NULL; /* Read different registers for different controllers. */ if (CHIP_IS_E1H(sc)) { wregs_count = wregs_count_e1h; pwreg_addrs = &wreg_addrs_e1h[0]; } else { wregs_count = wregs_count_e1; pwreg_addrs = &wreg_addrs_e1[0]; } for (reg_addrs_index = 0; reg_addrs_index < wregs_count; reg_addrs_index++) { reg_add_read = pwreg_addrs[reg_addrs_index].addr; for (reg_add_count = 0; reg_add_count < pwreg_addrs[reg_addrs_index].size; reg_add_count++) { buf[(*index)++] = REG_RD_IND(sc, reg_add_read); reg_add_read += sizeof(uint32_t); for (cam_index = 0; cam_index < pwreg_addrs[reg_addrs_index].const_regs_count; cam_index++) buf[(*index)++] = REG_RD_IND(sc, pwreg_addrs[reg_addrs_index].const_regs[cam_index]); } } } /* * Performs a debug dump for offline diagnostics. * * Note that when this routine is called the STORM * processors will be stopped in order to create a * cohesive dump. The controller will need to be * reset before the device can begin passing traffic * again. * * Returns: * None. */ static void bxe_grcdump(struct bxe_softc *sc, int log) { uint32_t *buf, i, index; index = 1; buf = sc->grcdump_buffer; if (buf != NULL) { /* Write the header and regsiters contents to the dump buffer. */ bxe_dump_debug_header(sc, &index); bxe_dump_debug_writes(sc); bxe_dump_debug_reg_read(sc,&index); bxe_dump_debug_reg_wread(sc, &index); /* Print the results to the system log is necessary. */ if (log) { BXE_PRINTF( "-----------------------------" " grcdump " "-----------------------------\n"); BXE_PRINTF("Buffer length = 0x%08X bytes\n", index * 4); for (i = 0; i < index; i += 8) { BXE_PRINTF( "0x%08X - 0x%08X 0x%08X 0x%08X 0x%08X " "0x%08X 0x%08X 0x%08X 0x%08X\n", i * 4, buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } } else { BXE_PRINTF("No grcdump buffer allocated!\n"); } } /* * Check that an Etherent frame is valid and prints out debug info if it's * not. * * Returns: * Nothing. */ static __noinline void bxe_validate_rx_packet(struct bxe_fastpath *fp, uint16_t comp_cons, union eth_rx_cqe *cqe, struct mbuf *m) { struct bxe_softc *sc; int error; sc = fp->sc; /* Check that the mbuf is sane. */ error = m_sanity(m, FALSE); if (error != 1 || ((m->m_len < ETHER_HDR_LEN) | (m->m_len > ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))) { m_print(m, 128); bxe_dump_enet(sc, m); bxe_dump_cqe(fp, comp_cons, cqe); /* Make sure the packet has a valid length. */ } } /* * Prints out Ethernet frame information from an mbuf. * * Partially decode an Ethernet frame to look at some important headers. * * Returns: * Nothing. */ static __noinline void bxe_dump_enet(struct bxe_softc *sc, struct mbuf *m) { struct ether_vlan_header *eh; uint16_t etype; int e_hlen; struct ip *ip; struct tcphdr *th; struct udphdr *uh; struct arphdr *ah; BXE_PRINTF( "-----------------------------" " Frame Decode " "-----------------------------\n"); eh = mtod(m, struct ether_vlan_header *); /* Handle VLAN encapsulation if present. */ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { etype = ntohs(eh->evl_proto); e_hlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; } else { etype = ntohs(eh->evl_encap_proto); e_hlen = ETHER_HDR_LEN; } BXE_PRINTF("enet: dest = %6D, src = %6D, type = 0x%04X, e_hlen = %d\n", eh->evl_dhost, ":", eh->evl_shost, ":", etype, e_hlen); switch (etype) { case ETHERTYPE_IP: ip = (struct ip *)(m->m_data + e_hlen); BXE_PRINTF( "--ip: dest = 0x%08X , src = 0x%08X, " "ip_hlen = %d bytes, len = %d bytes, protocol = 0x%02X, " "ip_id = 0x%04X, csum = 0x%04X\n", ntohl(ip->ip_dst.s_addr), ntohl(ip->ip_src.s_addr), (ip->ip_hl << 2), ntohs(ip->ip_len), ip->ip_p, ntohs(ip->ip_id), ntohs(ip->ip_sum)); switch (ip->ip_p) { case IPPROTO_TCP: th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); BXE_PRINTF( "-tcp: dest = %d, src = %d, tcp_hlen = %d " "bytes, flags = 0x%b, csum = 0x%04X\n", ntohs(th->th_dport), ntohs(th->th_sport), (th->th_off << 2), th->th_flags, "\20\10CWR\07ECE\06URG\05ACK\04PSH\03RST\02SYN\01FIN", ntohs(th->th_sum)); break; case IPPROTO_UDP: uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); BXE_PRINTF( "-udp: dest = %d, src = %d, udp_hlen = %d " "bytes, len = %d bytes, csum = 0x%04X\n", ntohs(uh->uh_dport), ntohs(uh->uh_sport), (int)sizeof(struct udphdr), ntohs(uh->uh_ulen), ntohs(uh->uh_sum)); break; case IPPROTO_ICMP: BXE_PRINTF("icmp:\n"); break; default: BXE_PRINTF("----: Other IP protocol.\n"); } break; case ETHERTYPE_IPV6: /* ToDo: Add IPv6 support. */ BXE_PRINTF("IPv6 not supported!.\n"); break; case ETHERTYPE_ARP: BXE_PRINTF("-arp: "); ah = (struct arphdr *) (m->m_data + e_hlen); switch (ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: printf("reverse ARP request\n"); break; case ARPOP_REVREPLY: printf("reverse ARP reply\n"); break; case ARPOP_REQUEST: printf("ARP request\n"); break; case ARPOP_REPLY: printf("ARP reply\n"); break; default: printf("other ARP operation\n"); } break; default: BXE_PRINTF("----: Other protocol.\n"); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } #if 0 static void bxe_dump_mbuf_data(struct mbuf *m, int len) { uint8_t *ptr; int i; ptr = mtod(m, uint8_t *); printf("\nmbuf->m_data:"); printf("\n0x"); for (i = 0; i < len; i++){ if (i != 0 && i % 40 == 0) printf("\n0x"); else if (i != 0 && i % 6 == 0) printf(" 0x"); printf("%02x", *ptr++); } printf("\n\n"); } #endif /* * Prints out information about an mbuf. * * Returns: * Nothing. */ static __noinline void bxe_dump_mbuf(struct bxe_softc *sc, struct mbuf *m) { if (m == NULL) { BXE_PRINTF("mbuf: null pointer\n"); return; } while (m) { BXE_PRINTF("mbuf: %p, m_len = %d, m_flags = 0x%b, " "m_data = %p\n", m, m->m_len, m->m_flags, "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY", m->m_data); if (m->m_flags & M_PKTHDR) { BXE_PRINTF("- m_pkthdr: len = %d, flags = 0x%b, " "csum_flags = %b\n", m->m_pkthdr.len, m->m_flags, "\20\12M_BCAST\13M_MCAST\14M_FRAG" "\15M_FIRSTFRAG\16M_LASTFRAG\21M_VLANTAG" "\22M_PROMISC\23M_NOFREE", m->m_pkthdr.csum_flags, "\20\1CSUM_IP\2CSUM_TCP\3CSUM_UDP\4CSUM_IP_FRAGS" "\5CSUM_FRAGMENT\6CSUM_TSO\11CSUM_IP_CHECKED" "\12CSUM_IP_VALID\13CSUM_DATA_VALID" "\14CSUM_PSEUDO_HDR"); } if (m->m_flags & M_EXT) { BXE_PRINTF("- m_ext: %p, ext_size = %d, type = ", m->m_ext.ext_buf, m->m_ext.ext_size); switch (m->m_ext.ext_type) { case EXT_CLUSTER: printf("EXT_CLUSTER\n"); break; case EXT_SFBUF: printf("EXT_SFBUF\n"); break; case EXT_JUMBO9: printf("EXT_JUMBO9\n"); break; case EXT_JUMBO16: printf("EXT_JUMBO16\n"); break; case EXT_PACKET: printf("EXT_PACKET\n"); break; case EXT_MBUF: printf("EXT_MBUF\n"); break; case EXT_NET_DRV: printf("EXT_NET_DRV\n"); break; case EXT_MOD_TYPE: printf("EXT_MOD_TYPE\n"); break; case EXT_DISPOSABLE: printf("EXT_DISPOSABLE\n"); break; case EXT_EXTREF: printf("EXT_EXTREF\n"); break; default: printf("UNKNOWN\n"); } } m = m->m_next; } } /* * Prints out information about an rx_bd. * * Returns: * Nothing. */ static __noinline void bxe_dump_rxbd(struct bxe_fastpath *fp, int idx, struct eth_rx_bd *rx_bd) { struct bxe_softc *sc; sc = fp->sc; /* Check if index out of range. */ if (idx > MAX_RX_BD) { BXE_PRINTF("fp[%02d].rx_bd[0x%04X] XX: Invalid rx_bd index!\n", fp->index, idx); } else if ((idx & RX_BD_PER_PAGE_MASK) >= USABLE_RX_BD_PER_PAGE) { /* RX Chain page pointer. */ BXE_PRINTF("fp[%02d].rx_bd[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, rx_bd->addr_hi, rx_bd->addr_lo); } else { BXE_PRINTF("fp[%02d].rx_bd[0x%04X] RX: haddr=0x%08X:%08X\n", fp->index, idx, rx_bd->addr_hi, rx_bd->addr_lo); } } /* * Prints out a completion queue entry. * * Returns: * Nothing. */ static __noinline void bxe_dump_cqe(struct bxe_fastpath *fp, int idx, union eth_rx_cqe *cqe) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_RCQ_ENTRIES) { /* Index out of range. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X]: Invalid rx_cqe index!\n", fp->index, idx); } else if ((idx & USABLE_RCQ_ENTRIES_PER_PAGE) == USABLE_RCQ_ENTRIES_PER_PAGE) { /* CQE next page pointer. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, le32toh(cqe->next_page_cqe.addr_hi), le32toh(cqe->next_page_cqe.addr_lo)); } else { /* Normal CQE. */ BXE_PRINTF("fp[%02d].rx_cqe[0x%04X] CQ: error_flags=0x%b, " "pkt_len=0x%04X, status_flags=0x%02X, vlan=0x%04X " "rss_hash=0x%08X\n", fp->index, idx, cqe->fast_path_cqe.type_error_flags, BXE_ETH_FAST_PATH_RX_CQE_ERROR_FLAGS_PRINTFB, le16toh(cqe->fast_path_cqe.pkt_len), cqe->fast_path_cqe.status_flags, le16toh(cqe->fast_path_cqe.vlan_tag), le32toh(cqe->fast_path_cqe.rss_hash_result)); } } /* * Prints out information about a TX parsing BD. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_parsing_bd(struct bxe_fastpath *fp, int idx, struct eth_tx_parse_bd *p_bd) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_TX_BD){ /* Index out of range. */ BXE_PRINTF("fp[%02d].tx_bd[0x%04X] XX: Invalid tx_bd index!\n", fp->index, idx); } else { BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] PB: global_data=0x%b, " "tcp_flags=0x%b, ip_hlen=%04d, total_hlen=%04d, " "tcp_pseudo_csum=0x%04X, lso_mss=0x%04X, ip_id=0x%04X, " "tcp_send_seq=0x%08X\n", fp->index, idx, p_bd->global_data, BXE_ETH_TX_PARSE_BD_GLOBAL_DATA_PRINTFB, p_bd->tcp_flags, BXE_ETH_TX_PARSE_BD_TCP_FLAGS_PRINTFB, p_bd->ip_hlen, p_bd->total_hlen, p_bd->tcp_pseudo_csum, p_bd->lso_mss, p_bd->ip_id, p_bd->tcp_send_seq); } } /* * Prints out information about a tx_bd. * * Returns: * Nothing. */ static __noinline void bxe_dump_txbd(struct bxe_fastpath *fp, int idx, union eth_tx_bd_types *tx_bd) { struct bxe_softc *sc; sc = fp->sc; if (idx > MAX_TX_BD){ /* Index out of range. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] XX: Invalid tx_bd index!\n", fp->index, idx); } else if ((idx & USABLE_TX_BD_PER_PAGE) == USABLE_TX_BD_PER_PAGE) { /* TX next page BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] NP: haddr=0x%08X:%08X\n", fp->index, idx, tx_bd->next_bd.addr_hi, tx_bd->next_bd.addr_lo); } else if ((tx_bd->start_bd.bd_flags.as_bitfield & ETH_TX_BD_FLAGS_START_BD) != 0) { /* TX start BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] ST: haddr=0x%08X:%08X, " "nbd=%02d, nbytes=%05d, vlan/idx=0x%04X, flags=0x%b, " "gendata=0x%02X\n", fp->index, idx, tx_bd->start_bd.addr_hi, tx_bd->start_bd.addr_lo, tx_bd->start_bd.nbd, tx_bd->start_bd.nbytes, tx_bd->start_bd.vlan, tx_bd->start_bd.bd_flags.as_bitfield, BXE_ETH_TX_BD_FLAGS_PRINTFB, tx_bd->start_bd.general_data); } else { /* Regular TX BD. */ BXE_PRINTF("fp[%02d]:tx_bd[0x%04X] TX: haddr=0x%08X:%08X, " "total_pkt_bytes=%05d, nbytes=%05d\n", fp->index, idx, tx_bd->reg_bd.addr_hi, tx_bd->reg_bd.addr_lo, tx_bd->reg_bd.total_pkt_bytes, tx_bd->reg_bd.nbytes); } } /* * Prints out the transmit chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_chain(struct bxe_fastpath * fp, int tx_bd_prod, int count) { struct bxe_softc *sc; union eth_tx_bd_types *tx_bd; uint32_t val_hi, val_lo; int i, parsing_bd = 0; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " tx_bd chain " "----------------------------\n"); val_hi = U64_HI(fp->tx_dma.paddr); val_lo = U64_LO(fp->tx_dma.paddr); BXE_PRINTF( "0x%08X:%08X - (fp[%02d]->tx_dma.paddr) TX Chain physical address\n", val_hi, val_lo, fp->index); BXE_PRINTF( "page size = 0x%08X, tx chain pages = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t)NUM_TX_PAGES); BXE_PRINTF( "tx_bd per page = 0x%08X, usable tx_bd per page = 0x%08X\n", (uint32_t)TOTAL_TX_BD_PER_PAGE, (uint32_t)USABLE_TX_BD_PER_PAGE); BXE_PRINTF( "total tx_bd = 0x%08X\n", (uint32_t)TOTAL_TX_BD); BXE_PRINTF( "-----------------------------" " tx_bd data " "-----------------------------\n"); /* Now print out the tx_bd's themselves. */ for (i = 0; i < count; i++) { tx_bd = &fp->tx_chain[tx_bd_prod]; if (parsing_bd) { struct eth_tx_parse_bd *p_bd; p_bd = (struct eth_tx_parse_bd *) &fp->tx_chain[tx_bd_prod].parse_bd; bxe_dump_tx_parsing_bd(fp, tx_bd_prod, p_bd); parsing_bd = 0; } else { bxe_dump_txbd(fp, tx_bd_prod, tx_bd); if ((tx_bd->start_bd.bd_flags.as_bitfield & ETH_TX_BD_FLAGS_START_BD) != 0) /* * There is always a parsing BD following the * tx_bd with the start bit set. */ parsing_bd = 1; } /* Don't skip next page pointers. */ tx_bd_prod = ((tx_bd_prod + 1) & MAX_TX_BD); } BXE_PRINTF( "-----------------------------" "--------------" "-----------------------------\n"); } /* * Prints out the receive completion queue chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_cq_chain(struct bxe_fastpath *fp, int rx_cq_prod, int count) { struct bxe_softc *sc; union eth_rx_cqe *cqe; int i; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " CQE Chain " "----------------------------\n"); BXE_PRINTF("fp[%02d]->rcq_dma.paddr = 0x%jX\n", fp->index, (uintmax_t) fp->rcq_dma.paddr); BXE_PRINTF("page size = 0x%08X, cq chain pages " " = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t) NUM_RCQ_PAGES); BXE_PRINTF("cqe_bd per page = 0x%08X, usable cqe_bd per " "page = 0x%08X\n", (uint32_t) TOTAL_RCQ_ENTRIES_PER_PAGE, (uint32_t) USABLE_RCQ_ENTRIES_PER_PAGE); BXE_PRINTF("total cqe_bd = 0x%08X\n",(uint32_t) TOTAL_RCQ_ENTRIES); /* Now the CQE entries themselves. */ BXE_PRINTF( "----------------------------" " CQE Data " "----------------------------\n"); for (i = 0; i < count; i++) { cqe = (union eth_rx_cqe *)&fp->rcq_chain[rx_cq_prod]; bxe_dump_cqe(fp, rx_cq_prod, cqe); /* Don't skip next page pointers. */ rx_cq_prod = ((rx_cq_prod + 1) & MAX_RCQ_ENTRIES); } BXE_PRINTF( "----------------------------" "--------------" "----------------------------\n"); } /* * Prints out the receive chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_bd_chain(struct bxe_fastpath *fp, int prod, int count) { struct bxe_softc *sc; struct eth_rx_bd *rx_bd; struct mbuf *m; int i; sc = fp->sc; /* First some info about the tx_bd chain structure. */ BXE_PRINTF( "----------------------------" " rx_bd chain " "----------------------------\n"); BXE_PRINTF( "----- RX_BD Chain -----\n"); BXE_PRINTF("fp[%02d]->rx_dma.paddr = 0x%jX\n", fp->index, (uintmax_t) fp->rx_dma.paddr); BXE_PRINTF( "page size = 0x%08X, rx chain pages = 0x%08X\n", (uint32_t)BCM_PAGE_SIZE, (uint32_t)NUM_RX_PAGES); BXE_PRINTF( "rx_bd per page = 0x%08X, usable rx_bd per page = 0x%08X\n", (uint32_t)TOTAL_RX_BD_PER_PAGE, (uint32_t)USABLE_RX_BD_PER_PAGE); BXE_PRINTF( "total rx_bd = 0x%08X\n", (uint32_t)TOTAL_RX_BD); /* Now the rx_bd entries themselves. */ BXE_PRINTF( "----------------------------" " rx_bd data " "----------------------------\n"); /* Now print out the rx_bd's themselves. */ for (i = 0; i < count; i++) { rx_bd = (struct eth_rx_bd *) (&fp->rx_chain[prod]); m = sc->fp->rx_mbuf_ptr[prod]; bxe_dump_rxbd(fp, prod, rx_bd); bxe_dump_mbuf(sc, m); /* Don't skip next page pointers. */ prod = ((prod + 1) & MAX_RX_BD); } BXE_PRINTF( "----------------------------" "--------------" "----------------------------\n"); } /* * Prints out a register dump. * * Returns: * Nothing. */ static __noinline void bxe_dump_hw_state(struct bxe_softc *sc) { int i; BXE_PRINTF( "----------------------------" " Hardware State " "----------------------------\n"); for (i = 0x2000; i < 0x10000; i += 0x10) BXE_PRINTF("0x%04X: 0x%08X 0x%08X 0x%08X 0x%08X\n", i, REG_RD(sc, 0 + i), REG_RD(sc, 0 + i + 0x4), REG_RD(sc, 0 + i + 0x8), REG_RD(sc, 0 + i + 0xC)); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the RX mbuf chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_rx_mbuf_chain(struct bxe_softc *sc, int chain_prod, int count) { struct mbuf *m; int i; BXE_PRINTF( "----------------------------" " rx mbuf data " "----------------------------\n"); for (i = 0; i < count; i++) { m = sc->fp->rx_mbuf_ptr[chain_prod]; BXE_PRINTF("rxmbuf[0x%04X]\n", chain_prod); bxe_dump_mbuf(sc, m); chain_prod = RX_BD(NEXT_RX_BD(chain_prod)); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the mbufs in the TX mbuf chain. * * Returns: * Nothing. */ static __noinline void bxe_dump_tx_mbuf_chain(struct bxe_softc *sc, int chain_prod, int count) { struct mbuf *m; int i; BXE_PRINTF( "----------------------------" " tx mbuf data " "----------------------------\n"); for (i = 0; i < count; i++) { m = sc->fp->tx_mbuf_ptr[chain_prod]; BXE_PRINTF("txmbuf[%d]\n", chain_prod); bxe_dump_mbuf(sc, m); chain_prod = TX_BD(NEXT_TX_BD(chain_prod)); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the status block from host memory. * * Returns: * Nothing. */ static __noinline void bxe_dump_status_block(struct bxe_softc *sc) { struct bxe_fastpath *fp; struct host_def_status_block *def_sb; struct host_status_block *fpsb; int i; def_sb = sc->def_sb; BXE_PRINTF( "----------------------------" " Status Block " "----------------------------\n"); for (i = 0; i < sc->num_queues; i++) { fp = &sc->fp[i]; fpsb = fp->status_block; BXE_PRINTF( "----------------------------" " fp[%02d] " "----------------------------\n", fp->index); /* Print the USTORM fields (HC_USTORM_SB_NUM_INDICES). */ BXE_PRINTF( "0x%08X - USTORM Flags (F/W RESERVED)\n", fpsb->u_status_block.__flags); BXE_PRINTF( " 0x%02X - USTORM PCIe Function\n", fpsb->u_status_block.func); BXE_PRINTF( " 0x%02X - USTORM Status Block ID\n", fpsb->u_status_block.status_block_id); BXE_PRINTF( " 0x%04X - USTORM Status Block Index (Tag)\n", fpsb->u_status_block.status_block_index); BXE_PRINTF( " 0x%04X - USTORM [TOE_RX_CQ_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_TOE_RX_CQ_CONS]); BXE_PRINTF( " 0x%04X - USTORM [ETH_RX_CQ_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_ETH_RX_CQ_CONS]); BXE_PRINTF( " 0x%04X - USTORM [ETH_RX_BD_CONS]\n", fpsb->u_status_block.index_values[HC_INDEX_U_ETH_RX_BD_CONS]); BXE_PRINTF( " 0x%04X - USTORM [RESERVED]\n", fpsb->u_status_block.index_values[3]); /* Print the CSTORM fields (HC_CSTORM_SB_NUM_INDICES). */ BXE_PRINTF( "0x%08X - CSTORM Flags (F/W RESERVED)\n", fpsb->c_status_block.__flags); BXE_PRINTF( " 0x%02X - CSTORM PCIe Function\n", fpsb->c_status_block.func); BXE_PRINTF( " 0x%02X - CSTORM Status Block ID\n", fpsb->c_status_block.status_block_id); BXE_PRINTF( " 0x%04X - CSTORM Status Block Index (Tag)\n", fpsb->c_status_block.status_block_index); BXE_PRINTF( " 0x%04X - CSTORM [TOE_TX_CQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_TOE_TX_CQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [ETH_TX_CQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_ETH_TX_CQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [ISCSI_EQ_CONS]\n", fpsb->c_status_block.index_values[HC_INDEX_C_ISCSI_EQ_CONS]); BXE_PRINTF( " 0x%04X - CSTORM [RESERVED]\n", fpsb->c_status_block.index_values[3]); } BXE_PRINTF( "--------------------------" " Def Status Block " "--------------------------\n"); /* Print attention information. */ BXE_PRINTF( " 0x%02X - Status Block ID\n", def_sb->atten_status_block.status_block_id); BXE_PRINTF( "0x%08X - Attn Bits\n", def_sb->atten_status_block.attn_bits); BXE_PRINTF( "0x%08X - Attn Bits Ack\n", def_sb->atten_status_block.attn_bits_ack); BXE_PRINTF( " 0x%04X - Attn Block Index\n", le16toh(def_sb->atten_status_block.attn_bits_index)); /* Print the USTORM fields (HC_USTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - USTORM Status Block ID\n", def_sb->u_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - USTORM Status Block Index\n", le16toh(def_sb->u_def_status_block.status_block_index)); BXE_PRINTF( " 0x%04X - USTORM [ETH_RDMA_RX_CQ_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_RDMA_RX_CQ_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_ISCSI_RX_CQ_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_RDMA_RX_BD_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_RDMA_RX_BD_CONS])); BXE_PRINTF( " 0x%04X - USTORM [ETH_ISCSI_RX_BD_CONS]\n", le16toh(def_sb->u_def_status_block.index_values[HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS])); /* Print the CSTORM fields (HC_CSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - CSTORM Status Block ID\n", def_sb->c_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - CSTORM Status Block Index\n", le16toh(def_sb->c_def_status_block.status_block_index)); BXE_PRINTF( " 0x%04X - CSTORM [RDMA_EQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_RDMA_EQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [RDMA_NAL_PROD]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_RDMA_NAL_PROD])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_FW_TX_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_SLOW_PATH]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_SLOW_PATH])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_RDMA_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_RDMA_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [ETH_ISCSI_CQ_CONS]\n", le16toh(def_sb->c_def_status_block.index_values[HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS])); BXE_PRINTF( " 0x%04X - CSTORM [UNUSED]\n", le16toh(def_sb->c_def_status_block.index_values[6])); BXE_PRINTF( " 0x%04X - CSTORM [UNUSED]\n", le16toh(def_sb->c_def_status_block.index_values[7])); /* Print the TSTORM fields (HC_TSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - TSTORM Status Block ID\n", def_sb->t_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - TSTORM Status Block Index\n", le16toh(def_sb->t_def_status_block.status_block_index)); for (i = 0; i < HC_TSTORM_DEF_SB_NUM_INDICES; i++) BXE_PRINTF( " 0x%04X - TSTORM [UNUSED]\n", le16toh(def_sb->t_def_status_block.index_values[i])); /* Print the XSTORM fields (HC_XSTORM_DEF_SB_NUM_INDICES). */ BXE_PRINTF( " 0x%02X - XSTORM Status Block ID\n", def_sb->x_def_status_block.status_block_id); BXE_PRINTF( " 0x%04X - XSTORM Status Block Index\n", le16toh(def_sb->x_def_status_block.status_block_index)); for (i = 0; i < HC_XSTORM_DEF_SB_NUM_INDICES; i++) BXE_PRINTF( " 0x%04X - XSTORM [UNUSED]\n", le16toh(def_sb->x_def_status_block.index_values[i])); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out the statistics block from host memory. * * Returns: * Nothing. */ static __noinline void bxe_dump_stats_block(struct bxe_softc *sc) { } /* * Prints out a summary of the fastpath state. * * Returns: * Nothing. */ static __noinline void bxe_dump_fp_state(struct bxe_fastpath *fp) { struct bxe_softc *sc; uint32_t val_hi, val_lo; int i; sc = fp->sc; BXE_PRINTF( "----------------------------" " Fastpath State " "----------------------------\n"); val_hi = U64_HI(fp); val_lo = U64_LO(fp); BXE_PRINTF( "0x%08X:%08X - (fp[%02d]) fastpath virtual address\n", val_hi, val_lo, fp->index); BXE_PRINTF( " %3d - (fp[%02d]->sb_id)\n", fp->sb_id, fp->index); BXE_PRINTF( " %3d - (fp[%02d]->cl_id)\n", fp->cl_id, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->state)\n", (uint32_t)fp->state, fp->index); /* Receive state. */ BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_bd_prod)\n", fp->rx_bd_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_bd_cons)\n", fp->rx_bd_cons, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_cq_prod)\n", fp->rx_cq_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->rx_cq_cons)\n", fp->rx_cq_cons, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->rx_pkts)\n", fp->rx_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->rx_mbuf_alloc)\n", fp->rx_mbuf_alloc, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->ipackets)\n", fp->ipackets, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->rx_soft_errors)\n", fp->rx_soft_errors, fp->index); /* Transmit state. */ BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_used)\n", fp->tx_bd_used, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_prod)\n", fp->tx_bd_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_bd_cons)\n", fp->tx_bd_cons, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_pkt_prod)\n", fp->tx_pkt_prod, fp->index); BXE_PRINTF( " 0x%04X - (fp[%02d]->tx_pkt_cons)\n", fp->tx_pkt_cons, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->tx_pkts)\n", fp->tx_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->tx_mbuf_alloc)\n", fp->tx_mbuf_alloc, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->opackets)\n", fp->opackets, fp->index); BXE_PRINTF( " %16lu - (fp[%02d]->tx_soft_errors)\n", fp->tx_soft_errors, fp->index); /* TPA state. */ if (TPA_ENABLED(sc)) { BXE_PRINTF( " %16lu - (fp[%02d]->rx_tpa_pkts)\n", fp->rx_tpa_pkts, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_mbuf_alloc)\n", fp->tpa_mbuf_alloc, fp->index); BXE_PRINTF( " 0x%08X - (fp[%02d]->sge_mbuf_alloc)\n", fp->sge_mbuf_alloc, fp->index); if (CHIP_IS_E1(sc)) { for (i = 0; i < ETH_MAX_AGGREGATION_QUEUES_E1; i++) BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_state[%02d])\n", (uint32_t)fp->tpa_state[i], fp->index, i); } else { for (i = 0; i < ETH_MAX_AGGREGATION_QUEUES_E1; i++) BXE_PRINTF( " 0x%08X - (fp[%02d]->tpa_state[%02d])\n", (uint32_t)fp->tpa_state[i], fp->index, i); } } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Returns: * Nothing. */ static __noinline void bxe_dump_port_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "------------------------------" " Port State " "------------------------------\n"); BXE_PRINTF( " %2d - (port) pmf\n", sc->port.pmf); BXE_PRINTF( "0x%08X - (port) link_config\n", sc->port.link_config); BXE_PRINTF( "0x%08X - (port) supported\n", sc->port.supported); BXE_PRINTF( "0x%08X - (port) advertising\n", sc->port.advertising); BXE_PRINTF( "0x%08X - (port) port_stx\n", sc->port.port_stx); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Returns: * Nothing. */ static __noinline void bxe_dump_link_vars_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "---------------------------" " Link Vars State " "----------------------------\n"); switch (sc->link_vars.mac_type) { case MAC_TYPE_NONE: BXE_PRINTF(" NONE"); break; case MAC_TYPE_EMAC: BXE_PRINTF(" EMAC"); break; case MAC_TYPE_BMAC: BXE_PRINTF(" BMAC"); break; default: BXE_PRINTF(" UNKN"); } printf(" - (link_vars->mac_type)\n"); BXE_PRINTF( " %2d - (link_vars->phy_link_up)\n", sc->link_vars.phy_link_up); BXE_PRINTF( " %2d - (link_vars->link_up)\n", sc->link_vars.link_up); BXE_PRINTF( " %2d - (link_vars->duplex)\n", sc->link_vars.duplex); BXE_PRINTF( " 0x%04X - (link_vars->flow_ctrl)\n", sc->link_vars.flow_ctrl); BXE_PRINTF( " 0x%04X - (link_vars->line_speed)\n", sc->link_vars.line_speed); BXE_PRINTF( "0x%08X - (link_vars->ieee_fc)\n", sc->link_vars.ieee_fc); BXE_PRINTF( "0x%08X - (link_vars->autoneg)\n", sc->link_vars.autoneg); BXE_PRINTF( "0x%08X - (link_vars->phy_flags)\n", sc->link_vars.phy_flags); BXE_PRINTF( "0x%08X - (link_vars->link_status)\n", sc->link_vars.link_status); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * * Returns: * Nothing. */ static __noinline void bxe_dump_link_params_state_locked(struct bxe_softc *sc) { BXE_PRINTF( "--------------------------" " Link Params State " "---------------------------\n"); BXE_PRINTF( " %2d - (link_params->port)\n", sc->link_params.port); BXE_PRINTF( " %2d - (link_params->loopback_mode)\n", sc->link_params.loopback_mode); BXE_PRINTF( " %3d - (link_params->phy_addr)\n", sc->link_params.phy_addr); BXE_PRINTF( " 0x%04X - (link_params->req_duplex)\n", sc->link_params.req_duplex); BXE_PRINTF( " 0x%04X - (link_params->req_flow_ctrl)\n", sc->link_params.req_flow_ctrl); BXE_PRINTF( " 0x%04X - (link_params->req_line_speed)\n", sc->link_params.req_line_speed); BXE_PRINTF( " %5d - (link_params->ether_mtu)\n", sc->port.ether_mtu); BXE_PRINTF( "0x%08X - (link_params->shmem_base) shared memory base address\n", sc->link_params.shmem_base); BXE_PRINTF( "0x%08X - (link_params->speed_cap_mask)\n", sc->link_params.speed_cap_mask); BXE_PRINTF( "0x%08X - (link_params->ext_phy_config)\n", sc->link_params.ext_phy_config); BXE_PRINTF( "0x%08X - (link_params->switch_cfg)\n", sc->link_params.switch_cfg); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Prints out a summary of the driver state. * * Returns: * Nothing. */ static __noinline void bxe_dump_driver_state(struct bxe_softc *sc) { uint32_t val_hi, val_lo; BXE_PRINTF( "-----------------------------" " Driver State " "-----------------------------\n"); val_hi = U64_HI(sc); val_lo = U64_LO(sc); BXE_PRINTF( "0x%08X:%08X - (sc) driver softc structure virtual address\n", val_hi, val_lo); val_hi = U64_HI(sc->bxe_vhandle); val_lo = U64_LO(sc->bxe_vhandle); BXE_PRINTF( "0x%08X:%08X - (sc->bxe_vhandle) PCI BAR0 virtual address\n", val_hi, val_lo); val_hi = U64_HI(sc->bxe_db_vhandle); val_lo = U64_LO(sc->bxe_db_vhandle); BXE_PRINTF( "0x%08X:%08X - (sc->bxe_db_vhandle) PCI BAR2 virtual address\n", val_hi, val_lo); BXE_PRINTF(" 0x%08X - (sc->num_queues) Fastpath queues\n", sc->num_queues); BXE_PRINTF(" 0x%08X - (sc->rx_lane_swap) RX XAUI lane swap\n", sc->rx_lane_swap); BXE_PRINTF(" 0x%08X - (sc->tx_lane_swap) TX XAUI lane swap\n", sc->tx_lane_swap); BXE_PRINTF(" %16lu - (sc->debug_sim_mbuf_alloc_failed)\n", sc->debug_sim_mbuf_alloc_failed); BXE_PRINTF(" %16lu - (sc->debug_sim_mbuf_map_failed)\n", sc->debug_sim_mbuf_map_failed); BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); bxe_dump_port_state_locked(sc); bxe_dump_link_params_state_locked(sc); bxe_dump_link_vars_state_locked(sc); } /* * Dump bootcode (MCP) debug buffer to the console. * * Returns: * None */ static __noinline void bxe_dump_fw(struct bxe_softc *sc) { uint32_t addr, mark, data[9], offset; int word; addr = sc->common.shmem_base - 0x0800 + 4; mark = REG_RD(sc, addr); mark = MCP_REG_MCPR_SCRATCH + ((mark + 0x3) & ~0x3) - 0x08000000; BXE_PRINTF( "---------------------------" " MCP Debug Buffer " "---------------------------\n"); /* Read from "mark" to the end of the buffer. */ for (offset = mark; offset <= sc->common.shmem_base; offset += (0x8 * 4)) { for (word = 0; word < 8; word++) data[word] = htonl(REG_RD(sc, offset + 4 * word)); data[8] = 0x0; printf("%s", (char *) data); } /* Read from the start of the buffer to "mark". */ for (offset = addr + 4; offset <= mark; offset += (0x8 * 4)) { for (word = 0; word < 8; word++) data[word] = htonl(REG_RD(sc, offset + 4 * word)); data[8] = 0x0; printf("%s", (char *) data); } BXE_PRINTF( "----------------------------" "----------------" "----------------------------\n"); } /* * Decode firmware messages. * * Returns: * None */ static void bxe_decode_mb_msgs(struct bxe_softc *sc, uint32_t drv_mb_header, uint32_t fw_mb_header) { if (drv_mb_header) { BXE_PRINTF("Driver message is "); switch (drv_mb_header & DRV_MSG_CODE_MASK) { case DRV_MSG_CODE_LOAD_REQ: printf( "LOAD_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_REQ); break; case DRV_MSG_CODE_LOAD_DONE: printf( "LOAD_DONE (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_DONE); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_EN: printf( "UNLOAD_REQ_WOL_EN (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_EN); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS: printf( "UNLOAD_REQ_WOL_DIS (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS); break; case DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP: printf( "UNLOADREQ_WOL_MCP (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP); break; case DRV_MSG_CODE_UNLOAD_DONE: printf( "UNLOAD_DONE (0x%08X)", (uint32_t)DRV_MSG_CODE_UNLOAD_DONE); break; case DRV_MSG_CODE_DIAG_ENTER_REQ: printf( "DIAG_ENTER_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_DIAG_ENTER_REQ); break; case DRV_MSG_CODE_DIAG_EXIT_REQ: printf( "DIAG_EXIT_REQ (0x%08X)", (uint32_t)DRV_MSG_CODE_DIAG_EXIT_REQ); break; case DRV_MSG_CODE_VALIDATE_KEY: printf( "CODE_VALIDITY_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_VALIDATE_KEY); break; case DRV_MSG_CODE_GET_CURR_KEY: printf( "GET_CURR_KEY (0x%08X)", (uint32_t) DRV_MSG_CODE_GET_CURR_KEY); break; case DRV_MSG_CODE_GET_UPGRADE_KEY: printf( "GET_UPGRADE_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_GET_UPGRADE_KEY); break; case DRV_MSG_CODE_GET_MANUF_KEY: printf( "GET_MANUF_KEY (0x%08X)", (uint32_t)DRV_MSG_CODE_GET_MANUF_KEY); break; case DRV_MSG_CODE_LOAD_L2B_PRAM: printf( "LOAD_L2B_PRAM (0x%08X)", (uint32_t)DRV_MSG_CODE_LOAD_L2B_PRAM); break; case BIOS_MSG_CODE_LIC_CHALLENGE: printf( "LIC_CHALLENGE (0x%08X)", (uint32_t)BIOS_MSG_CODE_LIC_CHALLENGE); break; case BIOS_MSG_CODE_LIC_RESPONSE: printf( "LIC_RESPONSE (0x%08X)", (uint32_t)BIOS_MSG_CODE_LIC_RESPONSE); break; case BIOS_MSG_CODE_VIRT_MAC_PRIM: printf( "VIRT_MAC_PRIM (0x%08X)", (uint32_t)BIOS_MSG_CODE_VIRT_MAC_PRIM); break; case BIOS_MSG_CODE_VIRT_MAC_ISCSI: printf( "VIRT_MAC_ISCSI (0x%08X)", (uint32_t)BIOS_MSG_CODE_VIRT_MAC_ISCSI); break; default: printf( "Unknown command (0x%08X)!", (drv_mb_header & DRV_MSG_CODE_MASK)); } printf(" (seq = 0x%04X)\n", (drv_mb_header & DRV_MSG_SEQ_NUMBER_MASK)); } if (fw_mb_header) { BXE_PRINTF("Firmware response is "); switch (fw_mb_header & FW_MSG_CODE_MASK) { case FW_MSG_CODE_DRV_LOAD_COMMON: printf( "DRV_LOAD_COMMON (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_COMMON); break; case FW_MSG_CODE_DRV_LOAD_PORT: printf( "DRV_LOAD_PORT (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_PORT); break; case FW_MSG_CODE_DRV_LOAD_FUNCTION: printf( "DRV_LOAD_FUNCTION (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_FUNCTION); break; case FW_MSG_CODE_DRV_LOAD_REFUSED: printf( "DRV_LOAD_REFUSED (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_REFUSED); break; case FW_MSG_CODE_DRV_LOAD_DONE: printf( "DRV_LOAD_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_LOAD_DONE); break; case FW_MSG_CODE_DRV_UNLOAD_COMMON: printf( "DRV_UNLOAD_COMMON (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_COMMON); break; case FW_MSG_CODE_DRV_UNLOAD_PORT: printf( "DRV_UNLOAD_PORT (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_PORT); break; case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: printf( "DRV_UNLOAD_FUNCTION (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_FUNCTION); break; case FW_MSG_CODE_DRV_UNLOAD_DONE: printf( "DRV_UNLOAD_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DRV_UNLOAD_DONE); break; case FW_MSG_CODE_DIAG_ENTER_DONE: printf( "DIAG_ENTER_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_ENTER_DONE); break; case FW_MSG_CODE_DIAG_REFUSE: printf( "DIAG_REFUSE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_REFUSE); break; case FW_MSG_CODE_DIAG_EXIT_DONE: printf( "DIAG_EXIT_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_DIAG_EXIT_DONE); break; case FW_MSG_CODE_VALIDATE_KEY_SUCCESS: printf( "VALIDATE_KEY_SUCCESS (0x%08X)", (uint32_t)FW_MSG_CODE_VALIDATE_KEY_SUCCESS); break; case FW_MSG_CODE_VALIDATE_KEY_FAILURE: printf( "VALIDATE_KEY_FAILURE (0x%08X)", (uint32_t)FW_MSG_CODE_VALIDATE_KEY_FAILURE); break; case FW_MSG_CODE_GET_KEY_DONE: printf( "GET_KEY_DONE (0x%08X)", (uint32_t)FW_MSG_CODE_GET_KEY_DONE); break; case FW_MSG_CODE_NO_KEY: printf( "NO_KEY (0x%08X)", (uint32_t)FW_MSG_CODE_NO_KEY); break; default: printf( "unknown value (0x%08X)!", (fw_mb_header & FW_MSG_CODE_MASK)); } printf(" (seq = 0x%04X)\n", (fw_mb_header & FW_MSG_SEQ_NUMBER_MASK)); } } /* * Prints a text string for the ramrod command. * * Returns: * None */ static void bxe_decode_ramrod_cmd(struct bxe_softc *sc, int command) { BXE_PRINTF("Ramrod command = "); switch (command) { case RAMROD_CMD_ID_ETH_PORT_SETUP: printf("ETH_PORT_SETUP\n"); break; case RAMROD_CMD_ID_ETH_CLIENT_SETUP: printf("ETH_CLIENT_SETUP\n"); break; case RAMROD_CMD_ID_ETH_STAT_QUERY: printf("ETH_STAT_QUERY\n"); break; case RAMROD_CMD_ID_ETH_UPDATE: printf("ETH_UPDATE\n"); break; case RAMROD_CMD_ID_ETH_HALT: printf("ETH_HALT\n"); break; case RAMROD_CMD_ID_ETH_SET_MAC: printf("ETH_SET_MAC\n"); break; case RAMROD_CMD_ID_ETH_CFC_DEL: printf("ETH_CFC_DEL\n"); break; case RAMROD_CMD_ID_ETH_PORT_DEL: printf("ETH_PORT_DEL\n"); break; case RAMROD_CMD_ID_ETH_FORWARD_SETUP: printf("ETH_FORWARD_SETUP\n"); break; default: printf("Unknown ramrod command!\n"); } } /* * Prints out driver information and forces a kernel breakpoint. * * Returns: * Nothing. */ static void bxe_breakpoint(struct bxe_softc *sc) { struct bxe_fastpath *fp; int i; fp = &sc->fp[0]; /* Unreachable code to silence the compiler about unused functions. */ if (0) { bxe_reg_read16(sc, PCICFG_OFFSET); bxe_dump_tx_mbuf_chain(sc, 0, USABLE_TX_BD); bxe_dump_rx_mbuf_chain(sc, 0, USABLE_RX_BD); bxe_dump_tx_chain(fp, 0, USABLE_TX_BD); bxe_dump_rx_cq_chain(fp, 0, USABLE_RCQ_ENTRIES); bxe_dump_rx_bd_chain(fp, 0, USABLE_RX_BD); bxe_dump_status_block(sc); bxe_dump_stats_block(sc); bxe_dump_fp_state(fp); bxe_dump_driver_state(sc); bxe_dump_hw_state(sc); bxe_dump_fw(sc); } /* * Do some device sanity checking. Run it twice in case * the hardware is still running so we can identify any * transient conditions. */ bxe_idle_chk(sc); bxe_idle_chk(sc); bxe_dump_driver_state(sc); for (i = 0; i < sc->num_queues; i++) bxe_dump_fp_state(&sc->fp[i]); bxe_dump_status_block(sc); bxe_dump_fw(sc); /* Call the OS debugger. */ breakpoint(); } #endif Index: head/sys/dev/ciss/ciss.c =================================================================== --- head/sys/dev/ciss/ciss.c (revision 232853) +++ head/sys/dev/ciss/ciss.c (revision 232854) @@ -1,4659 +1,4659 @@ /*- * Copyright (c) 2001 Michael Smith * Copyright (c) 2004 Paul Saab * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Common Interface for SCSI-3 Support driver. * * CISS claims to provide a common interface between a generic SCSI * transport and an intelligent host adapter. * * This driver supports CISS as defined in the document "CISS Command * Interface for SCSI-3 Support Open Specification", Version 1.04, * Valence Number 1, dated 20001127, produced by Compaq Computer * Corporation. This document appears to be a hastily and somewhat * arbitrarlily cut-down version of a larger (and probably even more * chaotic and inconsistent) Compaq internal document. Various * details were also gleaned from Compaq's "cciss" driver for Linux. * * We provide a shim layer between the CISS interface and CAM, * offloading most of the queueing and being-a-disk chores onto CAM. * Entry to the driver is via the PCI bus attachment (ciss_probe, * ciss_attach, etc) and via the CAM interface (ciss_cam_action, * ciss_cam_poll). The Compaq CISS adapters are, however, poor SCSI * citizens and we have to fake up some responses to get reasonable * behaviour out of them. In addition, the CISS command set is by no * means adequate to support the functionality of a RAID controller, * and thus the supported Compaq adapters utilise portions of the * control protocol from earlier Compaq adapter families. * * Note that we only support the "simple" transport layer over PCI. * This interface (ab)uses the I2O register set (specifically the post * queues) to exchange commands with the adapter. Other interfaces * are available, but we aren't supposed to know about them, and it is * dubious whether they would provide major performance improvements * except under extreme load. * * Currently the only supported CISS adapters are the Compaq Smart * Array 5* series (5300, 5i, 532). Even with only three adapters, * Compaq still manage to have interface variations. * * * Thanks must go to Fred Harris and Darryl DeVinney at Compaq, as * well as Paul Saab at Yahoo! for their assistance in making this * driver happen. * * More thanks must go to John Cagle at HP for the countless hours * spent making this driver "work" with the MSA* series storage * enclosures. Without his help (and nagging), this driver could not * be used with these enclosures. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(CISS_MALLOC_CLASS, "ciss_data", "ciss internal data buffers"); /* pci interface */ static int ciss_lookup(device_t dev); static int ciss_probe(device_t dev); static int ciss_attach(device_t dev); static int ciss_detach(device_t dev); static int ciss_shutdown(device_t dev); /* (de)initialisation functions, control wrappers */ static int ciss_init_pci(struct ciss_softc *sc); static int ciss_setup_msix(struct ciss_softc *sc); static int ciss_init_perf(struct ciss_softc *sc); static int ciss_wait_adapter(struct ciss_softc *sc); static int ciss_flush_adapter(struct ciss_softc *sc); static int ciss_init_requests(struct ciss_softc *sc); static void ciss_command_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int ciss_identify_adapter(struct ciss_softc *sc); static int ciss_init_logical(struct ciss_softc *sc); static int ciss_init_physical(struct ciss_softc *sc); static int ciss_filter_physical(struct ciss_softc *sc, struct ciss_lun_report *cll); static int ciss_identify_logical(struct ciss_softc *sc, struct ciss_ldrive *ld); static int ciss_get_ldrive_status(struct ciss_softc *sc, struct ciss_ldrive *ld); static int ciss_update_config(struct ciss_softc *sc); static int ciss_accept_media(struct ciss_softc *sc, struct ciss_ldrive *ld); static void ciss_init_sysctl(struct ciss_softc *sc); static void ciss_soft_reset(struct ciss_softc *sc); static void ciss_free(struct ciss_softc *sc); static void ciss_spawn_notify_thread(struct ciss_softc *sc); static void ciss_kill_notify_thread(struct ciss_softc *sc); /* request submission/completion */ static int ciss_start(struct ciss_request *cr); static void ciss_done(struct ciss_softc *sc, cr_qhead_t *qh); static void ciss_perf_done(struct ciss_softc *sc, cr_qhead_t *qh); static void ciss_intr(void *arg); static void ciss_perf_intr(void *arg); static void ciss_perf_msi_intr(void *arg); static void ciss_complete(struct ciss_softc *sc, cr_qhead_t *qh); static int _ciss_report_request(struct ciss_request *cr, int *command_status, int *scsi_status, const char *func); static int ciss_synch_request(struct ciss_request *cr, int timeout); static int ciss_poll_request(struct ciss_request *cr, int timeout); static int ciss_wait_request(struct ciss_request *cr, int timeout); #if 0 static int ciss_abort_request(struct ciss_request *cr); #endif /* request queueing */ static int ciss_get_request(struct ciss_softc *sc, struct ciss_request **crp); static void ciss_preen_command(struct ciss_request *cr); static void ciss_release_request(struct ciss_request *cr); /* request helpers */ static int ciss_get_bmic_request(struct ciss_softc *sc, struct ciss_request **crp, int opcode, void **bufp, size_t bufsize); static int ciss_user_command(struct ciss_softc *sc, IOCTL_Command_struct *ioc); /* DMA map/unmap */ static int ciss_map_request(struct ciss_request *cr); static void ciss_request_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static void ciss_unmap_request(struct ciss_request *cr); /* CAM interface */ static int ciss_cam_init(struct ciss_softc *sc); static void ciss_cam_rescan_target(struct ciss_softc *sc, int bus, int target); static void ciss_cam_action(struct cam_sim *sim, union ccb *ccb); static int ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio); static int ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio); static void ciss_cam_poll(struct cam_sim *sim); static void ciss_cam_complete(struct ciss_request *cr); static void ciss_cam_complete_fixup(struct ciss_softc *sc, struct ccb_scsiio *csio); static struct cam_periph *ciss_find_periph(struct ciss_softc *sc, int bus, int target); static int ciss_name_device(struct ciss_softc *sc, int bus, int target); /* periodic status monitoring */ static void ciss_periodic(void *arg); static void ciss_nop_complete(struct ciss_request *cr); static void ciss_disable_adapter(struct ciss_softc *sc); static void ciss_notify_event(struct ciss_softc *sc); static void ciss_notify_complete(struct ciss_request *cr); static int ciss_notify_abort(struct ciss_softc *sc); static int ciss_notify_abort_bmic(struct ciss_softc *sc); static void ciss_notify_hotplug(struct ciss_softc *sc, struct ciss_notify *cn); static void ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn); static void ciss_notify_physical(struct ciss_softc *sc, struct ciss_notify *cn); /* debugging output */ static void ciss_print_request(struct ciss_request *cr); static void ciss_print_ldrive(struct ciss_softc *sc, struct ciss_ldrive *ld); static const char *ciss_name_ldrive_status(int status); static int ciss_decode_ldrive_status(int status); static const char *ciss_name_ldrive_org(int org); static const char *ciss_name_command_status(int status); /* * PCI bus interface. */ static device_method_t ciss_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ciss_probe), DEVMETHOD(device_attach, ciss_attach), DEVMETHOD(device_detach, ciss_detach), DEVMETHOD(device_shutdown, ciss_shutdown), { 0, 0 } }; static driver_t ciss_pci_driver = { "ciss", ciss_methods, sizeof(struct ciss_softc) }; static devclass_t ciss_devclass; DRIVER_MODULE(ciss, pci, ciss_pci_driver, ciss_devclass, 0, 0); MODULE_DEPEND(ciss, cam, 1, 1, 1); MODULE_DEPEND(ciss, pci, 1, 1, 1); /* * Control device interface. */ static d_open_t ciss_open; static d_close_t ciss_close; static d_ioctl_t ciss_ioctl; static struct cdevsw ciss_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = ciss_open, .d_close = ciss_close, .d_ioctl = ciss_ioctl, .d_name = "ciss", }; /* * This tunable can be set at boot time and controls whether physical devices * that are marked hidden by the firmware should be exposed anyways. */ static unsigned int ciss_expose_hidden_physical = 0; TUNABLE_INT("hw.ciss.expose_hidden_physical", &ciss_expose_hidden_physical); static unsigned int ciss_nop_message_heartbeat = 0; TUNABLE_INT("hw.ciss.nop_message_heartbeat", &ciss_nop_message_heartbeat); /* * This tunable can force a particular transport to be used: * <= 0 : use default * 1 : force simple * 2 : force performant */ static int ciss_force_transport = 0; TUNABLE_INT("hw.ciss.force_transport", &ciss_force_transport); /* * This tunable can force a particular interrupt delivery method to be used: * <= 0 : use default * 1 : force INTx * 2 : force MSIX */ static int ciss_force_interrupt = 0; TUNABLE_INT("hw.ciss.force_interrupt", &ciss_force_interrupt); /************************************************************************ * CISS adapters amazingly don't have a defined programming interface * value. (One could say some very despairing things about PCI and * people just not getting the general idea.) So we are forced to * stick with matching against subvendor/subdevice, and thus have to * be updated for every new CISS adapter that appears. */ #define CISS_BOARD_UNKNWON 0 #define CISS_BOARD_SA5 1 #define CISS_BOARD_SA5B 2 #define CISS_BOARD_NOMSI (1<<4) static struct { u_int16_t subvendor; u_int16_t subdevice; int flags; char *desc; } ciss_vendor_data[] = { { 0x0e11, 0x4070, CISS_BOARD_SA5|CISS_BOARD_NOMSI, "Compaq Smart Array 5300" }, { 0x0e11, 0x4080, CISS_BOARD_SA5B|CISS_BOARD_NOMSI, "Compaq Smart Array 5i" }, { 0x0e11, 0x4082, CISS_BOARD_SA5B|CISS_BOARD_NOMSI, "Compaq Smart Array 532" }, { 0x0e11, 0x4083, CISS_BOARD_SA5B|CISS_BOARD_NOMSI, "HP Smart Array 5312" }, { 0x0e11, 0x4091, CISS_BOARD_SA5, "HP Smart Array 6i" }, { 0x0e11, 0x409A, CISS_BOARD_SA5, "HP Smart Array 641" }, { 0x0e11, 0x409B, CISS_BOARD_SA5, "HP Smart Array 642" }, { 0x0e11, 0x409C, CISS_BOARD_SA5, "HP Smart Array 6400" }, { 0x0e11, 0x409D, CISS_BOARD_SA5, "HP Smart Array 6400 EM" }, { 0x103C, 0x3211, CISS_BOARD_SA5, "HP Smart Array E200i" }, { 0x103C, 0x3212, CISS_BOARD_SA5, "HP Smart Array E200" }, { 0x103C, 0x3213, CISS_BOARD_SA5, "HP Smart Array E200i" }, { 0x103C, 0x3214, CISS_BOARD_SA5, "HP Smart Array E200i" }, { 0x103C, 0x3215, CISS_BOARD_SA5, "HP Smart Array E200i" }, { 0x103C, 0x3220, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3222, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3223, CISS_BOARD_SA5, "HP Smart Array P800" }, { 0x103C, 0x3225, CISS_BOARD_SA5, "HP Smart Array P600" }, { 0x103C, 0x3230, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3231, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3232, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3233, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3234, CISS_BOARD_SA5, "HP Smart Array P400" }, { 0x103C, 0x3235, CISS_BOARD_SA5, "HP Smart Array P400i" }, { 0x103C, 0x3236, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3237, CISS_BOARD_SA5, "HP Smart Array E500" }, { 0x103C, 0x3238, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3239, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x323A, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x323B, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x323C, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x323D, CISS_BOARD_SA5, "HP Smart Array P700m" }, { 0x103C, 0x3241, CISS_BOARD_SA5, "HP Smart Array P212" }, { 0x103C, 0x3243, CISS_BOARD_SA5, "HP Smart Array P410" }, { 0x103C, 0x3245, CISS_BOARD_SA5, "HP Smart Array P410i" }, { 0x103C, 0x3247, CISS_BOARD_SA5, "HP Smart Array P411" }, { 0x103C, 0x3249, CISS_BOARD_SA5, "HP Smart Array P812" }, { 0x103C, 0x324A, CISS_BOARD_SA5, "HP Smart Array P712m" }, { 0x103C, 0x324B, CISS_BOARD_SA5, "HP Smart Array" }, { 0x103C, 0x3350, CISS_BOARD_SA5, "HP Smart Array P222" }, { 0x103C, 0x3351, CISS_BOARD_SA5, "HP Smart Array P420" }, { 0x103C, 0x3352, CISS_BOARD_SA5, "HP Smart Array P421" }, { 0x103C, 0x3353, CISS_BOARD_SA5, "HP Smart Array P822" }, { 0x103C, 0x3354, CISS_BOARD_SA5, "HP Smart Array P420i" }, { 0x103C, 0x3355, CISS_BOARD_SA5, "HP Smart Array P220i" }, { 0x103C, 0x3356, CISS_BOARD_SA5, "HP Smart Array P721m" }, { 0, 0, 0, NULL } }; /************************************************************************ * Find a match for the device in our list of known adapters. */ static int ciss_lookup(device_t dev) { int i; for (i = 0; ciss_vendor_data[i].desc != NULL; i++) if ((pci_get_subvendor(dev) == ciss_vendor_data[i].subvendor) && (pci_get_subdevice(dev) == ciss_vendor_data[i].subdevice)) { return(i); } return(-1); } /************************************************************************ * Match a known CISS adapter. */ static int ciss_probe(device_t dev) { int i; i = ciss_lookup(dev); if (i != -1) { device_set_desc(dev, ciss_vendor_data[i].desc); return(BUS_PROBE_DEFAULT); } return(ENOENT); } /************************************************************************ * Attach the driver to this adapter. */ static int ciss_attach(device_t dev) { struct ciss_softc *sc; int error; debug_called(1); #ifdef CISS_DEBUG /* print structure/union sizes */ debug_struct(ciss_command); debug_struct(ciss_header); debug_union(ciss_device_address); debug_struct(ciss_cdb); debug_struct(ciss_report_cdb); debug_struct(ciss_notify_cdb); debug_struct(ciss_notify); debug_struct(ciss_message_cdb); debug_struct(ciss_error_info_pointer); debug_struct(ciss_error_info); debug_struct(ciss_sg_entry); debug_struct(ciss_config_table); debug_struct(ciss_bmic_cdb); debug_struct(ciss_bmic_id_ldrive); debug_struct(ciss_bmic_id_lstatus); debug_struct(ciss_bmic_id_table); debug_struct(ciss_bmic_id_pdrive); debug_struct(ciss_bmic_blink_pdrive); debug_struct(ciss_bmic_flush_cache); debug_const(CISS_MAX_REQUESTS); debug_const(CISS_MAX_LOGICAL); debug_const(CISS_INTERRUPT_COALESCE_DELAY); debug_const(CISS_INTERRUPT_COALESCE_COUNT); debug_const(CISS_COMMAND_ALLOC_SIZE); debug_const(CISS_COMMAND_SG_LENGTH); debug_type(cciss_pci_info_struct); debug_type(cciss_coalint_struct); debug_type(cciss_coalint_struct); debug_type(NodeName_type); debug_type(NodeName_type); debug_type(Heartbeat_type); debug_type(BusTypes_type); debug_type(FirmwareVer_type); debug_type(DriverVer_type); debug_type(IOCTL_Command_struct); #endif sc = device_get_softc(dev); sc->ciss_dev = dev; mtx_init(&sc->ciss_mtx, "cissmtx", NULL, MTX_DEF); callout_init_mtx(&sc->ciss_periodic, &sc->ciss_mtx, 0); /* * Do PCI-specific init. */ if ((error = ciss_init_pci(sc)) != 0) goto out; /* * Initialise driver queues. */ ciss_initq_free(sc); ciss_initq_notify(sc); /* * Initalize device sysctls. */ ciss_init_sysctl(sc); /* * Initialise command/request pool. */ if ((error = ciss_init_requests(sc)) != 0) goto out; /* * Get adapter information. */ if ((error = ciss_identify_adapter(sc)) != 0) goto out; /* * Find all the physical devices. */ if ((error = ciss_init_physical(sc)) != 0) goto out; /* * Build our private table of logical devices. */ if ((error = ciss_init_logical(sc)) != 0) goto out; /* * Enable interrupts so that the CAM scan can complete. */ CISS_TL_SIMPLE_ENABLE_INTERRUPTS(sc); /* * Initialise the CAM interface. */ if ((error = ciss_cam_init(sc)) != 0) goto out; /* * Start the heartbeat routine and event chain. */ ciss_periodic(sc); /* * Create the control device. */ sc->ciss_dev_t = make_dev(&ciss_cdevsw, device_get_unit(sc->ciss_dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "ciss%d", device_get_unit(sc->ciss_dev)); sc->ciss_dev_t->si_drv1 = sc; /* * The adapter is running; synchronous commands can now sleep * waiting for an interrupt to signal completion. */ sc->ciss_flags |= CISS_FLAG_RUNNING; ciss_spawn_notify_thread(sc); error = 0; out: if (error != 0) { /* ciss_free() expects the mutex to be held */ mtx_lock(&sc->ciss_mtx); ciss_free(sc); } return(error); } /************************************************************************ * Detach the driver from this adapter. */ static int ciss_detach(device_t dev) { struct ciss_softc *sc = device_get_softc(dev); debug_called(1); mtx_lock(&sc->ciss_mtx); if (sc->ciss_flags & CISS_FLAG_CONTROL_OPEN) { mtx_unlock(&sc->ciss_mtx); return (EBUSY); } /* flush adapter cache */ ciss_flush_adapter(sc); /* release all resources. The mutex is released and freed here too. */ ciss_free(sc); return(0); } /************************************************************************ * Prepare adapter for system shutdown. */ static int ciss_shutdown(device_t dev) { struct ciss_softc *sc = device_get_softc(dev); debug_called(1); mtx_lock(&sc->ciss_mtx); /* flush adapter cache */ ciss_flush_adapter(sc); if (sc->ciss_soft_reset) ciss_soft_reset(sc); mtx_unlock(&sc->ciss_mtx); return(0); } static void ciss_init_sysctl(struct ciss_softc *sc) { SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->ciss_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ciss_dev)), OID_AUTO, "soft_reset", CTLFLAG_RW, &sc->ciss_soft_reset, 0, ""); } /************************************************************************ * Perform PCI-specific attachment actions. */ static int ciss_init_pci(struct ciss_softc *sc) { uintptr_t cbase, csize, cofs; uint32_t method, supported_methods; int error, sqmask, i; void *intr; debug_called(1); /* * Work out adapter type. */ i = ciss_lookup(sc->ciss_dev); if (i < 0) { ciss_printf(sc, "unknown adapter type\n"); return (ENXIO); } if (ciss_vendor_data[i].flags & CISS_BOARD_SA5) { sqmask = CISS_TL_SIMPLE_INTR_OPQ_SA5; } else if (ciss_vendor_data[i].flags & CISS_BOARD_SA5B) { sqmask = CISS_TL_SIMPLE_INTR_OPQ_SA5B; } else { /* * XXX Big hammer, masks/unmasks all possible interrupts. This should * work on all hardware variants. Need to add code to handle the * "controller crashed" interupt bit that this unmasks. */ sqmask = ~0; } /* * Allocate register window first (we need this to find the config * struct). */ error = ENXIO; sc->ciss_regs_rid = CISS_TL_SIMPLE_BAR_REGS; if ((sc->ciss_regs_resource = bus_alloc_resource_any(sc->ciss_dev, SYS_RES_MEMORY, &sc->ciss_regs_rid, RF_ACTIVE)) == NULL) { ciss_printf(sc, "can't allocate register window\n"); return(ENXIO); } sc->ciss_regs_bhandle = rman_get_bushandle(sc->ciss_regs_resource); sc->ciss_regs_btag = rman_get_bustag(sc->ciss_regs_resource); /* * Find the BAR holding the config structure. If it's not the one * we already mapped for registers, map it too. */ sc->ciss_cfg_rid = CISS_TL_SIMPLE_READ(sc, CISS_TL_SIMPLE_CFG_BAR) & 0xffff; if (sc->ciss_cfg_rid != sc->ciss_regs_rid) { if ((sc->ciss_cfg_resource = bus_alloc_resource_any(sc->ciss_dev, SYS_RES_MEMORY, &sc->ciss_cfg_rid, RF_ACTIVE)) == NULL) { ciss_printf(sc, "can't allocate config window\n"); return(ENXIO); } cbase = (uintptr_t)rman_get_virtual(sc->ciss_cfg_resource); csize = rman_get_end(sc->ciss_cfg_resource) - rman_get_start(sc->ciss_cfg_resource) + 1; } else { cbase = (uintptr_t)rman_get_virtual(sc->ciss_regs_resource); csize = rman_get_end(sc->ciss_regs_resource) - rman_get_start(sc->ciss_regs_resource) + 1; } cofs = CISS_TL_SIMPLE_READ(sc, CISS_TL_SIMPLE_CFG_OFF); /* * Use the base/size/offset values we just calculated to * sanity-check the config structure. If it's OK, point to it. */ if ((cofs + sizeof(struct ciss_config_table)) > csize) { ciss_printf(sc, "config table outside window\n"); return(ENXIO); } sc->ciss_cfg = (struct ciss_config_table *)(cbase + cofs); debug(1, "config struct at %p", sc->ciss_cfg); /* * Calculate the number of request structures/commands we are * going to provide for this adapter. */ sc->ciss_max_requests = min(CISS_MAX_REQUESTS, sc->ciss_cfg->max_outstanding_commands); /* * Validate the config structure. If we supported other transport * methods, we could select amongst them at this point in time. */ if (strncmp(sc->ciss_cfg->signature, "CISS", 4)) { ciss_printf(sc, "config signature mismatch (got '%c%c%c%c')\n", sc->ciss_cfg->signature[0], sc->ciss_cfg->signature[1], sc->ciss_cfg->signature[2], sc->ciss_cfg->signature[3]); return(ENXIO); } /* * Select the mode of operation, prefer Performant. */ if (!(sc->ciss_cfg->supported_methods & (CISS_TRANSPORT_METHOD_SIMPLE | CISS_TRANSPORT_METHOD_PERF))) { ciss_printf(sc, "No supported transport layers: 0x%x\n", sc->ciss_cfg->supported_methods); } switch (ciss_force_transport) { case 1: supported_methods = CISS_TRANSPORT_METHOD_SIMPLE; break; case 2: supported_methods = CISS_TRANSPORT_METHOD_PERF; break; default: supported_methods = sc->ciss_cfg->supported_methods; break; } setup: if ((supported_methods & CISS_TRANSPORT_METHOD_PERF) != 0) { method = CISS_TRANSPORT_METHOD_PERF; sc->ciss_perf = (struct ciss_perf_config *)(cbase + cofs + sc->ciss_cfg->transport_offset); if (ciss_init_perf(sc)) { supported_methods &= ~method; goto setup; } } else if (supported_methods & CISS_TRANSPORT_METHOD_SIMPLE) { method = CISS_TRANSPORT_METHOD_SIMPLE; } else { ciss_printf(sc, "No supported transport methods: 0x%x\n", sc->ciss_cfg->supported_methods); return(ENXIO); } /* * Tell it we're using the low 4GB of RAM. Set the default interrupt * coalescing options. */ sc->ciss_cfg->requested_method = method; sc->ciss_cfg->command_physlimit = 0; sc->ciss_cfg->interrupt_coalesce_delay = CISS_INTERRUPT_COALESCE_DELAY; sc->ciss_cfg->interrupt_coalesce_count = CISS_INTERRUPT_COALESCE_COUNT; #ifdef __i386__ sc->ciss_cfg->host_driver |= CISS_DRIVER_SCSI_PREFETCH; #endif if (ciss_update_config(sc)) { ciss_printf(sc, "adapter refuses to accept config update (IDBR 0x%x)\n", CISS_TL_SIMPLE_READ(sc, CISS_TL_SIMPLE_IDBR)); return(ENXIO); } if ((sc->ciss_cfg->active_method & method) == 0) { supported_methods &= ~method; if (supported_methods == 0) { ciss_printf(sc, "adapter refuses to go into available transports " "mode (0x%x, 0x%x)\n", supported_methods, sc->ciss_cfg->active_method); return(ENXIO); } else goto setup; } /* * Wait for the adapter to come ready. */ if ((error = ciss_wait_adapter(sc)) != 0) return(error); /* Prepare to possibly use MSIX and/or PERFORMANT interrupts. Normal * interrupts have a rid of 0, this will be overridden if MSIX is used. */ sc->ciss_irq_rid[0] = 0; if (method == CISS_TRANSPORT_METHOD_PERF) { ciss_printf(sc, "PERFORMANT Transport\n"); if ((ciss_force_interrupt != 1) && (ciss_setup_msix(sc) == 0)) { intr = ciss_perf_msi_intr; } else { intr = ciss_perf_intr; } /* XXX The docs say that the 0x01 bit is only for SAS controllers. * Unfortunately, there is no good way to know if this is a SAS * controller. Hopefully enabling this bit universally will work OK. * It seems to work fine for SA6i controllers. */ sc->ciss_interrupt_mask = CISS_TL_PERF_INTR_OPQ | CISS_TL_PERF_INTR_MSI; } else { ciss_printf(sc, "SIMPLE Transport\n"); /* MSIX doesn't seem to work in SIMPLE mode, only enable if it forced */ if (ciss_force_interrupt == 2) /* If this fails, we automatically revert to INTx */ ciss_setup_msix(sc); sc->ciss_perf = NULL; intr = ciss_intr; sc->ciss_interrupt_mask = sqmask; } /* * Turn off interrupts before we go routing anything. */ CISS_TL_SIMPLE_DISABLE_INTERRUPTS(sc); /* * Allocate and set up our interrupt. */ if ((sc->ciss_irq_resource = bus_alloc_resource_any(sc->ciss_dev, SYS_RES_IRQ, &sc->ciss_irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) { ciss_printf(sc, "can't allocate interrupt\n"); return(ENXIO); } if (bus_setup_intr(sc->ciss_dev, sc->ciss_irq_resource, INTR_TYPE_CAM|INTR_MPSAFE, NULL, intr, sc, &sc->ciss_intr)) { ciss_printf(sc, "can't set up interrupt\n"); return(ENXIO); } /* * Allocate the parent bus DMA tag appropriate for our PCI * interface. * * Note that "simple" adapters can only address within a 32-bit * span. */ - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(sc->ciss_dev),/* PCI parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ CISS_MAX_SG_ELEMENTS, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ciss_parent_dmat)) { ciss_printf(sc, "can't allocate parent DMA tag\n"); return(ENOMEM); } /* * Create DMA tag for mapping buffers into adapter-addressable * space. */ if (bus_dma_tag_create(sc->ciss_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, CISS_MAX_SG_ELEMENTS, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, &sc->ciss_mtx, /* lockfunc, lockarg */ &sc->ciss_buffer_dmat)) { ciss_printf(sc, "can't allocate buffer DMA tag\n"); return(ENOMEM); } return(0); } /************************************************************************ * Setup MSI/MSIX operation (Performant only) * Four interrupts are available, but we only use 1 right now. If MSI-X * isn't avaialble, try using MSI instead. */ static int ciss_setup_msix(struct ciss_softc *sc) { int val, i; /* Weed out devices that don't actually support MSI */ i = ciss_lookup(sc->ciss_dev); if (ciss_vendor_data[i].flags & CISS_BOARD_NOMSI) return (EINVAL); /* * Only need to use the minimum number of MSI vectors, as the driver * doesn't support directed MSIX interrupts. */ val = pci_msix_count(sc->ciss_dev); if (val < CISS_MSI_COUNT) { val = pci_msi_count(sc->ciss_dev); device_printf(sc->ciss_dev, "got %d MSI messages]\n", val); if (val < CISS_MSI_COUNT) return (EINVAL); } val = MIN(val, CISS_MSI_COUNT); if (pci_alloc_msix(sc->ciss_dev, &val) != 0) { if (pci_alloc_msi(sc->ciss_dev, &val) != 0) return (EINVAL); } sc->ciss_msi = val; if (bootverbose) ciss_printf(sc, "Using %d MSIX interrupt%s\n", val, (val != 1) ? "s" : ""); for (i = 0; i < val; i++) sc->ciss_irq_rid[i] = i + 1; return (0); } /************************************************************************ * Setup the Performant structures. */ static int ciss_init_perf(struct ciss_softc *sc) { struct ciss_perf_config *pc = sc->ciss_perf; int reply_size; /* * Create the DMA tag for the reply queue. */ reply_size = sizeof(uint64_t) * sc->ciss_max_requests; if (bus_dma_tag_create(sc->ciss_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ reply_size, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ciss_reply_dmat)) { ciss_printf(sc, "can't allocate reply DMA tag\n"); return(ENOMEM); } /* * Allocate memory and make it available for DMA. */ if (bus_dmamem_alloc(sc->ciss_reply_dmat, (void **)&sc->ciss_reply, BUS_DMA_NOWAIT, &sc->ciss_reply_map)) { ciss_printf(sc, "can't allocate reply memory\n"); return(ENOMEM); } bus_dmamap_load(sc->ciss_reply_dmat, sc->ciss_reply_map, sc->ciss_reply, reply_size, ciss_command_map_helper, &sc->ciss_reply_phys, 0); bzero(sc->ciss_reply, reply_size); sc->ciss_cycle = 0x1; sc->ciss_rqidx = 0; /* * Preload the fetch table with common command sizes. This allows the * hardware to not waste bus cycles for typical i/o commands, but also not * tax the driver to be too exact in choosing sizes. The table is optimized * for page-aligned i/o's, but since most i/o comes from the various pagers, * it's a reasonable assumption to make. */ pc->fetch_count[CISS_SG_FETCH_NONE] = (sizeof(struct ciss_command) + 15) / 16; pc->fetch_count[CISS_SG_FETCH_1] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 1 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_2] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 2 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_4] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 4 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_8] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 8 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_16] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 16 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_32] = (sizeof(struct ciss_command) + sizeof(struct ciss_sg_entry) * 32 + 15) / 16; pc->fetch_count[CISS_SG_FETCH_MAX] = (CISS_COMMAND_ALLOC_SIZE + 15) / 16; pc->rq_size = sc->ciss_max_requests; /* XXX less than the card supports? */ pc->rq_count = 1; /* XXX Hardcode for a single queue */ pc->rq_bank_hi = 0; pc->rq_bank_lo = 0; pc->rq[0].rq_addr_hi = 0x0; pc->rq[0].rq_addr_lo = sc->ciss_reply_phys; return(0); } /************************************************************************ * Wait for the adapter to come ready. */ static int ciss_wait_adapter(struct ciss_softc *sc) { int i; debug_called(1); /* * Wait for the adapter to come ready. */ if (!(sc->ciss_cfg->active_method & CISS_TRANSPORT_METHOD_READY)) { ciss_printf(sc, "waiting for adapter to come ready...\n"); for (i = 0; !(sc->ciss_cfg->active_method & CISS_TRANSPORT_METHOD_READY); i++) { DELAY(1000000); /* one second */ if (i > 30) { ciss_printf(sc, "timed out waiting for adapter to come ready\n"); return(EIO); } } } return(0); } /************************************************************************ * Flush the adapter cache. */ static int ciss_flush_adapter(struct ciss_softc *sc) { struct ciss_request *cr; struct ciss_bmic_flush_cache *cbfc; int error, command_status; debug_called(1); cr = NULL; cbfc = NULL; /* * Build a BMIC request to flush the cache. We don't disable * it, as we may be going to do more I/O (eg. we are emulating * the Synchronise Cache command). */ if ((cbfc = malloc(sizeof(*cbfc), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; goto out; } if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_FLUSH_CACHE, (void **)&cbfc, sizeof(*cbfc))) != 0) goto out; /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC FLUSH_CACHE command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: break; default: ciss_printf(sc, "error flushing cache (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } out: if (cbfc != NULL) free(cbfc, CISS_MALLOC_CLASS); if (cr != NULL) ciss_release_request(cr); return(error); } static void ciss_soft_reset(struct ciss_softc *sc) { struct ciss_request *cr = NULL; struct ciss_command *cc; int i, error = 0; for (i = 0; i < sc->ciss_max_logical_bus; i++) { /* only reset proxy controllers */ if (sc->ciss_controllers[i].physical.bus == 0) continue; if ((error = ciss_get_request(sc, &cr)) != 0) break; if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_SOFT_RESET, NULL, 0)) != 0) break; cc = cr->cr_cc; cc->header.address = sc->ciss_controllers[i]; if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) break; ciss_release_request(cr); } if (error) ciss_printf(sc, "error resetting controller (%d)\n", error); if (cr != NULL) ciss_release_request(cr); } /************************************************************************ * Allocate memory for the adapter command structures, initialise * the request structures. * * Note that the entire set of commands are allocated in a single * contiguous slab. */ static int ciss_init_requests(struct ciss_softc *sc) { struct ciss_request *cr; int i; debug_called(1); if (bootverbose) ciss_printf(sc, "using %d of %d available commands\n", sc->ciss_max_requests, sc->ciss_cfg->max_outstanding_commands); /* * Create the DMA tag for commands. */ if (bus_dma_tag_create(sc->ciss_parent_dmat, /* parent */ 32, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ CISS_COMMAND_ALLOC_SIZE * sc->ciss_max_requests, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ciss_command_dmat)) { ciss_printf(sc, "can't allocate command DMA tag\n"); return(ENOMEM); } /* * Allocate memory and make it available for DMA. */ if (bus_dmamem_alloc(sc->ciss_command_dmat, (void **)&sc->ciss_command, BUS_DMA_NOWAIT, &sc->ciss_command_map)) { ciss_printf(sc, "can't allocate command memory\n"); return(ENOMEM); } bus_dmamap_load(sc->ciss_command_dmat, sc->ciss_command_map,sc->ciss_command, CISS_COMMAND_ALLOC_SIZE * sc->ciss_max_requests, ciss_command_map_helper, &sc->ciss_command_phys, 0); bzero(sc->ciss_command, CISS_COMMAND_ALLOC_SIZE * sc->ciss_max_requests); /* * Set up the request and command structures, push requests onto * the free queue. */ for (i = 1; i < sc->ciss_max_requests; i++) { cr = &sc->ciss_request[i]; cr->cr_sc = sc; cr->cr_tag = i; cr->cr_cc = (struct ciss_command *)((uintptr_t)sc->ciss_command + CISS_COMMAND_ALLOC_SIZE * i); cr->cr_ccphys = sc->ciss_command_phys + CISS_COMMAND_ALLOC_SIZE * i; bus_dmamap_create(sc->ciss_buffer_dmat, 0, &cr->cr_datamap); ciss_enqueue_free(cr); } return(0); } static void ciss_command_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { uint32_t *addr; addr = arg; *addr = segs[0].ds_addr; } /************************************************************************ * Identify the adapter, print some information about it. */ static int ciss_identify_adapter(struct ciss_softc *sc) { struct ciss_request *cr; int error, command_status; debug_called(1); cr = NULL; /* * Get a request, allocate storage for the adapter data. */ if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_ID_CTLR, (void **)&sc->ciss_id, sizeof(*sc->ciss_id))) != 0) goto out; /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC ID_CTLR command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: /* buffer right size */ break; case CISS_CMD_STATUS_DATA_UNDERRUN: case CISS_CMD_STATUS_DATA_OVERRUN: ciss_printf(sc, "data over/underrun reading adapter information\n"); default: ciss_printf(sc, "error reading adapter information (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } /* sanity-check reply */ if (!sc->ciss_id->big_map_supported) { ciss_printf(sc, "adapter does not support BIG_MAP\n"); error = ENXIO; goto out; } #if 0 /* XXX later revisions may not need this */ sc->ciss_flags |= CISS_FLAG_FAKE_SYNCH; #endif /* XXX only really required for old 5300 adapters? */ sc->ciss_flags |= CISS_FLAG_BMIC_ABORT; /* print information */ if (bootverbose) { #if 0 /* XXX proxy volumes??? */ ciss_printf(sc, " %d logical drive%s configured\n", sc->ciss_id->configured_logical_drives, (sc->ciss_id->configured_logical_drives == 1) ? "" : "s"); #endif ciss_printf(sc, " firmware %4.4s\n", sc->ciss_id->running_firmware_revision); ciss_printf(sc, " %d SCSI channels\n", sc->ciss_id->scsi_bus_count); ciss_printf(sc, " signature '%.4s'\n", sc->ciss_cfg->signature); ciss_printf(sc, " valence %d\n", sc->ciss_cfg->valence); ciss_printf(sc, " supported I/O methods 0x%b\n", sc->ciss_cfg->supported_methods, "\20\1READY\2simple\3performant\4MEMQ\n"); ciss_printf(sc, " active I/O method 0x%b\n", sc->ciss_cfg->active_method, "\20\2simple\3performant\4MEMQ\n"); ciss_printf(sc, " 4G page base 0x%08x\n", sc->ciss_cfg->command_physlimit); ciss_printf(sc, " interrupt coalesce delay %dus\n", sc->ciss_cfg->interrupt_coalesce_delay); ciss_printf(sc, " interrupt coalesce count %d\n", sc->ciss_cfg->interrupt_coalesce_count); ciss_printf(sc, " max outstanding commands %d\n", sc->ciss_cfg->max_outstanding_commands); ciss_printf(sc, " bus types 0x%b\n", sc->ciss_cfg->bus_types, "\20\1ultra2\2ultra3\10fibre1\11fibre2\n"); ciss_printf(sc, " server name '%.16s'\n", sc->ciss_cfg->server_name); ciss_printf(sc, " heartbeat 0x%x\n", sc->ciss_cfg->heartbeat); } out: if (error) { if (sc->ciss_id != NULL) { free(sc->ciss_id, CISS_MALLOC_CLASS); sc->ciss_id = NULL; } } if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Helper routine for generating a list of logical and physical luns. */ static struct ciss_lun_report * ciss_report_luns(struct ciss_softc *sc, int opcode, int nunits) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_report_cdb *crc; struct ciss_lun_report *cll; int command_status; int report_size; int error = 0; debug_called(1); cr = NULL; cll = NULL; /* * Get a request, allocate storage for the address list. */ if ((error = ciss_get_request(sc, &cr)) != 0) goto out; report_size = sizeof(*cll) + nunits * sizeof(union ciss_device_address); if ((cll = malloc(report_size, CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO)) == NULL) { ciss_printf(sc, "can't allocate memory for lun report\n"); error = ENOMEM; goto out; } /* * Build the Report Logical/Physical LUNs command. */ cc = cr->cr_cc; cr->cr_data = cll; cr->cr_length = report_size; cr->cr_flags = CISS_REQ_DATAIN; cc->header.address.physical.mode = CISS_HDR_ADDRESS_MODE_PERIPHERAL; cc->header.address.physical.bus = 0; cc->header.address.physical.target = 0; cc->cdb.cdb_length = sizeof(*crc); cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_READ; cc->cdb.timeout = 30; /* XXX better suggestions? */ crc = (struct ciss_report_cdb *)&(cc->cdb.cdb[0]); bzero(crc, sizeof(*crc)); crc->opcode = opcode; crc->length = htonl(report_size); /* big-endian field */ cll->list_size = htonl(report_size - sizeof(*cll)); /* big-endian field */ /* * Submit the request and wait for it to complete. (timeout * here should be much greater than above) */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending %d LUN command (%d)\n", opcode, error); goto out; } /* * Check response. Note that data over/underrun is OK. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: /* buffer right size */ case CISS_CMD_STATUS_DATA_UNDERRUN: /* buffer too large, not bad */ break; case CISS_CMD_STATUS_DATA_OVERRUN: ciss_printf(sc, "WARNING: more units than driver limit (%d)\n", CISS_MAX_LOGICAL); break; default: ciss_printf(sc, "error detecting logical drive configuration (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } ciss_release_request(cr); cr = NULL; out: if (cr != NULL) ciss_release_request(cr); if (error && cll != NULL) { free(cll, CISS_MALLOC_CLASS); cll = NULL; } return(cll); } /************************************************************************ * Find logical drives on the adapter. */ static int ciss_init_logical(struct ciss_softc *sc) { struct ciss_lun_report *cll; int error = 0, i, j; int ndrives; debug_called(1); cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_LOGICAL_LUNS, CISS_MAX_LOGICAL); if (cll == NULL) { error = ENXIO; goto out; } /* sanity-check reply */ ndrives = (ntohl(cll->list_size) / sizeof(union ciss_device_address)); if ((ndrives < 0) || (ndrives > CISS_MAX_LOGICAL)) { ciss_printf(sc, "adapter claims to report absurd number of logical drives (%d > %d)\n", ndrives, CISS_MAX_LOGICAL); error = ENXIO; goto out; } /* * Save logical drive information. */ if (bootverbose) { ciss_printf(sc, "%d logical drive%s\n", ndrives, (ndrives > 1 || ndrives == 0) ? "s" : ""); } sc->ciss_logical = malloc(sc->ciss_max_logical_bus * sizeof(struct ciss_ldrive *), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_logical == NULL) { error = ENXIO; goto out; } for (i = 0; i <= sc->ciss_max_logical_bus; i++) { sc->ciss_logical[i] = malloc(CISS_MAX_LOGICAL * sizeof(struct ciss_ldrive), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_logical[i] == NULL) { error = ENXIO; goto out; } for (j = 0; j < CISS_MAX_LOGICAL; j++) sc->ciss_logical[i][j].cl_status = CISS_LD_NONEXISTENT; } for (i = 0; i < CISS_MAX_LOGICAL; i++) { if (i < ndrives) { struct ciss_ldrive *ld; int bus, target; bus = CISS_LUN_TO_BUS(cll->lun[i].logical.lun); target = CISS_LUN_TO_TARGET(cll->lun[i].logical.lun); ld = &sc->ciss_logical[bus][target]; ld->cl_address = cll->lun[i]; ld->cl_controller = &sc->ciss_controllers[bus]; if (ciss_identify_logical(sc, ld) != 0) continue; /* * If the drive has had media exchanged, we should bring it online. */ if (ld->cl_lstatus->media_exchanged) ciss_accept_media(sc, ld); } } out: if (cll != NULL) free(cll, CISS_MALLOC_CLASS); return(error); } static int ciss_init_physical(struct ciss_softc *sc) { struct ciss_lun_report *cll; int error = 0, i; int nphys; int bus, target; debug_called(1); bus = 0; target = 0; cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_PHYSICAL_LUNS, CISS_MAX_PHYSICAL); if (cll == NULL) { error = ENXIO; goto out; } nphys = (ntohl(cll->list_size) / sizeof(union ciss_device_address)); if (bootverbose) { ciss_printf(sc, "%d physical device%s\n", nphys, (nphys > 1 || nphys == 0) ? "s" : ""); } /* * Figure out the bus mapping. * Logical buses include both the local logical bus for local arrays and * proxy buses for remote arrays. Physical buses are numbered by the * controller and represent physical buses that hold physical devices. * We shift these bus numbers so that everything fits into a single flat * numbering space for CAM. Logical buses occupy the first 32 CAM bus * numbers, and the physical bus numbers are shifted to be above that. * This results in the various driver arrays being indexed as follows: * * ciss_controllers[] - indexed by logical bus * ciss_cam_sim[] - indexed by both logical and physical, with physical * being shifted by 32. * ciss_logical[][] - indexed by logical bus * ciss_physical[][] - indexed by physical bus * * XXX This is getting more and more hackish. CISS really doesn't play * well with a standard SCSI model; devices are addressed via magic * cookies, not via b/t/l addresses. Since there is no way to store * the cookie in the CAM device object, we have to keep these lookup * tables handy so that the devices can be found quickly at the cost * of wasting memory and having a convoluted lookup scheme. This * driver should probably be converted to block interface. */ /* * If the L2 and L3 SCSI addresses are 0, this signifies a proxy * controller. A proxy controller is another physical controller * behind the primary PCI controller. We need to know about this * so that BMIC commands can be properly targeted. There can be * proxy controllers attached to a single PCI controller, so * find the highest numbered one so the array can be properly * sized. */ sc->ciss_max_logical_bus = 1; for (i = 0; i < nphys; i++) { if (cll->lun[i].physical.extra_address == 0) { bus = cll->lun[i].physical.bus; sc->ciss_max_logical_bus = max(sc->ciss_max_logical_bus, bus) + 1; } else { bus = CISS_EXTRA_BUS2(cll->lun[i].physical.extra_address); sc->ciss_max_physical_bus = max(sc->ciss_max_physical_bus, bus); } } sc->ciss_controllers = malloc(sc->ciss_max_logical_bus * sizeof (union ciss_device_address), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_controllers == NULL) { ciss_printf(sc, "Could not allocate memory for controller map\n"); error = ENOMEM; goto out; } /* setup a map of controller addresses */ for (i = 0; i < nphys; i++) { if (cll->lun[i].physical.extra_address == 0) { sc->ciss_controllers[cll->lun[i].physical.bus] = cll->lun[i]; } } sc->ciss_physical = malloc(sc->ciss_max_physical_bus * sizeof(struct ciss_pdrive *), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_physical == NULL) { ciss_printf(sc, "Could not allocate memory for physical device map\n"); error = ENOMEM; goto out; } for (i = 0; i < sc->ciss_max_physical_bus; i++) { sc->ciss_physical[i] = malloc(sizeof(struct ciss_pdrive) * CISS_MAX_PHYSTGT, CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_physical[i] == NULL) { ciss_printf(sc, "Could not allocate memory for target map\n"); error = ENOMEM; goto out; } } ciss_filter_physical(sc, cll); out: if (cll != NULL) free(cll, CISS_MALLOC_CLASS); return(error); } static int ciss_filter_physical(struct ciss_softc *sc, struct ciss_lun_report *cll) { u_int32_t ea; int i, nphys; int bus, target; nphys = (ntohl(cll->list_size) / sizeof(union ciss_device_address)); for (i = 0; i < nphys; i++) { if (cll->lun[i].physical.extra_address == 0) continue; /* * Filter out devices that we don't want. Level 3 LUNs could * probably be supported, but the docs don't give enough of a * hint to know how. * * The mode field of the physical address is likely set to have * hard disks masked out. Honor it unless the user has overridden * us with the tunable. We also munge the inquiry data for these * disks so that they only show up as passthrough devices. Keeping * them visible in this fashion is useful for doing things like * flashing firmware. */ ea = cll->lun[i].physical.extra_address; if ((CISS_EXTRA_BUS3(ea) != 0) || (CISS_EXTRA_TARGET3(ea) != 0) || (CISS_EXTRA_MODE2(ea) == 0x3)) continue; if ((ciss_expose_hidden_physical == 0) && (cll->lun[i].physical.mode == CISS_HDR_ADDRESS_MODE_MASK_PERIPHERAL)) continue; /* * Note: CISS firmware numbers physical busses starting at '1', not * '0'. This numbering is internal to the firmware and is only * used as a hint here. */ bus = CISS_EXTRA_BUS2(ea) - 1; target = CISS_EXTRA_TARGET2(ea); sc->ciss_physical[bus][target].cp_address = cll->lun[i]; sc->ciss_physical[bus][target].cp_online = 1; } return (0); } static int ciss_inquiry_logical(struct ciss_softc *sc, struct ciss_ldrive *ld) { struct ciss_request *cr; struct ciss_command *cc; struct scsi_inquiry *inq; int error; int command_status; cr = NULL; bzero(&ld->cl_geometry, sizeof(ld->cl_geometry)); if ((error = ciss_get_request(sc, &cr)) != 0) goto out; cc = cr->cr_cc; cr->cr_data = &ld->cl_geometry; cr->cr_length = sizeof(ld->cl_geometry); cr->cr_flags = CISS_REQ_DATAIN; cc->header.address = ld->cl_address; cc->cdb.cdb_length = 6; cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_READ; cc->cdb.timeout = 30; inq = (struct scsi_inquiry *)&(cc->cdb.cdb[0]); inq->opcode = INQUIRY; inq->byte2 = SI_EVPD; inq->page_code = CISS_VPD_LOGICAL_DRIVE_GEOMETRY; scsi_ulto2b(sizeof(ld->cl_geometry), inq->length); if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error getting geometry (%d)\n", error); goto out; } ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: case CISS_CMD_STATUS_DATA_UNDERRUN: break; case CISS_CMD_STATUS_DATA_OVERRUN: ciss_printf(sc, "WARNING: Data overrun\n"); break; default: ciss_printf(sc, "Error detecting logical drive geometry (%s)\n", ciss_name_command_status(command_status)); break; } out: if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Identify a logical drive, initialise state related to it. */ static int ciss_identify_logical(struct ciss_softc *sc, struct ciss_ldrive *ld) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_bmic_cdb *cbc; int error, command_status; debug_called(1); cr = NULL; /* * Build a BMIC request to fetch the drive ID. */ if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_ID_LDRIVE, (void **)&ld->cl_ldrive, sizeof(*ld->cl_ldrive))) != 0) goto out; cc = cr->cr_cc; cc->header.address = *ld->cl_controller; /* target controller */ cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]); cbc->log_drive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun); /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC LDRIVE command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: /* buffer right size */ break; case CISS_CMD_STATUS_DATA_UNDERRUN: case CISS_CMD_STATUS_DATA_OVERRUN: ciss_printf(sc, "data over/underrun reading logical drive ID\n"); default: ciss_printf(sc, "error reading logical drive ID (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } ciss_release_request(cr); cr = NULL; /* * Build a CISS BMIC command to get the logical drive status. */ if ((error = ciss_get_ldrive_status(sc, ld)) != 0) goto out; /* * Get the logical drive geometry. */ if ((error = ciss_inquiry_logical(sc, ld)) != 0) goto out; /* * Print the drive's basic characteristics. */ if (bootverbose) { ciss_printf(sc, "logical drive (b%dt%d): %s, %dMB ", CISS_LUN_TO_BUS(ld->cl_address.logical.lun), CISS_LUN_TO_TARGET(ld->cl_address.logical.lun), ciss_name_ldrive_org(ld->cl_ldrive->fault_tolerance), ((ld->cl_ldrive->blocks_available / (1024 * 1024)) * ld->cl_ldrive->block_size)); ciss_print_ldrive(sc, ld); } out: if (error != 0) { /* make the drive not-exist */ ld->cl_status = CISS_LD_NONEXISTENT; if (ld->cl_ldrive != NULL) { free(ld->cl_ldrive, CISS_MALLOC_CLASS); ld->cl_ldrive = NULL; } if (ld->cl_lstatus != NULL) { free(ld->cl_lstatus, CISS_MALLOC_CLASS); ld->cl_lstatus = NULL; } } if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Get status for a logical drive. * * XXX should we also do this in response to Test Unit Ready? */ static int ciss_get_ldrive_status(struct ciss_softc *sc, struct ciss_ldrive *ld) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_bmic_cdb *cbc; int error, command_status; /* * Build a CISS BMIC command to get the logical drive status. */ if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_ID_LSTATUS, (void **)&ld->cl_lstatus, sizeof(*ld->cl_lstatus))) != 0) goto out; cc = cr->cr_cc; cc->header.address = *ld->cl_controller; /* target controller */ cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]); cbc->log_drive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun); /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC LSTATUS command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: /* buffer right size */ break; case CISS_CMD_STATUS_DATA_UNDERRUN: case CISS_CMD_STATUS_DATA_OVERRUN: ciss_printf(sc, "data over/underrun reading logical drive status\n"); default: ciss_printf(sc, "error reading logical drive status (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } /* * Set the drive's summary status based on the returned status. * * XXX testing shows that a failed JBOD drive comes back at next * boot in "queued for expansion" mode. WTF? */ ld->cl_status = ciss_decode_ldrive_status(ld->cl_lstatus->status); out: if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Notify the adapter of a config update. */ static int ciss_update_config(struct ciss_softc *sc) { int i; debug_called(1); CISS_TL_SIMPLE_WRITE(sc, CISS_TL_SIMPLE_IDBR, CISS_TL_SIMPLE_IDBR_CFG_TABLE); for (i = 0; i < 1000; i++) { if (!(CISS_TL_SIMPLE_READ(sc, CISS_TL_SIMPLE_IDBR) & CISS_TL_SIMPLE_IDBR_CFG_TABLE)) { return(0); } DELAY(1000); } return(1); } /************************************************************************ * Accept new media into a logical drive. * * XXX The drive has previously been offline; it would be good if we * could make sure it's not open right now. */ static int ciss_accept_media(struct ciss_softc *sc, struct ciss_ldrive *ld) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_bmic_cdb *cbc; int command_status; int error = 0, ldrive; ldrive = CISS_LUN_TO_TARGET(ld->cl_address.logical.lun); debug(0, "bringing logical drive %d back online"); /* * Build a CISS BMIC command to bring the drive back online. */ if ((error = ciss_get_bmic_request(sc, &cr, CISS_BMIC_ACCEPT_MEDIA, NULL, 0)) != 0) goto out; cc = cr->cr_cc; cc->header.address = *ld->cl_controller; /* target controller */ cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]); cbc->log_drive = ldrive; /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC ACCEPT MEDIA command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: /* all OK */ /* we should get a logical drive status changed event here */ break; default: ciss_printf(cr->cr_sc, "error accepting media into failed logical drive (%s)\n", ciss_name_command_status(command_status)); break; } out: if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Release adapter resources. */ static void ciss_free(struct ciss_softc *sc) { struct ciss_request *cr; int i, j; debug_called(1); /* we're going away */ sc->ciss_flags |= CISS_FLAG_ABORTING; /* terminate the periodic heartbeat routine */ callout_stop(&sc->ciss_periodic); /* cancel the Event Notify chain */ ciss_notify_abort(sc); ciss_kill_notify_thread(sc); /* disconnect from CAM */ if (sc->ciss_cam_sim) { for (i = 0; i < sc->ciss_max_logical_bus; i++) { if (sc->ciss_cam_sim[i]) { xpt_bus_deregister(cam_sim_path(sc->ciss_cam_sim[i])); cam_sim_free(sc->ciss_cam_sim[i], 0); } } for (i = CISS_PHYSICAL_BASE; i < sc->ciss_max_physical_bus + CISS_PHYSICAL_BASE; i++) { if (sc->ciss_cam_sim[i]) { xpt_bus_deregister(cam_sim_path(sc->ciss_cam_sim[i])); cam_sim_free(sc->ciss_cam_sim[i], 0); } } free(sc->ciss_cam_sim, CISS_MALLOC_CLASS); } if (sc->ciss_cam_devq) cam_simq_free(sc->ciss_cam_devq); /* remove the control device */ mtx_unlock(&sc->ciss_mtx); if (sc->ciss_dev_t != NULL) destroy_dev(sc->ciss_dev_t); /* Final cleanup of the callout. */ callout_drain(&sc->ciss_periodic); mtx_destroy(&sc->ciss_mtx); /* free the controller data */ if (sc->ciss_id != NULL) free(sc->ciss_id, CISS_MALLOC_CLASS); /* release I/O resources */ if (sc->ciss_regs_resource != NULL) bus_release_resource(sc->ciss_dev, SYS_RES_MEMORY, sc->ciss_regs_rid, sc->ciss_regs_resource); if (sc->ciss_cfg_resource != NULL) bus_release_resource(sc->ciss_dev, SYS_RES_MEMORY, sc->ciss_cfg_rid, sc->ciss_cfg_resource); if (sc->ciss_intr != NULL) bus_teardown_intr(sc->ciss_dev, sc->ciss_irq_resource, sc->ciss_intr); if (sc->ciss_irq_resource != NULL) bus_release_resource(sc->ciss_dev, SYS_RES_IRQ, sc->ciss_irq_rid[0], sc->ciss_irq_resource); if (sc->ciss_msi) pci_release_msi(sc->ciss_dev); while ((cr = ciss_dequeue_free(sc)) != NULL) bus_dmamap_destroy(sc->ciss_buffer_dmat, cr->cr_datamap); if (sc->ciss_buffer_dmat) bus_dma_tag_destroy(sc->ciss_buffer_dmat); /* destroy command memory and DMA tag */ if (sc->ciss_command != NULL) { bus_dmamap_unload(sc->ciss_command_dmat, sc->ciss_command_map); bus_dmamem_free(sc->ciss_command_dmat, sc->ciss_command, sc->ciss_command_map); } if (sc->ciss_command_dmat) bus_dma_tag_destroy(sc->ciss_command_dmat); if (sc->ciss_reply) { bus_dmamap_unload(sc->ciss_reply_dmat, sc->ciss_reply_map); bus_dmamem_free(sc->ciss_reply_dmat, sc->ciss_reply, sc->ciss_reply_map); } if (sc->ciss_reply_dmat) bus_dma_tag_destroy(sc->ciss_reply_dmat); /* destroy DMA tags */ if (sc->ciss_parent_dmat) bus_dma_tag_destroy(sc->ciss_parent_dmat); if (sc->ciss_logical) { for (i = 0; i <= sc->ciss_max_logical_bus; i++) { for (j = 0; j < CISS_MAX_LOGICAL; j++) { if (sc->ciss_logical[i][j].cl_ldrive) free(sc->ciss_logical[i][j].cl_ldrive, CISS_MALLOC_CLASS); if (sc->ciss_logical[i][j].cl_lstatus) free(sc->ciss_logical[i][j].cl_lstatus, CISS_MALLOC_CLASS); } free(sc->ciss_logical[i], CISS_MALLOC_CLASS); } free(sc->ciss_logical, CISS_MALLOC_CLASS); } if (sc->ciss_physical) { for (i = 0; i < sc->ciss_max_physical_bus; i++) free(sc->ciss_physical[i], CISS_MALLOC_CLASS); free(sc->ciss_physical, CISS_MALLOC_CLASS); } if (sc->ciss_controllers) free(sc->ciss_controllers, CISS_MALLOC_CLASS); } /************************************************************************ * Give a command to the adapter. * * Note that this uses the simple transport layer directly. If we * want to add support for other layers, we'll need a switch of some * sort. * * Note that the simple transport layer has no way of refusing a * command; we only have as many request structures as the adapter * supports commands, so we don't have to check (this presumes that * the adapter can handle commands as fast as we throw them at it). */ static int ciss_start(struct ciss_request *cr) { struct ciss_command *cc; /* XXX debugging only */ int error; cc = cr->cr_cc; debug(2, "post command %d tag %d ", cr->cr_tag, cc->header.host_tag); /* * Map the request's data. */ if ((error = ciss_map_request(cr))) return(error); #if 0 ciss_print_request(cr); #endif return(0); } /************************************************************************ * Fetch completed request(s) from the adapter, queue them for * completion handling. * * Note that this uses the simple transport layer directly. If we * want to add support for other layers, we'll need a switch of some * sort. * * Note that the simple transport mechanism does not require any * reentrancy protection; the OPQ read is atomic. If there is a * chance of a race with something else that might move the request * off the busy list, then we will have to lock against that * (eg. timeouts, etc.) */ static void ciss_done(struct ciss_softc *sc, cr_qhead_t *qh) { struct ciss_request *cr; struct ciss_command *cc; u_int32_t tag, index; debug_called(3); /* * Loop quickly taking requests from the adapter and moving them * to the completed queue. */ for (;;) { tag = CISS_TL_SIMPLE_FETCH_CMD(sc); if (tag == CISS_TL_SIMPLE_OPQ_EMPTY) break; index = tag >> 2; debug(2, "completed command %d%s", index, (tag & CISS_HDR_HOST_TAG_ERROR) ? " with error" : ""); if (index >= sc->ciss_max_requests) { ciss_printf(sc, "completed invalid request %d (0x%x)\n", index, tag); continue; } cr = &(sc->ciss_request[index]); cc = cr->cr_cc; cc->header.host_tag = tag; /* not updated by adapter */ ciss_enqueue_complete(cr, qh); } } static void ciss_perf_done(struct ciss_softc *sc, cr_qhead_t *qh) { struct ciss_request *cr; struct ciss_command *cc; u_int32_t tag, index; debug_called(3); /* * Loop quickly taking requests from the adapter and moving them * to the completed queue. */ for (;;) { tag = sc->ciss_reply[sc->ciss_rqidx]; if ((tag & CISS_CYCLE_MASK) != sc->ciss_cycle) break; index = tag >> 2; debug(2, "completed command %d%s\n", index, (tag & CISS_HDR_HOST_TAG_ERROR) ? " with error" : ""); if (index < sc->ciss_max_requests) { cr = &(sc->ciss_request[index]); cc = cr->cr_cc; cc->header.host_tag = tag; /* not updated by adapter */ ciss_enqueue_complete(cr, qh); } else { ciss_printf(sc, "completed invalid request %d (0x%x)\n", index, tag); } if (++sc->ciss_rqidx == sc->ciss_max_requests) { sc->ciss_rqidx = 0; sc->ciss_cycle ^= 1; } } } /************************************************************************ * Take an interrupt from the adapter. */ static void ciss_intr(void *arg) { cr_qhead_t qh; struct ciss_softc *sc = (struct ciss_softc *)arg; /* * The only interrupt we recognise indicates that there are * entries in the outbound post queue. */ STAILQ_INIT(&qh); ciss_done(sc, &qh); mtx_lock(&sc->ciss_mtx); ciss_complete(sc, &qh); mtx_unlock(&sc->ciss_mtx); } static void ciss_perf_intr(void *arg) { struct ciss_softc *sc = (struct ciss_softc *)arg; /* Clear the interrupt and flush the bridges. Docs say that the flush * needs to be done twice, which doesn't seem right. */ CISS_TL_PERF_CLEAR_INT(sc); CISS_TL_PERF_FLUSH_INT(sc); ciss_perf_msi_intr(sc); } static void ciss_perf_msi_intr(void *arg) { cr_qhead_t qh; struct ciss_softc *sc = (struct ciss_softc *)arg; STAILQ_INIT(&qh); ciss_perf_done(sc, &qh); mtx_lock(&sc->ciss_mtx); ciss_complete(sc, &qh); mtx_unlock(&sc->ciss_mtx); } /************************************************************************ * Process completed requests. * * Requests can be completed in three fashions: * * - by invoking a callback function (cr_complete is non-null) * - by waking up a sleeper (cr_flags has CISS_REQ_SLEEP set) * - by clearing the CISS_REQ_POLL flag in interrupt/timeout context */ static void ciss_complete(struct ciss_softc *sc, cr_qhead_t *qh) { struct ciss_request *cr; debug_called(2); /* * Loop taking requests off the completed queue and performing * completion processing on them. */ for (;;) { if ((cr = ciss_dequeue_complete(sc, qh)) == NULL) break; ciss_unmap_request(cr); if ((cr->cr_flags & CISS_REQ_BUSY) == 0) ciss_printf(sc, "WARNING: completing non-busy request\n"); cr->cr_flags &= ~CISS_REQ_BUSY; /* * If the request has a callback, invoke it. */ if (cr->cr_complete != NULL) { cr->cr_complete(cr); continue; } /* * If someone is sleeping on this request, wake them up. */ if (cr->cr_flags & CISS_REQ_SLEEP) { cr->cr_flags &= ~CISS_REQ_SLEEP; wakeup(cr); continue; } /* * If someone is polling this request for completion, signal. */ if (cr->cr_flags & CISS_REQ_POLL) { cr->cr_flags &= ~CISS_REQ_POLL; continue; } /* * Give up and throw the request back on the free queue. This * should never happen; resources will probably be lost. */ ciss_printf(sc, "WARNING: completed command with no submitter\n"); ciss_enqueue_free(cr); } } /************************************************************************ * Report on the completion status of a request, and pass back SCSI * and command status values. */ static int _ciss_report_request(struct ciss_request *cr, int *command_status, int *scsi_status, const char *func) { struct ciss_command *cc; struct ciss_error_info *ce; debug_called(2); cc = cr->cr_cc; ce = (struct ciss_error_info *)&(cc->sg[0]); /* * We don't consider data under/overrun an error for the Report * Logical/Physical LUNs commands. */ if ((cc->header.host_tag & CISS_HDR_HOST_TAG_ERROR) && ((ce->command_status == CISS_CMD_STATUS_DATA_OVERRUN) || (ce->command_status == CISS_CMD_STATUS_DATA_UNDERRUN)) && ((cc->cdb.cdb[0] == CISS_OPCODE_REPORT_LOGICAL_LUNS) || (cc->cdb.cdb[0] == CISS_OPCODE_REPORT_PHYSICAL_LUNS) || (cc->cdb.cdb[0] == INQUIRY))) { cc->header.host_tag &= ~CISS_HDR_HOST_TAG_ERROR; debug(2, "ignoring irrelevant under/overrun error"); } /* * Check the command's error bit, if clear, there's no status and * everything is OK. */ if (!(cc->header.host_tag & CISS_HDR_HOST_TAG_ERROR)) { if (scsi_status != NULL) *scsi_status = SCSI_STATUS_OK; if (command_status != NULL) *command_status = CISS_CMD_STATUS_SUCCESS; return(0); } else { if (command_status != NULL) *command_status = ce->command_status; if (scsi_status != NULL) { if (ce->command_status == CISS_CMD_STATUS_TARGET_STATUS) { *scsi_status = ce->scsi_status; } else { *scsi_status = -1; } } if (bootverbose) ciss_printf(cr->cr_sc, "command status 0x%x (%s) scsi status 0x%x\n", ce->command_status, ciss_name_command_status(ce->command_status), ce->scsi_status); if (ce->command_status == CISS_CMD_STATUS_INVALID_COMMAND) { ciss_printf(cr->cr_sc, "invalid command, offense size %d at %d, value 0x%x, function %s\n", ce->additional_error_info.invalid_command.offense_size, ce->additional_error_info.invalid_command.offense_offset, ce->additional_error_info.invalid_command.offense_value, func); } } #if 0 ciss_print_request(cr); #endif return(1); } /************************************************************************ * Issue a request and don't return until it's completed. * * Depending on adapter status, we may poll or sleep waiting for * completion. */ static int ciss_synch_request(struct ciss_request *cr, int timeout) { if (cr->cr_sc->ciss_flags & CISS_FLAG_RUNNING) { return(ciss_wait_request(cr, timeout)); } else { return(ciss_poll_request(cr, timeout)); } } /************************************************************************ * Issue a request and poll for completion. * * Timeout in milliseconds. */ static int ciss_poll_request(struct ciss_request *cr, int timeout) { cr_qhead_t qh; struct ciss_softc *sc; int error; debug_called(2); STAILQ_INIT(&qh); sc = cr->cr_sc; cr->cr_flags |= CISS_REQ_POLL; if ((error = ciss_start(cr)) != 0) return(error); do { if (sc->ciss_perf) ciss_perf_done(sc, &qh); else ciss_done(sc, &qh); ciss_complete(sc, &qh); if (!(cr->cr_flags & CISS_REQ_POLL)) return(0); DELAY(1000); } while (timeout-- >= 0); return(EWOULDBLOCK); } /************************************************************************ * Issue a request and sleep waiting for completion. * * Timeout in milliseconds. Note that a spurious wakeup will reset * the timeout. */ static int ciss_wait_request(struct ciss_request *cr, int timeout) { int error; debug_called(2); cr->cr_flags |= CISS_REQ_SLEEP; if ((error = ciss_start(cr)) != 0) return(error); while ((cr->cr_flags & CISS_REQ_SLEEP) && (error != EWOULDBLOCK)) { error = msleep(cr, &cr->cr_sc->ciss_mtx, PRIBIO, "cissREQ", (timeout * hz) / 1000); } return(error); } #if 0 /************************************************************************ * Abort a request. Note that a potential exists here to race the * request being completed; the caller must deal with this. */ static int ciss_abort_request(struct ciss_request *ar) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_message_cdb *cmc; int error; debug_called(1); /* get a request */ if ((error = ciss_get_request(ar->cr_sc, &cr)) != 0) return(error); /* build the abort command */ cc = cr->cr_cc; cc->header.address.mode.mode = CISS_HDR_ADDRESS_MODE_PERIPHERAL; /* addressing? */ cc->header.address.physical.target = 0; cc->header.address.physical.bus = 0; cc->cdb.cdb_length = sizeof(*cmc); cc->cdb.type = CISS_CDB_TYPE_MESSAGE; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_NONE; cc->cdb.timeout = 30; cmc = (struct ciss_message_cdb *)&(cc->cdb.cdb[0]); cmc->opcode = CISS_OPCODE_MESSAGE_ABORT; cmc->type = CISS_MESSAGE_ABORT_TASK; cmc->abort_tag = ar->cr_tag; /* endianness?? */ /* * Send the request and wait for a response. If we believe we * aborted the request OK, clear the flag that indicates it's * running. */ error = ciss_synch_request(cr, 35 * 1000); if (!error) error = ciss_report_request(cr, NULL, NULL); ciss_release_request(cr); return(error); } #endif /************************************************************************ * Fetch and initialise a request */ static int ciss_get_request(struct ciss_softc *sc, struct ciss_request **crp) { struct ciss_request *cr; debug_called(2); /* * Get a request and clean it up. */ if ((cr = ciss_dequeue_free(sc)) == NULL) return(ENOMEM); cr->cr_data = NULL; cr->cr_flags = 0; cr->cr_complete = NULL; cr->cr_private = NULL; cr->cr_sg_tag = CISS_SG_MAX; /* Backstop to prevent accidents */ ciss_preen_command(cr); *crp = cr; return(0); } static void ciss_preen_command(struct ciss_request *cr) { struct ciss_command *cc; u_int32_t cmdphys; /* * Clean up the command structure. * * Note that we set up the error_info structure here, since the * length can be overwritten by any command. */ cc = cr->cr_cc; cc->header.sg_in_list = 0; /* kinda inefficient this way */ cc->header.sg_total = 0; cc->header.host_tag = cr->cr_tag << 2; cc->header.host_tag_zeroes = 0; cmdphys = cr->cr_ccphys; cc->error_info.error_info_address = cmdphys + sizeof(struct ciss_command); cc->error_info.error_info_length = CISS_COMMAND_ALLOC_SIZE - sizeof(struct ciss_command); } /************************************************************************ * Release a request to the free list. */ static void ciss_release_request(struct ciss_request *cr) { struct ciss_softc *sc; debug_called(2); sc = cr->cr_sc; /* release the request to the free queue */ ciss_requeue_free(cr); } /************************************************************************ * Allocate a request that will be used to send a BMIC command. Do some * of the common setup here to avoid duplicating it everywhere else. */ static int ciss_get_bmic_request(struct ciss_softc *sc, struct ciss_request **crp, int opcode, void **bufp, size_t bufsize) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_bmic_cdb *cbc; void *buf; int error; int dataout; debug_called(2); cr = NULL; buf = NULL; /* * Get a request. */ if ((error = ciss_get_request(sc, &cr)) != 0) goto out; /* * Allocate data storage if requested, determine the data direction. */ dataout = 0; if ((bufsize > 0) && (bufp != NULL)) { if (*bufp == NULL) { if ((buf = malloc(bufsize, CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; goto out; } } else { buf = *bufp; dataout = 1; /* we are given a buffer, so we are writing */ } } /* * Build a CISS BMIC command to get the logical drive ID. */ cr->cr_data = buf; cr->cr_length = bufsize; if (!dataout) cr->cr_flags = CISS_REQ_DATAIN; cc = cr->cr_cc; cc->header.address.physical.mode = CISS_HDR_ADDRESS_MODE_PERIPHERAL; cc->header.address.physical.bus = 0; cc->header.address.physical.target = 0; cc->cdb.cdb_length = sizeof(*cbc); cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = dataout ? CISS_CDB_DIRECTION_WRITE : CISS_CDB_DIRECTION_READ; cc->cdb.timeout = 0; cbc = (struct ciss_bmic_cdb *)&(cc->cdb.cdb[0]); bzero(cbc, sizeof(*cbc)); cbc->opcode = dataout ? CISS_ARRAY_CONTROLLER_WRITE : CISS_ARRAY_CONTROLLER_READ; cbc->bmic_opcode = opcode; cbc->size = htons((u_int16_t)bufsize); out: if (error) { if (cr != NULL) ciss_release_request(cr); } else { *crp = cr; if ((bufp != NULL) && (*bufp == NULL) && (buf != NULL)) *bufp = buf; } return(error); } /************************************************************************ * Handle a command passed in from userspace. */ static int ciss_user_command(struct ciss_softc *sc, IOCTL_Command_struct *ioc) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_error_info *ce; int error = 0; debug_called(1); cr = NULL; /* * Get a request. */ while (ciss_get_request(sc, &cr) != 0) msleep(sc, &sc->ciss_mtx, PPAUSE, "cissREQ", hz); cc = cr->cr_cc; /* * Allocate an in-kernel databuffer if required, copy in user data. */ mtx_unlock(&sc->ciss_mtx); cr->cr_length = ioc->buf_size; if (ioc->buf_size > 0) { if ((cr->cr_data = malloc(ioc->buf_size, CISS_MALLOC_CLASS, M_NOWAIT)) == NULL) { error = ENOMEM; goto out_unlocked; } if ((error = copyin(ioc->buf, cr->cr_data, ioc->buf_size))) { debug(0, "copyin: bad data buffer %p/%d", ioc->buf, ioc->buf_size); goto out_unlocked; } } /* * Build the request based on the user command. */ bcopy(&ioc->LUN_info, &cc->header.address, sizeof(cc->header.address)); bcopy(&ioc->Request, &cc->cdb, sizeof(cc->cdb)); /* XXX anything else to populate here? */ mtx_lock(&sc->ciss_mtx); /* * Run the command. */ if ((error = ciss_synch_request(cr, 60 * 1000))) { debug(0, "request failed - %d", error); goto out; } /* * Check to see if the command succeeded. */ ce = (struct ciss_error_info *)&(cc->sg[0]); if ((cc->header.host_tag & CISS_HDR_HOST_TAG_ERROR) == 0) bzero(ce, sizeof(*ce)); /* * Copy the results back to the user. */ bcopy(ce, &ioc->error_info, sizeof(*ce)); mtx_unlock(&sc->ciss_mtx); if ((ioc->buf_size > 0) && (error = copyout(cr->cr_data, ioc->buf, ioc->buf_size))) { debug(0, "copyout: bad data buffer %p/%d", ioc->buf, ioc->buf_size); goto out_unlocked; } /* done OK */ error = 0; out_unlocked: mtx_lock(&sc->ciss_mtx); out: if ((cr != NULL) && (cr->cr_data != NULL)) free(cr->cr_data, CISS_MALLOC_CLASS); if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Map a request into bus-visible space, initialise the scatter/gather * list. */ static int ciss_map_request(struct ciss_request *cr) { struct ciss_softc *sc; int error = 0; debug_called(2); sc = cr->cr_sc; /* check that mapping is necessary */ if (cr->cr_flags & CISS_REQ_MAPPED) return(0); cr->cr_flags |= CISS_REQ_MAPPED; bus_dmamap_sync(sc->ciss_command_dmat, sc->ciss_command_map, BUS_DMASYNC_PREWRITE); if (cr->cr_data != NULL) { error = bus_dmamap_load(sc->ciss_buffer_dmat, cr->cr_datamap, cr->cr_data, cr->cr_length, ciss_request_map_helper, cr, 0); if (error != 0) return (error); } else { /* * Post the command to the adapter. */ cr->cr_sg_tag = CISS_SG_NONE; cr->cr_flags |= CISS_REQ_BUSY; if (sc->ciss_perf) CISS_TL_PERF_POST_CMD(sc, cr); else CISS_TL_SIMPLE_POST_CMD(sc, cr->cr_ccphys); } return(0); } static void ciss_request_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct ciss_command *cc; struct ciss_request *cr; struct ciss_softc *sc; int i; debug_called(2); cr = (struct ciss_request *)arg; sc = cr->cr_sc; cc = cr->cr_cc; for (i = 0; i < nseg; i++) { cc->sg[i].address = segs[i].ds_addr; cc->sg[i].length = segs[i].ds_len; cc->sg[i].extension = 0; } /* we leave the s/g table entirely within the command */ cc->header.sg_in_list = nseg; cc->header.sg_total = nseg; if (cr->cr_flags & CISS_REQ_DATAIN) bus_dmamap_sync(sc->ciss_buffer_dmat, cr->cr_datamap, BUS_DMASYNC_PREREAD); if (cr->cr_flags & CISS_REQ_DATAOUT) bus_dmamap_sync(sc->ciss_buffer_dmat, cr->cr_datamap, BUS_DMASYNC_PREWRITE); if (nseg == 0) cr->cr_sg_tag = CISS_SG_NONE; else if (nseg == 1) cr->cr_sg_tag = CISS_SG_1; else if (nseg == 2) cr->cr_sg_tag = CISS_SG_2; else if (nseg <= 4) cr->cr_sg_tag = CISS_SG_4; else if (nseg <= 8) cr->cr_sg_tag = CISS_SG_8; else if (nseg <= 16) cr->cr_sg_tag = CISS_SG_16; else if (nseg <= 32) cr->cr_sg_tag = CISS_SG_32; else cr->cr_sg_tag = CISS_SG_MAX; /* * Post the command to the adapter. */ cr->cr_flags |= CISS_REQ_BUSY; if (sc->ciss_perf) CISS_TL_PERF_POST_CMD(sc, cr); else CISS_TL_SIMPLE_POST_CMD(sc, cr->cr_ccphys); } /************************************************************************ * Unmap a request from bus-visible space. */ static void ciss_unmap_request(struct ciss_request *cr) { struct ciss_softc *sc; debug_called(2); sc = cr->cr_sc; /* check that unmapping is necessary */ if ((cr->cr_flags & CISS_REQ_MAPPED) == 0) return; bus_dmamap_sync(sc->ciss_command_dmat, sc->ciss_command_map, BUS_DMASYNC_POSTWRITE); if (cr->cr_data == NULL) goto out; if (cr->cr_flags & CISS_REQ_DATAIN) bus_dmamap_sync(sc->ciss_buffer_dmat, cr->cr_datamap, BUS_DMASYNC_POSTREAD); if (cr->cr_flags & CISS_REQ_DATAOUT) bus_dmamap_sync(sc->ciss_buffer_dmat, cr->cr_datamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->ciss_buffer_dmat, cr->cr_datamap); out: cr->cr_flags &= ~CISS_REQ_MAPPED; } /************************************************************************ * Attach the driver to CAM. * * We put all the logical drives on a single SCSI bus. */ static int ciss_cam_init(struct ciss_softc *sc) { int i, maxbus; debug_called(1); /* * Allocate a devq. We can reuse this for the masked physical * devices if we decide to export these as well. */ if ((sc->ciss_cam_devq = cam_simq_alloc(sc->ciss_max_requests - 2)) == NULL) { ciss_printf(sc, "can't allocate CAM SIM queue\n"); return(ENOMEM); } /* * Create a SIM. * * This naturally wastes a bit of memory. The alternative is to allocate * and register each bus as it is found, and then track them on a linked * list. Unfortunately, the driver has a few places where it needs to * look up the SIM based solely on bus number, and it's unclear whether * a list traversal would work for these situations. */ maxbus = max(sc->ciss_max_logical_bus, sc->ciss_max_physical_bus + CISS_PHYSICAL_BASE); sc->ciss_cam_sim = malloc(maxbus * sizeof(struct cam_sim*), CISS_MALLOC_CLASS, M_NOWAIT | M_ZERO); if (sc->ciss_cam_sim == NULL) { ciss_printf(sc, "can't allocate memory for controller SIM\n"); return(ENOMEM); } for (i = 0; i < sc->ciss_max_logical_bus; i++) { if ((sc->ciss_cam_sim[i] = cam_sim_alloc(ciss_cam_action, ciss_cam_poll, "ciss", sc, device_get_unit(sc->ciss_dev), &sc->ciss_mtx, 2, sc->ciss_max_requests - 2, sc->ciss_cam_devq)) == NULL) { ciss_printf(sc, "can't allocate CAM SIM for controller %d\n", i); return(ENOMEM); } /* * Register bus with this SIM. */ mtx_lock(&sc->ciss_mtx); if (i == 0 || sc->ciss_controllers[i].physical.bus != 0) { if (xpt_bus_register(sc->ciss_cam_sim[i], sc->ciss_dev, i) != 0) { ciss_printf(sc, "can't register SCSI bus %d\n", i); mtx_unlock(&sc->ciss_mtx); return (ENXIO); } } mtx_unlock(&sc->ciss_mtx); } for (i = CISS_PHYSICAL_BASE; i < sc->ciss_max_physical_bus + CISS_PHYSICAL_BASE; i++) { if ((sc->ciss_cam_sim[i] = cam_sim_alloc(ciss_cam_action, ciss_cam_poll, "ciss", sc, device_get_unit(sc->ciss_dev), &sc->ciss_mtx, 1, sc->ciss_max_requests - 2, sc->ciss_cam_devq)) == NULL) { ciss_printf(sc, "can't allocate CAM SIM for controller %d\n", i); return (ENOMEM); } mtx_lock(&sc->ciss_mtx); if (xpt_bus_register(sc->ciss_cam_sim[i], sc->ciss_dev, i) != 0) { ciss_printf(sc, "can't register SCSI bus %d\n", i); mtx_unlock(&sc->ciss_mtx); return (ENXIO); } mtx_unlock(&sc->ciss_mtx); } return(0); } /************************************************************************ * Initiate a rescan of the 'logical devices' SIM */ static void ciss_cam_rescan_target(struct ciss_softc *sc, int bus, int target) { union ccb *ccb; debug_called(1); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) { ciss_printf(sc, "rescan failed (can't allocate CCB)\n"); return; } if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(sc->ciss_cam_sim[bus]), target, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { ciss_printf(sc, "rescan failed (can't create path)\n"); xpt_free_ccb(ccb); return; } xpt_rescan(ccb); /* scan is now in progress */ } /************************************************************************ * Handle requests coming from CAM */ static void ciss_cam_action(struct cam_sim *sim, union ccb *ccb) { struct ciss_softc *sc; struct ccb_scsiio *csio; int bus, target; int physical; sc = cam_sim_softc(sim); bus = cam_sim_bus(sim); csio = (struct ccb_scsiio *)&ccb->csio; target = csio->ccb_h.target_id; physical = CISS_IS_PHYSICAL(bus); switch (ccb->ccb_h.func_code) { /* perform SCSI I/O */ case XPT_SCSI_IO: if (!ciss_cam_action_io(sim, csio)) return; break; /* perform geometry calculations */ case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg = &ccb->ccg; struct ciss_ldrive *ld; debug(1, "XPT_CALC_GEOMETRY %d:%d:%d", cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); ld = NULL; if (!physical) ld = &sc->ciss_logical[bus][target]; /* * Use the cached geometry settings unless the fault tolerance * is invalid. */ if (physical || ld->cl_geometry.fault_tolerance == 0xFF) { u_int32_t secs_per_cylinder; ccg->heads = 255; ccg->secs_per_track = 32; secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; } else { ccg->heads = ld->cl_geometry.heads; ccg->secs_per_track = ld->cl_geometry.sectors; ccg->cylinders = ntohs(ld->cl_geometry.cylinders); } ccb->ccb_h.status = CAM_REQ_CMP; break; } /* handle path attribute inquiry */ case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; debug(1, "XPT_PATH_INQ %d:%d:%d", cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); cpi->version_num = 1; cpi->hba_inquiry = PI_TAG_ABLE; /* XXX is this correct? */ cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->max_target = CISS_MAX_LOGICAL; cpi->max_lun = 0; /* 'logical drive' channel only */ cpi->initiator_id = CISS_MAX_LOGICAL; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "msmith@freebsd.org", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 132 * 1024; /* XXX what to set this to? */ cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->maxio = (CISS_MAX_SG_ELEMENTS - 1) * PAGE_SIZE; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; int bus, target; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; bus = cam_sim_bus(sim); target = cts->ccb_h.target_id; debug(1, "XPT_GET_TRAN_SETTINGS %d:%d", bus, target); /* disconnect always OK */ cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; spi->valid = CTS_SPI_VALID_DISC; spi->flags = CTS_SPI_FLAGS_DISC_ENB; scsi->valid = CTS_SCSI_VALID_TQ; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; cts->ccb_h.status = CAM_REQ_CMP; break; } default: /* we can't do this */ debug(1, "unspported func_code = 0x%x", ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } /************************************************************************ * Handle a CAM SCSI I/O request. */ static int ciss_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio) { struct ciss_softc *sc; int bus, target; struct ciss_request *cr; struct ciss_command *cc; int error; sc = cam_sim_softc(sim); bus = cam_sim_bus(sim); target = csio->ccb_h.target_id; debug(2, "XPT_SCSI_IO %d:%d:%d", bus, target, csio->ccb_h.target_lun); /* check that the CDB pointer is not to a physical address */ if ((csio->ccb_h.flags & CAM_CDB_POINTER) && (csio->ccb_h.flags & CAM_CDB_PHYS)) { debug(3, " CDB pointer is to physical address"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* if there is data transfer, it must be to/from a virtual address */ if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if (csio->ccb_h.flags & CAM_DATA_PHYS) { /* we can't map it */ debug(3, " data pointer is to physical address"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } if (csio->ccb_h.flags & CAM_SCATTER_VALID) { /* we want to do the s/g setup */ debug(3, " data has premature s/g setup"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } } /* abandon aborted ccbs or those that have failed validation */ if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { debug(3, "abandoning CCB due to abort/validation failure"); return(EINVAL); } /* handle emulation of some SCSI commands ourself */ if (ciss_cam_emulate(sc, csio)) return(0); /* * Get a request to manage this command. If we can't, return the * ccb, freeze the queue and flag so that we unfreeze it when a * request completes. */ if ((error = ciss_get_request(sc, &cr)) != 0) { xpt_freeze_simq(sim, 1); sc->ciss_flags |= CISS_FLAG_BUSY; csio->ccb_h.status |= CAM_REQUEUE_REQ; return(error); } /* * Build the command. */ cc = cr->cr_cc; cr->cr_data = csio->data_ptr; cr->cr_length = csio->dxfer_len; cr->cr_complete = ciss_cam_complete; cr->cr_private = csio; /* * Target the right logical volume. */ if (CISS_IS_PHYSICAL(bus)) cc->header.address = sc->ciss_physical[CISS_CAM_TO_PBUS(bus)][target].cp_address; else cc->header.address = sc->ciss_logical[bus][target].cl_address; cc->cdb.cdb_length = csio->cdb_len; cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; /* XXX ordered tags? */ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { cr->cr_flags = CISS_REQ_DATAOUT; cc->cdb.direction = CISS_CDB_DIRECTION_WRITE; } else if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cr->cr_flags = CISS_REQ_DATAIN; cc->cdb.direction = CISS_CDB_DIRECTION_READ; } else { cr->cr_flags = 0; cc->cdb.direction = CISS_CDB_DIRECTION_NONE; } cc->cdb.timeout = (csio->ccb_h.timeout / 1000) + 1; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, &cc->cdb.cdb[0], csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, &cc->cdb.cdb[0], csio->cdb_len); } /* * Submit the request to the adapter. * * Note that this may fail if we're unable to map the request (and * if we ever learn a transport layer other than simple, may fail * if the adapter rejects the command). */ if ((error = ciss_start(cr)) != 0) { xpt_freeze_simq(sim, 1); csio->ccb_h.status |= CAM_RELEASE_SIMQ; if (error == EINPROGRESS) { error = 0; } else { csio->ccb_h.status |= CAM_REQUEUE_REQ; ciss_release_request(cr); } return(error); } return(0); } /************************************************************************ * Emulate SCSI commands the adapter doesn't handle as we might like. */ static int ciss_cam_emulate(struct ciss_softc *sc, struct ccb_scsiio *csio) { int bus, target; u_int8_t opcode; target = csio->ccb_h.target_id; bus = cam_sim_bus(xpt_path_sim(csio->ccb_h.path)); opcode = (csio->ccb_h.flags & CAM_CDB_POINTER) ? *(u_int8_t *)csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes[0]; if (CISS_IS_PHYSICAL(bus)) { if (sc->ciss_physical[CISS_CAM_TO_PBUS(bus)][target].cp_online != 1) { csio->ccb_h.status |= CAM_SEL_TIMEOUT; xpt_done((union ccb *)csio); return(1); } else return(0); } /* * Handle requests for volumes that don't exist or are not online. * A selection timeout is slightly better than an illegal request. * Other errors might be better. */ if (sc->ciss_logical[bus][target].cl_status != CISS_LD_ONLINE) { csio->ccb_h.status |= CAM_SEL_TIMEOUT; xpt_done((union ccb *)csio); return(1); } /* if we have to fake Synchronise Cache */ if (sc->ciss_flags & CISS_FLAG_FAKE_SYNCH) { /* * If this is a Synchronise Cache command, typically issued when * a device is closed, flush the adapter and complete now. */ if (((csio->ccb_h.flags & CAM_CDB_POINTER) ? *(u_int8_t *)csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes[0]) == SYNCHRONIZE_CACHE) { ciss_flush_adapter(sc); csio->ccb_h.status |= CAM_REQ_CMP; xpt_done((union ccb *)csio); return(1); } } return(0); } /************************************************************************ * Check for possibly-completed commands. */ static void ciss_cam_poll(struct cam_sim *sim) { cr_qhead_t qh; struct ciss_softc *sc = cam_sim_softc(sim); debug_called(2); STAILQ_INIT(&qh); if (sc->ciss_perf) ciss_perf_done(sc, &qh); else ciss_done(sc, &qh); ciss_complete(sc, &qh); } /************************************************************************ * Handle completion of a command - pass results back through the CCB */ static void ciss_cam_complete(struct ciss_request *cr) { struct ciss_softc *sc; struct ciss_command *cc; struct ciss_error_info *ce; struct ccb_scsiio *csio; int scsi_status; int command_status; debug_called(2); sc = cr->cr_sc; cc = cr->cr_cc; ce = (struct ciss_error_info *)&(cc->sg[0]); csio = (struct ccb_scsiio *)cr->cr_private; /* * Extract status values from request. */ ciss_report_request(cr, &command_status, &scsi_status); csio->scsi_status = scsi_status; /* * Handle specific SCSI status values. */ switch(scsi_status) { /* no status due to adapter error */ case -1: debug(0, "adapter error"); csio->ccb_h.status |= CAM_REQ_CMP_ERR; break; /* no status due to command completed OK */ case SCSI_STATUS_OK: /* CISS_SCSI_STATUS_GOOD */ debug(2, "SCSI_STATUS_OK"); csio->ccb_h.status |= CAM_REQ_CMP; break; /* check condition, sense data included */ case SCSI_STATUS_CHECK_COND: /* CISS_SCSI_STATUS_CHECK_CONDITION */ debug(0, "SCSI_STATUS_CHECK_COND sense size %d resid %d\n", ce->sense_length, ce->residual_count); bzero(&csio->sense_data, SSD_FULL_SIZE); bcopy(&ce->sense_info[0], &csio->sense_data, ce->sense_length); if (csio->sense_len > ce->sense_length) csio->sense_resid = csio->sense_len - ce->sense_length; else csio->sense_resid = 0; csio->resid = ce->residual_count; csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; #ifdef CISS_DEBUG { struct scsi_sense_data *sns = (struct scsi_sense_data *)&ce->sense_info[0]; debug(0, "sense key %x", scsi_get_sense_key(sns, csio->sense_len - csio->sense_resid, /*show_errors*/ 1)); } #endif break; case SCSI_STATUS_BUSY: /* CISS_SCSI_STATUS_BUSY */ debug(0, "SCSI_STATUS_BUSY"); csio->ccb_h.status |= CAM_SCSI_BUSY; break; default: debug(0, "unknown status 0x%x", csio->scsi_status); csio->ccb_h.status |= CAM_REQ_CMP_ERR; break; } /* handle post-command fixup */ ciss_cam_complete_fixup(sc, csio); ciss_release_request(cr); if (sc->ciss_flags & CISS_FLAG_BUSY) { sc->ciss_flags &= ~CISS_FLAG_BUSY; if (csio->ccb_h.status & CAM_RELEASE_SIMQ) xpt_release_simq(xpt_path_sim(csio->ccb_h.path), 0); else csio->ccb_h.status |= CAM_RELEASE_SIMQ; } xpt_done((union ccb *)csio); } /******************************************************************************** * Fix up the result of some commands here. */ static void ciss_cam_complete_fixup(struct ciss_softc *sc, struct ccb_scsiio *csio) { struct scsi_inquiry_data *inq; struct ciss_ldrive *cl; uint8_t *cdb; int bus, target; cdb = (csio->ccb_h.flags & CAM_CDB_POINTER) ? (uint8_t *)csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes; if (cdb[0] == INQUIRY && (cdb[1] & SI_EVPD) == 0 && (csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN && csio->dxfer_len >= SHORT_INQUIRY_LENGTH) { inq = (struct scsi_inquiry_data *)csio->data_ptr; target = csio->ccb_h.target_id; bus = cam_sim_bus(xpt_path_sim(csio->ccb_h.path)); /* * Don't let hard drives be seen by the DA driver. They will still be * attached by the PASS driver. */ if (CISS_IS_PHYSICAL(bus)) { if (SID_TYPE(inq) == T_DIRECT) inq->device = (inq->device & 0xe0) | T_NODEVICE; return; } cl = &sc->ciss_logical[bus][target]; padstr(inq->vendor, "COMPAQ", 8); padstr(inq->product, ciss_name_ldrive_org(cl->cl_ldrive->fault_tolerance), 8); padstr(inq->revision, ciss_name_ldrive_status(cl->cl_lstatus->status), 16); } } /******************************************************************************** * Find a peripheral attached at (target) */ static struct cam_periph * ciss_find_periph(struct ciss_softc *sc, int bus, int target) { struct cam_periph *periph; struct cam_path *path; int status; status = xpt_create_path(&path, NULL, cam_sim_path(sc->ciss_cam_sim[bus]), target, 0); if (status == CAM_REQ_CMP) { periph = cam_periph_find(path, NULL); xpt_free_path(path); } else { periph = NULL; } return(periph); } /******************************************************************************** * Name the device at (target) * * XXX is this strictly correct? */ static int ciss_name_device(struct ciss_softc *sc, int bus, int target) { struct cam_periph *periph; if (CISS_IS_PHYSICAL(bus)) return (0); if ((periph = ciss_find_periph(sc, bus, target)) != NULL) { sprintf(sc->ciss_logical[bus][target].cl_name, "%s%d", periph->periph_name, periph->unit_number); return(0); } sc->ciss_logical[bus][target].cl_name[0] = 0; return(ENOENT); } /************************************************************************ * Periodic status monitoring. */ static void ciss_periodic(void *arg) { struct ciss_softc *sc; struct ciss_request *cr = NULL; struct ciss_command *cc = NULL; int error = 0; debug_called(1); sc = (struct ciss_softc *)arg; /* * Check the adapter heartbeat. */ if (sc->ciss_cfg->heartbeat == sc->ciss_heartbeat) { sc->ciss_heart_attack++; debug(0, "adapter heart attack in progress 0x%x/%d", sc->ciss_heartbeat, sc->ciss_heart_attack); if (sc->ciss_heart_attack == 3) { ciss_printf(sc, "ADAPTER HEARTBEAT FAILED\n"); ciss_disable_adapter(sc); return; } } else { sc->ciss_heartbeat = sc->ciss_cfg->heartbeat; sc->ciss_heart_attack = 0; debug(3, "new heartbeat 0x%x", sc->ciss_heartbeat); } /* * Send the NOP message and wait for a response. */ if (ciss_nop_message_heartbeat != 0 && (error = ciss_get_request(sc, &cr)) == 0) { cc = cr->cr_cc; cr->cr_complete = ciss_nop_complete; cc->cdb.cdb_length = 1; cc->cdb.type = CISS_CDB_TYPE_MESSAGE; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_WRITE; cc->cdb.timeout = 0; cc->cdb.cdb[0] = CISS_OPCODE_MESSAGE_NOP; if ((error = ciss_start(cr)) != 0) { ciss_printf(sc, "SENDING NOP MESSAGE FAILED\n"); } } /* * If the notify event request has died for some reason, or has * not started yet, restart it. */ if (!(sc->ciss_flags & CISS_FLAG_NOTIFY_OK)) { debug(0, "(re)starting Event Notify chain"); ciss_notify_event(sc); } /* * Reschedule. */ callout_reset(&sc->ciss_periodic, CISS_HEARTBEAT_RATE * hz, ciss_periodic, sc); } static void ciss_nop_complete(struct ciss_request *cr) { struct ciss_softc *sc; static int first_time = 1; sc = cr->cr_sc; if (ciss_report_request(cr, NULL, NULL) != 0) { if (first_time == 1) { first_time = 0; ciss_printf(sc, "SENDING NOP MESSAGE FAILED (not logging anymore)\n"); } } ciss_release_request(cr); } /************************************************************************ * Disable the adapter. * * The all requests in completed queue is failed with hardware error. * This will cause failover in a multipath configuration. */ static void ciss_disable_adapter(struct ciss_softc *sc) { cr_qhead_t qh; struct ciss_request *cr; struct ciss_command *cc; struct ciss_error_info *ce; int i; CISS_TL_SIMPLE_DISABLE_INTERRUPTS(sc); pci_disable_busmaster(sc->ciss_dev); sc->ciss_flags &= ~CISS_FLAG_RUNNING; for (i = 1; i < sc->ciss_max_requests; i++) { cr = &sc->ciss_request[i]; if ((cr->cr_flags & CISS_REQ_BUSY) == 0) continue; cc = cr->cr_cc; ce = (struct ciss_error_info *)&(cc->sg[0]); ce->command_status = CISS_CMD_STATUS_HARDWARE_ERROR; ciss_enqueue_complete(cr, &qh); } for (;;) { if ((cr = ciss_dequeue_complete(sc, &qh)) == NULL) break; /* * If the request has a callback, invoke it. */ if (cr->cr_complete != NULL) { cr->cr_complete(cr); continue; } /* * If someone is sleeping on this request, wake them up. */ if (cr->cr_flags & CISS_REQ_SLEEP) { cr->cr_flags &= ~CISS_REQ_SLEEP; wakeup(cr); continue; } } } /************************************************************************ * Request a notification response from the adapter. * * If (cr) is NULL, this is the first request of the adapter, so * reset the adapter's message pointer and start with the oldest * message available. */ static void ciss_notify_event(struct ciss_softc *sc) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_notify_cdb *cnc; int error; debug_called(1); cr = sc->ciss_periodic_notify; /* get a request if we don't already have one */ if (cr == NULL) { if ((error = ciss_get_request(sc, &cr)) != 0) { debug(0, "can't get notify event request"); goto out; } sc->ciss_periodic_notify = cr; cr->cr_complete = ciss_notify_complete; debug(1, "acquired request %d", cr->cr_tag); } /* * Get a databuffer if we don't already have one, note that the * adapter command wants a larger buffer than the actual * structure. */ if (cr->cr_data == NULL) { if ((cr->cr_data = malloc(CISS_NOTIFY_DATA_SIZE, CISS_MALLOC_CLASS, M_NOWAIT)) == NULL) { debug(0, "can't get notify event request buffer"); error = ENOMEM; goto out; } cr->cr_length = CISS_NOTIFY_DATA_SIZE; } /* re-setup the request's command (since we never release it) XXX overkill*/ ciss_preen_command(cr); /* (re)build the notify event command */ cc = cr->cr_cc; cc->header.address.physical.mode = CISS_HDR_ADDRESS_MODE_PERIPHERAL; cc->header.address.physical.bus = 0; cc->header.address.physical.target = 0; cc->cdb.cdb_length = sizeof(*cnc); cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_READ; cc->cdb.timeout = 0; /* no timeout, we hope */ cnc = (struct ciss_notify_cdb *)&(cc->cdb.cdb[0]); bzero(cr->cr_data, CISS_NOTIFY_DATA_SIZE); cnc->opcode = CISS_OPCODE_READ; cnc->command = CISS_COMMAND_NOTIFY_ON_EVENT; cnc->timeout = 0; /* no timeout, we hope */ cnc->synchronous = 0; cnc->ordered = 0; cnc->seek_to_oldest = 0; if ((sc->ciss_flags & CISS_FLAG_RUNNING) == 0) cnc->new_only = 1; else cnc->new_only = 0; cnc->length = htonl(CISS_NOTIFY_DATA_SIZE); /* submit the request */ error = ciss_start(cr); out: if (error) { if (cr != NULL) { if (cr->cr_data != NULL) free(cr->cr_data, CISS_MALLOC_CLASS); ciss_release_request(cr); } sc->ciss_periodic_notify = NULL; debug(0, "can't submit notify event request"); sc->ciss_flags &= ~CISS_FLAG_NOTIFY_OK; } else { debug(1, "notify event submitted"); sc->ciss_flags |= CISS_FLAG_NOTIFY_OK; } } static void ciss_notify_complete(struct ciss_request *cr) { struct ciss_command *cc; struct ciss_notify *cn; struct ciss_softc *sc; int scsi_status; int command_status; debug_called(1); cc = cr->cr_cc; cn = (struct ciss_notify *)cr->cr_data; sc = cr->cr_sc; /* * Report request results, decode status. */ ciss_report_request(cr, &command_status, &scsi_status); /* * Abort the chain on a fatal error. * * XXX which of these are actually errors? */ if ((command_status != CISS_CMD_STATUS_SUCCESS) && (command_status != CISS_CMD_STATUS_TARGET_STATUS) && (command_status != CISS_CMD_STATUS_TIMEOUT)) { /* XXX timeout? */ ciss_printf(sc, "fatal error in Notify Event request (%s)\n", ciss_name_command_status(command_status)); ciss_release_request(cr); sc->ciss_flags &= ~CISS_FLAG_NOTIFY_OK; return; } /* * If the adapter gave us a text message, print it. */ if (cn->message[0] != 0) ciss_printf(sc, "*** %.80s\n", cn->message); debug(0, "notify event class %d subclass %d detail %d", cn->class, cn->subclass, cn->detail); /* * If the response indicates that the notifier has been aborted, * release the notifier command. */ if ((cn->class == CISS_NOTIFY_NOTIFIER) && (cn->subclass == CISS_NOTIFY_NOTIFIER_STATUS) && (cn->detail == 1)) { debug(0, "notifier exiting"); sc->ciss_flags &= ~CISS_FLAG_NOTIFY_OK; ciss_release_request(cr); sc->ciss_periodic_notify = NULL; wakeup(&sc->ciss_periodic_notify); } else { /* Handle notify events in a kernel thread */ ciss_enqueue_notify(cr); sc->ciss_periodic_notify = NULL; wakeup(&sc->ciss_periodic_notify); wakeup(&sc->ciss_notify); } /* * Send a new notify event command, if we're not aborting. */ if (!(sc->ciss_flags & CISS_FLAG_ABORTING)) { ciss_notify_event(sc); } } /************************************************************************ * Abort the Notify Event chain. * * Note that we can't just abort the command in progress; we have to * explicitly issue an Abort Notify Event command in order for the * adapter to clean up correctly. * * If we are called with CISS_FLAG_ABORTING set in the adapter softc, * the chain will not restart itself. */ static int ciss_notify_abort(struct ciss_softc *sc) { struct ciss_request *cr; struct ciss_command *cc; struct ciss_notify_cdb *cnc; int error, command_status, scsi_status; debug_called(1); cr = NULL; error = 0; /* verify that there's an outstanding command */ if (!(sc->ciss_flags & CISS_FLAG_NOTIFY_OK)) goto out; /* get a command to issue the abort with */ if ((error = ciss_get_request(sc, &cr))) goto out; /* get a buffer for the result */ if ((cr->cr_data = malloc(CISS_NOTIFY_DATA_SIZE, CISS_MALLOC_CLASS, M_NOWAIT)) == NULL) { debug(0, "can't get notify event request buffer"); error = ENOMEM; goto out; } cr->cr_length = CISS_NOTIFY_DATA_SIZE; /* build the CDB */ cc = cr->cr_cc; cc->header.address.physical.mode = CISS_HDR_ADDRESS_MODE_PERIPHERAL; cc->header.address.physical.bus = 0; cc->header.address.physical.target = 0; cc->cdb.cdb_length = sizeof(*cnc); cc->cdb.type = CISS_CDB_TYPE_COMMAND; cc->cdb.attribute = CISS_CDB_ATTRIBUTE_SIMPLE; cc->cdb.direction = CISS_CDB_DIRECTION_READ; cc->cdb.timeout = 0; /* no timeout, we hope */ cnc = (struct ciss_notify_cdb *)&(cc->cdb.cdb[0]); bzero(cnc, sizeof(*cnc)); cnc->opcode = CISS_OPCODE_WRITE; cnc->command = CISS_COMMAND_ABORT_NOTIFY; cnc->length = htonl(CISS_NOTIFY_DATA_SIZE); ciss_print_request(cr); /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "Abort Notify Event command failed (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, &scsi_status); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: break; case CISS_CMD_STATUS_INVALID_COMMAND: /* * Some older adapters don't support the CISS version of this * command. Fall back to using the BMIC version. */ error = ciss_notify_abort_bmic(sc); if (error != 0) goto out; break; case CISS_CMD_STATUS_TARGET_STATUS: /* * This can happen if the adapter thinks there wasn't an outstanding * Notify Event command but we did. We clean up here. */ if (scsi_status == CISS_SCSI_STATUS_CHECK_CONDITION) { if (sc->ciss_periodic_notify != NULL) ciss_release_request(sc->ciss_periodic_notify); error = 0; goto out; } /* FALLTHROUGH */ default: ciss_printf(sc, "Abort Notify Event command failed (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } /* * Sleep waiting for the notifier command to complete. Note * that if it doesn't, we may end up in a bad situation, since * the adapter may deliver it later. Also note that the adapter * requires the Notify Event command to be cancelled in order to * maintain internal bookkeeping. */ while (sc->ciss_periodic_notify != NULL) { error = msleep(&sc->ciss_periodic_notify, &sc->ciss_mtx, PRIBIO, "cissNEA", hz * 5); if (error == EWOULDBLOCK) { ciss_printf(sc, "Notify Event command failed to abort, adapter may wedge.\n"); break; } } out: /* release the cancel request */ if (cr != NULL) { if (cr->cr_data != NULL) free(cr->cr_data, CISS_MALLOC_CLASS); ciss_release_request(cr); } if (error == 0) sc->ciss_flags &= ~CISS_FLAG_NOTIFY_OK; return(error); } /************************************************************************ * Abort the Notify Event chain using a BMIC command. */ static int ciss_notify_abort_bmic(struct ciss_softc *sc) { struct ciss_request *cr; int error, command_status; debug_called(1); cr = NULL; error = 0; /* verify that there's an outstanding command */ if (!(sc->ciss_flags & CISS_FLAG_NOTIFY_OK)) goto out; /* * Build a BMIC command to cancel the Notify on Event command. * * Note that we are sending a CISS opcode here. Odd. */ if ((error = ciss_get_bmic_request(sc, &cr, CISS_COMMAND_ABORT_NOTIFY, NULL, 0)) != 0) goto out; /* * Submit the request and wait for it to complete. */ if ((error = ciss_synch_request(cr, 60 * 1000)) != 0) { ciss_printf(sc, "error sending BMIC Cancel Notify on Event command (%d)\n", error); goto out; } /* * Check response. */ ciss_report_request(cr, &command_status, NULL); switch(command_status) { case CISS_CMD_STATUS_SUCCESS: break; default: ciss_printf(sc, "error cancelling Notify on Event (%s)\n", ciss_name_command_status(command_status)); error = EIO; goto out; } out: if (cr != NULL) ciss_release_request(cr); return(error); } /************************************************************************ * Handle rescanning all the logical volumes when a notify event * causes the drives to come online or offline. */ static void ciss_notify_rescan_logical(struct ciss_softc *sc) { struct ciss_lun_report *cll; struct ciss_ldrive *ld; int i, j, ndrives; /* * We must rescan all logical volumes to get the right logical * drive address. */ cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_LOGICAL_LUNS, CISS_MAX_LOGICAL); if (cll == NULL) return; ndrives = (ntohl(cll->list_size) / sizeof(union ciss_device_address)); /* * Delete any of the drives which were destroyed by the * firmware. */ for (i = 0; i < sc->ciss_max_logical_bus; i++) { for (j = 0; j < CISS_MAX_LOGICAL; j++) { ld = &sc->ciss_logical[i][j]; if (ld->cl_update == 0) continue; if (ld->cl_status != CISS_LD_ONLINE) { ciss_cam_rescan_target(sc, i, j); ld->cl_update = 0; if (ld->cl_ldrive) free(ld->cl_ldrive, CISS_MALLOC_CLASS); if (ld->cl_lstatus) free(ld->cl_lstatus, CISS_MALLOC_CLASS); ld->cl_ldrive = NULL; ld->cl_lstatus = NULL; } } } /* * Scan for new drives. */ for (i = 0; i < ndrives; i++) { int bus, target; bus = CISS_LUN_TO_BUS(cll->lun[i].logical.lun); target = CISS_LUN_TO_TARGET(cll->lun[i].logical.lun); ld = &sc->ciss_logical[bus][target]; if (ld->cl_update == 0) continue; ld->cl_update = 0; ld->cl_address = cll->lun[i]; ld->cl_controller = &sc->ciss_controllers[bus]; if (ciss_identify_logical(sc, ld) == 0) { ciss_cam_rescan_target(sc, bus, target); } } free(cll, CISS_MALLOC_CLASS); } /************************************************************************ * Handle a notify event relating to the status of a logical drive. * * XXX need to be able to defer some of these to properly handle * calling the "ID Physical drive" command, unless the 'extended' * drive IDs are always in BIG_MAP format. */ static void ciss_notify_logical(struct ciss_softc *sc, struct ciss_notify *cn) { struct ciss_ldrive *ld; int ostatus, bus, target; debug_called(2); bus = cn->device.physical.bus; target = cn->data.logical_status.logical_drive; ld = &sc->ciss_logical[bus][target]; switch (cn->subclass) { case CISS_NOTIFY_LOGICAL_STATUS: switch (cn->detail) { case 0: ciss_name_device(sc, bus, target); ciss_printf(sc, "logical drive %d (%s) changed status %s->%s, spare status 0x%b\n", cn->data.logical_status.logical_drive, ld->cl_name, ciss_name_ldrive_status(cn->data.logical_status.previous_state), ciss_name_ldrive_status(cn->data.logical_status.new_state), cn->data.logical_status.spare_state, "\20\1configured\2rebuilding\3failed\4in use\5available\n"); /* * Update our idea of the drive's status. */ ostatus = ciss_decode_ldrive_status(cn->data.logical_status.previous_state); ld->cl_status = ciss_decode_ldrive_status(cn->data.logical_status.new_state); if (ld->cl_lstatus != NULL) ld->cl_lstatus->status = cn->data.logical_status.new_state; /* * Have CAM rescan the drive if its status has changed. */ if (ostatus != ld->cl_status) { ld->cl_update = 1; ciss_notify_rescan_logical(sc); } break; case 1: /* logical drive has recognised new media, needs Accept Media Exchange */ ciss_name_device(sc, bus, target); ciss_printf(sc, "logical drive %d (%s) media exchanged, ready to go online\n", cn->data.logical_status.logical_drive, ld->cl_name); ciss_accept_media(sc, ld); ld->cl_update = 1; ld->cl_status = ciss_decode_ldrive_status(cn->data.logical_status.new_state); ciss_notify_rescan_logical(sc); break; case 2: case 3: ciss_printf(sc, "rebuild of logical drive %d (%s) failed due to %s error\n", cn->data.rebuild_aborted.logical_drive, ld->cl_name, (cn->detail == 2) ? "read" : "write"); break; } break; case CISS_NOTIFY_LOGICAL_ERROR: if (cn->detail == 0) { ciss_printf(sc, "FATAL I/O ERROR on logical drive %d (%s), SCSI port %d ID %d\n", cn->data.io_error.logical_drive, ld->cl_name, cn->data.io_error.failure_bus, cn->data.io_error.failure_drive); /* XXX should we take the drive down at this point, or will we be told? */ } break; case CISS_NOTIFY_LOGICAL_SURFACE: if (cn->detail == 0) ciss_printf(sc, "logical drive %d (%s) completed consistency initialisation\n", cn->data.consistency_completed.logical_drive, ld->cl_name); break; } } /************************************************************************ * Handle a notify event relating to the status of a physical drive. */ static void ciss_notify_physical(struct ciss_softc *sc, struct ciss_notify *cn) { } /************************************************************************ * Handle a notify event relating to the status of a physical drive. */ static void ciss_notify_hotplug(struct ciss_softc *sc, struct ciss_notify *cn) { struct ciss_lun_report *cll = NULL; int bus, target; switch (cn->subclass) { case CISS_NOTIFY_HOTPLUG_PHYSICAL: case CISS_NOTIFY_HOTPLUG_NONDISK: bus = CISS_BIG_MAP_BUS(sc, cn->data.drive.big_physical_drive_number); target = CISS_BIG_MAP_TARGET(sc, cn->data.drive.big_physical_drive_number); if (cn->detail == 0) { /* * Mark the device offline so that it'll start producing selection * timeouts to the upper layer. */ if ((bus >= 0) && (target >= 0)) sc->ciss_physical[bus][target].cp_online = 0; } else { /* * Rescan the physical lun list for new items */ cll = ciss_report_luns(sc, CISS_OPCODE_REPORT_PHYSICAL_LUNS, CISS_MAX_PHYSICAL); if (cll == NULL) { ciss_printf(sc, "Warning, cannot get physical lun list\n"); break; } ciss_filter_physical(sc, cll); } break; default: ciss_printf(sc, "Unknown hotplug event %d\n", cn->subclass); return; } if (cll != NULL) free(cll, CISS_MALLOC_CLASS); } /************************************************************************ * Handle deferred processing of notify events. Notify events may need * sleep which is unsafe during an interrupt. */ static void ciss_notify_thread(void *arg) { struct ciss_softc *sc; struct ciss_request *cr; struct ciss_notify *cn; sc = (struct ciss_softc *)arg; #if __FreeBSD_version >= 500000 mtx_lock(&sc->ciss_mtx); #endif for (;;) { if (STAILQ_EMPTY(&sc->ciss_notify) != 0 && (sc->ciss_flags & CISS_FLAG_THREAD_SHUT) == 0) { msleep(&sc->ciss_notify, &sc->ciss_mtx, PUSER, "idle", 0); } if (sc->ciss_flags & CISS_FLAG_THREAD_SHUT) break; cr = ciss_dequeue_notify(sc); if (cr == NULL) panic("cr null"); cn = (struct ciss_notify *)cr->cr_data; switch (cn->class) { case CISS_NOTIFY_HOTPLUG: ciss_notify_hotplug(sc, cn); break; case CISS_NOTIFY_LOGICAL: ciss_notify_logical(sc, cn); break; case CISS_NOTIFY_PHYSICAL: ciss_notify_physical(sc, cn); break; } ciss_release_request(cr); } sc->ciss_notify_thread = NULL; wakeup(&sc->ciss_notify_thread); #if __FreeBSD_version >= 500000 mtx_unlock(&sc->ciss_mtx); #endif kproc_exit(0); } /************************************************************************ * Start the notification kernel thread. */ static void ciss_spawn_notify_thread(struct ciss_softc *sc) { #if __FreeBSD_version > 500005 if (kproc_create((void(*)(void *))ciss_notify_thread, sc, &sc->ciss_notify_thread, 0, 0, "ciss_notify%d", device_get_unit(sc->ciss_dev))) #else if (kproc_create((void(*)(void *))ciss_notify_thread, sc, &sc->ciss_notify_thread, "ciss_notify%d", device_get_unit(sc->ciss_dev))) #endif panic("Could not create notify thread\n"); } /************************************************************************ * Kill the notification kernel thread. */ static void ciss_kill_notify_thread(struct ciss_softc *sc) { if (sc->ciss_notify_thread == NULL) return; sc->ciss_flags |= CISS_FLAG_THREAD_SHUT; wakeup(&sc->ciss_notify); msleep(&sc->ciss_notify_thread, &sc->ciss_mtx, PUSER, "thtrm", 0); } /************************************************************************ * Print a request. */ static void ciss_print_request(struct ciss_request *cr) { struct ciss_softc *sc; struct ciss_command *cc; int i; sc = cr->cr_sc; cc = cr->cr_cc; ciss_printf(sc, "REQUEST @ %p\n", cr); ciss_printf(sc, " data %p/%d tag %d flags %b\n", cr->cr_data, cr->cr_length, cr->cr_tag, cr->cr_flags, "\20\1mapped\2sleep\3poll\4dataout\5datain\n"); ciss_printf(sc, " sg list/total %d/%d host tag 0x%x\n", cc->header.sg_in_list, cc->header.sg_total, cc->header.host_tag); switch(cc->header.address.mode.mode) { case CISS_HDR_ADDRESS_MODE_PERIPHERAL: case CISS_HDR_ADDRESS_MODE_MASK_PERIPHERAL: ciss_printf(sc, " physical bus %d target %d\n", cc->header.address.physical.bus, cc->header.address.physical.target); break; case CISS_HDR_ADDRESS_MODE_LOGICAL: ciss_printf(sc, " logical unit %d\n", cc->header.address.logical.lun); break; } ciss_printf(sc, " %s cdb length %d type %s attribute %s\n", (cc->cdb.direction == CISS_CDB_DIRECTION_NONE) ? "no-I/O" : (cc->cdb.direction == CISS_CDB_DIRECTION_READ) ? "READ" : (cc->cdb.direction == CISS_CDB_DIRECTION_WRITE) ? "WRITE" : "??", cc->cdb.cdb_length, (cc->cdb.type == CISS_CDB_TYPE_COMMAND) ? "command" : (cc->cdb.type == CISS_CDB_TYPE_MESSAGE) ? "message" : "??", (cc->cdb.attribute == CISS_CDB_ATTRIBUTE_UNTAGGED) ? "untagged" : (cc->cdb.attribute == CISS_CDB_ATTRIBUTE_SIMPLE) ? "simple" : (cc->cdb.attribute == CISS_CDB_ATTRIBUTE_HEAD_OF_QUEUE) ? "head-of-queue" : (cc->cdb.attribute == CISS_CDB_ATTRIBUTE_ORDERED) ? "ordered" : (cc->cdb.attribute == CISS_CDB_ATTRIBUTE_AUTO_CONTINGENT) ? "auto-contingent" : "??"); ciss_printf(sc, " %*D\n", cc->cdb.cdb_length, &cc->cdb.cdb[0], " "); if (cc->header.host_tag & CISS_HDR_HOST_TAG_ERROR) { /* XXX print error info */ } else { /* since we don't use chained s/g, don't support it here */ for (i = 0; i < cc->header.sg_in_list; i++) { if ((i % 4) == 0) ciss_printf(sc, " "); printf("0x%08x/%d ", (u_int32_t)cc->sg[i].address, cc->sg[i].length); if ((((i + 1) % 4) == 0) || (i == (cc->header.sg_in_list - 1))) printf("\n"); } } } /************************************************************************ * Print information about the status of a logical drive. */ static void ciss_print_ldrive(struct ciss_softc *sc, struct ciss_ldrive *ld) { int bus, target, i; if (ld->cl_lstatus == NULL) { printf("does not exist\n"); return; } /* print drive status */ switch(ld->cl_lstatus->status) { case CISS_LSTATUS_OK: printf("online\n"); break; case CISS_LSTATUS_INTERIM_RECOVERY: printf("in interim recovery mode\n"); break; case CISS_LSTATUS_READY_RECOVERY: printf("ready to begin recovery\n"); break; case CISS_LSTATUS_RECOVERING: bus = CISS_BIG_MAP_BUS(sc, ld->cl_lstatus->drive_rebuilding); target = CISS_BIG_MAP_BUS(sc, ld->cl_lstatus->drive_rebuilding); printf("being recovered, working on physical drive %d.%d, %u blocks remaining\n", bus, target, ld->cl_lstatus->blocks_to_recover); break; case CISS_LSTATUS_EXPANDING: printf("being expanded, %u blocks remaining\n", ld->cl_lstatus->blocks_to_recover); break; case CISS_LSTATUS_QUEUED_FOR_EXPANSION: printf("queued for expansion\n"); break; case CISS_LSTATUS_FAILED: printf("queued for expansion\n"); break; case CISS_LSTATUS_WRONG_PDRIVE: printf("wrong physical drive inserted\n"); break; case CISS_LSTATUS_MISSING_PDRIVE: printf("missing a needed physical drive\n"); break; case CISS_LSTATUS_BECOMING_READY: printf("becoming ready\n"); break; } /* print failed physical drives */ for (i = 0; i < CISS_BIG_MAP_ENTRIES / 8; i++) { bus = CISS_BIG_MAP_BUS(sc, ld->cl_lstatus->drive_failure_map[i]); target = CISS_BIG_MAP_TARGET(sc, ld->cl_lstatus->drive_failure_map[i]); if (bus == -1) continue; ciss_printf(sc, "physical drive %d:%d (%x) failed\n", bus, target, ld->cl_lstatus->drive_failure_map[i]); } } #ifdef CISS_DEBUG /************************************************************************ * Print information about the controller/driver. */ static void ciss_print_adapter(struct ciss_softc *sc) { int i, j; ciss_printf(sc, "ADAPTER:\n"); for (i = 0; i < CISSQ_COUNT; i++) { ciss_printf(sc, "%s %d/%d\n", i == 0 ? "free" : i == 1 ? "busy" : "complete", sc->ciss_qstat[i].q_length, sc->ciss_qstat[i].q_max); } ciss_printf(sc, "max_requests %d\n", sc->ciss_max_requests); ciss_printf(sc, "flags %b\n", sc->ciss_flags, "\20\1notify_ok\2control_open\3aborting\4running\21fake_synch\22bmic_abort\n"); for (i = 0; i < sc->ciss_max_logical_bus; i++) { for (j = 0; j < CISS_MAX_LOGICAL; j++) { ciss_printf(sc, "LOGICAL DRIVE %d: ", i); ciss_print_ldrive(sc, &sc->ciss_logical[i][j]); } } /* XXX Should physical drives be printed out here? */ for (i = 1; i < sc->ciss_max_requests; i++) ciss_print_request(sc->ciss_request + i); } /* DDB hook */ static void ciss_print0(void) { struct ciss_softc *sc; sc = devclass_get_softc(devclass_find("ciss"), 0); if (sc == NULL) { printf("no ciss controllers\n"); } else { ciss_print_adapter(sc); } } #endif /************************************************************************ * Return a name for a logical drive status value. */ static const char * ciss_name_ldrive_status(int status) { switch (status) { case CISS_LSTATUS_OK: return("OK"); case CISS_LSTATUS_FAILED: return("failed"); case CISS_LSTATUS_NOT_CONFIGURED: return("not configured"); case CISS_LSTATUS_INTERIM_RECOVERY: return("interim recovery"); case CISS_LSTATUS_READY_RECOVERY: return("ready for recovery"); case CISS_LSTATUS_RECOVERING: return("recovering"); case CISS_LSTATUS_WRONG_PDRIVE: return("wrong physical drive inserted"); case CISS_LSTATUS_MISSING_PDRIVE: return("missing physical drive"); case CISS_LSTATUS_EXPANDING: return("expanding"); case CISS_LSTATUS_BECOMING_READY: return("becoming ready"); case CISS_LSTATUS_QUEUED_FOR_EXPANSION: return("queued for expansion"); } return("unknown status"); } /************************************************************************ * Return an online/offline/nonexistent value for a logical drive * status value. */ static int ciss_decode_ldrive_status(int status) { switch(status) { case CISS_LSTATUS_NOT_CONFIGURED: return(CISS_LD_NONEXISTENT); case CISS_LSTATUS_OK: case CISS_LSTATUS_INTERIM_RECOVERY: case CISS_LSTATUS_READY_RECOVERY: case CISS_LSTATUS_RECOVERING: case CISS_LSTATUS_EXPANDING: case CISS_LSTATUS_QUEUED_FOR_EXPANSION: return(CISS_LD_ONLINE); case CISS_LSTATUS_FAILED: case CISS_LSTATUS_WRONG_PDRIVE: case CISS_LSTATUS_MISSING_PDRIVE: case CISS_LSTATUS_BECOMING_READY: default: return(CISS_LD_OFFLINE); } } /************************************************************************ * Return a name for a logical drive's organisation. */ static const char * ciss_name_ldrive_org(int org) { switch(org) { case CISS_LDRIVE_RAID0: return("RAID 0"); case CISS_LDRIVE_RAID1: return("RAID 1(1+0)"); case CISS_LDRIVE_RAID4: return("RAID 4"); case CISS_LDRIVE_RAID5: return("RAID 5"); case CISS_LDRIVE_RAID51: return("RAID 5+1"); case CISS_LDRIVE_RAIDADG: return("RAID ADG"); } return("unkown"); } /************************************************************************ * Return a name for a command status value. */ static const char * ciss_name_command_status(int status) { switch(status) { case CISS_CMD_STATUS_SUCCESS: return("success"); case CISS_CMD_STATUS_TARGET_STATUS: return("target status"); case CISS_CMD_STATUS_DATA_UNDERRUN: return("data underrun"); case CISS_CMD_STATUS_DATA_OVERRUN: return("data overrun"); case CISS_CMD_STATUS_INVALID_COMMAND: return("invalid command"); case CISS_CMD_STATUS_PROTOCOL_ERROR: return("protocol error"); case CISS_CMD_STATUS_HARDWARE_ERROR: return("hardware error"); case CISS_CMD_STATUS_CONNECTION_LOST: return("connection lost"); case CISS_CMD_STATUS_ABORTED: return("aborted"); case CISS_CMD_STATUS_ABORT_FAILED: return("abort failed"); case CISS_CMD_STATUS_UNSOLICITED_ABORT: return("unsolicited abort"); case CISS_CMD_STATUS_TIMEOUT: return("timeout"); case CISS_CMD_STATUS_UNABORTABLE: return("unabortable"); } return("unknown status"); } /************************************************************************ * Handle an open on the control device. */ static int ciss_open(struct cdev *dev, int flags, int fmt, struct thread *p) { struct ciss_softc *sc; debug_called(1); sc = (struct ciss_softc *)dev->si_drv1; /* we might want to veto if someone already has us open */ mtx_lock(&sc->ciss_mtx); sc->ciss_flags |= CISS_FLAG_CONTROL_OPEN; mtx_unlock(&sc->ciss_mtx); return(0); } /************************************************************************ * Handle the last close on the control device. */ static int ciss_close(struct cdev *dev, int flags, int fmt, struct thread *p) { struct ciss_softc *sc; debug_called(1); sc = (struct ciss_softc *)dev->si_drv1; mtx_lock(&sc->ciss_mtx); sc->ciss_flags &= ~CISS_FLAG_CONTROL_OPEN; mtx_unlock(&sc->ciss_mtx); return (0); } /******************************************************************************** * Handle adapter-specific control operations. * * Note that the API here is compatible with the Linux driver, in order to * simplify the porting of Compaq's userland tools. */ static int ciss_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *p) { struct ciss_softc *sc; IOCTL_Command_struct *ioc = (IOCTL_Command_struct *)addr; #ifdef __amd64__ IOCTL_Command_struct32 *ioc32 = (IOCTL_Command_struct32 *)addr; IOCTL_Command_struct ioc_swab; #endif int error; debug_called(1); sc = (struct ciss_softc *)dev->si_drv1; error = 0; mtx_lock(&sc->ciss_mtx); switch(cmd) { case CCISS_GETQSTATS: { union ciss_statrequest *cr = (union ciss_statrequest *)addr; switch (cr->cs_item) { case CISSQ_FREE: case CISSQ_NOTIFY: bcopy(&sc->ciss_qstat[cr->cs_item], &cr->cs_qstat, sizeof(struct ciss_qstat)); break; default: error = ENOIOCTL; break; } break; } case CCISS_GETPCIINFO: { cciss_pci_info_struct *pis = (cciss_pci_info_struct *)addr; pis->bus = pci_get_bus(sc->ciss_dev); pis->dev_fn = pci_get_slot(sc->ciss_dev); pis->board_id = (pci_get_subvendor(sc->ciss_dev) << 16) | pci_get_subdevice(sc->ciss_dev); break; } case CCISS_GETINTINFO: { cciss_coalint_struct *cis = (cciss_coalint_struct *)addr; cis->delay = sc->ciss_cfg->interrupt_coalesce_delay; cis->count = sc->ciss_cfg->interrupt_coalesce_count; break; } case CCISS_SETINTINFO: { cciss_coalint_struct *cis = (cciss_coalint_struct *)addr; if ((cis->delay == 0) && (cis->count == 0)) { error = EINVAL; break; } /* * XXX apparently this is only safe if the controller is idle, * we should suspend it before doing this. */ sc->ciss_cfg->interrupt_coalesce_delay = cis->delay; sc->ciss_cfg->interrupt_coalesce_count = cis->count; if (ciss_update_config(sc)) error = EIO; /* XXX resume the controller here */ break; } case CCISS_GETNODENAME: bcopy(sc->ciss_cfg->server_name, (NodeName_type *)addr, sizeof(NodeName_type)); break; case CCISS_SETNODENAME: bcopy((NodeName_type *)addr, sc->ciss_cfg->server_name, sizeof(NodeName_type)); if (ciss_update_config(sc)) error = EIO; break; case CCISS_GETHEARTBEAT: *(Heartbeat_type *)addr = sc->ciss_cfg->heartbeat; break; case CCISS_GETBUSTYPES: *(BusTypes_type *)addr = sc->ciss_cfg->bus_types; break; case CCISS_GETFIRMVER: bcopy(sc->ciss_id->running_firmware_revision, (FirmwareVer_type *)addr, sizeof(FirmwareVer_type)); break; case CCISS_GETDRIVERVER: *(DriverVer_type *)addr = CISS_DRIVER_VERSION; break; case CCISS_REVALIDVOLS: /* * This is a bit ugly; to do it "right" we really need * to find any disks that have changed, kick CAM off them, * then rescan only these disks. It'd be nice if they * a) told us which disk(s) they were going to play with, * and b) which ones had arrived. 8( */ break; #ifdef __amd64__ case CCISS_PASSTHRU32: ioc_swab.LUN_info = ioc32->LUN_info; ioc_swab.Request = ioc32->Request; ioc_swab.error_info = ioc32->error_info; ioc_swab.buf_size = ioc32->buf_size; ioc_swab.buf = (u_int8_t *)(uintptr_t)ioc32->buf; ioc = &ioc_swab; /* FALLTHROUGH */ #endif case CCISS_PASSTHRU: error = ciss_user_command(sc, ioc); break; default: debug(0, "unknown ioctl 0x%lx", cmd); debug(1, "CCISS_GETPCIINFO: 0x%lx", CCISS_GETPCIINFO); debug(1, "CCISS_GETINTINFO: 0x%lx", CCISS_GETINTINFO); debug(1, "CCISS_SETINTINFO: 0x%lx", CCISS_SETINTINFO); debug(1, "CCISS_GETNODENAME: 0x%lx", CCISS_GETNODENAME); debug(1, "CCISS_SETNODENAME: 0x%lx", CCISS_SETNODENAME); debug(1, "CCISS_GETHEARTBEAT: 0x%lx", CCISS_GETHEARTBEAT); debug(1, "CCISS_GETBUSTYPES: 0x%lx", CCISS_GETBUSTYPES); debug(1, "CCISS_GETFIRMVER: 0x%lx", CCISS_GETFIRMVER); debug(1, "CCISS_GETDRIVERVER: 0x%lx", CCISS_GETDRIVERVER); debug(1, "CCISS_REVALIDVOLS: 0x%lx", CCISS_REVALIDVOLS); debug(1, "CCISS_PASSTHRU: 0x%lx", CCISS_PASSTHRU); error = ENOIOCTL; break; } mtx_unlock(&sc->ciss_mtx); return(error); } Index: head/sys/dev/cxgb/cxgb_sge.c =================================================================== --- head/sys/dev/cxgb/cxgb_sge.c (revision 232853) +++ head/sys/dev/cxgb/cxgb_sge.c (revision 232854) @@ -1,3856 +1,3856 @@ /************************************************************************** Copyright (c) 2007-2009, Chelsio Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Neither the name of the Chelsio Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int txq_fills = 0; int multiq_tx_enable = 1; extern struct sysctl_oid_list sysctl__hw_cxgb_children; int cxgb_txq_buf_ring_size = TX_ETH_Q_SIZE; TUNABLE_INT("hw.cxgb.txq_mr_size", &cxgb_txq_buf_ring_size); SYSCTL_INT(_hw_cxgb, OID_AUTO, txq_mr_size, CTLFLAG_RDTUN, &cxgb_txq_buf_ring_size, 0, "size of per-queue mbuf ring"); static int cxgb_tx_coalesce_force = 0; TUNABLE_INT("hw.cxgb.tx_coalesce_force", &cxgb_tx_coalesce_force); SYSCTL_INT(_hw_cxgb, OID_AUTO, tx_coalesce_force, CTLFLAG_RW, &cxgb_tx_coalesce_force, 0, "coalesce small packets into a single work request regardless of ring state"); #define COALESCE_START_DEFAULT TX_ETH_Q_SIZE>>1 #define COALESCE_START_MAX (TX_ETH_Q_SIZE-(TX_ETH_Q_SIZE>>3)) #define COALESCE_STOP_DEFAULT TX_ETH_Q_SIZE>>2 #define COALESCE_STOP_MIN TX_ETH_Q_SIZE>>5 #define TX_RECLAIM_DEFAULT TX_ETH_Q_SIZE>>5 #define TX_RECLAIM_MAX TX_ETH_Q_SIZE>>2 #define TX_RECLAIM_MIN TX_ETH_Q_SIZE>>6 static int cxgb_tx_coalesce_enable_start = COALESCE_START_DEFAULT; TUNABLE_INT("hw.cxgb.tx_coalesce_enable_start", &cxgb_tx_coalesce_enable_start); SYSCTL_INT(_hw_cxgb, OID_AUTO, tx_coalesce_enable_start, CTLFLAG_RW, &cxgb_tx_coalesce_enable_start, 0, "coalesce enable threshold"); static int cxgb_tx_coalesce_enable_stop = COALESCE_STOP_DEFAULT; TUNABLE_INT("hw.cxgb.tx_coalesce_enable_stop", &cxgb_tx_coalesce_enable_stop); SYSCTL_INT(_hw_cxgb, OID_AUTO, tx_coalesce_enable_stop, CTLFLAG_RW, &cxgb_tx_coalesce_enable_stop, 0, "coalesce disable threshold"); static int cxgb_tx_reclaim_threshold = TX_RECLAIM_DEFAULT; TUNABLE_INT("hw.cxgb.tx_reclaim_threshold", &cxgb_tx_reclaim_threshold); SYSCTL_INT(_hw_cxgb, OID_AUTO, tx_reclaim_threshold, CTLFLAG_RW, &cxgb_tx_reclaim_threshold, 0, "tx cleaning minimum threshold"); /* * XXX don't re-enable this until TOE stops assuming * we have an m_ext */ static int recycle_enable = 0; extern int cxgb_use_16k_clusters; extern int nmbjumbop; extern int nmbjumbo9; extern int nmbjumbo16; #define USE_GTS 0 #define SGE_RX_SM_BUF_SIZE 1536 #define SGE_RX_DROP_THRES 16 #define SGE_RX_COPY_THRES 128 /* * Period of the Tx buffer reclaim timer. This timer does not need to run * frequently as Tx buffers are usually reclaimed by new Tx packets. */ #define TX_RECLAIM_PERIOD (hz >> 1) /* * Values for sge_txq.flags */ enum { TXQ_RUNNING = 1 << 0, /* fetch engine is running */ TXQ_LAST_PKT_DB = 1 << 1, /* last packet rang the doorbell */ }; struct tx_desc { uint64_t flit[TX_DESC_FLITS]; } __packed; struct rx_desc { uint32_t addr_lo; uint32_t len_gen; uint32_t gen2; uint32_t addr_hi; } __packed; struct rsp_desc { /* response queue descriptor */ struct rss_header rss_hdr; uint32_t flags; uint32_t len_cq; uint8_t imm_data[47]; uint8_t intr_gen; } __packed; #define RX_SW_DESC_MAP_CREATED (1 << 0) #define TX_SW_DESC_MAP_CREATED (1 << 1) #define RX_SW_DESC_INUSE (1 << 3) #define TX_SW_DESC_MAPPED (1 << 4) #define RSPQ_NSOP_NEOP G_RSPD_SOP_EOP(0) #define RSPQ_EOP G_RSPD_SOP_EOP(F_RSPD_EOP) #define RSPQ_SOP G_RSPD_SOP_EOP(F_RSPD_SOP) #define RSPQ_SOP_EOP G_RSPD_SOP_EOP(F_RSPD_SOP|F_RSPD_EOP) struct tx_sw_desc { /* SW state per Tx descriptor */ struct mbuf *m; bus_dmamap_t map; int flags; }; struct rx_sw_desc { /* SW state per Rx descriptor */ caddr_t rxsd_cl; struct mbuf *m; bus_dmamap_t map; int flags; }; struct txq_state { unsigned int compl; unsigned int gen; unsigned int pidx; }; struct refill_fl_cb_arg { int error; bus_dma_segment_t seg; int nseg; }; /* * Maps a number of flits to the number of Tx descriptors that can hold them. * The formula is * * desc = 1 + (flits - 2) / (WR_FLITS - 1). * * HW allows up to 4 descriptors to be combined into a WR. */ static uint8_t flit_desc_map[] = { 0, #if SGE_NUM_GENBITS == 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 #elif SGE_NUM_GENBITS == 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, #else # error "SGE_NUM_GENBITS must be 1 or 2" #endif }; #define TXQ_LOCK_ASSERT(qs) mtx_assert(&(qs)->lock, MA_OWNED) #define TXQ_TRYLOCK(qs) mtx_trylock(&(qs)->lock) #define TXQ_LOCK(qs) mtx_lock(&(qs)->lock) #define TXQ_UNLOCK(qs) mtx_unlock(&(qs)->lock) #define TXQ_RING_EMPTY(qs) drbr_empty((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_NEEDS_ENQUEUE(qs) \ drbr_needs_enqueue((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_FLUSH(qs) drbr_flush((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) #define TXQ_RING_DEQUEUE_COND(qs, func, arg) \ drbr_dequeue_cond((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr, func, arg) #define TXQ_RING_DEQUEUE(qs) \ drbr_dequeue((qs)->port->ifp, (qs)->txq[TXQ_ETH].txq_mr) int cxgb_debug = 0; static void sge_timer_cb(void *arg); static void sge_timer_reclaim(void *arg, int ncount); static void sge_txq_reclaim_handler(void *arg, int ncount); static void cxgb_start_locked(struct sge_qset *qs); /* * XXX need to cope with bursty scheduling by looking at a wider * window than we are now for determining the need for coalescing * */ static __inline uint64_t check_pkt_coalesce(struct sge_qset *qs) { struct adapter *sc; struct sge_txq *txq; uint8_t *fill; if (__predict_false(cxgb_tx_coalesce_force)) return (1); txq = &qs->txq[TXQ_ETH]; sc = qs->port->adapter; fill = &sc->tunq_fill[qs->idx]; if (cxgb_tx_coalesce_enable_start > COALESCE_START_MAX) cxgb_tx_coalesce_enable_start = COALESCE_START_MAX; if (cxgb_tx_coalesce_enable_stop < COALESCE_STOP_MIN) cxgb_tx_coalesce_enable_start = COALESCE_STOP_MIN; /* * if the hardware transmit queue is more than 1/8 full * we mark it as coalescing - we drop back from coalescing * when we go below 1/32 full and there are no packets enqueued, * this provides us with some degree of hysteresis */ if (*fill != 0 && (txq->in_use <= cxgb_tx_coalesce_enable_stop) && TXQ_RING_EMPTY(qs) && (qs->coalescing == 0)) *fill = 0; else if (*fill == 0 && (txq->in_use >= cxgb_tx_coalesce_enable_start)) *fill = 1; return (sc->tunq_coalesce); } #ifdef __LP64__ static void set_wr_hdr(struct work_request_hdr *wrp, uint32_t wr_hi, uint32_t wr_lo) { uint64_t wr_hilo; #if _BYTE_ORDER == _LITTLE_ENDIAN wr_hilo = wr_hi; wr_hilo |= (((uint64_t)wr_lo)<<32); #else wr_hilo = wr_lo; wr_hilo |= (((uint64_t)wr_hi)<<32); #endif wrp->wrh_hilo = wr_hilo; } #else static void set_wr_hdr(struct work_request_hdr *wrp, uint32_t wr_hi, uint32_t wr_lo) { wrp->wrh_hi = wr_hi; wmb(); wrp->wrh_lo = wr_lo; } #endif struct coalesce_info { int count; int nbytes; }; static int coalesce_check(struct mbuf *m, void *arg) { struct coalesce_info *ci = arg; int *count = &ci->count; int *nbytes = &ci->nbytes; if ((*nbytes == 0) || ((*nbytes + m->m_len <= 10500) && (*count < 7) && (m->m_next == NULL))) { *count += 1; *nbytes += m->m_len; return (1); } return (0); } static struct mbuf * cxgb_dequeue(struct sge_qset *qs) { struct mbuf *m, *m_head, *m_tail; struct coalesce_info ci; if (check_pkt_coalesce(qs) == 0) return TXQ_RING_DEQUEUE(qs); m_head = m_tail = NULL; ci.count = ci.nbytes = 0; do { m = TXQ_RING_DEQUEUE_COND(qs, coalesce_check, &ci); if (m_head == NULL) { m_tail = m_head = m; } else if (m != NULL) { m_tail->m_nextpkt = m; m_tail = m; } } while (m != NULL); if (ci.count > 7) panic("trying to coalesce %d packets in to one WR", ci.count); return (m_head); } /** * reclaim_completed_tx - reclaims completed Tx descriptors * @adapter: the adapter * @q: the Tx queue to reclaim completed descriptors from * * Reclaims Tx descriptors that the SGE has indicated it has processed, * and frees the associated buffers if possible. Called with the Tx * queue's lock held. */ static __inline int reclaim_completed_tx(struct sge_qset *qs, int reclaim_min, int queue) { struct sge_txq *q = &qs->txq[queue]; int reclaim = desc_reclaimable(q); if ((cxgb_tx_reclaim_threshold > TX_RECLAIM_MAX) || (cxgb_tx_reclaim_threshold < TX_RECLAIM_MIN)) cxgb_tx_reclaim_threshold = TX_RECLAIM_DEFAULT; if (reclaim < reclaim_min) return (0); mtx_assert(&qs->lock, MA_OWNED); if (reclaim > 0) { t3_free_tx_desc(qs, reclaim, queue); q->cleaned += reclaim; q->in_use -= reclaim; } if (isset(&qs->txq_stopped, TXQ_ETH)) clrbit(&qs->txq_stopped, TXQ_ETH); return (reclaim); } /** * should_restart_tx - are there enough resources to restart a Tx queue? * @q: the Tx queue * * Checks if there are enough descriptors to restart a suspended Tx queue. */ static __inline int should_restart_tx(const struct sge_txq *q) { unsigned int r = q->processed - q->cleaned; return q->in_use - r < (q->size >> 1); } /** * t3_sge_init - initialize SGE * @adap: the adapter * @p: the SGE parameters * * Performs SGE initialization needed every time after a chip reset. * We do not initialize any of the queue sets here, instead the driver * top-level must request those individually. We also do not enable DMA * here, that should be done after the queues have been set up. */ void t3_sge_init(adapter_t *adap, struct sge_params *p) { u_int ctrl, ups; ups = 0; /* = ffs(pci_resource_len(adap->pdev, 2) >> 12); */ ctrl = F_DROPPKT | V_PKTSHIFT(2) | F_FLMODE | F_AVOIDCQOVFL | F_CQCRDTCTRL | F_CONGMODE | F_TNLFLMODE | F_FATLPERREN | V_HOSTPAGESIZE(PAGE_SHIFT - 11) | F_BIGENDIANINGRESS | V_USERSPACESIZE(ups ? ups - 1 : 0) | F_ISCSICOALESCING; #if SGE_NUM_GENBITS == 1 ctrl |= F_EGRGENCTRL; #endif if (adap->params.rev > 0) { if (!(adap->flags & (USING_MSIX | USING_MSI))) ctrl |= F_ONEINTMULTQ | F_OPTONEINTMULTQ; } t3_write_reg(adap, A_SG_CONTROL, ctrl); t3_write_reg(adap, A_SG_EGR_RCQ_DRB_THRSH, V_HIRCQDRBTHRSH(512) | V_LORCQDRBTHRSH(512)); t3_write_reg(adap, A_SG_TIMER_TICK, core_ticks_per_usec(adap) / 10); t3_write_reg(adap, A_SG_CMDQ_CREDIT_TH, V_THRESHOLD(32) | V_TIMEOUT(200 * core_ticks_per_usec(adap))); t3_write_reg(adap, A_SG_HI_DRB_HI_THRSH, adap->params.rev < T3_REV_C ? 1000 : 500); t3_write_reg(adap, A_SG_HI_DRB_LO_THRSH, 256); t3_write_reg(adap, A_SG_LO_DRB_HI_THRSH, 1000); t3_write_reg(adap, A_SG_LO_DRB_LO_THRSH, 256); t3_write_reg(adap, A_SG_OCO_BASE, V_BASE1(0xfff)); t3_write_reg(adap, A_SG_DRB_PRI_THRESH, 63 * 1024); } /** * sgl_len - calculates the size of an SGL of the given capacity * @n: the number of SGL entries * * Calculates the number of flits needed for a scatter/gather list that * can hold the given number of entries. */ static __inline unsigned int sgl_len(unsigned int n) { return ((3 * n) / 2 + (n & 1)); } /** * get_imm_packet - return the next ingress packet buffer from a response * @resp: the response descriptor containing the packet data * * Return a packet containing the immediate data of the given response. */ static int get_imm_packet(adapter_t *sc, const struct rsp_desc *resp, struct mbuf *m) { m->m_len = m->m_pkthdr.len = IMMED_PKT_SIZE; m->m_ext.ext_buf = NULL; m->m_ext.ext_type = 0; memcpy(mtod(m, uint8_t *), resp->imm_data, IMMED_PKT_SIZE); return (0); } static __inline u_int flits_to_desc(u_int n) { return (flit_desc_map[n]); } #define SGE_PARERR (F_CPPARITYERROR | F_OCPARITYERROR | F_RCPARITYERROR | \ F_IRPARITYERROR | V_ITPARITYERROR(M_ITPARITYERROR) | \ V_FLPARITYERROR(M_FLPARITYERROR) | F_LODRBPARITYERROR | \ F_HIDRBPARITYERROR | F_LORCQPARITYERROR | \ F_HIRCQPARITYERROR) #define SGE_FRAMINGERR (F_UC_REQ_FRAMINGERROR | F_R_REQ_FRAMINGERROR) #define SGE_FATALERR (SGE_PARERR | SGE_FRAMINGERR | F_RSPQCREDITOVERFOW | \ F_RSPQDISABLED) /** * t3_sge_err_intr_handler - SGE async event interrupt handler * @adapter: the adapter * * Interrupt handler for SGE asynchronous (non-data) events. */ void t3_sge_err_intr_handler(adapter_t *adapter) { unsigned int v, status; status = t3_read_reg(adapter, A_SG_INT_CAUSE); if (status & SGE_PARERR) CH_ALERT(adapter, "SGE parity error (0x%x)\n", status & SGE_PARERR); if (status & SGE_FRAMINGERR) CH_ALERT(adapter, "SGE framing error (0x%x)\n", status & SGE_FRAMINGERR); if (status & F_RSPQCREDITOVERFOW) CH_ALERT(adapter, "SGE response queue credit overflow\n"); if (status & F_RSPQDISABLED) { v = t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS); CH_ALERT(adapter, "packet delivered to disabled response queue (0x%x)\n", (v >> S_RSPQ0DISABLED) & 0xff); } t3_write_reg(adapter, A_SG_INT_CAUSE, status); if (status & SGE_FATALERR) t3_fatal_err(adapter); } void t3_sge_prep(adapter_t *adap, struct sge_params *p) { int i, nqsets, fl_q_size, jumbo_q_size, use_16k, jumbo_buf_size; nqsets = min(SGE_QSETS / adap->params.nports, mp_ncpus); nqsets *= adap->params.nports; fl_q_size = min(nmbclusters/(3*nqsets), FL_Q_SIZE); while (!powerof2(fl_q_size)) fl_q_size--; use_16k = cxgb_use_16k_clusters != -1 ? cxgb_use_16k_clusters : is_offload(adap); #if __FreeBSD_version >= 700111 if (use_16k) { jumbo_q_size = min(nmbjumbo16/(3*nqsets), JUMBO_Q_SIZE); jumbo_buf_size = MJUM16BYTES; } else { jumbo_q_size = min(nmbjumbo9/(3*nqsets), JUMBO_Q_SIZE); jumbo_buf_size = MJUM9BYTES; } #else jumbo_q_size = min(nmbjumbop/(3*nqsets), JUMBO_Q_SIZE); jumbo_buf_size = MJUMPAGESIZE; #endif while (!powerof2(jumbo_q_size)) jumbo_q_size--; if (fl_q_size < (FL_Q_SIZE / 4) || jumbo_q_size < (JUMBO_Q_SIZE / 2)) device_printf(adap->dev, "Insufficient clusters and/or jumbo buffers.\n"); p->max_pkt_size = jumbo_buf_size - sizeof(struct cpl_rx_data); for (i = 0; i < SGE_QSETS; ++i) { struct qset_params *q = p->qset + i; if (adap->params.nports > 2) { q->coalesce_usecs = 50; } else { #ifdef INVARIANTS q->coalesce_usecs = 10; #else q->coalesce_usecs = 5; #endif } q->polling = 0; q->rspq_size = RSPQ_Q_SIZE; q->fl_size = fl_q_size; q->jumbo_size = jumbo_q_size; q->jumbo_buf_size = jumbo_buf_size; q->txq_size[TXQ_ETH] = TX_ETH_Q_SIZE; q->txq_size[TXQ_OFLD] = is_offload(adap) ? TX_OFLD_Q_SIZE : 16; q->txq_size[TXQ_CTRL] = TX_CTRL_Q_SIZE; q->cong_thres = 0; } } int t3_sge_alloc(adapter_t *sc) { /* The parent tag. */ - if (bus_dma_tag_create( NULL, /* parent */ + if (bus_dma_tag_create( bus_get_dma_tag(sc->dev),/* PCI parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ NULL, NULL, /* lock, lockarg */ &sc->parent_dmat)) { device_printf(sc->dev, "Cannot allocate parent DMA tag\n"); return (ENOMEM); } /* * DMA tag for normal sized RX frames */ if (bus_dma_tag_create(sc->parent_dmat, MCLBYTES, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->rx_dmat)) { device_printf(sc->dev, "Cannot allocate RX DMA tag\n"); return (ENOMEM); } /* * DMA tag for jumbo sized RX frames. */ if (bus_dma_tag_create(sc->parent_dmat, MJUM16BYTES, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MJUM16BYTES, 1, MJUM16BYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->rx_jumbo_dmat)) { device_printf(sc->dev, "Cannot allocate RX jumbo DMA tag\n"); return (ENOMEM); } /* * DMA tag for TX frames. */ if (bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, TX_MAX_SIZE, TX_MAX_SEGS, TX_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->tx_dmat)) { device_printf(sc->dev, "Cannot allocate TX DMA tag\n"); return (ENOMEM); } return (0); } int t3_sge_free(struct adapter * sc) { if (sc->tx_dmat != NULL) bus_dma_tag_destroy(sc->tx_dmat); if (sc->rx_jumbo_dmat != NULL) bus_dma_tag_destroy(sc->rx_jumbo_dmat); if (sc->rx_dmat != NULL) bus_dma_tag_destroy(sc->rx_dmat); if (sc->parent_dmat != NULL) bus_dma_tag_destroy(sc->parent_dmat); return (0); } void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p) { qs->rspq.holdoff_tmr = max(p->coalesce_usecs * 10, 1U); qs->rspq.polling = 0 /* p->polling */; } #if !defined(__i386__) && !defined(__amd64__) static void refill_fl_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct refill_fl_cb_arg *cb_arg = arg; cb_arg->error = error; cb_arg->seg = segs[0]; cb_arg->nseg = nseg; } #endif /** * refill_fl - refill an SGE free-buffer list * @sc: the controller softc * @q: the free-list to refill * @n: the number of new buffers to allocate * * (Re)populate an SGE free-buffer list with up to @n new packet buffers. * The caller must assure that @n does not exceed the queue's capacity. */ static void refill_fl(adapter_t *sc, struct sge_fl *q, int n) { struct rx_sw_desc *sd = &q->sdesc[q->pidx]; struct rx_desc *d = &q->desc[q->pidx]; struct refill_fl_cb_arg cb_arg; struct mbuf *m; caddr_t cl; int err; cb_arg.error = 0; while (n--) { /* * We only allocate a cluster, mbuf allocation happens after rx */ if (q->zone == zone_pack) { if ((m = m_getcl(M_NOWAIT, MT_NOINIT, M_PKTHDR)) == NULL) break; cl = m->m_ext.ext_buf; } else { if ((cl = m_cljget(NULL, M_NOWAIT, q->buf_size)) == NULL) break; if ((m = m_gethdr(M_NOWAIT, MT_NOINIT)) == NULL) { uma_zfree(q->zone, cl); break; } } if ((sd->flags & RX_SW_DESC_MAP_CREATED) == 0) { if ((err = bus_dmamap_create(q->entry_tag, 0, &sd->map))) { log(LOG_WARNING, "bus_dmamap_create failed %d\n", err); uma_zfree(q->zone, cl); goto done; } sd->flags |= RX_SW_DESC_MAP_CREATED; } #if !defined(__i386__) && !defined(__amd64__) err = bus_dmamap_load(q->entry_tag, sd->map, cl, q->buf_size, refill_fl_cb, &cb_arg, 0); if (err != 0 || cb_arg.error) { if (q->zone == zone_pack) uma_zfree(q->zone, cl); m_free(m); goto done; } #else cb_arg.seg.ds_addr = pmap_kextract((vm_offset_t)cl); #endif sd->flags |= RX_SW_DESC_INUSE; sd->rxsd_cl = cl; sd->m = m; d->addr_lo = htobe32(cb_arg.seg.ds_addr & 0xffffffff); d->addr_hi = htobe32(((uint64_t)cb_arg.seg.ds_addr >>32) & 0xffffffff); d->len_gen = htobe32(V_FLD_GEN1(q->gen)); d->gen2 = htobe32(V_FLD_GEN2(q->gen)); d++; sd++; if (++q->pidx == q->size) { q->pidx = 0; q->gen ^= 1; sd = q->sdesc; d = q->desc; } q->credits++; q->db_pending++; } done: if (q->db_pending >= 32) { q->db_pending = 0; t3_write_reg(sc, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); } } /** * free_rx_bufs - free the Rx buffers on an SGE free list * @sc: the controle softc * @q: the SGE free list to clean up * * Release the buffers on an SGE free-buffer Rx queue. HW fetching from * this queue should be stopped before calling this function. */ static void free_rx_bufs(adapter_t *sc, struct sge_fl *q) { u_int cidx = q->cidx; while (q->credits--) { struct rx_sw_desc *d = &q->sdesc[cidx]; if (d->flags & RX_SW_DESC_INUSE) { bus_dmamap_unload(q->entry_tag, d->map); bus_dmamap_destroy(q->entry_tag, d->map); if (q->zone == zone_pack) { m_init(d->m, zone_pack, MCLBYTES, M_NOWAIT, MT_DATA, M_EXT); uma_zfree(zone_pack, d->m); } else { m_init(d->m, zone_mbuf, MLEN, M_NOWAIT, MT_DATA, 0); uma_zfree(zone_mbuf, d->m); uma_zfree(q->zone, d->rxsd_cl); } } d->rxsd_cl = NULL; d->m = NULL; if (++cidx == q->size) cidx = 0; } } static __inline void __refill_fl(adapter_t *adap, struct sge_fl *fl) { refill_fl(adap, fl, min(16U, fl->size - fl->credits)); } static __inline void __refill_fl_lt(adapter_t *adap, struct sge_fl *fl, int max) { uint32_t reclaimable = fl->size - fl->credits; if (reclaimable > 0) refill_fl(adap, fl, min(max, reclaimable)); } /** * recycle_rx_buf - recycle a receive buffer * @adapter: the adapter * @q: the SGE free list * @idx: index of buffer to recycle * * Recycles the specified buffer on the given free list by adding it at * the next available slot on the list. */ static void recycle_rx_buf(adapter_t *adap, struct sge_fl *q, unsigned int idx) { struct rx_desc *from = &q->desc[idx]; struct rx_desc *to = &q->desc[q->pidx]; q->sdesc[q->pidx] = q->sdesc[idx]; to->addr_lo = from->addr_lo; // already big endian to->addr_hi = from->addr_hi; // likewise wmb(); /* necessary ? */ to->len_gen = htobe32(V_FLD_GEN1(q->gen)); to->gen2 = htobe32(V_FLD_GEN2(q->gen)); q->credits++; if (++q->pidx == q->size) { q->pidx = 0; q->gen ^= 1; } t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); } static void alloc_ring_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { uint32_t *addr; addr = arg; *addr = segs[0].ds_addr; } static int alloc_ring(adapter_t *sc, size_t nelem, size_t elem_size, size_t sw_size, bus_addr_t *phys, void *desc, void *sdesc, bus_dma_tag_t *tag, bus_dmamap_t *map, bus_dma_tag_t parent_entry_tag, bus_dma_tag_t *entry_tag) { size_t len = nelem * elem_size; void *s = NULL; void *p = NULL; int err; if ((err = bus_dma_tag_create(sc->parent_dmat, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, len, 1, len, 0, NULL, NULL, tag)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor tag\n"); return (ENOMEM); } if ((err = bus_dmamem_alloc(*tag, (void **)&p, BUS_DMA_NOWAIT, map)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor memory\n"); return (ENOMEM); } bus_dmamap_load(*tag, *map, p, len, alloc_ring_cb, phys, 0); bzero(p, len); *(void **)desc = p; if (sw_size) { len = nelem * sw_size; s = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO); *(void **)sdesc = s; } if (parent_entry_tag == NULL) return (0); if ((err = bus_dma_tag_create(parent_entry_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, TX_MAX_SIZE, TX_MAX_SEGS, TX_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, entry_tag)) != 0) { device_printf(sc->dev, "Cannot allocate descriptor entry tag\n"); return (ENOMEM); } return (0); } static void sge_slow_intr_handler(void *arg, int ncount) { adapter_t *sc = arg; t3_slow_intr_handler(sc); t3_write_reg(sc, A_PL_INT_ENABLE0, sc->slow_intr_mask); (void) t3_read_reg(sc, A_PL_INT_ENABLE0); } /** * sge_timer_cb - perform periodic maintenance of an SGE qset * @data: the SGE queue set to maintain * * Runs periodically from a timer to perform maintenance of an SGE queue * set. It performs two tasks: * * a) Cleans up any completed Tx descriptors that may still be pending. * Normal descriptor cleanup happens when new packets are added to a Tx * queue so this timer is relatively infrequent and does any cleanup only * if the Tx queue has not seen any new packets in a while. We make a * best effort attempt to reclaim descriptors, in that we don't wait * around if we cannot get a queue's lock (which most likely is because * someone else is queueing new packets and so will also handle the clean * up). Since control queues use immediate data exclusively we don't * bother cleaning them up here. * * b) Replenishes Rx queues that have run out due to memory shortage. * Normally new Rx buffers are added when existing ones are consumed but * when out of memory a queue can become empty. We try to add only a few * buffers here, the queue will be replenished fully as these new buffers * are used up if memory shortage has subsided. * * c) Return coalesced response queue credits in case a response queue is * starved. * * d) Ring doorbells for T304 tunnel queues since we have seen doorbell * fifo overflows and the FW doesn't implement any recovery scheme yet. */ static void sge_timer_cb(void *arg) { adapter_t *sc = arg; if ((sc->flags & USING_MSIX) == 0) { struct port_info *pi; struct sge_qset *qs; struct sge_txq *txq; int i, j; int reclaim_ofl, refill_rx; if (sc->open_device_map == 0) return; for (i = 0; i < sc->params.nports; i++) { pi = &sc->port[i]; for (j = 0; j < pi->nqsets; j++) { qs = &sc->sge.qs[pi->first_qset + j]; txq = &qs->txq[0]; reclaim_ofl = txq[TXQ_OFLD].processed - txq[TXQ_OFLD].cleaned; refill_rx = ((qs->fl[0].credits < qs->fl[0].size) || (qs->fl[1].credits < qs->fl[1].size)); if (reclaim_ofl || refill_rx) { taskqueue_enqueue(sc->tq, &pi->timer_reclaim_task); break; } } } } if (sc->params.nports > 2) { int i; for_each_port(sc, i) { struct port_info *pi = &sc->port[i]; t3_write_reg(sc, A_SG_KDOORBELL, F_SELEGRCNTX | (FW_TUNNEL_SGEEC_START + pi->first_qset)); } } if (((sc->flags & USING_MSIX) == 0 || sc->params.nports > 2) && sc->open_device_map != 0) callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); } /* * This is meant to be a catch-all function to keep sge state private * to sge.c * */ int t3_sge_init_adapter(adapter_t *sc) { callout_init(&sc->sge_timer_ch, CALLOUT_MPSAFE); callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); TASK_INIT(&sc->slow_intr_task, 0, sge_slow_intr_handler, sc); return (0); } int t3_sge_reset_adapter(adapter_t *sc) { callout_reset(&sc->sge_timer_ch, TX_RECLAIM_PERIOD, sge_timer_cb, sc); return (0); } int t3_sge_init_port(struct port_info *pi) { TASK_INIT(&pi->timer_reclaim_task, 0, sge_timer_reclaim, pi); return (0); } /** * refill_rspq - replenish an SGE response queue * @adapter: the adapter * @q: the response queue to replenish * @credits: how many new responses to make available * * Replenishes a response queue by making the supplied number of responses * available to HW. */ static __inline void refill_rspq(adapter_t *sc, const struct sge_rspq *q, u_int credits) { /* mbufs are allocated on demand when a rspq entry is processed. */ t3_write_reg(sc, A_SG_RSPQ_CREDIT_RETURN, V_RSPQ(q->cntxt_id) | V_CREDITS(credits)); } static void sge_txq_reclaim_handler(void *arg, int ncount) { struct sge_qset *qs = arg; int i; for (i = 0; i < 3; i++) reclaim_completed_tx(qs, 16, i); } static void sge_timer_reclaim(void *arg, int ncount) { struct port_info *pi = arg; int i, nqsets = pi->nqsets; adapter_t *sc = pi->adapter; struct sge_qset *qs; struct mtx *lock; KASSERT((sc->flags & USING_MSIX) == 0, ("can't call timer reclaim for msi-x")); for (i = 0; i < nqsets; i++) { qs = &sc->sge.qs[pi->first_qset + i]; reclaim_completed_tx(qs, 16, TXQ_OFLD); lock = (sc->flags & USING_MSIX) ? &qs->rspq.lock : &sc->sge.qs[0].rspq.lock; if (mtx_trylock(lock)) { /* XXX currently assume that we are *NOT* polling */ uint32_t status = t3_read_reg(sc, A_SG_RSPQ_FL_STATUS); if (qs->fl[0].credits < qs->fl[0].size - 16) __refill_fl(sc, &qs->fl[0]); if (qs->fl[1].credits < qs->fl[1].size - 16) __refill_fl(sc, &qs->fl[1]); if (status & (1 << qs->rspq.cntxt_id)) { if (qs->rspq.credits) { refill_rspq(sc, &qs->rspq, 1); qs->rspq.credits--; t3_write_reg(sc, A_SG_RSPQ_FL_STATUS, 1 << qs->rspq.cntxt_id); } } mtx_unlock(lock); } } } /** * init_qset_cntxt - initialize an SGE queue set context info * @qs: the queue set * @id: the queue set id * * Initializes the TIDs and context ids for the queues of a queue set. */ static void init_qset_cntxt(struct sge_qset *qs, u_int id) { qs->rspq.cntxt_id = id; qs->fl[0].cntxt_id = 2 * id; qs->fl[1].cntxt_id = 2 * id + 1; qs->txq[TXQ_ETH].cntxt_id = FW_TUNNEL_SGEEC_START + id; qs->txq[TXQ_ETH].token = FW_TUNNEL_TID_START + id; qs->txq[TXQ_OFLD].cntxt_id = FW_OFLD_SGEEC_START + id; qs->txq[TXQ_CTRL].cntxt_id = FW_CTRL_SGEEC_START + id; qs->txq[TXQ_CTRL].token = FW_CTRL_TID_START + id; mbufq_init(&qs->txq[TXQ_ETH].sendq); mbufq_init(&qs->txq[TXQ_OFLD].sendq); mbufq_init(&qs->txq[TXQ_CTRL].sendq); } static void txq_prod(struct sge_txq *txq, unsigned int ndesc, struct txq_state *txqs) { txq->in_use += ndesc; /* * XXX we don't handle stopping of queue * presumably start handles this when we bump against the end */ txqs->gen = txq->gen; txq->unacked += ndesc; txqs->compl = (txq->unacked & 32) << (S_WR_COMPL - 5); txq->unacked &= 31; txqs->pidx = txq->pidx; txq->pidx += ndesc; #ifdef INVARIANTS if (((txqs->pidx > txq->cidx) && (txq->pidx < txqs->pidx) && (txq->pidx >= txq->cidx)) || ((txqs->pidx < txq->cidx) && (txq->pidx >= txq-> cidx)) || ((txqs->pidx < txq->cidx) && (txq->cidx < txqs->pidx))) panic("txqs->pidx=%d txq->pidx=%d txq->cidx=%d", txqs->pidx, txq->pidx, txq->cidx); #endif if (txq->pidx >= txq->size) { txq->pidx -= txq->size; txq->gen ^= 1; } } /** * calc_tx_descs - calculate the number of Tx descriptors for a packet * @m: the packet mbufs * @nsegs: the number of segments * * Returns the number of Tx descriptors needed for the given Ethernet * packet. Ethernet packets require addition of WR and CPL headers. */ static __inline unsigned int calc_tx_descs(const struct mbuf *m, int nsegs) { unsigned int flits; if (m->m_pkthdr.len <= PIO_LEN) return 1; flits = sgl_len(nsegs) + 2; if (m->m_pkthdr.csum_flags & CSUM_TSO) flits++; return flits_to_desc(flits); } static unsigned int busdma_map_mbufs(struct mbuf **m, struct sge_txq *txq, struct tx_sw_desc *txsd, bus_dma_segment_t *segs, int *nsegs) { struct mbuf *m0; int err, pktlen, pass = 0; bus_dma_tag_t tag = txq->entry_tag; retry: err = 0; m0 = *m; pktlen = m0->m_pkthdr.len; #if defined(__i386__) || defined(__amd64__) if (busdma_map_sg_collapse(tag, txsd->map, m, segs, nsegs) == 0) { goto done; } else #endif err = bus_dmamap_load_mbuf_sg(tag, txsd->map, m0, segs, nsegs, 0); if (err == 0) { goto done; } if (err == EFBIG && pass == 0) { pass = 1; /* Too many segments, try to defrag */ m0 = m_defrag(m0, M_DONTWAIT); if (m0 == NULL) { m_freem(*m); *m = NULL; return (ENOBUFS); } *m = m0; goto retry; } else if (err == ENOMEM) { return (err); } if (err) { if (cxgb_debug) printf("map failure err=%d pktlen=%d\n", err, pktlen); m_freem(m0); *m = NULL; return (err); } done: #if !defined(__i386__) && !defined(__amd64__) bus_dmamap_sync(tag, txsd->map, BUS_DMASYNC_PREWRITE); #endif txsd->flags |= TX_SW_DESC_MAPPED; return (0); } /** * make_sgl - populate a scatter/gather list for a packet * @sgp: the SGL to populate * @segs: the packet dma segments * @nsegs: the number of segments * * Generates a scatter/gather list for the buffers that make up a packet * and returns the SGL size in 8-byte words. The caller must size the SGL * appropriately. */ static __inline void make_sgl(struct sg_ent *sgp, bus_dma_segment_t *segs, int nsegs) { int i, idx; for (idx = 0, i = 0; i < nsegs; i++) { /* * firmware doesn't like empty segments */ if (segs[i].ds_len == 0) continue; if (i && idx == 0) ++sgp; sgp->len[idx] = htobe32(segs[i].ds_len); sgp->addr[idx] = htobe64(segs[i].ds_addr); idx ^= 1; } if (idx) { sgp->len[idx] = 0; sgp->addr[idx] = 0; } } /** * check_ring_tx_db - check and potentially ring a Tx queue's doorbell * @adap: the adapter * @q: the Tx queue * * Ring the doorbell if a Tx queue is asleep. There is a natural race, * where the HW is going to sleep just after we checked, however, * then the interrupt handler will detect the outstanding TX packet * and ring the doorbell for us. * * When GTS is disabled we unconditionally ring the doorbell. */ static __inline void check_ring_tx_db(adapter_t *adap, struct sge_txq *q, int mustring) { #if USE_GTS clear_bit(TXQ_LAST_PKT_DB, &q->flags); if (test_and_set_bit(TXQ_RUNNING, &q->flags) == 0) { set_bit(TXQ_LAST_PKT_DB, &q->flags); #ifdef T3_TRACE T3_TRACE1(adap->tb[q->cntxt_id & 7], "doorbell Tx, cntxt %d", q->cntxt_id); #endif t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } #else if (mustring || ++q->db_pending >= 32) { wmb(); /* write descriptors before telling HW */ t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); q->db_pending = 0; } #endif } static __inline void wr_gen2(struct tx_desc *d, unsigned int gen) { #if SGE_NUM_GENBITS == 2 d->flit[TX_DESC_FLITS - 1] = htobe64(gen); #endif } /** * write_wr_hdr_sgl - write a WR header and, optionally, SGL * @ndesc: number of Tx descriptors spanned by the SGL * @txd: first Tx descriptor to be written * @txqs: txq state (generation and producer index) * @txq: the SGE Tx queue * @sgl: the SGL * @flits: number of flits to the start of the SGL in the first descriptor * @sgl_flits: the SGL size in flits * @wr_hi: top 32 bits of WR header based on WR type (big endian) * @wr_lo: low 32 bits of WR header based on WR type (big endian) * * Write a work request header and an associated SGL. If the SGL is * small enough to fit into one Tx descriptor it has already been written * and we just need to write the WR header. Otherwise we distribute the * SGL across the number of descriptors it spans. */ static void write_wr_hdr_sgl(unsigned int ndesc, struct tx_desc *txd, struct txq_state *txqs, const struct sge_txq *txq, const struct sg_ent *sgl, unsigned int flits, unsigned int sgl_flits, unsigned int wr_hi, unsigned int wr_lo) { struct work_request_hdr *wrp = (struct work_request_hdr *)txd; struct tx_sw_desc *txsd = &txq->sdesc[txqs->pidx]; if (__predict_true(ndesc == 1)) { set_wr_hdr(wrp, htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | wr_hi, htonl(V_WR_LEN(flits + sgl_flits) | V_WR_GEN(txqs->gen)) | wr_lo); /* XXX gen? */ wr_gen2(txd, txqs->gen); } else { unsigned int ogen = txqs->gen; const uint64_t *fp = (const uint64_t *)sgl; struct work_request_hdr *wp = wrp; wrp->wrh_hi = htonl(F_WR_SOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | wr_hi; while (sgl_flits) { unsigned int avail = WR_FLITS - flits; if (avail > sgl_flits) avail = sgl_flits; memcpy(&txd->flit[flits], fp, avail * sizeof(*fp)); sgl_flits -= avail; ndesc--; if (!sgl_flits) break; fp += avail; txd++; txsd++; if (++txqs->pidx == txq->size) { txqs->pidx = 0; txqs->gen ^= 1; txd = txq->desc; txsd = txq->sdesc; } /* * when the head of the mbuf chain * is freed all clusters will be freed * with it */ wrp = (struct work_request_hdr *)txd; wrp->wrh_hi = htonl(V_WR_DATATYPE(1) | V_WR_SGLSFLT(1)) | wr_hi; wrp->wrh_lo = htonl(V_WR_LEN(min(WR_FLITS, sgl_flits + 1)) | V_WR_GEN(txqs->gen)) | wr_lo; wr_gen2(txd, txqs->gen); flits = 1; } wrp->wrh_hi |= htonl(F_WR_EOP); wmb(); wp->wrh_lo = htonl(V_WR_LEN(WR_FLITS) | V_WR_GEN(ogen)) | wr_lo; wr_gen2((struct tx_desc *)wp, ogen); } } /* sizeof(*eh) + sizeof(*ip) + sizeof(*tcp) */ #define TCPPKTHDRSIZE (ETHER_HDR_LEN + 20 + 20) #define GET_VTAG(cntrl, m) \ do { \ if ((m)->m_flags & M_VLANTAG) \ cntrl |= F_TXPKT_VLAN_VLD | V_TXPKT_VLAN((m)->m_pkthdr.ether_vtag); \ } while (0) static int t3_encap(struct sge_qset *qs, struct mbuf **m) { adapter_t *sc; struct mbuf *m0; struct sge_txq *txq; struct txq_state txqs; struct port_info *pi; unsigned int ndesc, flits, cntrl, mlen; int err, nsegs, tso_info = 0; struct work_request_hdr *wrp; struct tx_sw_desc *txsd; struct sg_ent *sgp, *sgl; uint32_t wr_hi, wr_lo, sgl_flits; bus_dma_segment_t segs[TX_MAX_SEGS]; struct tx_desc *txd; pi = qs->port; sc = pi->adapter; txq = &qs->txq[TXQ_ETH]; txd = &txq->desc[txq->pidx]; txsd = &txq->sdesc[txq->pidx]; sgl = txq->txq_sgl; prefetch(txd); m0 = *m; mtx_assert(&qs->lock, MA_OWNED); cntrl = V_TXPKT_INTF(pi->txpkt_intf); KASSERT(m0->m_flags & M_PKTHDR, ("not packet header\n")); if (m0->m_nextpkt == NULL && m0->m_next != NULL && m0->m_pkthdr.csum_flags & (CSUM_TSO)) tso_info = V_LSO_MSS(m0->m_pkthdr.tso_segsz); if (m0->m_nextpkt != NULL) { busdma_map_sg_vec(txq->entry_tag, txsd->map, m0, segs, &nsegs); ndesc = 1; mlen = 0; } else { if ((err = busdma_map_sg_collapse(txq->entry_tag, txsd->map, &m0, segs, &nsegs))) { if (cxgb_debug) printf("failed ... err=%d\n", err); return (err); } mlen = m0->m_pkthdr.len; ndesc = calc_tx_descs(m0, nsegs); } txq_prod(txq, ndesc, &txqs); KASSERT(m0->m_pkthdr.len, ("empty packet nsegs=%d", nsegs)); txsd->m = m0; if (m0->m_nextpkt != NULL) { struct cpl_tx_pkt_batch *cpl_batch = (struct cpl_tx_pkt_batch *)txd; int i, fidx; if (nsegs > 7) panic("trying to coalesce %d packets in to one WR", nsegs); txq->txq_coalesced += nsegs; wrp = (struct work_request_hdr *)txd; flits = nsegs*2 + 1; for (fidx = 1, i = 0; i < nsegs; i++, fidx += 2) { struct cpl_tx_pkt_batch_entry *cbe; uint64_t flit; uint32_t *hflit = (uint32_t *)&flit; int cflags = m0->m_pkthdr.csum_flags; cntrl = V_TXPKT_INTF(pi->txpkt_intf); GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT); if (__predict_false(!(cflags & CSUM_IP))) cntrl |= F_TXPKT_IPCSUM_DIS; if (__predict_false(!(cflags & (CSUM_TCP | CSUM_UDP)))) cntrl |= F_TXPKT_L4CSUM_DIS; hflit[0] = htonl(cntrl); hflit[1] = htonl(segs[i].ds_len | 0x80000000); flit |= htobe64(1 << 24); cbe = &cpl_batch->pkt_entry[i]; cbe->cntrl = hflit[0]; cbe->len = hflit[1]; cbe->addr = htobe64(segs[i].ds_addr); } wr_hi = htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) | V_WR_SGLSFLT(flits)) | htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen)) | htonl(V_WR_TID(txq->token)); set_wr_hdr(wrp, wr_hi, wr_lo); wmb(); ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq, 0); return (0); } else if (tso_info) { uint16_t eth_type; struct cpl_tx_pkt_lso *hdr = (struct cpl_tx_pkt_lso *)txd; struct ether_header *eh; void *l3hdr; struct tcphdr *tcp; txd->flit[2] = 0; GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO); hdr->cntrl = htonl(cntrl); hdr->len = htonl(mlen | 0x80000000); if (__predict_false(mlen < TCPPKTHDRSIZE)) { printf("mbuf=%p,len=%d,tso_segsz=%d,csum_flags=%#x,flags=%#x", m0, mlen, m0->m_pkthdr.tso_segsz, m0->m_pkthdr.csum_flags, m0->m_flags); panic("tx tso packet too small"); } /* Make sure that ether, ip, tcp headers are all in m0 */ if (__predict_false(m0->m_len < TCPPKTHDRSIZE)) { m0 = m_pullup(m0, TCPPKTHDRSIZE); if (__predict_false(m0 == NULL)) { /* XXX panic probably an overreaction */ panic("couldn't fit header into mbuf"); } } eh = mtod(m0, struct ether_header *); eth_type = eh->ether_type; if (eth_type == htons(ETHERTYPE_VLAN)) { struct ether_vlan_header *evh = (void *)eh; tso_info |= V_LSO_ETH_TYPE(CPL_ETH_II_VLAN); l3hdr = evh + 1; eth_type = evh->evl_proto; } else { tso_info |= V_LSO_ETH_TYPE(CPL_ETH_II); l3hdr = eh + 1; } if (eth_type == htons(ETHERTYPE_IP)) { struct ip *ip = l3hdr; tso_info |= V_LSO_IPHDR_WORDS(ip->ip_hl); tcp = (struct tcphdr *)(ip + 1); } else if (eth_type == htons(ETHERTYPE_IPV6)) { struct ip6_hdr *ip6 = l3hdr; KASSERT(ip6->ip6_nxt == IPPROTO_TCP, ("%s: CSUM_TSO with ip6_nxt %d", __func__, ip6->ip6_nxt)); tso_info |= F_LSO_IPV6; tso_info |= V_LSO_IPHDR_WORDS(sizeof(*ip6) >> 2); tcp = (struct tcphdr *)(ip6 + 1); } else panic("%s: CSUM_TSO but neither ip nor ip6", __func__); tso_info |= V_LSO_TCPHDR_WORDS(tcp->th_off); hdr->lso_info = htonl(tso_info); if (__predict_false(mlen <= PIO_LEN)) { /* * pkt not undersized but fits in PIO_LEN * Indicates a TSO bug at the higher levels. */ txsd->m = NULL; m_copydata(m0, 0, mlen, (caddr_t)&txd->flit[3]); flits = (mlen + 7) / 8 + 3; wr_hi = htonl(V_WR_BCNTLFLT(mlen & 7) | V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | F_WR_SOP | F_WR_EOP | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen) | V_WR_TID(txq->token)); set_wr_hdr(&hdr->wr, wr_hi, wr_lo); wmb(); ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq, 0); m_freem(m0); return (0); } flits = 3; } else { struct cpl_tx_pkt *cpl = (struct cpl_tx_pkt *)txd; GET_VTAG(cntrl, m0); cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT); if (__predict_false(!(m0->m_pkthdr.csum_flags & CSUM_IP))) cntrl |= F_TXPKT_IPCSUM_DIS; if (__predict_false(!(m0->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)))) cntrl |= F_TXPKT_L4CSUM_DIS; cpl->cntrl = htonl(cntrl); cpl->len = htonl(mlen | 0x80000000); if (mlen <= PIO_LEN) { txsd->m = NULL; m_copydata(m0, 0, mlen, (caddr_t)&txd->flit[2]); flits = (mlen + 7) / 8 + 2; wr_hi = htonl(V_WR_BCNTLFLT(mlen & 7) | V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | F_WR_SOP | F_WR_EOP | txqs.compl); wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(txqs.gen) | V_WR_TID(txq->token)); set_wr_hdr(&cpl->wr, wr_hi, wr_lo); wmb(); ETHER_BPF_MTAP(pi->ifp, m0); wr_gen2(txd, txqs.gen); check_ring_tx_db(sc, txq, 0); m_freem(m0); return (0); } flits = 2; } wrp = (struct work_request_hdr *)txd; sgp = (ndesc == 1) ? (struct sg_ent *)&txd->flit[flits] : sgl; make_sgl(sgp, segs, nsegs); sgl_flits = sgl_len(nsegs); ETHER_BPF_MTAP(pi->ifp, m0); KASSERT(ndesc <= 4, ("ndesc too large %d", ndesc)); wr_hi = htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | txqs.compl); wr_lo = htonl(V_WR_TID(txq->token)); write_wr_hdr_sgl(ndesc, txd, &txqs, txq, sgl, flits, sgl_flits, wr_hi, wr_lo); check_ring_tx_db(sc, txq, 0); return (0); } void cxgb_tx_watchdog(void *arg) { struct sge_qset *qs = arg; struct sge_txq *txq = &qs->txq[TXQ_ETH]; if (qs->coalescing != 0 && (txq->in_use <= cxgb_tx_coalesce_enable_stop) && TXQ_RING_EMPTY(qs)) qs->coalescing = 0; else if (qs->coalescing == 0 && (txq->in_use >= cxgb_tx_coalesce_enable_start)) qs->coalescing = 1; if (TXQ_TRYLOCK(qs)) { qs->qs_flags |= QS_FLUSHING; cxgb_start_locked(qs); qs->qs_flags &= ~QS_FLUSHING; TXQ_UNLOCK(qs); } if (qs->port->ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset_on(&txq->txq_watchdog, hz/4, cxgb_tx_watchdog, qs, txq->txq_watchdog.c_cpu); } static void cxgb_tx_timeout(void *arg) { struct sge_qset *qs = arg; struct sge_txq *txq = &qs->txq[TXQ_ETH]; if (qs->coalescing == 0 && (txq->in_use >= (txq->size>>3))) qs->coalescing = 1; if (TXQ_TRYLOCK(qs)) { qs->qs_flags |= QS_TIMEOUT; cxgb_start_locked(qs); qs->qs_flags &= ~QS_TIMEOUT; TXQ_UNLOCK(qs); } } static void cxgb_start_locked(struct sge_qset *qs) { struct mbuf *m_head = NULL; struct sge_txq *txq = &qs->txq[TXQ_ETH]; struct port_info *pi = qs->port; struct ifnet *ifp = pi->ifp; if (qs->qs_flags & (QS_FLUSHING|QS_TIMEOUT)) reclaim_completed_tx(qs, 0, TXQ_ETH); if (!pi->link_config.link_ok) { TXQ_RING_FLUSH(qs); return; } TXQ_LOCK_ASSERT(qs); while (!TXQ_RING_EMPTY(qs) && (ifp->if_drv_flags & IFF_DRV_RUNNING) && pi->link_config.link_ok) { reclaim_completed_tx(qs, cxgb_tx_reclaim_threshold, TXQ_ETH); if (txq->size - txq->in_use <= TX_MAX_DESC) break; if ((m_head = cxgb_dequeue(qs)) == NULL) break; /* * Encapsulation can modify our pointer, and or make it * NULL on failure. In that event, we can't requeue. */ if (t3_encap(qs, &m_head) || m_head == NULL) break; m_head = NULL; } if (txq->db_pending) check_ring_tx_db(pi->adapter, txq, 1); if (!TXQ_RING_EMPTY(qs) && callout_pending(&txq->txq_timer) == 0 && pi->link_config.link_ok) callout_reset_on(&txq->txq_timer, 1, cxgb_tx_timeout, qs, txq->txq_timer.c_cpu); if (m_head != NULL) m_freem(m_head); } static int cxgb_transmit_locked(struct ifnet *ifp, struct sge_qset *qs, struct mbuf *m) { struct port_info *pi = qs->port; struct sge_txq *txq = &qs->txq[TXQ_ETH]; struct buf_ring *br = txq->txq_mr; int error, avail; avail = txq->size - txq->in_use; TXQ_LOCK_ASSERT(qs); /* * We can only do a direct transmit if the following are true: * - we aren't coalescing (ring < 3/4 full) * - the link is up -- checked in caller * - there are no packets enqueued already * - there is space in hardware transmit queue */ if (check_pkt_coalesce(qs) == 0 && !TXQ_RING_NEEDS_ENQUEUE(qs) && avail > TX_MAX_DESC) { if (t3_encap(qs, &m)) { if (m != NULL && (error = drbr_enqueue(ifp, br, m)) != 0) return (error); } else { if (txq->db_pending) check_ring_tx_db(pi->adapter, txq, 1); /* * We've bypassed the buf ring so we need to update * the stats directly */ txq->txq_direct_packets++; txq->txq_direct_bytes += m->m_pkthdr.len; } } else if ((error = drbr_enqueue(ifp, br, m)) != 0) return (error); reclaim_completed_tx(qs, cxgb_tx_reclaim_threshold, TXQ_ETH); if (!TXQ_RING_EMPTY(qs) && pi->link_config.link_ok && (!check_pkt_coalesce(qs) || (drbr_inuse(ifp, br) >= 7))) cxgb_start_locked(qs); else if (!TXQ_RING_EMPTY(qs) && !callout_pending(&txq->txq_timer)) callout_reset_on(&txq->txq_timer, 1, cxgb_tx_timeout, qs, txq->txq_timer.c_cpu); return (0); } int cxgb_transmit(struct ifnet *ifp, struct mbuf *m) { struct sge_qset *qs; struct port_info *pi = ifp->if_softc; int error, qidx = pi->first_qset; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||(!pi->link_config.link_ok)) { m_freem(m); return (0); } if (m->m_flags & M_FLOWID) qidx = (m->m_pkthdr.flowid % pi->nqsets) + pi->first_qset; qs = &pi->adapter->sge.qs[qidx]; if (TXQ_TRYLOCK(qs)) { /* XXX running */ error = cxgb_transmit_locked(ifp, qs, m); TXQ_UNLOCK(qs); } else error = drbr_enqueue(ifp, qs->txq[TXQ_ETH].txq_mr, m); return (error); } void cxgb_qflush(struct ifnet *ifp) { /* * flush any enqueued mbufs in the buf_rings * and in the transmit queues * no-op for now */ return; } /** * write_imm - write a packet into a Tx descriptor as immediate data * @d: the Tx descriptor to write * @m: the packet * @len: the length of packet data to write as immediate data * @gen: the generation bit value to write * * Writes a packet as immediate data into a Tx descriptor. The packet * contains a work request at its beginning. We must write the packet * carefully so the SGE doesn't read accidentally before it's written in * its entirety. */ static __inline void write_imm(struct tx_desc *d, struct mbuf *m, unsigned int len, unsigned int gen) { struct work_request_hdr *from = mtod(m, struct work_request_hdr *); struct work_request_hdr *to = (struct work_request_hdr *)d; uint32_t wr_hi, wr_lo; if (len > WR_LEN) panic("len too big %d\n", len); if (len < sizeof(*from)) panic("len too small %d", len); memcpy(&to[1], &from[1], len - sizeof(*from)); wr_hi = from->wrh_hi | htonl(F_WR_SOP | F_WR_EOP | V_WR_BCNTLFLT(len & 7)); wr_lo = from->wrh_lo | htonl(V_WR_GEN(gen) | V_WR_LEN((len + 7) / 8)); set_wr_hdr(to, wr_hi, wr_lo); wmb(); wr_gen2(d, gen); /* * This check is a hack we should really fix the logic so * that this can't happen */ if (m->m_type != MT_DONTFREE) m_freem(m); } /** * check_desc_avail - check descriptor availability on a send queue * @adap: the adapter * @q: the TX queue * @m: the packet needing the descriptors * @ndesc: the number of Tx descriptors needed * @qid: the Tx queue number in its queue set (TXQ_OFLD or TXQ_CTRL) * * Checks if the requested number of Tx descriptors is available on an * SGE send queue. If the queue is already suspended or not enough * descriptors are available the packet is queued for later transmission. * Must be called with the Tx queue locked. * * Returns 0 if enough descriptors are available, 1 if there aren't * enough descriptors and the packet has been queued, and 2 if the caller * needs to retry because there weren't enough descriptors at the * beginning of the call but some freed up in the mean time. */ static __inline int check_desc_avail(adapter_t *adap, struct sge_txq *q, struct mbuf *m, unsigned int ndesc, unsigned int qid) { /* * XXX We currently only use this for checking the control queue * the control queue is only used for binding qsets which happens * at init time so we are guaranteed enough descriptors */ if (__predict_false(!mbufq_empty(&q->sendq))) { addq_exit: mbufq_tail(&q->sendq, m); return 1; } if (__predict_false(q->size - q->in_use < ndesc)) { struct sge_qset *qs = txq_to_qset(q, qid); setbit(&qs->txq_stopped, qid); if (should_restart_tx(q) && test_and_clear_bit(qid, &qs->txq_stopped)) return 2; q->stops++; goto addq_exit; } return 0; } /** * reclaim_completed_tx_imm - reclaim completed control-queue Tx descs * @q: the SGE control Tx queue * * This is a variant of reclaim_completed_tx() that is used for Tx queues * that send only immediate data (presently just the control queues) and * thus do not have any mbufs */ static __inline void reclaim_completed_tx_imm(struct sge_txq *q) { unsigned int reclaim = q->processed - q->cleaned; q->in_use -= reclaim; q->cleaned += reclaim; } static __inline int immediate(const struct mbuf *m) { return m->m_len <= WR_LEN && m->m_pkthdr.len <= WR_LEN ; } /** * ctrl_xmit - send a packet through an SGE control Tx queue * @adap: the adapter * @q: the control queue * @m: the packet * * Send a packet through an SGE control Tx queue. Packets sent through * a control queue must fit entirely as immediate data in a single Tx * descriptor and have no page fragments. */ static int ctrl_xmit(adapter_t *adap, struct sge_qset *qs, struct mbuf *m) { int ret; struct work_request_hdr *wrp = mtod(m, struct work_request_hdr *); struct sge_txq *q = &qs->txq[TXQ_CTRL]; if (__predict_false(!immediate(m))) { m_freem(m); return 0; } wrp->wrh_hi |= htonl(F_WR_SOP | F_WR_EOP); wrp->wrh_lo = htonl(V_WR_TID(q->token)); TXQ_LOCK(qs); again: reclaim_completed_tx_imm(q); ret = check_desc_avail(adap, q, m, 1, TXQ_CTRL); if (__predict_false(ret)) { if (ret == 1) { TXQ_UNLOCK(qs); return (ENOSPC); } goto again; } write_imm(&q->desc[q->pidx], m, m->m_len, q->gen); q->in_use++; if (++q->pidx >= q->size) { q->pidx = 0; q->gen ^= 1; } TXQ_UNLOCK(qs); wmb(); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); return (0); } /** * restart_ctrlq - restart a suspended control queue * @qs: the queue set cotaining the control queue * * Resumes transmission on a suspended Tx control queue. */ static void restart_ctrlq(void *data, int npending) { struct mbuf *m; struct sge_qset *qs = (struct sge_qset *)data; struct sge_txq *q = &qs->txq[TXQ_CTRL]; adapter_t *adap = qs->port->adapter; TXQ_LOCK(qs); again: reclaim_completed_tx_imm(q); while (q->in_use < q->size && (m = mbufq_dequeue(&q->sendq)) != NULL) { write_imm(&q->desc[q->pidx], m, m->m_len, q->gen); if (++q->pidx >= q->size) { q->pidx = 0; q->gen ^= 1; } q->in_use++; } if (!mbufq_empty(&q->sendq)) { setbit(&qs->txq_stopped, TXQ_CTRL); if (should_restart_tx(q) && test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) goto again; q->stops++; } TXQ_UNLOCK(qs); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /* * Send a management message through control queue 0 */ int t3_mgmt_tx(struct adapter *adap, struct mbuf *m) { return ctrl_xmit(adap, &adap->sge.qs[0], m); } /** * free_qset - free the resources of an SGE queue set * @sc: the controller owning the queue set * @q: the queue set * * Release the HW and SW resources associated with an SGE queue set, such * as HW contexts, packet buffers, and descriptor rings. Traffic to the * queue set must be quiesced prior to calling this. */ static void t3_free_qset(adapter_t *sc, struct sge_qset *q) { int i; reclaim_completed_tx(q, 0, TXQ_ETH); if (q->txq[TXQ_ETH].txq_mr != NULL) buf_ring_free(q->txq[TXQ_ETH].txq_mr, M_DEVBUF); if (q->txq[TXQ_ETH].txq_ifq != NULL) { ifq_delete(q->txq[TXQ_ETH].txq_ifq); free(q->txq[TXQ_ETH].txq_ifq, M_DEVBUF); } for (i = 0; i < SGE_RXQ_PER_SET; ++i) { if (q->fl[i].desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_disable_fl(sc, q->fl[i].cntxt_id); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->fl[i].desc_tag, q->fl[i].desc_map); bus_dmamem_free(q->fl[i].desc_tag, q->fl[i].desc, q->fl[i].desc_map); bus_dma_tag_destroy(q->fl[i].desc_tag); bus_dma_tag_destroy(q->fl[i].entry_tag); } if (q->fl[i].sdesc) { free_rx_bufs(sc, &q->fl[i]); free(q->fl[i].sdesc, M_DEVBUF); } } mtx_unlock(&q->lock); MTX_DESTROY(&q->lock); for (i = 0; i < SGE_TXQ_PER_SET; i++) { if (q->txq[i].desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_enable_ecntxt(sc, q->txq[i].cntxt_id, 0); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->txq[i].desc_tag, q->txq[i].desc_map); bus_dmamem_free(q->txq[i].desc_tag, q->txq[i].desc, q->txq[i].desc_map); bus_dma_tag_destroy(q->txq[i].desc_tag); bus_dma_tag_destroy(q->txq[i].entry_tag); } if (q->txq[i].sdesc) { free(q->txq[i].sdesc, M_DEVBUF); } } if (q->rspq.desc) { mtx_lock_spin(&sc->sge.reg_lock); t3_sge_disable_rspcntxt(sc, q->rspq.cntxt_id); mtx_unlock_spin(&sc->sge.reg_lock); bus_dmamap_unload(q->rspq.desc_tag, q->rspq.desc_map); bus_dmamem_free(q->rspq.desc_tag, q->rspq.desc, q->rspq.desc_map); bus_dma_tag_destroy(q->rspq.desc_tag); MTX_DESTROY(&q->rspq.lock); } #ifdef INET tcp_lro_free(&q->lro.ctrl); #endif bzero(q, sizeof(*q)); } /** * t3_free_sge_resources - free SGE resources * @sc: the adapter softc * * Frees resources used by the SGE queue sets. */ void t3_free_sge_resources(adapter_t *sc, int nqsets) { int i; for (i = 0; i < nqsets; ++i) { TXQ_LOCK(&sc->sge.qs[i]); t3_free_qset(sc, &sc->sge.qs[i]); } } /** * t3_sge_start - enable SGE * @sc: the controller softc * * Enables the SGE for DMAs. This is the last step in starting packet * transfers. */ void t3_sge_start(adapter_t *sc) { t3_set_reg_field(sc, A_SG_CONTROL, F_GLOBALENABLE, F_GLOBALENABLE); } /** * t3_sge_stop - disable SGE operation * @sc: the adapter * * Disables the DMA engine. This can be called in emeregencies (e.g., * from error interrupts) or from normal process context. In the latter * case it also disables any pending queue restart tasklets. Note that * if it is called in interrupt context it cannot disable the restart * tasklets as it cannot wait, however the tasklets will have no effect * since the doorbells are disabled and the driver will call this again * later from process context, at which time the tasklets will be stopped * if they are still running. */ void t3_sge_stop(adapter_t *sc) { int i, nqsets; t3_set_reg_field(sc, A_SG_CONTROL, F_GLOBALENABLE, 0); if (sc->tq == NULL) return; for (nqsets = i = 0; i < (sc)->params.nports; i++) nqsets += sc->port[i].nqsets; #ifdef notyet /* * * XXX */ for (i = 0; i < nqsets; ++i) { struct sge_qset *qs = &sc->sge.qs[i]; taskqueue_drain(sc->tq, &qs->txq[TXQ_OFLD].qresume_task); taskqueue_drain(sc->tq, &qs->txq[TXQ_CTRL].qresume_task); } #endif } /** * t3_free_tx_desc - reclaims Tx descriptors and their buffers * @adapter: the adapter * @q: the Tx queue to reclaim descriptors from * @reclaimable: the number of descriptors to reclaim * @m_vec_size: maximum number of buffers to reclaim * @desc_reclaimed: returns the number of descriptors reclaimed * * Reclaims Tx descriptors from an SGE Tx queue and frees the associated * Tx buffers. Called with the Tx queue lock held. * * Returns number of buffers of reclaimed */ void t3_free_tx_desc(struct sge_qset *qs, int reclaimable, int queue) { struct tx_sw_desc *txsd; unsigned int cidx, mask; struct sge_txq *q = &qs->txq[queue]; #ifdef T3_TRACE T3_TRACE2(sc->tb[q->cntxt_id & 7], "reclaiming %u Tx descriptors at cidx %u", reclaimable, cidx); #endif cidx = q->cidx; mask = q->size - 1; txsd = &q->sdesc[cidx]; mtx_assert(&qs->lock, MA_OWNED); while (reclaimable--) { prefetch(q->sdesc[(cidx + 1) & mask].m); prefetch(q->sdesc[(cidx + 2) & mask].m); if (txsd->m != NULL) { if (txsd->flags & TX_SW_DESC_MAPPED) { bus_dmamap_unload(q->entry_tag, txsd->map); txsd->flags &= ~TX_SW_DESC_MAPPED; } m_freem_list(txsd->m); txsd->m = NULL; } else q->txq_skipped++; ++txsd; if (++cidx == q->size) { cidx = 0; txsd = q->sdesc; } } q->cidx = cidx; } /** * is_new_response - check if a response is newly written * @r: the response descriptor * @q: the response queue * * Returns true if a response descriptor contains a yet unprocessed * response. */ static __inline int is_new_response(const struct rsp_desc *r, const struct sge_rspq *q) { return (r->intr_gen & F_RSPD_GEN2) == q->gen; } #define RSPD_GTS_MASK (F_RSPD_TXQ0_GTS | F_RSPD_TXQ1_GTS) #define RSPD_CTRL_MASK (RSPD_GTS_MASK | \ V_RSPD_TXQ0_CR(M_RSPD_TXQ0_CR) | \ V_RSPD_TXQ1_CR(M_RSPD_TXQ1_CR) | \ V_RSPD_TXQ2_CR(M_RSPD_TXQ2_CR)) /* How long to delay the next interrupt in case of memory shortage, in 0.1us. */ #define NOMEM_INTR_DELAY 2500 /** * write_ofld_wr - write an offload work request * @adap: the adapter * @m: the packet to send * @q: the Tx queue * @pidx: index of the first Tx descriptor to write * @gen: the generation value to use * @ndesc: number of descriptors the packet will occupy * * Write an offload work request to send the supplied packet. The packet * data already carry the work request with most fields populated. */ static void write_ofld_wr(adapter_t *adap, struct mbuf *m, struct sge_txq *q, unsigned int pidx, unsigned int gen, unsigned int ndesc, bus_dma_segment_t *segs, unsigned int nsegs) { unsigned int sgl_flits, flits; struct work_request_hdr *from; struct sg_ent *sgp, sgl[TX_MAX_SEGS / 2 + 1]; struct tx_desc *d = &q->desc[pidx]; struct txq_state txqs; if (immediate(m) && nsegs == 0) { write_imm(d, m, m->m_len, gen); return; } /* Only TX_DATA builds SGLs */ from = mtod(m, struct work_request_hdr *); memcpy(&d->flit[1], &from[1], m->m_len - sizeof(*from)); flits = m->m_len / 8; sgp = (ndesc == 1) ? (struct sg_ent *)&d->flit[flits] : sgl; make_sgl(sgp, segs, nsegs); sgl_flits = sgl_len(nsegs); txqs.gen = gen; txqs.pidx = pidx; txqs.compl = 0; write_wr_hdr_sgl(ndesc, d, &txqs, q, sgl, flits, sgl_flits, from->wrh_hi, from->wrh_lo); } /** * calc_tx_descs_ofld - calculate # of Tx descriptors for an offload packet * @m: the packet * * Returns the number of Tx descriptors needed for the given offload * packet. These packets are already fully constructed. */ static __inline unsigned int calc_tx_descs_ofld(struct mbuf *m, unsigned int nsegs) { unsigned int flits, cnt = 0; int ndescs; if (m->m_len <= WR_LEN && nsegs == 0) return (1); /* packet fits as immediate data */ /* * This needs to be re-visited for TOE */ cnt = nsegs; /* headers */ flits = m->m_len / 8; ndescs = flits_to_desc(flits + sgl_len(cnt)); return (ndescs); } /** * ofld_xmit - send a packet through an offload queue * @adap: the adapter * @q: the Tx offload queue * @m: the packet * * Send an offload packet through an SGE offload queue. */ static int ofld_xmit(adapter_t *adap, struct sge_qset *qs, struct mbuf *m) { int ret, nsegs; unsigned int ndesc; unsigned int pidx, gen; struct sge_txq *q = &qs->txq[TXQ_OFLD]; bus_dma_segment_t segs[TX_MAX_SEGS], *vsegs; struct tx_sw_desc *stx; nsegs = m_get_sgllen(m); vsegs = m_get_sgl(m); ndesc = calc_tx_descs_ofld(m, nsegs); busdma_map_sgl(vsegs, segs, nsegs); stx = &q->sdesc[q->pidx]; TXQ_LOCK(qs); again: reclaim_completed_tx(qs, 16, TXQ_OFLD); ret = check_desc_avail(adap, q, m, ndesc, TXQ_OFLD); if (__predict_false(ret)) { if (ret == 1) { printf("no ofld desc avail\n"); m_set_priority(m, ndesc); /* save for restart */ TXQ_UNLOCK(qs); return (EINTR); } goto again; } gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; } #ifdef T3_TRACE T3_TRACE5(adap->tb[q->cntxt_id & 7], "ofld_xmit: ndesc %u, pidx %u, len %u, main %u, frags %u", ndesc, pidx, skb->len, skb->len - skb->data_len, skb_shinfo(skb)->nr_frags); #endif TXQ_UNLOCK(qs); write_ofld_wr(adap, m, q, pidx, gen, ndesc, segs, nsegs); check_ring_tx_db(adap, q, 1); return (0); } /** * restart_offloadq - restart a suspended offload queue * @qs: the queue set cotaining the offload queue * * Resumes transmission on a suspended Tx offload queue. */ static void restart_offloadq(void *data, int npending) { struct mbuf *m; struct sge_qset *qs = data; struct sge_txq *q = &qs->txq[TXQ_OFLD]; adapter_t *adap = qs->port->adapter; bus_dma_segment_t segs[TX_MAX_SEGS]; struct tx_sw_desc *stx = &q->sdesc[q->pidx]; int nsegs, cleaned; TXQ_LOCK(qs); again: cleaned = reclaim_completed_tx(qs, 16, TXQ_OFLD); while ((m = mbufq_peek(&q->sendq)) != NULL) { unsigned int gen, pidx; unsigned int ndesc = m_get_priority(m); if (__predict_false(q->size - q->in_use < ndesc)) { setbit(&qs->txq_stopped, TXQ_OFLD); if (should_restart_tx(q) && test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) goto again; q->stops++; break; } gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; } (void)mbufq_dequeue(&q->sendq); busdma_map_mbufs(&m, q, stx, segs, &nsegs); TXQ_UNLOCK(qs); write_ofld_wr(adap, m, q, pidx, gen, ndesc, segs, nsegs); TXQ_LOCK(qs); } #if USE_GTS set_bit(TXQ_RUNNING, &q->flags); set_bit(TXQ_LAST_PKT_DB, &q->flags); #endif TXQ_UNLOCK(qs); wmb(); t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /** * queue_set - return the queue set a packet should use * @m: the packet * * Maps a packet to the SGE queue set it should use. The desired queue * set is carried in bits 1-3 in the packet's priority. */ static __inline int queue_set(const struct mbuf *m) { return m_get_priority(m) >> 1; } /** * is_ctrl_pkt - return whether an offload packet is a control packet * @m: the packet * * Determines whether an offload packet should use an OFLD or a CTRL * Tx queue. This is indicated by bit 0 in the packet's priority. */ static __inline int is_ctrl_pkt(const struct mbuf *m) { return m_get_priority(m) & 1; } /** * t3_offload_tx - send an offload packet * @tdev: the offload device to send to * @m: the packet * * Sends an offload packet. We use the packet priority to select the * appropriate Tx queue as follows: bit 0 indicates whether the packet * should be sent as regular or control, bits 1-3 select the queue set. */ int t3_offload_tx(struct t3cdev *tdev, struct mbuf *m) { adapter_t *adap = tdev2adap(tdev); struct sge_qset *qs = &adap->sge.qs[queue_set(m)]; if (__predict_false(is_ctrl_pkt(m))) return ctrl_xmit(adap, qs, m); return ofld_xmit(adap, qs, m); } /** * deliver_partial_bundle - deliver a (partial) bundle of Rx offload pkts * @tdev: the offload device that will be receiving the packets * @q: the SGE response queue that assembled the bundle * @m: the partial bundle * @n: the number of packets in the bundle * * Delivers a (partial) bundle of Rx offload packets to an offload device. */ static __inline void deliver_partial_bundle(struct t3cdev *tdev, struct sge_rspq *q, struct mbuf *mbufs[], int n) { if (n) { q->offload_bundles++; cxgb_ofld_recv(tdev, mbufs, n); } } static __inline int rx_offload(struct t3cdev *tdev, struct sge_rspq *rq, struct mbuf *m, struct mbuf *rx_gather[], unsigned int gather_idx) { rq->offload_pkts++; m->m_pkthdr.header = mtod(m, void *); rx_gather[gather_idx++] = m; if (gather_idx == RX_BUNDLE_SIZE) { cxgb_ofld_recv(tdev, rx_gather, RX_BUNDLE_SIZE); gather_idx = 0; rq->offload_bundles++; } return (gather_idx); } static void restart_tx(struct sge_qset *qs) { struct adapter *sc = qs->port->adapter; if (isset(&qs->txq_stopped, TXQ_OFLD) && should_restart_tx(&qs->txq[TXQ_OFLD]) && test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) { qs->txq[TXQ_OFLD].restarts++; DPRINTF("restarting TXQ_OFLD\n"); taskqueue_enqueue(sc->tq, &qs->txq[TXQ_OFLD].qresume_task); } DPRINTF("stopped=0x%x restart=%d processed=%d cleaned=%d in_use=%d\n", qs->txq_stopped, should_restart_tx(&qs->txq[TXQ_CTRL]), qs->txq[TXQ_CTRL].processed, qs->txq[TXQ_CTRL].cleaned, qs->txq[TXQ_CTRL].in_use); if (isset(&qs->txq_stopped, TXQ_CTRL) && should_restart_tx(&qs->txq[TXQ_CTRL]) && test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) { qs->txq[TXQ_CTRL].restarts++; DPRINTF("restarting TXQ_CTRL\n"); taskqueue_enqueue(sc->tq, &qs->txq[TXQ_CTRL].qresume_task); } } /** * t3_sge_alloc_qset - initialize an SGE queue set * @sc: the controller softc * @id: the queue set id * @nports: how many Ethernet ports will be using this queue set * @irq_vec_idx: the IRQ vector index for response queue interrupts * @p: configuration parameters for this queue set * @ntxq: number of Tx queues for the queue set * @pi: port info for queue set * * Allocate resources and initialize an SGE queue set. A queue set * comprises a response queue, two Rx free-buffer queues, and up to 3 * Tx queues. The Tx queues are assigned roles in the order Ethernet * queue, offload queue, and control queue. */ int t3_sge_alloc_qset(adapter_t *sc, u_int id, int nports, int irq_vec_idx, const struct qset_params *p, int ntxq, struct port_info *pi) { struct sge_qset *q = &sc->sge.qs[id]; int i, ret = 0; MTX_INIT(&q->lock, q->namebuf, NULL, MTX_DEF); q->port = pi; if ((q->txq[TXQ_ETH].txq_mr = buf_ring_alloc(cxgb_txq_buf_ring_size, M_DEVBUF, M_WAITOK, &q->lock)) == NULL) { device_printf(sc->dev, "failed to allocate mbuf ring\n"); goto err; } if ((q->txq[TXQ_ETH].txq_ifq = malloc(sizeof(struct ifaltq), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(sc->dev, "failed to allocate ifq\n"); goto err; } ifq_init(q->txq[TXQ_ETH].txq_ifq, pi->ifp); callout_init(&q->txq[TXQ_ETH].txq_timer, 1); callout_init(&q->txq[TXQ_ETH].txq_watchdog, 1); q->txq[TXQ_ETH].txq_timer.c_cpu = id % mp_ncpus; q->txq[TXQ_ETH].txq_watchdog.c_cpu = id % mp_ncpus; init_qset_cntxt(q, id); q->idx = id; if ((ret = alloc_ring(sc, p->fl_size, sizeof(struct rx_desc), sizeof(struct rx_sw_desc), &q->fl[0].phys_addr, &q->fl[0].desc, &q->fl[0].sdesc, &q->fl[0].desc_tag, &q->fl[0].desc_map, sc->rx_dmat, &q->fl[0].entry_tag)) != 0) { printf("error %d from alloc ring fl0\n", ret); goto err; } if ((ret = alloc_ring(sc, p->jumbo_size, sizeof(struct rx_desc), sizeof(struct rx_sw_desc), &q->fl[1].phys_addr, &q->fl[1].desc, &q->fl[1].sdesc, &q->fl[1].desc_tag, &q->fl[1].desc_map, sc->rx_jumbo_dmat, &q->fl[1].entry_tag)) != 0) { printf("error %d from alloc ring fl1\n", ret); goto err; } if ((ret = alloc_ring(sc, p->rspq_size, sizeof(struct rsp_desc), 0, &q->rspq.phys_addr, &q->rspq.desc, NULL, &q->rspq.desc_tag, &q->rspq.desc_map, NULL, NULL)) != 0) { printf("error %d from alloc ring rspq\n", ret); goto err; } for (i = 0; i < ntxq; ++i) { size_t sz = i == TXQ_CTRL ? 0 : sizeof(struct tx_sw_desc); if ((ret = alloc_ring(sc, p->txq_size[i], sizeof(struct tx_desc), sz, &q->txq[i].phys_addr, &q->txq[i].desc, &q->txq[i].sdesc, &q->txq[i].desc_tag, &q->txq[i].desc_map, sc->tx_dmat, &q->txq[i].entry_tag)) != 0) { printf("error %d from alloc ring tx %i\n", ret, i); goto err; } mbufq_init(&q->txq[i].sendq); q->txq[i].gen = 1; q->txq[i].size = p->txq_size[i]; } TASK_INIT(&q->txq[TXQ_OFLD].qresume_task, 0, restart_offloadq, q); TASK_INIT(&q->txq[TXQ_CTRL].qresume_task, 0, restart_ctrlq, q); TASK_INIT(&q->txq[TXQ_ETH].qreclaim_task, 0, sge_txq_reclaim_handler, q); TASK_INIT(&q->txq[TXQ_OFLD].qreclaim_task, 0, sge_txq_reclaim_handler, q); q->fl[0].gen = q->fl[1].gen = 1; q->fl[0].size = p->fl_size; q->fl[1].size = p->jumbo_size; q->rspq.gen = 1; q->rspq.cidx = 0; q->rspq.size = p->rspq_size; q->txq[TXQ_ETH].stop_thres = nports * flits_to_desc(sgl_len(TX_MAX_SEGS + 1) + 3); q->fl[0].buf_size = MCLBYTES; q->fl[0].zone = zone_pack; q->fl[0].type = EXT_PACKET; if (p->jumbo_buf_size == MJUM16BYTES) { q->fl[1].zone = zone_jumbo16; q->fl[1].type = EXT_JUMBO16; } else if (p->jumbo_buf_size == MJUM9BYTES) { q->fl[1].zone = zone_jumbo9; q->fl[1].type = EXT_JUMBO9; } else if (p->jumbo_buf_size == MJUMPAGESIZE) { q->fl[1].zone = zone_jumbop; q->fl[1].type = EXT_JUMBOP; } else { KASSERT(0, ("can't deal with jumbo_buf_size %d.", p->jumbo_buf_size)); ret = EDOOFUS; goto err; } q->fl[1].buf_size = p->jumbo_buf_size; /* Allocate and setup the lro_ctrl structure */ q->lro.enabled = !!(pi->ifp->if_capenable & IFCAP_LRO); #ifdef INET ret = tcp_lro_init(&q->lro.ctrl); if (ret) { printf("error %d from tcp_lro_init\n", ret); goto err; } #endif q->lro.ctrl.ifp = pi->ifp; mtx_lock_spin(&sc->sge.reg_lock); ret = -t3_sge_init_rspcntxt(sc, q->rspq.cntxt_id, irq_vec_idx, q->rspq.phys_addr, q->rspq.size, q->fl[0].buf_size, 1, 0); if (ret) { printf("error %d from t3_sge_init_rspcntxt\n", ret); goto err_unlock; } for (i = 0; i < SGE_RXQ_PER_SET; ++i) { ret = -t3_sge_init_flcntxt(sc, q->fl[i].cntxt_id, 0, q->fl[i].phys_addr, q->fl[i].size, q->fl[i].buf_size, p->cong_thres, 1, 0); if (ret) { printf("error %d from t3_sge_init_flcntxt for index i=%d\n", ret, i); goto err_unlock; } } ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_ETH].cntxt_id, USE_GTS, SGE_CNTXT_ETH, id, q->txq[TXQ_ETH].phys_addr, q->txq[TXQ_ETH].size, q->txq[TXQ_ETH].token, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } if (ntxq > 1) { ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_OFLD].cntxt_id, USE_GTS, SGE_CNTXT_OFLD, id, q->txq[TXQ_OFLD].phys_addr, q->txq[TXQ_OFLD].size, 0, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } } if (ntxq > 2) { ret = -t3_sge_init_ecntxt(sc, q->txq[TXQ_CTRL].cntxt_id, 0, SGE_CNTXT_CTRL, id, q->txq[TXQ_CTRL].phys_addr, q->txq[TXQ_CTRL].size, q->txq[TXQ_CTRL].token, 1, 0); if (ret) { printf("error %d from t3_sge_init_ecntxt\n", ret); goto err_unlock; } } snprintf(q->rspq.lockbuf, RSPQ_NAME_LEN, "t3 rspq lock %d:%d", device_get_unit(sc->dev), irq_vec_idx); MTX_INIT(&q->rspq.lock, q->rspq.lockbuf, NULL, MTX_DEF); mtx_unlock_spin(&sc->sge.reg_lock); t3_update_qset_coalesce(q, p); q->port = pi; refill_fl(sc, &q->fl[0], q->fl[0].size); refill_fl(sc, &q->fl[1], q->fl[1].size); refill_rspq(sc, &q->rspq, q->rspq.size - 1); t3_write_reg(sc, A_SG_GTS, V_RSPQ(q->rspq.cntxt_id) | V_NEWTIMER(q->rspq.holdoff_tmr)); return (0); err_unlock: mtx_unlock_spin(&sc->sge.reg_lock); err: TXQ_LOCK(q); t3_free_qset(sc, q); return (ret); } /* * Remove CPL_RX_PKT headers from the mbuf and reduce it to a regular mbuf with * ethernet data. Hardware assistance with various checksums and any vlan tag * will also be taken into account here. */ void t3_rx_eth(struct adapter *adap, struct sge_rspq *rq, struct mbuf *m, int ethpad) { struct cpl_rx_pkt *cpl = (struct cpl_rx_pkt *)(mtod(m, uint8_t *) + ethpad); struct port_info *pi = &adap->port[adap->rxpkt_map[cpl->iff]]; struct ifnet *ifp = pi->ifp; DPRINTF("rx_eth m=%p m->m_data=%p p->iff=%d\n", m, mtod(m, uint8_t *), cpl->iff); if ((ifp->if_capenable & IFCAP_RXCSUM) && !cpl->fragment && cpl->csum_valid && cpl->csum == 0xffff) { m->m_pkthdr.csum_flags = (CSUM_IP_CHECKED|CSUM_IP_VALID); rspq_to_qset(rq)->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; m->m_pkthdr.csum_flags = (CSUM_IP_CHECKED|CSUM_IP_VALID|CSUM_DATA_VALID|CSUM_PSEUDO_HDR); m->m_pkthdr.csum_data = 0xffff; } if (cpl->vlan_valid) { m->m_pkthdr.ether_vtag = ntohs(cpl->vlan); m->m_flags |= M_VLANTAG; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.header = mtod(m, uint8_t *) + sizeof(*cpl) + ethpad; /* * adjust after conversion to mbuf chain */ m->m_pkthdr.len -= (sizeof(*cpl) + ethpad); m->m_len -= (sizeof(*cpl) + ethpad); m->m_data += (sizeof(*cpl) + ethpad); } /** * get_packet - return the next ingress packet buffer from a free list * @adap: the adapter that received the packet * @drop_thres: # of remaining buffers before we start dropping packets * @qs: the qset that the SGE free list holding the packet belongs to * @mh: the mbuf header, contains a pointer to the head and tail of the mbuf chain * @r: response descriptor * * Get the next packet from a free list and complete setup of the * sk_buff. If the packet is small we make a copy and recycle the * original buffer, otherwise we use the original buffer itself. If a * positive drop threshold is supplied packets are dropped and their * buffers recycled if (a) the number of remaining buffers is under the * threshold and the packet is too big to copy, or (b) the packet should * be copied but there is no memory for the copy. */ static int get_packet(adapter_t *adap, unsigned int drop_thres, struct sge_qset *qs, struct t3_mbuf_hdr *mh, struct rsp_desc *r) { unsigned int len_cq = ntohl(r->len_cq); struct sge_fl *fl = (len_cq & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; int mask, cidx = fl->cidx; struct rx_sw_desc *sd = &fl->sdesc[cidx]; uint32_t len = G_RSPD_LEN(len_cq); uint32_t flags = M_EXT; uint8_t sopeop = G_RSPD_SOP_EOP(ntohl(r->flags)); caddr_t cl; struct mbuf *m; int ret = 0; mask = fl->size - 1; prefetch(fl->sdesc[(cidx + 1) & mask].m); prefetch(fl->sdesc[(cidx + 2) & mask].m); prefetch(fl->sdesc[(cidx + 1) & mask].rxsd_cl); prefetch(fl->sdesc[(cidx + 2) & mask].rxsd_cl); fl->credits--; bus_dmamap_sync(fl->entry_tag, sd->map, BUS_DMASYNC_POSTREAD); if (recycle_enable && len <= SGE_RX_COPY_THRES && sopeop == RSPQ_SOP_EOP) { if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) goto skip_recycle; cl = mtod(m, void *); memcpy(cl, sd->rxsd_cl, len); recycle_rx_buf(adap, fl, fl->cidx); m->m_pkthdr.len = m->m_len = len; m->m_flags = 0; mh->mh_head = mh->mh_tail = m; ret = 1; goto done; } else { skip_recycle: bus_dmamap_unload(fl->entry_tag, sd->map); cl = sd->rxsd_cl; m = sd->m; if ((sopeop == RSPQ_SOP_EOP) || (sopeop == RSPQ_SOP)) flags |= M_PKTHDR; m_init(m, fl->zone, fl->buf_size, M_NOWAIT, MT_DATA, flags); if (fl->zone == zone_pack) { /* * restore clobbered data pointer */ m->m_data = m->m_ext.ext_buf; } else { m_cljset(m, cl, fl->type); } m->m_len = len; } switch(sopeop) { case RSPQ_SOP_EOP: ret = 1; /* FALLTHROUGH */ case RSPQ_SOP: mh->mh_head = mh->mh_tail = m; m->m_pkthdr.len = len; break; case RSPQ_EOP: ret = 1; /* FALLTHROUGH */ case RSPQ_NSOP_NEOP: if (mh->mh_tail == NULL) { log(LOG_ERR, "discarding intermediate descriptor entry\n"); m_freem(m); break; } mh->mh_tail->m_next = m; mh->mh_tail = m; mh->mh_head->m_pkthdr.len += len; break; } if (cxgb_debug) printf("len=%d pktlen=%d\n", m->m_len, m->m_pkthdr.len); done: if (++fl->cidx == fl->size) fl->cidx = 0; return (ret); } /** * handle_rsp_cntrl_info - handles control information in a response * @qs: the queue set corresponding to the response * @flags: the response control flags * * Handles the control information of an SGE response, such as GTS * indications and completion credits for the queue set's Tx queues. * HW coalesces credits, we don't do any extra SW coalescing. */ static __inline void handle_rsp_cntrl_info(struct sge_qset *qs, uint32_t flags) { unsigned int credits; #if USE_GTS if (flags & F_RSPD_TXQ0_GTS) clear_bit(TXQ_RUNNING, &qs->txq[TXQ_ETH].flags); #endif credits = G_RSPD_TXQ0_CR(flags); if (credits) qs->txq[TXQ_ETH].processed += credits; credits = G_RSPD_TXQ2_CR(flags); if (credits) qs->txq[TXQ_CTRL].processed += credits; # if USE_GTS if (flags & F_RSPD_TXQ1_GTS) clear_bit(TXQ_RUNNING, &qs->txq[TXQ_OFLD].flags); # endif credits = G_RSPD_TXQ1_CR(flags); if (credits) qs->txq[TXQ_OFLD].processed += credits; } static void check_ring_db(adapter_t *adap, struct sge_qset *qs, unsigned int sleeping) { ; } /** * process_responses - process responses from an SGE response queue * @adap: the adapter * @qs: the queue set to which the response queue belongs * @budget: how many responses can be processed in this round * * Process responses from an SGE response queue up to the supplied budget. * Responses include received packets as well as credits and other events * for the queues that belong to the response queue's queue set. * A negative budget is effectively unlimited. * * Additionally choose the interrupt holdoff time for the next interrupt * on this queue. If the system is under memory shortage use a fairly * long delay to help recovery. */ static int process_responses(adapter_t *adap, struct sge_qset *qs, int budget) { struct sge_rspq *rspq = &qs->rspq; struct rsp_desc *r = &rspq->desc[rspq->cidx]; int budget_left = budget; unsigned int sleeping = 0; int lro_enabled = qs->lro.enabled; int skip_lro; struct lro_ctrl *lro_ctrl = &qs->lro.ctrl; struct mbuf *offload_mbufs[RX_BUNDLE_SIZE]; int ngathered = 0; struct t3_mbuf_hdr *mh = &rspq->rspq_mh; #ifdef DEBUG static int last_holdoff = 0; if (cxgb_debug && rspq->holdoff_tmr != last_holdoff) { printf("next_holdoff=%d\n", rspq->holdoff_tmr); last_holdoff = rspq->holdoff_tmr; } #endif rspq->next_holdoff = rspq->holdoff_tmr; while (__predict_true(budget_left && is_new_response(r, rspq))) { int eth, eop = 0, ethpad = 0; uint32_t flags = ntohl(r->flags); uint32_t rss_csum = *(const uint32_t *)r; uint32_t rss_hash = be32toh(r->rss_hdr.rss_hash_val); eth = (r->rss_hdr.opcode == CPL_RX_PKT); if (__predict_false(flags & F_RSPD_ASYNC_NOTIF)) { struct mbuf *m; if (cxgb_debug) printf("async notification\n"); if (mh->mh_head == NULL) { mh->mh_head = m_gethdr(M_DONTWAIT, MT_DATA); m = mh->mh_head; } else { m = m_gethdr(M_DONTWAIT, MT_DATA); } if (m == NULL) goto no_mem; memcpy(mtod(m, char *), r, AN_PKT_SIZE); m->m_len = m->m_pkthdr.len = AN_PKT_SIZE; *mtod(m, char *) = CPL_ASYNC_NOTIF; rss_csum = htonl(CPL_ASYNC_NOTIF << 24); eop = 1; rspq->async_notif++; goto skip; } else if (flags & F_RSPD_IMM_DATA_VALID) { struct mbuf *m = NULL; DPRINTF("IMM DATA VALID opcode=0x%x rspq->cidx=%d\n", r->rss_hdr.opcode, rspq->cidx); if (mh->mh_head == NULL) mh->mh_head = m_gethdr(M_DONTWAIT, MT_DATA); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (mh->mh_head == NULL && m == NULL) { no_mem: rspq->next_holdoff = NOMEM_INTR_DELAY; budget_left--; break; } get_imm_packet(adap, r, mh->mh_head); eop = 1; rspq->imm_data++; } else if (r->len_cq) { int drop_thresh = eth ? SGE_RX_DROP_THRES : 0; eop = get_packet(adap, drop_thresh, qs, mh, r); if (eop) { if (r->rss_hdr.hash_type && !adap->timestamp) mh->mh_head->m_flags |= M_FLOWID; mh->mh_head->m_pkthdr.flowid = rss_hash; } ethpad = 2; } else { rspq->pure_rsps++; } skip: if (flags & RSPD_CTRL_MASK) { sleeping |= flags & RSPD_GTS_MASK; handle_rsp_cntrl_info(qs, flags); } r++; if (__predict_false(++rspq->cidx == rspq->size)) { rspq->cidx = 0; rspq->gen ^= 1; r = rspq->desc; } if (++rspq->credits >= 64) { refill_rspq(adap, rspq, rspq->credits); rspq->credits = 0; } if (!eth && eop) { mh->mh_head->m_pkthdr.csum_data = rss_csum; /* * XXX size mismatch */ m_set_priority(mh->mh_head, rss_hash); ngathered = rx_offload(&adap->tdev, rspq, mh->mh_head, offload_mbufs, ngathered); mh->mh_head = NULL; DPRINTF("received offload packet\n"); } else if (eth && eop) { struct mbuf *m = mh->mh_head; t3_rx_eth(adap, rspq, m, ethpad); /* * The T304 sends incoming packets on any qset. If LRO * is also enabled, we could end up sending packet up * lro_ctrl->ifp's input. That is incorrect. * * The mbuf's rcvif was derived from the cpl header and * is accurate. Skip LRO and just use that. */ skip_lro = __predict_false(qs->port->ifp != m->m_pkthdr.rcvif); if (lro_enabled && lro_ctrl->lro_cnt && !skip_lro #ifdef INET && (tcp_lro_rx(lro_ctrl, m, 0) == 0) #endif ) { /* successfully queue'd for LRO */ } else { /* * LRO not enabled, packet unsuitable for LRO, * or unable to queue. Pass it up right now in * either case. */ struct ifnet *ifp = m->m_pkthdr.rcvif; (*ifp->if_input)(ifp, m); } mh->mh_head = NULL; } __refill_fl_lt(adap, &qs->fl[0], 32); __refill_fl_lt(adap, &qs->fl[1], 32); --budget_left; } deliver_partial_bundle(&adap->tdev, rspq, offload_mbufs, ngathered); #ifdef INET /* Flush LRO */ while (!SLIST_EMPTY(&lro_ctrl->lro_active)) { struct lro_entry *queued = SLIST_FIRST(&lro_ctrl->lro_active); SLIST_REMOVE_HEAD(&lro_ctrl->lro_active, next); tcp_lro_flush(lro_ctrl, queued); } #endif if (sleeping) check_ring_db(adap, qs, sleeping); mb(); /* commit Tx queue processed updates */ if (__predict_false(qs->txq_stopped > 1)) restart_tx(qs); __refill_fl_lt(adap, &qs->fl[0], 512); __refill_fl_lt(adap, &qs->fl[1], 512); budget -= budget_left; return (budget); } /* * A helper function that processes responses and issues GTS. */ static __inline int process_responses_gts(adapter_t *adap, struct sge_rspq *rq) { int work; static int last_holdoff = 0; work = process_responses(adap, rspq_to_qset(rq), -1); if (cxgb_debug && (rq->next_holdoff != last_holdoff)) { printf("next_holdoff=%d\n", rq->next_holdoff); last_holdoff = rq->next_holdoff; } t3_write_reg(adap, A_SG_GTS, V_RSPQ(rq->cntxt_id) | V_NEWTIMER(rq->next_holdoff) | V_NEWINDEX(rq->cidx)); return (work); } /* * Interrupt handler for legacy INTx interrupts for T3B-based cards. * Handles data events from SGE response queues as well as error and other * async events as they all use the same interrupt pin. We use one SGE * response queue per port in this mode and protect all response queues with * queue 0's lock. */ void t3b_intr(void *data) { uint32_t i, map; adapter_t *adap = data; struct sge_rspq *q0 = &adap->sge.qs[0].rspq; t3_write_reg(adap, A_PL_CLI, 0); map = t3_read_reg(adap, A_SG_DATA_INTR); if (!map) return; if (__predict_false(map & F_ERRINTR)) { t3_write_reg(adap, A_PL_INT_ENABLE0, 0); (void) t3_read_reg(adap, A_PL_INT_ENABLE0); taskqueue_enqueue(adap->tq, &adap->slow_intr_task); } mtx_lock(&q0->lock); for_each_port(adap, i) if (map & (1 << i)) process_responses_gts(adap, &adap->sge.qs[i].rspq); mtx_unlock(&q0->lock); } /* * The MSI interrupt handler. This needs to handle data events from SGE * response queues as well as error and other async events as they all use * the same MSI vector. We use one SGE response queue per port in this mode * and protect all response queues with queue 0's lock. */ void t3_intr_msi(void *data) { adapter_t *adap = data; struct sge_rspq *q0 = &adap->sge.qs[0].rspq; int i, new_packets = 0; mtx_lock(&q0->lock); for_each_port(adap, i) if (process_responses_gts(adap, &adap->sge.qs[i].rspq)) new_packets = 1; mtx_unlock(&q0->lock); if (new_packets == 0) { t3_write_reg(adap, A_PL_INT_ENABLE0, 0); (void) t3_read_reg(adap, A_PL_INT_ENABLE0); taskqueue_enqueue(adap->tq, &adap->slow_intr_task); } } void t3_intr_msix(void *data) { struct sge_qset *qs = data; adapter_t *adap = qs->port->adapter; struct sge_rspq *rspq = &qs->rspq; if (process_responses_gts(adap, rspq) == 0) rspq->unhandled_irqs++; } #define QDUMP_SBUF_SIZE 32 * 400 static int t3_dump_rspq(SYSCTL_HANDLER_ARGS) { struct sge_rspq *rspq; struct sge_qset *qs; int i, err, dump_end, idx; struct sbuf *sb; struct rsp_desc *rspd; uint32_t data[4]; rspq = arg1; qs = rspq_to_qset(rspq); if (rspq->rspq_dump_count == 0) return (0); if (rspq->rspq_dump_count > RSPQ_Q_SIZE) { log(LOG_WARNING, "dump count is too large %d\n", rspq->rspq_dump_count); rspq->rspq_dump_count = 0; return (EINVAL); } if (rspq->rspq_dump_start > (RSPQ_Q_SIZE-1)) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", rspq->rspq_dump_start); rspq->rspq_dump_start = 0; return (EINVAL); } err = t3_sge_read_rspq(qs->port->adapter, rspq->cntxt_id, data); if (err) return (err); err = sysctl_wire_old_buffer(req, 0); if (err) return (err); sb = sbuf_new_for_sysctl(NULL, NULL, QDUMP_SBUF_SIZE, req); sbuf_printf(sb, " \n index=%u size=%u MSI-X/RspQ=%u intr enable=%u intr armed=%u\n", (data[0] & 0xffff), data[0] >> 16, ((data[2] >> 20) & 0x3f), ((data[2] >> 26) & 1), ((data[2] >> 27) & 1)); sbuf_printf(sb, " generation=%u CQ mode=%u FL threshold=%u\n", ((data[2] >> 28) & 1), ((data[2] >> 31) & 1), data[3]); sbuf_printf(sb, " start=%d -> end=%d\n", rspq->rspq_dump_start, (rspq->rspq_dump_start + rspq->rspq_dump_count) & (RSPQ_Q_SIZE-1)); dump_end = rspq->rspq_dump_start + rspq->rspq_dump_count; for (i = rspq->rspq_dump_start; i < dump_end; i++) { idx = i & (RSPQ_Q_SIZE-1); rspd = &rspq->desc[idx]; sbuf_printf(sb, "\tidx=%04d opcode=%02x cpu_idx=%x hash_type=%x cq_idx=%x\n", idx, rspd->rss_hdr.opcode, rspd->rss_hdr.cpu_idx, rspd->rss_hdr.hash_type, be16toh(rspd->rss_hdr.cq_idx)); sbuf_printf(sb, "\trss_hash_val=%x flags=%08x len_cq=%x intr_gen=%x\n", rspd->rss_hdr.rss_hash_val, be32toh(rspd->flags), be32toh(rspd->len_cq), rspd->intr_gen); } err = sbuf_finish(sb); /* Output a trailing NUL. */ if (err == 0) err = SYSCTL_OUT(req, "", 1); sbuf_delete(sb); return (err); } static int t3_dump_txq_eth(SYSCTL_HANDLER_ARGS) { struct sge_txq *txq; struct sge_qset *qs; int i, j, err, dump_end; struct sbuf *sb; struct tx_desc *txd; uint32_t *WR, wr_hi, wr_lo, gen; uint32_t data[4]; txq = arg1; qs = txq_to_qset(txq, TXQ_ETH); if (txq->txq_dump_count == 0) { return (0); } if (txq->txq_dump_count > TX_ETH_Q_SIZE) { log(LOG_WARNING, "dump count is too large %d\n", txq->txq_dump_count); txq->txq_dump_count = 1; return (EINVAL); } if (txq->txq_dump_start > (TX_ETH_Q_SIZE-1)) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", txq->txq_dump_start); txq->txq_dump_start = 0; return (EINVAL); } err = t3_sge_read_ecntxt(qs->port->adapter, qs->rspq.cntxt_id, data); if (err) return (err); err = sysctl_wire_old_buffer(req, 0); if (err) return (err); sb = sbuf_new_for_sysctl(NULL, NULL, QDUMP_SBUF_SIZE, req); sbuf_printf(sb, " \n credits=%u GTS=%u index=%u size=%u rspq#=%u cmdq#=%u\n", (data[0] & 0x7fff), ((data[0] >> 15) & 1), (data[0] >> 16), (data[1] & 0xffff), ((data[3] >> 4) & 7), ((data[3] >> 7) & 1)); sbuf_printf(sb, " TUN=%u TOE=%u generation%u uP token=%u valid=%u\n", ((data[3] >> 8) & 1), ((data[3] >> 9) & 1), ((data[3] >> 10) & 1), ((data[3] >> 11) & 0xfffff), ((data[3] >> 31) & 1)); sbuf_printf(sb, " qid=%d start=%d -> end=%d\n", qs->idx, txq->txq_dump_start, (txq->txq_dump_start + txq->txq_dump_count) & (TX_ETH_Q_SIZE-1)); dump_end = txq->txq_dump_start + txq->txq_dump_count; for (i = txq->txq_dump_start; i < dump_end; i++) { txd = &txq->desc[i & (TX_ETH_Q_SIZE-1)]; WR = (uint32_t *)txd->flit; wr_hi = ntohl(WR[0]); wr_lo = ntohl(WR[1]); gen = G_WR_GEN(wr_lo); sbuf_printf(sb," wr_hi %08x wr_lo %08x gen %d\n", wr_hi, wr_lo, gen); for (j = 2; j < 30; j += 4) sbuf_printf(sb, "\t%08x %08x %08x %08x \n", WR[j], WR[j + 1], WR[j + 2], WR[j + 3]); } err = sbuf_finish(sb); /* Output a trailing NUL. */ if (err == 0) err = SYSCTL_OUT(req, "", 1); sbuf_delete(sb); return (err); } static int t3_dump_txq_ctrl(SYSCTL_HANDLER_ARGS) { struct sge_txq *txq; struct sge_qset *qs; int i, j, err, dump_end; struct sbuf *sb; struct tx_desc *txd; uint32_t *WR, wr_hi, wr_lo, gen; txq = arg1; qs = txq_to_qset(txq, TXQ_CTRL); if (txq->txq_dump_count == 0) { return (0); } if (txq->txq_dump_count > 256) { log(LOG_WARNING, "dump count is too large %d\n", txq->txq_dump_count); txq->txq_dump_count = 1; return (EINVAL); } if (txq->txq_dump_start > 255) { log(LOG_WARNING, "dump start of %d is greater than queue size\n", txq->txq_dump_start); txq->txq_dump_start = 0; return (EINVAL); } err = sysctl_wire_old_buffer(req, 0); if (err != 0) return (err); sb = sbuf_new_for_sysctl(NULL, NULL, QDUMP_SBUF_SIZE, req); sbuf_printf(sb, " qid=%d start=%d -> end=%d\n", qs->idx, txq->txq_dump_start, (txq->txq_dump_start + txq->txq_dump_count) & 255); dump_end = txq->txq_dump_start + txq->txq_dump_count; for (i = txq->txq_dump_start; i < dump_end; i++) { txd = &txq->desc[i & (255)]; WR = (uint32_t *)txd->flit; wr_hi = ntohl(WR[0]); wr_lo = ntohl(WR[1]); gen = G_WR_GEN(wr_lo); sbuf_printf(sb," wr_hi %08x wr_lo %08x gen %d\n", wr_hi, wr_lo, gen); for (j = 2; j < 30; j += 4) sbuf_printf(sb, "\t%08x %08x %08x %08x \n", WR[j], WR[j + 1], WR[j + 2], WR[j + 3]); } err = sbuf_finish(sb); /* Output a trailing NUL. */ if (err == 0) err = SYSCTL_OUT(req, "", 1); sbuf_delete(sb); return (err); } static int t3_set_coalesce_usecs(SYSCTL_HANDLER_ARGS) { adapter_t *sc = arg1; struct qset_params *qsp = &sc->params.sge.qset[0]; int coalesce_usecs; struct sge_qset *qs; int i, j, err, nqsets = 0; struct mtx *lock; if ((sc->flags & FULL_INIT_DONE) == 0) return (ENXIO); coalesce_usecs = qsp->coalesce_usecs; err = sysctl_handle_int(oidp, &coalesce_usecs, arg2, req); if (err != 0) { return (err); } if (coalesce_usecs == qsp->coalesce_usecs) return (0); for (i = 0; i < sc->params.nports; i++) for (j = 0; j < sc->port[i].nqsets; j++) nqsets++; coalesce_usecs = max(1, coalesce_usecs); for (i = 0; i < nqsets; i++) { qs = &sc->sge.qs[i]; qsp = &sc->params.sge.qset[i]; qsp->coalesce_usecs = coalesce_usecs; lock = (sc->flags & USING_MSIX) ? &qs->rspq.lock : &sc->sge.qs[0].rspq.lock; mtx_lock(lock); t3_update_qset_coalesce(qs, qsp); t3_write_reg(sc, A_SG_GTS, V_RSPQ(qs->rspq.cntxt_id) | V_NEWTIMER(qs->rspq.holdoff_tmr)); mtx_unlock(lock); } return (0); } static int t3_pkt_timestamp(SYSCTL_HANDLER_ARGS) { adapter_t *sc = arg1; int rc, timestamp; if ((sc->flags & FULL_INIT_DONE) == 0) return (ENXIO); timestamp = sc->timestamp; rc = sysctl_handle_int(oidp, ×tamp, arg2, req); if (rc != 0) return (rc); if (timestamp != sc->timestamp) { t3_set_reg_field(sc, A_TP_PC_CONFIG2, F_ENABLERXPKTTMSTPRSS, timestamp ? F_ENABLERXPKTTMSTPRSS : 0); sc->timestamp = timestamp; } return (0); } void t3_add_attach_sysctls(adapter_t *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; ctx = device_get_sysctl_ctx(sc->dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); /* random information */ SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "firmware_version", CTLFLAG_RD, &sc->fw_version, 0, "firmware version"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "hw_revision", CTLFLAG_RD, &sc->params.rev, 0, "chip model"); SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "port_types", CTLFLAG_RD, &sc->port_types, 0, "type of ports"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "enable_debug", CTLFLAG_RW, &cxgb_debug, 0, "enable verbose debugging output"); SYSCTL_ADD_UQUAD(ctx, children, OID_AUTO, "tunq_coalesce", CTLFLAG_RD, &sc->tunq_coalesce, "#tunneled packets freed"); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "txq_overrun", CTLFLAG_RD, &txq_fills, 0, "#times txq overrun"); SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "core_clock", CTLFLAG_RD, &sc->params.vpd.cclk, 0, "core clock frequency (in KHz)"); } static const char *rspq_name = "rspq"; static const char *txq_names[] = { "txq_eth", "txq_ofld", "txq_ctrl" }; static int sysctl_handle_macstat(SYSCTL_HANDLER_ARGS) { struct port_info *p = arg1; uint64_t *parg; if (!p) return (EINVAL); parg = (uint64_t *) ((uint8_t *)&p->mac.stats + arg2); PORT_LOCK(p); t3_mac_update_stats(&p->mac); PORT_UNLOCK(p); return (sysctl_handle_64(oidp, parg, 0, req)); } void t3_add_configured_sysctls(adapter_t *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; int i, j; ctx = device_get_sysctl_ctx(sc->dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "intr_coal", CTLTYPE_INT|CTLFLAG_RW, sc, 0, t3_set_coalesce_usecs, "I", "interrupt coalescing timer (us)"); SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "pkt_timestamp", CTLTYPE_INT | CTLFLAG_RW, sc, 0, t3_pkt_timestamp, "I", "provide packet timestamp instead of connection hash"); for (i = 0; i < sc->params.nports; i++) { struct port_info *pi = &sc->port[i]; struct sysctl_oid *poid; struct sysctl_oid_list *poidlist; struct mac_stats *mstats = &pi->mac.stats; snprintf(pi->namebuf, PORT_NAME_LEN, "port%d", i); poid = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, pi->namebuf, CTLFLAG_RD, NULL, "port statistics"); poidlist = SYSCTL_CHILDREN(poid); SYSCTL_ADD_UINT(ctx, poidlist, OID_AUTO, "nqsets", CTLFLAG_RD, &pi->nqsets, 0, "#queue sets"); for (j = 0; j < pi->nqsets; j++) { struct sge_qset *qs = &sc->sge.qs[pi->first_qset + j]; struct sysctl_oid *qspoid, *rspqpoid, *txqpoid, *ctrlqpoid, *lropoid; struct sysctl_oid_list *qspoidlist, *rspqpoidlist, *txqpoidlist, *ctrlqpoidlist, *lropoidlist; struct sge_txq *txq = &qs->txq[TXQ_ETH]; snprintf(qs->namebuf, QS_NAME_LEN, "qs%d", j); qspoid = SYSCTL_ADD_NODE(ctx, poidlist, OID_AUTO, qs->namebuf, CTLFLAG_RD, NULL, "qset statistics"); qspoidlist = SYSCTL_CHILDREN(qspoid); SYSCTL_ADD_UINT(ctx, qspoidlist, OID_AUTO, "fl0_empty", CTLFLAG_RD, &qs->fl[0].empty, 0, "freelist #0 empty"); SYSCTL_ADD_UINT(ctx, qspoidlist, OID_AUTO, "fl1_empty", CTLFLAG_RD, &qs->fl[1].empty, 0, "freelist #1 empty"); rspqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, rspq_name, CTLFLAG_RD, NULL, "rspq statistics"); rspqpoidlist = SYSCTL_CHILDREN(rspqpoid); txqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, txq_names[0], CTLFLAG_RD, NULL, "txq statistics"); txqpoidlist = SYSCTL_CHILDREN(txqpoid); ctrlqpoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, txq_names[2], CTLFLAG_RD, NULL, "ctrlq statistics"); ctrlqpoidlist = SYSCTL_CHILDREN(ctrlqpoid); lropoid = SYSCTL_ADD_NODE(ctx, qspoidlist, OID_AUTO, "lro_stats", CTLFLAG_RD, NULL, "LRO statistics"); lropoidlist = SYSCTL_CHILDREN(lropoid); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "size", CTLFLAG_RD, &qs->rspq.size, 0, "#entries in response queue"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "cidx", CTLFLAG_RD, &qs->rspq.cidx, 0, "consumer index"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "credits", CTLFLAG_RD, &qs->rspq.credits, 0, "#credits"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "starved", CTLFLAG_RD, &qs->rspq.starved, 0, "#times starved"); SYSCTL_ADD_ULONG(ctx, rspqpoidlist, OID_AUTO, "phys_addr", CTLFLAG_RD, &qs->rspq.phys_addr, "physical_address_of the queue"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->rspq.rspq_dump_start, 0, "start rspq dump entry"); SYSCTL_ADD_UINT(ctx, rspqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->rspq.rspq_dump_count, 0, "#rspq entries to dump"); SYSCTL_ADD_PROC(ctx, rspqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->rspq, 0, t3_dump_rspq, "A", "dump of the response queue"); SYSCTL_ADD_UQUAD(ctx, txqpoidlist, OID_AUTO, "dropped", CTLFLAG_RD, &qs->txq[TXQ_ETH].txq_mr->br_drops, "#tunneled packets dropped"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "sendqlen", CTLFLAG_RD, &qs->txq[TXQ_ETH].sendq.qlen, 0, "#tunneled packets waiting to be sent"); #if 0 SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "queue_pidx", CTLFLAG_RD, (uint32_t *)(uintptr_t)&qs->txq[TXQ_ETH].txq_mr.br_prod, 0, "#tunneled packets queue producer index"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "queue_cidx", CTLFLAG_RD, (uint32_t *)(uintptr_t)&qs->txq[TXQ_ETH].txq_mr.br_cons, 0, "#tunneled packets queue consumer index"); #endif SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "processed", CTLFLAG_RD, &qs->txq[TXQ_ETH].processed, 0, "#tunneled packets processed by the card"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "cleaned", CTLFLAG_RD, &txq->cleaned, 0, "#tunneled packets cleaned"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "in_use", CTLFLAG_RD, &txq->in_use, 0, "#tunneled packet slots in use"); SYSCTL_ADD_ULONG(ctx, txqpoidlist, OID_AUTO, "frees", CTLFLAG_RD, &txq->txq_frees, "#tunneled packets freed"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "skipped", CTLFLAG_RD, &txq->txq_skipped, 0, "#tunneled packet descriptors skipped"); SYSCTL_ADD_UQUAD(ctx, txqpoidlist, OID_AUTO, "coalesced", CTLFLAG_RD, &txq->txq_coalesced, "#tunneled packets coalesced"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "enqueued", CTLFLAG_RD, &txq->txq_enqueued, 0, "#tunneled packets enqueued to hardware"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "stopped_flags", CTLFLAG_RD, &qs->txq_stopped, 0, "tx queues stopped"); SYSCTL_ADD_ULONG(ctx, txqpoidlist, OID_AUTO, "phys_addr", CTLFLAG_RD, &txq->phys_addr, "physical_address_of the queue"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "qgen", CTLFLAG_RW, &qs->txq[TXQ_ETH].gen, 0, "txq generation"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "hw_cidx", CTLFLAG_RD, &txq->cidx, 0, "hardware queue cidx"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "hw_pidx", CTLFLAG_RD, &txq->pidx, 0, "hardware queue pidx"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->txq[TXQ_ETH].txq_dump_start, 0, "txq start idx for dump"); SYSCTL_ADD_UINT(ctx, txqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->txq[TXQ_ETH].txq_dump_count, 0, "txq #entries to dump"); SYSCTL_ADD_PROC(ctx, txqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->txq[TXQ_ETH], 0, t3_dump_txq_eth, "A", "dump of the transmit queue"); SYSCTL_ADD_UINT(ctx, ctrlqpoidlist, OID_AUTO, "dump_start", CTLFLAG_RW, &qs->txq[TXQ_CTRL].txq_dump_start, 0, "ctrlq start idx for dump"); SYSCTL_ADD_UINT(ctx, ctrlqpoidlist, OID_AUTO, "dump_count", CTLFLAG_RW, &qs->txq[TXQ_CTRL].txq_dump_count, 0, "ctrl #entries to dump"); SYSCTL_ADD_PROC(ctx, ctrlqpoidlist, OID_AUTO, "qdump", CTLTYPE_STRING | CTLFLAG_RD, &qs->txq[TXQ_CTRL], 0, t3_dump_txq_ctrl, "A", "dump of the transmit queue"); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_queued", CTLFLAG_RD, &qs->lro.ctrl.lro_queued, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_flushed", CTLFLAG_RD, &qs->lro.ctrl.lro_flushed, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_bad_csum", CTLFLAG_RD, &qs->lro.ctrl.lro_bad_csum, 0, NULL); SYSCTL_ADD_INT(ctx, lropoidlist, OID_AUTO, "lro_cnt", CTLFLAG_RD, &qs->lro.ctrl.lro_cnt, 0, NULL); } /* Now add a node for mac stats. */ poid = SYSCTL_ADD_NODE(ctx, poidlist, OID_AUTO, "mac_stats", CTLFLAG_RD, NULL, "MAC statistics"); poidlist = SYSCTL_CHILDREN(poid); /* * We (ab)use the length argument (arg2) to pass on the offset * of the data that we are interested in. This is only required * for the quad counters that are updated from the hardware (we * make sure that we return the latest value). * sysctl_handle_macstat first updates *all* the counters from * the hardware, and then returns the latest value of the * requested counter. Best would be to update only the * requested counter from hardware, but t3_mac_update_stats() * hides all the register details and we don't want to dive into * all that here. */ #define CXGB_SYSCTL_ADD_QUAD(a) SYSCTL_ADD_OID(ctx, poidlist, OID_AUTO, #a, \ (CTLTYPE_U64 | CTLFLAG_RD), pi, offsetof(struct mac_stats, a), \ sysctl_handle_macstat, "QU", 0) CXGB_SYSCTL_ADD_QUAD(tx_octets); CXGB_SYSCTL_ADD_QUAD(tx_octets_bad); CXGB_SYSCTL_ADD_QUAD(tx_frames); CXGB_SYSCTL_ADD_QUAD(tx_mcast_frames); CXGB_SYSCTL_ADD_QUAD(tx_bcast_frames); CXGB_SYSCTL_ADD_QUAD(tx_pause); CXGB_SYSCTL_ADD_QUAD(tx_deferred); CXGB_SYSCTL_ADD_QUAD(tx_late_collisions); CXGB_SYSCTL_ADD_QUAD(tx_total_collisions); CXGB_SYSCTL_ADD_QUAD(tx_excess_collisions); CXGB_SYSCTL_ADD_QUAD(tx_underrun); CXGB_SYSCTL_ADD_QUAD(tx_len_errs); CXGB_SYSCTL_ADD_QUAD(tx_mac_internal_errs); CXGB_SYSCTL_ADD_QUAD(tx_excess_deferral); CXGB_SYSCTL_ADD_QUAD(tx_fcs_errs); CXGB_SYSCTL_ADD_QUAD(tx_frames_64); CXGB_SYSCTL_ADD_QUAD(tx_frames_65_127); CXGB_SYSCTL_ADD_QUAD(tx_frames_128_255); CXGB_SYSCTL_ADD_QUAD(tx_frames_256_511); CXGB_SYSCTL_ADD_QUAD(tx_frames_512_1023); CXGB_SYSCTL_ADD_QUAD(tx_frames_1024_1518); CXGB_SYSCTL_ADD_QUAD(tx_frames_1519_max); CXGB_SYSCTL_ADD_QUAD(rx_octets); CXGB_SYSCTL_ADD_QUAD(rx_octets_bad); CXGB_SYSCTL_ADD_QUAD(rx_frames); CXGB_SYSCTL_ADD_QUAD(rx_mcast_frames); CXGB_SYSCTL_ADD_QUAD(rx_bcast_frames); CXGB_SYSCTL_ADD_QUAD(rx_pause); CXGB_SYSCTL_ADD_QUAD(rx_fcs_errs); CXGB_SYSCTL_ADD_QUAD(rx_align_errs); CXGB_SYSCTL_ADD_QUAD(rx_symbol_errs); CXGB_SYSCTL_ADD_QUAD(rx_data_errs); CXGB_SYSCTL_ADD_QUAD(rx_sequence_errs); CXGB_SYSCTL_ADD_QUAD(rx_runt); CXGB_SYSCTL_ADD_QUAD(rx_jabber); CXGB_SYSCTL_ADD_QUAD(rx_short); CXGB_SYSCTL_ADD_QUAD(rx_too_long); CXGB_SYSCTL_ADD_QUAD(rx_mac_internal_errs); CXGB_SYSCTL_ADD_QUAD(rx_cong_drops); CXGB_SYSCTL_ADD_QUAD(rx_frames_64); CXGB_SYSCTL_ADD_QUAD(rx_frames_65_127); CXGB_SYSCTL_ADD_QUAD(rx_frames_128_255); CXGB_SYSCTL_ADD_QUAD(rx_frames_256_511); CXGB_SYSCTL_ADD_QUAD(rx_frames_512_1023); CXGB_SYSCTL_ADD_QUAD(rx_frames_1024_1518); CXGB_SYSCTL_ADD_QUAD(rx_frames_1519_max); #undef CXGB_SYSCTL_ADD_QUAD #define CXGB_SYSCTL_ADD_ULONG(a) SYSCTL_ADD_ULONG(ctx, poidlist, OID_AUTO, #a, \ CTLFLAG_RD, &mstats->a, 0) CXGB_SYSCTL_ADD_ULONG(tx_fifo_parity_err); CXGB_SYSCTL_ADD_ULONG(rx_fifo_parity_err); CXGB_SYSCTL_ADD_ULONG(tx_fifo_urun); CXGB_SYSCTL_ADD_ULONG(rx_fifo_ovfl); CXGB_SYSCTL_ADD_ULONG(serdes_signal_loss); CXGB_SYSCTL_ADD_ULONG(xaui_pcs_ctc_err); CXGB_SYSCTL_ADD_ULONG(xaui_pcs_align_change); CXGB_SYSCTL_ADD_ULONG(num_toggled); CXGB_SYSCTL_ADD_ULONG(num_resets); CXGB_SYSCTL_ADD_ULONG(link_faults); #undef CXGB_SYSCTL_ADD_ULONG } } /** * t3_get_desc - dump an SGE descriptor for debugging purposes * @qs: the queue set * @qnum: identifies the specific queue (0..2: Tx, 3:response, 4..5: Rx) * @idx: the descriptor index in the queue * @data: where to dump the descriptor contents * * Dumps the contents of a HW descriptor of an SGE queue. Returns the * size of the descriptor. */ int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx, unsigned char *data) { if (qnum >= 6) return (EINVAL); if (qnum < 3) { if (!qs->txq[qnum].desc || idx >= qs->txq[qnum].size) return -EINVAL; memcpy(data, &qs->txq[qnum].desc[idx], sizeof(struct tx_desc)); return sizeof(struct tx_desc); } if (qnum == 3) { if (!qs->rspq.desc || idx >= qs->rspq.size) return (EINVAL); memcpy(data, &qs->rspq.desc[idx], sizeof(struct rsp_desc)); return sizeof(struct rsp_desc); } qnum -= 4; if (!qs->fl[qnum].desc || idx >= qs->fl[qnum].size) return (EINVAL); memcpy(data, &qs->fl[qnum].desc[idx], sizeof(struct rx_desc)); return sizeof(struct rx_desc); } Index: head/sys/dev/dpt/dpt_pci.c =================================================================== --- head/sys/dev/dpt/dpt_pci.c (revision 232853) +++ head/sys/dev/dpt/dpt_pci.c (revision 232854) @@ -1,200 +1,200 @@ /*- * Copyright (c) 2000 Matthew N. Dodd * All rights reserved. * * Copyright (c) 1997 Simon Shapiro * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DPT_VENDOR_ID 0x1044 #define DPT_DEVICE_ID 0xa400 #define DPT_PCI_IOADDR PCIR_BAR(0) /* I/O Address */ #define DPT_PCI_MEMADDR PCIR_BAR(1) /* Mem I/O Address */ #define ISA_PRIMARY_WD_ADDRESS 0x1f8 static int dpt_pci_probe (device_t); static int dpt_pci_attach (device_t); static int dpt_pci_probe (device_t dev) { if ((pci_get_vendor(dev) == DPT_VENDOR_ID) && (pci_get_device(dev) == DPT_DEVICE_ID)) { device_set_desc(dev, "DPT Caching SCSI RAID Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int dpt_pci_attach (device_t dev) { dpt_softc_t * dpt; int s; int error = 0; u_int32_t command; dpt = device_get_softc(dev); dpt->dev = dev; command = pci_read_config(dev, PCIR_COMMAND, /*bytes*/1); #ifdef DPT_ALLOW_MMIO if ((command & PCIM_CMD_MEMEN) != 0) { dpt->io_rid = DPT_PCI_MEMADDR; dpt->io_type = SYS_RES_MEMORY; dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, &dpt->io_rid, RF_ACTIVE); } #endif if (dpt->io_res == NULL && (command & PCIM_CMD_PORTEN) != 0) { dpt->io_rid = DPT_PCI_IOADDR; dpt->io_type = SYS_RES_IOPORT; dpt->io_res = bus_alloc_resource_any(dev, dpt->io_type, &dpt->io_rid, RF_ACTIVE); } if (dpt->io_res == NULL) { device_printf(dev, "can't allocate register resources\n"); error = ENOMEM; goto bad; } dpt->io_offset = 0x10; dpt->irq_rid = 0; dpt->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &dpt->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (dpt->irq_res == NULL) { device_printf(dev, "No irq?!\n"); error = ENOMEM; goto bad; } /* Ensure busmastering is enabled */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, /*bytes*/1); if (rman_get_start(dpt->io_res) == (ISA_PRIMARY_WD_ADDRESS - 0x10)) { #ifdef DPT_DEBUG_WARN device_printf(dev, "Mapped as an IDE controller. " "Disabling SCSI setup\n"); #endif error = ENXIO; goto bad; } dpt_alloc(dev); /* Allocate a dmatag representing the capabilities of this attachment */ /* XXX Should be a child of the PCI bus dma tag */ - if (bus_dma_tag_create( /* parent */ NULL, + if (bus_dma_tag_create( /* PCI parent */ bus_get_dma_tag(dev), /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* nsegments */ ~0, /* maxsegsz */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, /* lockfunc */ busdma_lock_mutex, /* lockarg */ &Giant, &dpt->parent_dmat) != 0) { error = ENXIO; goto bad; } s = splcam(); if (dpt_init(dpt) != 0) { error = ENXIO; goto bad; } /* Register with the XPT */ dpt_attach(dpt); splx(s); if (bus_setup_intr(dev, dpt->irq_res, INTR_TYPE_CAM | INTR_ENTROPY, NULL, dpt_intr, dpt, &dpt->ih)) { device_printf(dev, "Unable to register interrupt handler\n"); error = ENXIO; goto bad; } return (error); bad: dpt_release_resources(dev); dpt_free(dpt); return (error); } static device_method_t dpt_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpt_pci_probe), DEVMETHOD(device_attach, dpt_pci_attach), DEVMETHOD(device_detach, dpt_detach), { 0, 0 } }; static driver_t dpt_pci_driver = { "dpt", dpt_pci_methods, sizeof(dpt_softc_t), }; DRIVER_MODULE(dpt, pci, dpt_pci_driver, dpt_devclass, 0, 0); MODULE_DEPEND(dpt, pci, 1, 1, 1); MODULE_DEPEND(dpt, cam, 1, 1, 1); Index: head/sys/dev/hifn/hifn7751.c =================================================================== --- head/sys/dev/hifn/hifn7751.c (revision 232853) +++ head/sys/dev/hifn/hifn7751.c (revision 232854) @@ -1,2934 +1,2934 @@ /* $OpenBSD: hifn7751.c,v 1.120 2002/05/17 00:33:34 deraadt Exp $ */ /*- * Invertex AEON / Hifn 7751 driver * Copyright (c) 1999 Invertex Inc. All rights reserved. * Copyright (c) 1999 Theo de Raadt * Copyright (c) 2000-2001 Network Security Technologies, Inc. * http://www.netsec.net * Copyright (c) 2003 Hifn Inc. * * This driver is based on a previous driver by Invertex, for which they * requested: Please send any comments, feedback, bug-fixes, or feature * requests to software@invertex.com. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Effort sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F30602-01-2-0537. */ #include __FBSDID("$FreeBSD$"); /* * Driver for various Hifn encryption processors. */ #include "opt_hifn.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cryptodev_if.h" #include #include #ifdef HIFN_RNDTEST #include #endif #include #include #ifdef HIFN_VULCANDEV #include #include static struct cdevsw vulcanpk_cdevsw; /* forward declaration */ #endif /* * Prototypes and count for the pci_device structure */ static int hifn_probe(device_t); static int hifn_attach(device_t); static int hifn_detach(device_t); static int hifn_suspend(device_t); static int hifn_resume(device_t); static int hifn_shutdown(device_t); static int hifn_newsession(device_t, u_int32_t *, struct cryptoini *); static int hifn_freesession(device_t, u_int64_t); static int hifn_process(device_t, struct cryptop *, int); static device_method_t hifn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hifn_probe), DEVMETHOD(device_attach, hifn_attach), DEVMETHOD(device_detach, hifn_detach), DEVMETHOD(device_suspend, hifn_suspend), DEVMETHOD(device_resume, hifn_resume), DEVMETHOD(device_shutdown, hifn_shutdown), /* crypto device methods */ DEVMETHOD(cryptodev_newsession, hifn_newsession), DEVMETHOD(cryptodev_freesession,hifn_freesession), DEVMETHOD(cryptodev_process, hifn_process), DEVMETHOD_END }; static driver_t hifn_driver = { "hifn", hifn_methods, sizeof (struct hifn_softc) }; static devclass_t hifn_devclass; DRIVER_MODULE(hifn, pci, hifn_driver, hifn_devclass, 0, 0); MODULE_DEPEND(hifn, crypto, 1, 1, 1); #ifdef HIFN_RNDTEST MODULE_DEPEND(hifn, rndtest, 1, 1, 1); #endif static void hifn_reset_board(struct hifn_softc *, int); static void hifn_reset_puc(struct hifn_softc *); static void hifn_puc_wait(struct hifn_softc *); static int hifn_enable_crypto(struct hifn_softc *); static void hifn_set_retry(struct hifn_softc *sc); static void hifn_init_dma(struct hifn_softc *); static void hifn_init_pci_registers(struct hifn_softc *); static int hifn_sramsize(struct hifn_softc *); static int hifn_dramsize(struct hifn_softc *); static int hifn_ramtype(struct hifn_softc *); static void hifn_sessions(struct hifn_softc *); static void hifn_intr(void *); static u_int hifn_write_command(struct hifn_command *, u_int8_t *); static u_int32_t hifn_next_signature(u_int32_t a, u_int cnt); static void hifn_callback(struct hifn_softc *, struct hifn_command *, u_int8_t *); static int hifn_crypto(struct hifn_softc *, struct hifn_command *, struct cryptop *, int); static int hifn_readramaddr(struct hifn_softc *, int, u_int8_t *); static int hifn_writeramaddr(struct hifn_softc *, int, u_int8_t *); static int hifn_dmamap_load_src(struct hifn_softc *, struct hifn_command *); static int hifn_dmamap_load_dst(struct hifn_softc *, struct hifn_command *); static int hifn_init_pubrng(struct hifn_softc *); static void hifn_rng(void *); static void hifn_tick(void *); static void hifn_abort(struct hifn_softc *); static void hifn_alloc_slot(struct hifn_softc *, int *, int *, int *, int *); static void hifn_write_reg_0(struct hifn_softc *, bus_size_t, u_int32_t); static void hifn_write_reg_1(struct hifn_softc *, bus_size_t, u_int32_t); static __inline u_int32_t READ_REG_0(struct hifn_softc *sc, bus_size_t reg) { u_int32_t v = bus_space_read_4(sc->sc_st0, sc->sc_sh0, reg); sc->sc_bar0_lastreg = (bus_size_t) -1; return (v); } #define WRITE_REG_0(sc, reg, val) hifn_write_reg_0(sc, reg, val) static __inline u_int32_t READ_REG_1(struct hifn_softc *sc, bus_size_t reg) { u_int32_t v = bus_space_read_4(sc->sc_st1, sc->sc_sh1, reg); sc->sc_bar1_lastreg = (bus_size_t) -1; return (v); } #define WRITE_REG_1(sc, reg, val) hifn_write_reg_1(sc, reg, val) static SYSCTL_NODE(_hw, OID_AUTO, hifn, CTLFLAG_RD, 0, "Hifn driver parameters"); #ifdef HIFN_DEBUG static int hifn_debug = 0; SYSCTL_INT(_hw_hifn, OID_AUTO, debug, CTLFLAG_RW, &hifn_debug, 0, "control debugging msgs"); #endif static struct hifn_stats hifnstats; SYSCTL_STRUCT(_hw_hifn, OID_AUTO, stats, CTLFLAG_RD, &hifnstats, hifn_stats, "driver statistics"); static int hifn_maxbatch = 1; SYSCTL_INT(_hw_hifn, OID_AUTO, maxbatch, CTLFLAG_RW, &hifn_maxbatch, 0, "max ops to batch w/o interrupt"); /* * Probe for a supported device. The PCI vendor and device * IDs are used to detect devices we know how to handle. */ static int hifn_probe(device_t dev) { if (pci_get_vendor(dev) == PCI_VENDOR_INVERTEX && pci_get_device(dev) == PCI_PRODUCT_INVERTEX_AEON) return (BUS_PROBE_DEFAULT); if (pci_get_vendor(dev) == PCI_VENDOR_HIFN && (pci_get_device(dev) == PCI_PRODUCT_HIFN_7751 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7951 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7956 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7811)) return (BUS_PROBE_DEFAULT); if (pci_get_vendor(dev) == PCI_VENDOR_NETSEC && pci_get_device(dev) == PCI_PRODUCT_NETSEC_7751) return (BUS_PROBE_DEFAULT); return (ENXIO); } static void hifn_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *paddr = (bus_addr_t*) arg; *paddr = segs->ds_addr; } static const char* hifn_partname(struct hifn_softc *sc) { /* XXX sprintf numbers when not decoded */ switch (pci_get_vendor(sc->sc_dev)) { case PCI_VENDOR_HIFN: switch (pci_get_device(sc->sc_dev)) { case PCI_PRODUCT_HIFN_6500: return "Hifn 6500"; case PCI_PRODUCT_HIFN_7751: return "Hifn 7751"; case PCI_PRODUCT_HIFN_7811: return "Hifn 7811"; case PCI_PRODUCT_HIFN_7951: return "Hifn 7951"; case PCI_PRODUCT_HIFN_7955: return "Hifn 7955"; case PCI_PRODUCT_HIFN_7956: return "Hifn 7956"; } return "Hifn unknown-part"; case PCI_VENDOR_INVERTEX: switch (pci_get_device(sc->sc_dev)) { case PCI_PRODUCT_INVERTEX_AEON: return "Invertex AEON"; } return "Invertex unknown-part"; case PCI_VENDOR_NETSEC: switch (pci_get_device(sc->sc_dev)) { case PCI_PRODUCT_NETSEC_7751: return "NetSec 7751"; } return "NetSec unknown-part"; } return "Unknown-vendor unknown-part"; } static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { random_harvest(buf, count, count*NBBY, 0, RANDOM_PURE); } static u_int checkmaxmin(device_t dev, const char *what, u_int v, u_int min, u_int max) { if (v > max) { device_printf(dev, "Warning, %s %u out of range, " "using max %u\n", what, v, max); v = max; } else if (v < min) { device_printf(dev, "Warning, %s %u out of range, " "using min %u\n", what, v, min); v = min; } return v; } /* * Select PLL configuration for 795x parts. This is complicated in * that we cannot determine the optimal parameters without user input. * The reference clock is derived from an external clock through a * multiplier. The external clock is either the host bus (i.e. PCI) * or an external clock generator. When using the PCI bus we assume * the clock is either 33 or 66 MHz; for an external source we cannot * tell the speed. * * PLL configuration is done with a string: "pci" for PCI bus, or "ext" * for an external source, followed by the frequency. We calculate * the appropriate multiplier and PLL register contents accordingly. * When no configuration is given we default to "pci66" since that * always will allow the card to work. If a card is using the PCI * bus clock and in a 33MHz slot then it will be operating at half * speed until the correct information is provided. * * We use a default setting of "ext66" because according to Mike Ham * of HiFn, almost every board in existence has an external crystal * populated at 66Mhz. Using PCI can be a problem on modern motherboards, * because PCI33 can have clocks from 0 to 33Mhz, and some have * non-PCI-compliant spread-spectrum clocks, which can confuse the pll. */ static void hifn_getpllconfig(device_t dev, u_int *pll) { const char *pllspec; u_int freq, mul, fl, fh; u_int32_t pllconfig; char *nxt; if (resource_string_value("hifn", device_get_unit(dev), "pllconfig", &pllspec)) pllspec = "ext66"; fl = 33, fh = 66; pllconfig = 0; if (strncmp(pllspec, "ext", 3) == 0) { pllspec += 3; pllconfig |= HIFN_PLL_REF_SEL; switch (pci_get_device(dev)) { case PCI_PRODUCT_HIFN_7955: case PCI_PRODUCT_HIFN_7956: fl = 20, fh = 100; break; #ifdef notyet case PCI_PRODUCT_HIFN_7954: fl = 20, fh = 66; break; #endif } } else if (strncmp(pllspec, "pci", 3) == 0) pllspec += 3; freq = strtoul(pllspec, &nxt, 10); if (nxt == pllspec) freq = 66; else freq = checkmaxmin(dev, "frequency", freq, fl, fh); /* * Calculate multiplier. We target a Fck of 266 MHz, * allowing only even values, possibly rounded down. * Multipliers > 8 must set the charge pump current. */ mul = checkmaxmin(dev, "PLL divisor", (266 / freq) &~ 1, 2, 12); pllconfig |= (mul / 2 - 1) << HIFN_PLL_ND_SHIFT; if (mul > 8) pllconfig |= HIFN_PLL_IS; *pll = pllconfig; } /* * Attach an interface that successfully probed. */ static int hifn_attach(device_t dev) { struct hifn_softc *sc = device_get_softc(dev); caddr_t kva; int rseg, rid; char rbase; u_int16_t ena, rev; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "hifn driver", MTX_DEF); /* XXX handle power management */ /* * The 7951 and 795x have a random number generator and * public key support; note this. */ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN && (pci_get_device(dev) == PCI_PRODUCT_HIFN_7951 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7956)) sc->sc_flags = HIFN_HAS_RNG | HIFN_HAS_PUBLIC; /* * The 7811 has a random number generator and * we also note it's identity 'cuz of some quirks. */ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN && pci_get_device(dev) == PCI_PRODUCT_HIFN_7811) sc->sc_flags |= HIFN_IS_7811 | HIFN_HAS_RNG; /* * The 795x parts support AES. */ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN && (pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 || pci_get_device(dev) == PCI_PRODUCT_HIFN_7956)) { sc->sc_flags |= HIFN_IS_7956 | HIFN_HAS_AES; /* * Select PLL configuration. This depends on the * bus and board design and must be manually configured * if the default setting is unacceptable. */ hifn_getpllconfig(dev, &sc->sc_pllconfig); } /* * Setup PCI resources. Note that we record the bus * tag and handle for each register mapping, this is * used by the READ_REG_0, WRITE_REG_0, READ_REG_1, * and WRITE_REG_1 macros throughout the driver. */ pci_enable_busmaster(dev); rid = HIFN_BAR0; sc->sc_bar0res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_bar0res == NULL) { device_printf(dev, "cannot map bar%d register space\n", 0); goto fail_pci; } sc->sc_st0 = rman_get_bustag(sc->sc_bar0res); sc->sc_sh0 = rman_get_bushandle(sc->sc_bar0res); sc->sc_bar0_lastreg = (bus_size_t) -1; rid = HIFN_BAR1; sc->sc_bar1res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_bar1res == NULL) { device_printf(dev, "cannot map bar%d register space\n", 1); goto fail_io0; } sc->sc_st1 = rman_get_bustag(sc->sc_bar1res); sc->sc_sh1 = rman_get_bushandle(sc->sc_bar1res); sc->sc_bar1_lastreg = (bus_size_t) -1; hifn_set_retry(sc); /* * Setup the area where the Hifn DMA's descriptors * and associated data structures. */ - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ HIFN_MAX_DMALEN, /* maxsize */ MAX_SCATTER, /* nsegments */ HIFN_MAX_SEGLEN, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto fail_io1; } if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, &sc->sc_dmamap)) { device_printf(dev, "cannot create dma map\n"); bus_dma_tag_destroy(sc->sc_dmat); goto fail_io1; } if (bus_dmamem_alloc(sc->sc_dmat, (void**) &kva, BUS_DMA_NOWAIT, &sc->sc_dmamap)) { device_printf(dev, "cannot alloc dma buffer\n"); bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); bus_dma_tag_destroy(sc->sc_dmat); goto fail_io1; } if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, kva, sizeof (*sc->sc_dma), hifn_dmamap_cb, &sc->sc_dma_physaddr, BUS_DMA_NOWAIT)) { device_printf(dev, "cannot load dma map\n"); bus_dmamem_free(sc->sc_dmat, kva, sc->sc_dmamap); bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); bus_dma_tag_destroy(sc->sc_dmat); goto fail_io1; } sc->sc_dma = (struct hifn_dma *)kva; bzero(sc->sc_dma, sizeof(*sc->sc_dma)); KASSERT(sc->sc_st0 != 0, ("hifn_attach: null bar0 tag!")); KASSERT(sc->sc_sh0 != 0, ("hifn_attach: null bar0 handle!")); KASSERT(sc->sc_st1 != 0, ("hifn_attach: null bar1 tag!")); KASSERT(sc->sc_sh1 != 0, ("hifn_attach: null bar1 handle!")); /* * Reset the board and do the ``secret handshake'' * to enable the crypto support. Then complete the * initialization procedure by setting up the interrupt * and hooking in to the system crypto support so we'll * get used for system services like the crypto device, * IPsec, RNG device, etc. */ hifn_reset_board(sc, 0); if (hifn_enable_crypto(sc) != 0) { device_printf(dev, "crypto enabling failed\n"); goto fail_mem; } hifn_reset_puc(sc); hifn_init_dma(sc); hifn_init_pci_registers(sc); /* XXX can't dynamically determine ram type for 795x; force dram */ if (sc->sc_flags & HIFN_IS_7956) sc->sc_drammodel = 1; else if (hifn_ramtype(sc)) goto fail_mem; if (sc->sc_drammodel == 0) hifn_sramsize(sc); else hifn_dramsize(sc); /* * Workaround for NetSec 7751 rev A: half ram size because two * of the address lines were left floating */ if (pci_get_vendor(dev) == PCI_VENDOR_NETSEC && pci_get_device(dev) == PCI_PRODUCT_NETSEC_7751 && pci_get_revid(dev) == 0x61) /*XXX???*/ sc->sc_ramsize >>= 1; /* * Arrange the interrupt line. */ rid = 0; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (sc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto fail_mem; } /* * NB: Network code assumes we are blocked with splimp() * so make sure the IRQ is marked appropriately. */ if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, hifn_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "could not setup interrupt\n"); goto fail_intr2; } hifn_sessions(sc); /* * NB: Keep only the low 16 bits; this masks the chip id * from the 7951. */ rev = READ_REG_1(sc, HIFN_1_REVID) & 0xffff; rseg = sc->sc_ramsize / 1024; rbase = 'K'; if (sc->sc_ramsize >= (1024 * 1024)) { rbase = 'M'; rseg /= 1024; } device_printf(sc->sc_dev, "%s, rev %u, %d%cB %cram", hifn_partname(sc), rev, rseg, rbase, sc->sc_drammodel ? 'd' : 's'); if (sc->sc_flags & HIFN_IS_7956) printf(", pll=0x%x<%s clk, %ux mult>", sc->sc_pllconfig, sc->sc_pllconfig & HIFN_PLL_REF_SEL ? "ext" : "pci", 2 + 2*((sc->sc_pllconfig & HIFN_PLL_ND) >> 11)); printf("\n"); sc->sc_cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE); if (sc->sc_cid < 0) { device_printf(dev, "could not get crypto driver id\n"); goto fail_intr; } WRITE_REG_0(sc, HIFN_0_PUCNFG, READ_REG_0(sc, HIFN_0_PUCNFG) | HIFN_PUCNFG_CHIPID); ena = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA; switch (ena) { case HIFN_PUSTAT_ENA_2: crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0); crypto_register(sc->sc_cid, CRYPTO_ARC4, 0, 0); if (sc->sc_flags & HIFN_HAS_AES) crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0); /*FALLTHROUGH*/ case HIFN_PUSTAT_ENA_1: crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0); crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0); crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0); crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0); crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0); break; } bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); if (sc->sc_flags & (HIFN_HAS_PUBLIC | HIFN_HAS_RNG)) hifn_init_pubrng(sc); callout_init(&sc->sc_tickto, CALLOUT_MPSAFE); callout_reset(&sc->sc_tickto, hz, hifn_tick, sc); return (0); fail_intr: bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand); fail_intr2: /* XXX don't store rid */ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq); fail_mem: bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap); bus_dmamem_free(sc->sc_dmat, sc->sc_dma, sc->sc_dmamap); bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); bus_dma_tag_destroy(sc->sc_dmat); /* Turn off DMA polling */ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE); fail_io1: bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR1, sc->sc_bar1res); fail_io0: bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR0, sc->sc_bar0res); fail_pci: mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* * Detach an interface that successfully probed. */ static int hifn_detach(device_t dev) { struct hifn_softc *sc = device_get_softc(dev); KASSERT(sc != NULL, ("hifn_detach: null software carrier!")); /* disable interrupts */ WRITE_REG_1(sc, HIFN_1_DMA_IER, 0); /*XXX other resources */ callout_stop(&sc->sc_tickto); callout_stop(&sc->sc_rngto); #ifdef HIFN_RNDTEST if (sc->sc_rndtest) rndtest_detach(sc->sc_rndtest); #endif /* Turn off DMA polling */ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE); crypto_unregister_all(sc->sc_cid); bus_generic_detach(dev); /*XXX should be no children, right? */ bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand); /* XXX don't store rid */ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq); bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap); bus_dmamem_free(sc->sc_dmat, sc->sc_dma, sc->sc_dmamap); bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR1, sc->sc_bar1res); bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR0, sc->sc_bar0res); mtx_destroy(&sc->sc_mtx); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int hifn_shutdown(device_t dev) { #ifdef notyet hifn_stop(device_get_softc(dev)); #endif return (0); } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int hifn_suspend(device_t dev) { struct hifn_softc *sc = device_get_softc(dev); #ifdef notyet hifn_stop(sc); #endif sc->sc_suspended = 1; return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int hifn_resume(device_t dev) { struct hifn_softc *sc = device_get_softc(dev); #ifdef notyet /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) rl_init(sc); #endif sc->sc_suspended = 0; return (0); } static int hifn_init_pubrng(struct hifn_softc *sc) { u_int32_t r; int i; #ifdef HIFN_RNDTEST sc->sc_rndtest = rndtest_attach(sc->sc_dev); if (sc->sc_rndtest) sc->sc_harvest = rndtest_harvest; else sc->sc_harvest = default_harvest; #else sc->sc_harvest = default_harvest; #endif if ((sc->sc_flags & HIFN_IS_7811) == 0) { /* Reset 7951 public key/rng engine */ WRITE_REG_1(sc, HIFN_1_PUB_RESET, READ_REG_1(sc, HIFN_1_PUB_RESET) | HIFN_PUBRST_RESET); for (i = 0; i < 100; i++) { DELAY(1000); if ((READ_REG_1(sc, HIFN_1_PUB_RESET) & HIFN_PUBRST_RESET) == 0) break; } if (i == 100) { device_printf(sc->sc_dev, "public key init failed\n"); return (1); } } /* Enable the rng, if available */ if (sc->sc_flags & HIFN_HAS_RNG) { if (sc->sc_flags & HIFN_IS_7811) { r = READ_REG_1(sc, HIFN_1_7811_RNGENA); if (r & HIFN_7811_RNGENA_ENA) { r &= ~HIFN_7811_RNGENA_ENA; WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r); } WRITE_REG_1(sc, HIFN_1_7811_RNGCFG, HIFN_7811_RNGCFG_DEFL); r |= HIFN_7811_RNGENA_ENA; WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r); } else WRITE_REG_1(sc, HIFN_1_RNG_CONFIG, READ_REG_1(sc, HIFN_1_RNG_CONFIG) | HIFN_RNGCFG_ENA); sc->sc_rngfirst = 1; if (hz >= 100) sc->sc_rnghz = hz / 100; else sc->sc_rnghz = 1; callout_init(&sc->sc_rngto, CALLOUT_MPSAFE); callout_reset(&sc->sc_rngto, sc->sc_rnghz, hifn_rng, sc); } /* Enable public key engine, if available */ if (sc->sc_flags & HIFN_HAS_PUBLIC) { WRITE_REG_1(sc, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE); sc->sc_dmaier |= HIFN_DMAIER_PUBDONE; WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier); #ifdef HIFN_VULCANDEV sc->sc_pkdev = make_dev(&vulcanpk_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "vulcanpk"); sc->sc_pkdev->si_drv1 = sc; #endif } return (0); } static void hifn_rng(void *vsc) { #define RANDOM_BITS(n) (n)*sizeof (u_int32_t), (n)*sizeof (u_int32_t)*NBBY, 0 struct hifn_softc *sc = vsc; u_int32_t sts, num[2]; int i; if (sc->sc_flags & HIFN_IS_7811) { /* ONLY VALID ON 7811!!!! */ for (i = 0; i < 5; i++) { sts = READ_REG_1(sc, HIFN_1_7811_RNGSTS); if (sts & HIFN_7811_RNGSTS_UFL) { device_printf(sc->sc_dev, "RNG underflow: disabling\n"); return; } if ((sts & HIFN_7811_RNGSTS_RDY) == 0) break; /* * There are at least two words in the RNG FIFO * at this point. */ num[0] = READ_REG_1(sc, HIFN_1_7811_RNGDAT); num[1] = READ_REG_1(sc, HIFN_1_7811_RNGDAT); /* NB: discard first data read */ if (sc->sc_rngfirst) sc->sc_rngfirst = 0; else (*sc->sc_harvest)(sc->sc_rndtest, num, sizeof (num)); } } else { num[0] = READ_REG_1(sc, HIFN_1_RNG_DATA); /* NB: discard first data read */ if (sc->sc_rngfirst) sc->sc_rngfirst = 0; else (*sc->sc_harvest)(sc->sc_rndtest, num, sizeof (num[0])); } callout_reset(&sc->sc_rngto, sc->sc_rnghz, hifn_rng, sc); #undef RANDOM_BITS } static void hifn_puc_wait(struct hifn_softc *sc) { int i; int reg = HIFN_0_PUCTRL; if (sc->sc_flags & HIFN_IS_7956) { reg = HIFN_0_PUCTRL2; } for (i = 5000; i > 0; i--) { DELAY(1); if (!(READ_REG_0(sc, reg) & HIFN_PUCTRL_RESET)) break; } if (!i) device_printf(sc->sc_dev, "proc unit did not reset\n"); } /* * Reset the processing unit. */ static void hifn_reset_puc(struct hifn_softc *sc) { /* Reset processing unit */ int reg = HIFN_0_PUCTRL; if (sc->sc_flags & HIFN_IS_7956) { reg = HIFN_0_PUCTRL2; } WRITE_REG_0(sc, reg, HIFN_PUCTRL_DMAENA); hifn_puc_wait(sc); } /* * Set the Retry and TRDY registers; note that we set them to * zero because the 7811 locks up when forced to retry (section * 3.6 of "Specification Update SU-0014-04". Not clear if we * should do this for all Hifn parts, but it doesn't seem to hurt. */ static void hifn_set_retry(struct hifn_softc *sc) { /* NB: RETRY only responds to 8-bit reads/writes */ pci_write_config(sc->sc_dev, HIFN_RETRY_TIMEOUT, 0, 1); pci_write_config(sc->sc_dev, HIFN_TRDY_TIMEOUT, 0, 1); } /* * Resets the board. Values in the regesters are left as is * from the reset (i.e. initial values are assigned elsewhere). */ static void hifn_reset_board(struct hifn_softc *sc, int full) { u_int32_t reg; /* * Set polling in the DMA configuration register to zero. 0x7 avoids * resetting the board and zeros out the other fields. */ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE); /* * Now that polling has been disabled, we have to wait 1 ms * before resetting the board. */ DELAY(1000); /* Reset the DMA unit */ if (full) { WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE); DELAY(1000); } else { WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE | HIFN_DMACNFG_MSTRESET); hifn_reset_puc(sc); } KASSERT(sc->sc_dma != NULL, ("hifn_reset_board: null DMA tag!")); bzero(sc->sc_dma, sizeof(*sc->sc_dma)); /* Bring dma unit out of reset */ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE); hifn_puc_wait(sc); hifn_set_retry(sc); if (sc->sc_flags & HIFN_IS_7811) { for (reg = 0; reg < 1000; reg++) { if (READ_REG_1(sc, HIFN_1_7811_MIPSRST) & HIFN_MIPSRST_CRAMINIT) break; DELAY(1000); } if (reg == 1000) printf(": cram init timeout\n"); } else { /* set up DMA configuration register #2 */ /* turn off all PK and BAR0 swaps */ WRITE_REG_1(sc, HIFN_1_DMA_CNFG2, (3 << HIFN_DMACNFG2_INIT_WRITE_BURST_SHIFT)| (3 << HIFN_DMACNFG2_INIT_READ_BURST_SHIFT)| (2 << HIFN_DMACNFG2_TGT_WRITE_BURST_SHIFT)| (2 << HIFN_DMACNFG2_TGT_READ_BURST_SHIFT)); } } static u_int32_t hifn_next_signature(u_int32_t a, u_int cnt) { int i; u_int32_t v; for (i = 0; i < cnt; i++) { /* get the parity */ v = a & 0x80080125; v ^= v >> 16; v ^= v >> 8; v ^= v >> 4; v ^= v >> 2; v ^= v >> 1; a = (v & 1) ^ (a << 1); } return a; } struct pci2id { u_short pci_vendor; u_short pci_prod; char card_id[13]; }; static struct pci2id pci2id[] = { { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7951, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7955, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7956, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { PCI_VENDOR_NETSEC, PCI_PRODUCT_NETSEC_7751, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { PCI_VENDOR_INVERTEX, PCI_PRODUCT_INVERTEX_AEON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7811, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, { /* * Other vendors share this PCI ID as well, such as * http://www.powercrypt.com, and obviously they also * use the same key. */ PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7751, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }, }; /* * Checks to see if crypto is already enabled. If crypto isn't enable, * "hifn_enable_crypto" is called to enable it. The check is important, * as enabling crypto twice will lock the board. */ static int hifn_enable_crypto(struct hifn_softc *sc) { u_int32_t dmacfg, ramcfg, encl, addr, i; char *offtbl = NULL; for (i = 0; i < sizeof(pci2id)/sizeof(pci2id[0]); i++) { if (pci2id[i].pci_vendor == pci_get_vendor(sc->sc_dev) && pci2id[i].pci_prod == pci_get_device(sc->sc_dev)) { offtbl = pci2id[i].card_id; break; } } if (offtbl == NULL) { device_printf(sc->sc_dev, "Unknown card!\n"); return (1); } ramcfg = READ_REG_0(sc, HIFN_0_PUCNFG); dmacfg = READ_REG_1(sc, HIFN_1_DMA_CNFG); /* * The RAM config register's encrypt level bit needs to be set before * every read performed on the encryption level register. */ WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID); encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA; /* * Make sure we don't re-unlock. Two unlocks kills chip until the * next reboot. */ if (encl == HIFN_PUSTAT_ENA_1 || encl == HIFN_PUSTAT_ENA_2) { #ifdef HIFN_DEBUG if (hifn_debug) device_printf(sc->sc_dev, "Strong crypto already enabled!\n"); #endif goto report; } if (encl != 0 && encl != HIFN_PUSTAT_ENA_0) { #ifdef HIFN_DEBUG if (hifn_debug) device_printf(sc->sc_dev, "Unknown encryption level 0x%x\n", encl); #endif return 1; } WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_UNLOCK | HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE); DELAY(1000); addr = READ_REG_1(sc, HIFN_UNLOCK_SECRET1); DELAY(1000); WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, 0); DELAY(1000); for (i = 0; i <= 12; i++) { addr = hifn_next_signature(addr, offtbl[i] + 0x101); WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, addr); DELAY(1000); } WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID); encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA; #ifdef HIFN_DEBUG if (hifn_debug) { if (encl != HIFN_PUSTAT_ENA_1 && encl != HIFN_PUSTAT_ENA_2) device_printf(sc->sc_dev, "Engine is permanently " "locked until next system reset!\n"); else device_printf(sc->sc_dev, "Engine enabled " "successfully!\n"); } #endif report: WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg); WRITE_REG_1(sc, HIFN_1_DMA_CNFG, dmacfg); switch (encl) { case HIFN_PUSTAT_ENA_1: case HIFN_PUSTAT_ENA_2: break; case HIFN_PUSTAT_ENA_0: default: device_printf(sc->sc_dev, "disabled"); break; } return 0; } /* * Give initial values to the registers listed in the "Register Space" * section of the HIFN Software Development reference manual. */ static void hifn_init_pci_registers(struct hifn_softc *sc) { /* write fixed values needed by the Initialization registers */ WRITE_REG_0(sc, HIFN_0_PUCTRL, HIFN_PUCTRL_DMAENA); WRITE_REG_0(sc, HIFN_0_FIFOCNFG, HIFN_FIFOCNFG_THRESHOLD); WRITE_REG_0(sc, HIFN_0_PUIER, HIFN_PUIER_DSTOVER); /* write all 4 ring address registers */ WRITE_REG_1(sc, HIFN_1_DMA_CRAR, sc->sc_dma_physaddr + offsetof(struct hifn_dma, cmdr[0])); WRITE_REG_1(sc, HIFN_1_DMA_SRAR, sc->sc_dma_physaddr + offsetof(struct hifn_dma, srcr[0])); WRITE_REG_1(sc, HIFN_1_DMA_DRAR, sc->sc_dma_physaddr + offsetof(struct hifn_dma, dstr[0])); WRITE_REG_1(sc, HIFN_1_DMA_RRAR, sc->sc_dma_physaddr + offsetof(struct hifn_dma, resr[0])); DELAY(2000); /* write status register */ WRITE_REG_1(sc, HIFN_1_DMA_CSR, HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_D_ABORT | HIFN_DMACSR_D_DONE | HIFN_DMACSR_D_LAST | HIFN_DMACSR_D_WAIT | HIFN_DMACSR_D_OVER | HIFN_DMACSR_R_ABORT | HIFN_DMACSR_R_DONE | HIFN_DMACSR_R_LAST | HIFN_DMACSR_R_WAIT | HIFN_DMACSR_R_OVER | HIFN_DMACSR_S_ABORT | HIFN_DMACSR_S_DONE | HIFN_DMACSR_S_LAST | HIFN_DMACSR_S_WAIT | HIFN_DMACSR_C_ABORT | HIFN_DMACSR_C_DONE | HIFN_DMACSR_C_LAST | HIFN_DMACSR_C_WAIT | HIFN_DMACSR_ENGINE | ((sc->sc_flags & HIFN_HAS_PUBLIC) ? HIFN_DMACSR_PUBDONE : 0) | ((sc->sc_flags & HIFN_IS_7811) ? HIFN_DMACSR_ILLW | HIFN_DMACSR_ILLR : 0)); sc->sc_d_busy = sc->sc_r_busy = sc->sc_s_busy = sc->sc_c_busy = 0; sc->sc_dmaier |= HIFN_DMAIER_R_DONE | HIFN_DMAIER_C_ABORT | HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER | HIFN_DMAIER_S_ABORT | HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT | ((sc->sc_flags & HIFN_IS_7811) ? HIFN_DMAIER_ILLW | HIFN_DMAIER_ILLR : 0); sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT; WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier); if (sc->sc_flags & HIFN_IS_7956) { u_int32_t pll; WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING | HIFN_PUCNFG_TCALLPHASES | HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32); /* turn off the clocks and insure bypass is set */ pll = READ_REG_1(sc, HIFN_1_PLL); pll = (pll &~ (HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL)) | HIFN_PLL_BP | HIFN_PLL_MBSET; WRITE_REG_1(sc, HIFN_1_PLL, pll); DELAY(10*1000); /* 10ms */ /* change configuration */ pll = (pll &~ HIFN_PLL_CONFIG) | sc->sc_pllconfig; WRITE_REG_1(sc, HIFN_1_PLL, pll); DELAY(10*1000); /* 10ms */ /* disable bypass */ pll &= ~HIFN_PLL_BP; WRITE_REG_1(sc, HIFN_1_PLL, pll); /* enable clocks with new configuration */ pll |= HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL; WRITE_REG_1(sc, HIFN_1_PLL, pll); } else { WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING | HIFN_PUCNFG_DRFR_128 | HIFN_PUCNFG_TCALLPHASES | HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32 | (sc->sc_drammodel ? HIFN_PUCNFG_DRAM : HIFN_PUCNFG_SRAM)); } WRITE_REG_0(sc, HIFN_0_PUISR, HIFN_PUISR_DSTOVER); WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE | HIFN_DMACNFG_LAST | ((HIFN_POLL_FREQUENCY << 16 ) & HIFN_DMACNFG_POLLFREQ) | ((HIFN_POLL_SCALAR << 8) & HIFN_DMACNFG_POLLINVAL)); } /* * The maximum number of sessions supported by the card * is dependent on the amount of context ram, which * encryption algorithms are enabled, and how compression * is configured. This should be configured before this * routine is called. */ static void hifn_sessions(struct hifn_softc *sc) { u_int32_t pucnfg; int ctxsize; pucnfg = READ_REG_0(sc, HIFN_0_PUCNFG); if (pucnfg & HIFN_PUCNFG_COMPSING) { if (pucnfg & HIFN_PUCNFG_ENCCNFG) ctxsize = 128; else ctxsize = 512; /* * 7955/7956 has internal context memory of 32K */ if (sc->sc_flags & HIFN_IS_7956) sc->sc_maxses = 32768 / ctxsize; else sc->sc_maxses = 1 + ((sc->sc_ramsize - 32768) / ctxsize); } else sc->sc_maxses = sc->sc_ramsize / 16384; if (sc->sc_maxses > 2048) sc->sc_maxses = 2048; } /* * Determine ram type (sram or dram). Board should be just out of a reset * state when this is called. */ static int hifn_ramtype(struct hifn_softc *sc) { u_int8_t data[8], dataexpect[8]; int i; for (i = 0; i < sizeof(data); i++) data[i] = dataexpect[i] = 0x55; if (hifn_writeramaddr(sc, 0, data)) return (-1); if (hifn_readramaddr(sc, 0, data)) return (-1); if (bcmp(data, dataexpect, sizeof(data)) != 0) { sc->sc_drammodel = 1; return (0); } for (i = 0; i < sizeof(data); i++) data[i] = dataexpect[i] = 0xaa; if (hifn_writeramaddr(sc, 0, data)) return (-1); if (hifn_readramaddr(sc, 0, data)) return (-1); if (bcmp(data, dataexpect, sizeof(data)) != 0) { sc->sc_drammodel = 1; return (0); } return (0); } #define HIFN_SRAM_MAX (32 << 20) #define HIFN_SRAM_STEP_SIZE 16384 #define HIFN_SRAM_GRANULARITY (HIFN_SRAM_MAX / HIFN_SRAM_STEP_SIZE) static int hifn_sramsize(struct hifn_softc *sc) { u_int32_t a; u_int8_t data[8]; u_int8_t dataexpect[sizeof(data)]; int32_t i; for (i = 0; i < sizeof(data); i++) data[i] = dataexpect[i] = i ^ 0x5a; for (i = HIFN_SRAM_GRANULARITY - 1; i >= 0; i--) { a = i * HIFN_SRAM_STEP_SIZE; bcopy(&i, data, sizeof(i)); hifn_writeramaddr(sc, a, data); } for (i = 0; i < HIFN_SRAM_GRANULARITY; i++) { a = i * HIFN_SRAM_STEP_SIZE; bcopy(&i, dataexpect, sizeof(i)); if (hifn_readramaddr(sc, a, data) < 0) return (0); if (bcmp(data, dataexpect, sizeof(data)) != 0) return (0); sc->sc_ramsize = a + HIFN_SRAM_STEP_SIZE; } return (0); } /* * XXX For dram boards, one should really try all of the * HIFN_PUCNFG_DSZ_*'s. This just assumes that PUCNFG * is already set up correctly. */ static int hifn_dramsize(struct hifn_softc *sc) { u_int32_t cnfg; if (sc->sc_flags & HIFN_IS_7956) { /* * 7955/7956 have a fixed internal ram of only 32K. */ sc->sc_ramsize = 32768; } else { cnfg = READ_REG_0(sc, HIFN_0_PUCNFG) & HIFN_PUCNFG_DRAMMASK; sc->sc_ramsize = 1 << ((cnfg >> 13) + 18); } return (0); } static void hifn_alloc_slot(struct hifn_softc *sc, int *cmdp, int *srcp, int *dstp, int *resp) { struct hifn_dma *dma = sc->sc_dma; if (sc->sc_cmdi == HIFN_D_CMD_RSIZE) { sc->sc_cmdi = 0; dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } *cmdp = sc->sc_cmdi++; sc->sc_cmdk = sc->sc_cmdi; if (sc->sc_srci == HIFN_D_SRC_RSIZE) { sc->sc_srci = 0; dma->srcr[HIFN_D_SRC_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } *srcp = sc->sc_srci++; sc->sc_srck = sc->sc_srci; if (sc->sc_dsti == HIFN_D_DST_RSIZE) { sc->sc_dsti = 0; dma->dstr[HIFN_D_DST_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_DSTR_SYNC(sc, HIFN_D_DST_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } *dstp = sc->sc_dsti++; sc->sc_dstk = sc->sc_dsti; if (sc->sc_resi == HIFN_D_RES_RSIZE) { sc->sc_resi = 0; dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } *resp = sc->sc_resi++; sc->sc_resk = sc->sc_resi; } static int hifn_writeramaddr(struct hifn_softc *sc, int addr, u_int8_t *data) { struct hifn_dma *dma = sc->sc_dma; hifn_base_command_t wc; const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ; int r, cmdi, resi, srci, dsti; wc.masks = htole16(3 << 13); wc.session_num = htole16(addr >> 14); wc.total_source_count = htole16(8); wc.total_dest_count = htole16(addr & 0x3fff); hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi); WRITE_REG_1(sc, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA | HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA); /* build write command */ bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND); *(hifn_base_command_t *)dma->command_bufs[cmdi] = wc; bcopy(data, &dma->test_src, sizeof(dma->test_src)); dma->srcr[srci].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, test_src)); dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, test_dst)); dma->cmdr[cmdi].l = htole32(16 | masks); dma->srcr[srci].l = htole32(8 | masks); dma->dstr[dsti].l = htole32(4 | masks); dma->resr[resi].l = htole32(4 | masks); bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); for (r = 10000; r >= 0; r--) { DELAY(10); bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0) break; bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } if (r == 0) { device_printf(sc->sc_dev, "writeramaddr -- " "result[%d](addr %d) still valid\n", resi, addr); r = -1; return (-1); } else r = 0; WRITE_REG_1(sc, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS); return (r); } static int hifn_readramaddr(struct hifn_softc *sc, int addr, u_int8_t *data) { struct hifn_dma *dma = sc->sc_dma; hifn_base_command_t rc; const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ; int r, cmdi, srci, dsti, resi; rc.masks = htole16(2 << 13); rc.session_num = htole16(addr >> 14); rc.total_source_count = htole16(addr & 0x3fff); rc.total_dest_count = htole16(8); hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi); WRITE_REG_1(sc, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA | HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA); bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND); *(hifn_base_command_t *)dma->command_bufs[cmdi] = rc; dma->srcr[srci].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, test_src)); dma->test_src = 0; dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, test_dst)); dma->test_dst = 0; dma->cmdr[cmdi].l = htole32(8 | masks); dma->srcr[srci].l = htole32(8 | masks); dma->dstr[dsti].l = htole32(8 | masks); dma->resr[resi].l = htole32(HIFN_MAX_RESULT | masks); bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); for (r = 10000; r >= 0; r--) { DELAY(10); bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0) break; bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } if (r == 0) { device_printf(sc->sc_dev, "readramaddr -- " "result[%d](addr %d) still valid\n", resi, addr); r = -1; } else { r = 0; bcopy(&dma->test_dst, data, sizeof(dma->test_dst)); } WRITE_REG_1(sc, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS); return (r); } /* * Initialize the descriptor rings. */ static void hifn_init_dma(struct hifn_softc *sc) { struct hifn_dma *dma = sc->sc_dma; int i; hifn_set_retry(sc); /* initialize static pointer values */ for (i = 0; i < HIFN_D_CMD_RSIZE; i++) dma->cmdr[i].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, command_bufs[i][0])); for (i = 0; i < HIFN_D_RES_RSIZE; i++) dma->resr[i].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, result_bufs[i][0])); dma->cmdr[HIFN_D_CMD_RSIZE].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, cmdr[0])); dma->srcr[HIFN_D_SRC_RSIZE].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, srcr[0])); dma->dstr[HIFN_D_DST_RSIZE].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, dstr[0])); dma->resr[HIFN_D_RES_RSIZE].p = htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, resr[0])); sc->sc_cmdu = sc->sc_srcu = sc->sc_dstu = sc->sc_resu = 0; sc->sc_cmdi = sc->sc_srci = sc->sc_dsti = sc->sc_resi = 0; sc->sc_cmdk = sc->sc_srck = sc->sc_dstk = sc->sc_resk = 0; } /* * Writes out the raw command buffer space. Returns the * command buffer size. */ static u_int hifn_write_command(struct hifn_command *cmd, u_int8_t *buf) { u_int8_t *buf_pos; hifn_base_command_t *base_cmd; hifn_mac_command_t *mac_cmd; hifn_crypt_command_t *cry_cmd; int using_mac, using_crypt, len, ivlen; u_int32_t dlen, slen; buf_pos = buf; using_mac = cmd->base_masks & HIFN_BASE_CMD_MAC; using_crypt = cmd->base_masks & HIFN_BASE_CMD_CRYPT; base_cmd = (hifn_base_command_t *)buf_pos; base_cmd->masks = htole16(cmd->base_masks); slen = cmd->src_mapsize; if (cmd->sloplen) dlen = cmd->dst_mapsize - cmd->sloplen + sizeof(u_int32_t); else dlen = cmd->dst_mapsize; base_cmd->total_source_count = htole16(slen & HIFN_BASE_CMD_LENMASK_LO); base_cmd->total_dest_count = htole16(dlen & HIFN_BASE_CMD_LENMASK_LO); dlen >>= 16; slen >>= 16; base_cmd->session_num = htole16( ((slen << HIFN_BASE_CMD_SRCLEN_S) & HIFN_BASE_CMD_SRCLEN_M) | ((dlen << HIFN_BASE_CMD_DSTLEN_S) & HIFN_BASE_CMD_DSTLEN_M)); buf_pos += sizeof(hifn_base_command_t); if (using_mac) { mac_cmd = (hifn_mac_command_t *)buf_pos; dlen = cmd->maccrd->crd_len; mac_cmd->source_count = htole16(dlen & 0xffff); dlen >>= 16; mac_cmd->masks = htole16(cmd->mac_masks | ((dlen << HIFN_MAC_CMD_SRCLEN_S) & HIFN_MAC_CMD_SRCLEN_M)); mac_cmd->header_skip = htole16(cmd->maccrd->crd_skip); mac_cmd->reserved = 0; buf_pos += sizeof(hifn_mac_command_t); } if (using_crypt) { cry_cmd = (hifn_crypt_command_t *)buf_pos; dlen = cmd->enccrd->crd_len; cry_cmd->source_count = htole16(dlen & 0xffff); dlen >>= 16; cry_cmd->masks = htole16(cmd->cry_masks | ((dlen << HIFN_CRYPT_CMD_SRCLEN_S) & HIFN_CRYPT_CMD_SRCLEN_M)); cry_cmd->header_skip = htole16(cmd->enccrd->crd_skip); cry_cmd->reserved = 0; buf_pos += sizeof(hifn_crypt_command_t); } if (using_mac && cmd->mac_masks & HIFN_MAC_CMD_NEW_KEY) { bcopy(cmd->mac, buf_pos, HIFN_MAC_KEY_LENGTH); buf_pos += HIFN_MAC_KEY_LENGTH; } if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_KEY) { switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) { case HIFN_CRYPT_CMD_ALG_3DES: bcopy(cmd->ck, buf_pos, HIFN_3DES_KEY_LENGTH); buf_pos += HIFN_3DES_KEY_LENGTH; break; case HIFN_CRYPT_CMD_ALG_DES: bcopy(cmd->ck, buf_pos, HIFN_DES_KEY_LENGTH); buf_pos += HIFN_DES_KEY_LENGTH; break; case HIFN_CRYPT_CMD_ALG_RC4: len = 256; do { int clen; clen = MIN(cmd->cklen, len); bcopy(cmd->ck, buf_pos, clen); len -= clen; buf_pos += clen; } while (len > 0); bzero(buf_pos, 4); buf_pos += 4; break; case HIFN_CRYPT_CMD_ALG_AES: /* * AES keys are variable 128, 192 and * 256 bits (16, 24 and 32 bytes). */ bcopy(cmd->ck, buf_pos, cmd->cklen); buf_pos += cmd->cklen; break; } } if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_IV) { switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) { case HIFN_CRYPT_CMD_ALG_AES: ivlen = HIFN_AES_IV_LENGTH; break; default: ivlen = HIFN_IV_LENGTH; break; } bcopy(cmd->iv, buf_pos, ivlen); buf_pos += ivlen; } if ((cmd->base_masks & (HIFN_BASE_CMD_MAC|HIFN_BASE_CMD_CRYPT)) == 0) { bzero(buf_pos, 8); buf_pos += 8; } return (buf_pos - buf); } static int hifn_dmamap_aligned(struct hifn_operand *op) { int i; for (i = 0; i < op->nsegs; i++) { if (op->segs[i].ds_addr & 3) return (0); if ((i != (op->nsegs - 1)) && (op->segs[i].ds_len & 3)) return (0); } return (1); } static __inline int hifn_dmamap_dstwrap(struct hifn_softc *sc, int idx) { struct hifn_dma *dma = sc->sc_dma; if (++idx == HIFN_D_DST_RSIZE) { dma->dstr[idx].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); idx = 0; } return (idx); } static int hifn_dmamap_load_dst(struct hifn_softc *sc, struct hifn_command *cmd) { struct hifn_dma *dma = sc->sc_dma; struct hifn_operand *dst = &cmd->dst; u_int32_t p, l; int idx, used = 0, i; idx = sc->sc_dsti; for (i = 0; i < dst->nsegs - 1; i++) { dma->dstr[idx].p = htole32(dst->segs[i].ds_addr); dma->dstr[idx].l = htole32(HIFN_D_VALID | HIFN_D_MASKDONEIRQ | dst->segs[i].ds_len); HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); used++; idx = hifn_dmamap_dstwrap(sc, idx); } if (cmd->sloplen == 0) { p = dst->segs[i].ds_addr; l = HIFN_D_VALID | HIFN_D_MASKDONEIRQ | HIFN_D_LAST | dst->segs[i].ds_len; } else { p = sc->sc_dma_physaddr + offsetof(struct hifn_dma, slop[cmd->slopidx]); l = HIFN_D_VALID | HIFN_D_MASKDONEIRQ | HIFN_D_LAST | sizeof(u_int32_t); if ((dst->segs[i].ds_len - cmd->sloplen) != 0) { dma->dstr[idx].p = htole32(dst->segs[i].ds_addr); dma->dstr[idx].l = htole32(HIFN_D_VALID | HIFN_D_MASKDONEIRQ | (dst->segs[i].ds_len - cmd->sloplen)); HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); used++; idx = hifn_dmamap_dstwrap(sc, idx); } } dma->dstr[idx].p = htole32(p); dma->dstr[idx].l = htole32(l); HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); used++; idx = hifn_dmamap_dstwrap(sc, idx); sc->sc_dsti = idx; sc->sc_dstu += used; return (idx); } static __inline int hifn_dmamap_srcwrap(struct hifn_softc *sc, int idx) { struct hifn_dma *dma = sc->sc_dma; if (++idx == HIFN_D_SRC_RSIZE) { dma->srcr[idx].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); idx = 0; } return (idx); } static int hifn_dmamap_load_src(struct hifn_softc *sc, struct hifn_command *cmd) { struct hifn_dma *dma = sc->sc_dma; struct hifn_operand *src = &cmd->src; int idx, i; u_int32_t last = 0; idx = sc->sc_srci; for (i = 0; i < src->nsegs; i++) { if (i == src->nsegs - 1) last = HIFN_D_LAST; dma->srcr[idx].p = htole32(src->segs[i].ds_addr); dma->srcr[idx].l = htole32(src->segs[i].ds_len | HIFN_D_VALID | HIFN_D_MASKDONEIRQ | last); HIFN_SRCR_SYNC(sc, idx, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); idx = hifn_dmamap_srcwrap(sc, idx); } sc->sc_srci = idx; sc->sc_srcu += src->nsegs; return (idx); } static void hifn_op_cb(void* arg, bus_dma_segment_t *seg, int nsegs, bus_size_t mapsize, int error) { struct hifn_operand *op = arg; KASSERT(nsegs <= MAX_SCATTER, ("hifn_op_cb: too many DMA segments (%u > %u) " "returned when mapping operand", nsegs, MAX_SCATTER)); op->mapsize = mapsize; op->nsegs = nsegs; bcopy(seg, op->segs, nsegs * sizeof (seg[0])); } static int hifn_crypto( struct hifn_softc *sc, struct hifn_command *cmd, struct cryptop *crp, int hint) { struct hifn_dma *dma = sc->sc_dma; u_int32_t cmdlen, csr; int cmdi, resi, err = 0; /* * need 1 cmd, and 1 res * * NB: check this first since it's easy. */ HIFN_LOCK(sc); if ((sc->sc_cmdu + 1) > HIFN_D_CMD_RSIZE || (sc->sc_resu + 1) > HIFN_D_RES_RSIZE) { #ifdef HIFN_DEBUG if (hifn_debug) { device_printf(sc->sc_dev, "cmd/result exhaustion, cmdu %u resu %u\n", sc->sc_cmdu, sc->sc_resu); } #endif hifnstats.hst_nomem_cr++; HIFN_UNLOCK(sc); return (ERESTART); } if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, &cmd->src_map)) { hifnstats.hst_nomem_map++; HIFN_UNLOCK(sc); return (ENOMEM); } if (crp->crp_flags & CRYPTO_F_IMBUF) { if (bus_dmamap_load_mbuf(sc->sc_dmat, cmd->src_map, cmd->src_m, hifn_op_cb, &cmd->src, BUS_DMA_NOWAIT)) { hifnstats.hst_nomem_load++; err = ENOMEM; goto err_srcmap1; } } else if (crp->crp_flags & CRYPTO_F_IOV) { if (bus_dmamap_load_uio(sc->sc_dmat, cmd->src_map, cmd->src_io, hifn_op_cb, &cmd->src, BUS_DMA_NOWAIT)) { hifnstats.hst_nomem_load++; err = ENOMEM; goto err_srcmap1; } } else { err = EINVAL; goto err_srcmap1; } if (hifn_dmamap_aligned(&cmd->src)) { cmd->sloplen = cmd->src_mapsize & 3; cmd->dst = cmd->src; } else { if (crp->crp_flags & CRYPTO_F_IOV) { err = EINVAL; goto err_srcmap; } else if (crp->crp_flags & CRYPTO_F_IMBUF) { int totlen, len; struct mbuf *m, *m0, *mlast; KASSERT(cmd->dst_m == cmd->src_m, ("hifn_crypto: dst_m initialized improperly")); hifnstats.hst_unaligned++; /* * Source is not aligned on a longword boundary. * Copy the data to insure alignment. If we fail * to allocate mbufs or clusters while doing this * we return ERESTART so the operation is requeued * at the crypto later, but only if there are * ops already posted to the hardware; otherwise we * have no guarantee that we'll be re-entered. */ totlen = cmd->src_mapsize; if (cmd->src_m->m_flags & M_PKTHDR) { len = MHLEN; MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 && !m_dup_pkthdr(m0, cmd->src_m, M_DONTWAIT)) { m_free(m0); m0 = NULL; } } else { len = MLEN; MGET(m0, M_DONTWAIT, MT_DATA); } if (m0 == NULL) { hifnstats.hst_nomem_mbuf++; err = sc->sc_cmdu ? ERESTART : ENOMEM; goto err_srcmap; } if (totlen >= MINCLSIZE) { MCLGET(m0, M_DONTWAIT); if ((m0->m_flags & M_EXT) == 0) { hifnstats.hst_nomem_mcl++; err = sc->sc_cmdu ? ERESTART : ENOMEM; m_freem(m0); goto err_srcmap; } len = MCLBYTES; } totlen -= len; m0->m_pkthdr.len = m0->m_len = len; mlast = m0; while (totlen > 0) { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) { hifnstats.hst_nomem_mbuf++; err = sc->sc_cmdu ? ERESTART : ENOMEM; m_freem(m0); goto err_srcmap; } len = MLEN; if (totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { hifnstats.hst_nomem_mcl++; err = sc->sc_cmdu ? ERESTART : ENOMEM; mlast->m_next = m; m_freem(m0); goto err_srcmap; } len = MCLBYTES; } m->m_len = len; m0->m_pkthdr.len += len; totlen -= len; mlast->m_next = m; mlast = m; } cmd->dst_m = m0; } } if (cmd->dst_map == NULL) { if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, &cmd->dst_map)) { hifnstats.hst_nomem_map++; err = ENOMEM; goto err_srcmap; } if (crp->crp_flags & CRYPTO_F_IMBUF) { if (bus_dmamap_load_mbuf(sc->sc_dmat, cmd->dst_map, cmd->dst_m, hifn_op_cb, &cmd->dst, BUS_DMA_NOWAIT)) { hifnstats.hst_nomem_map++; err = ENOMEM; goto err_dstmap1; } } else if (crp->crp_flags & CRYPTO_F_IOV) { if (bus_dmamap_load_uio(sc->sc_dmat, cmd->dst_map, cmd->dst_io, hifn_op_cb, &cmd->dst, BUS_DMA_NOWAIT)) { hifnstats.hst_nomem_load++; err = ENOMEM; goto err_dstmap1; } } } #ifdef HIFN_DEBUG if (hifn_debug) { device_printf(sc->sc_dev, "Entering cmd: stat %8x ien %8x u %d/%d/%d/%d n %d/%d\n", READ_REG_1(sc, HIFN_1_DMA_CSR), READ_REG_1(sc, HIFN_1_DMA_IER), sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu, cmd->src_nsegs, cmd->dst_nsegs); } #endif if (cmd->src_map == cmd->dst_map) { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); } else { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->sc_dmat, cmd->dst_map, BUS_DMASYNC_PREREAD); } /* * need N src, and N dst */ if ((sc->sc_srcu + cmd->src_nsegs) > HIFN_D_SRC_RSIZE || (sc->sc_dstu + cmd->dst_nsegs + 1) > HIFN_D_DST_RSIZE) { #ifdef HIFN_DEBUG if (hifn_debug) { device_printf(sc->sc_dev, "src/dst exhaustion, srcu %u+%u dstu %u+%u\n", sc->sc_srcu, cmd->src_nsegs, sc->sc_dstu, cmd->dst_nsegs); } #endif hifnstats.hst_nomem_sd++; err = ERESTART; goto err_dstmap; } if (sc->sc_cmdi == HIFN_D_CMD_RSIZE) { sc->sc_cmdi = 0; dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } cmdi = sc->sc_cmdi++; cmdlen = hifn_write_command(cmd, dma->command_bufs[cmdi]); HIFN_CMD_SYNC(sc, cmdi, BUS_DMASYNC_PREWRITE); /* .p for command/result already set */ dma->cmdr[cmdi].l = htole32(cmdlen | HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ); HIFN_CMDR_SYNC(sc, cmdi, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); sc->sc_cmdu++; /* * We don't worry about missing an interrupt (which a "command wait" * interrupt salvages us from), unless there is more than one command * in the queue. */ if (sc->sc_cmdu > 1) { sc->sc_dmaier |= HIFN_DMAIER_C_WAIT; WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier); } hifnstats.hst_ipackets++; hifnstats.hst_ibytes += cmd->src_mapsize; hifn_dmamap_load_src(sc, cmd); /* * Unlike other descriptors, we don't mask done interrupt from * result descriptor. */ #ifdef HIFN_DEBUG if (hifn_debug) printf("load res\n"); #endif if (sc->sc_resi == HIFN_D_RES_RSIZE) { sc->sc_resi = 0; dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_VALID | HIFN_D_JUMP | HIFN_D_MASKDONEIRQ); HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } resi = sc->sc_resi++; KASSERT(sc->sc_hifn_commands[resi] == NULL, ("hifn_crypto: command slot %u busy", resi)); sc->sc_hifn_commands[resi] = cmd; HIFN_RES_SYNC(sc, resi, BUS_DMASYNC_PREREAD); if ((hint & CRYPTO_HINT_MORE) && sc->sc_curbatch < hifn_maxbatch) { dma->resr[resi].l = htole32(HIFN_MAX_RESULT | HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ); sc->sc_curbatch++; if (sc->sc_curbatch > hifnstats.hst_maxbatch) hifnstats.hst_maxbatch = sc->sc_curbatch; hifnstats.hst_totbatch++; } else { dma->resr[resi].l = htole32(HIFN_MAX_RESULT | HIFN_D_VALID | HIFN_D_LAST); sc->sc_curbatch = 0; } HIFN_RESR_SYNC(sc, resi, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->sc_resu++; if (cmd->sloplen) cmd->slopidx = resi; hifn_dmamap_load_dst(sc, cmd); csr = 0; if (sc->sc_c_busy == 0) { csr |= HIFN_DMACSR_C_CTRL_ENA; sc->sc_c_busy = 1; } if (sc->sc_s_busy == 0) { csr |= HIFN_DMACSR_S_CTRL_ENA; sc->sc_s_busy = 1; } if (sc->sc_r_busy == 0) { csr |= HIFN_DMACSR_R_CTRL_ENA; sc->sc_r_busy = 1; } if (sc->sc_d_busy == 0) { csr |= HIFN_DMACSR_D_CTRL_ENA; sc->sc_d_busy = 1; } if (csr) WRITE_REG_1(sc, HIFN_1_DMA_CSR, csr); #ifdef HIFN_DEBUG if (hifn_debug) { device_printf(sc->sc_dev, "command: stat %8x ier %8x\n", READ_REG_1(sc, HIFN_1_DMA_CSR), READ_REG_1(sc, HIFN_1_DMA_IER)); } #endif sc->sc_active = 5; HIFN_UNLOCK(sc); KASSERT(err == 0, ("hifn_crypto: success with error %u", err)); return (err); /* success */ err_dstmap: if (cmd->src_map != cmd->dst_map) bus_dmamap_unload(sc->sc_dmat, cmd->dst_map); err_dstmap1: if (cmd->src_map != cmd->dst_map) bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map); err_srcmap: if (crp->crp_flags & CRYPTO_F_IMBUF) { if (cmd->src_m != cmd->dst_m) m_freem(cmd->dst_m); } bus_dmamap_unload(sc->sc_dmat, cmd->src_map); err_srcmap1: bus_dmamap_destroy(sc->sc_dmat, cmd->src_map); HIFN_UNLOCK(sc); return (err); } static void hifn_tick(void* vsc) { struct hifn_softc *sc = vsc; HIFN_LOCK(sc); if (sc->sc_active == 0) { u_int32_t r = 0; if (sc->sc_cmdu == 0 && sc->sc_c_busy) { sc->sc_c_busy = 0; r |= HIFN_DMACSR_C_CTRL_DIS; } if (sc->sc_srcu == 0 && sc->sc_s_busy) { sc->sc_s_busy = 0; r |= HIFN_DMACSR_S_CTRL_DIS; } if (sc->sc_dstu == 0 && sc->sc_d_busy) { sc->sc_d_busy = 0; r |= HIFN_DMACSR_D_CTRL_DIS; } if (sc->sc_resu == 0 && sc->sc_r_busy) { sc->sc_r_busy = 0; r |= HIFN_DMACSR_R_CTRL_DIS; } if (r) WRITE_REG_1(sc, HIFN_1_DMA_CSR, r); } else sc->sc_active--; HIFN_UNLOCK(sc); callout_reset(&sc->sc_tickto, hz, hifn_tick, sc); } static void hifn_intr(void *arg) { struct hifn_softc *sc = arg; struct hifn_dma *dma; u_int32_t dmacsr, restart; int i, u; dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR); /* Nothing in the DMA unit interrupted */ if ((dmacsr & sc->sc_dmaier) == 0) return; HIFN_LOCK(sc); dma = sc->sc_dma; #ifdef HIFN_DEBUG if (hifn_debug) { device_printf(sc->sc_dev, "irq: stat %08x ien %08x damier %08x i %d/%d/%d/%d k %d/%d/%d/%d u %d/%d/%d/%d\n", dmacsr, READ_REG_1(sc, HIFN_1_DMA_IER), sc->sc_dmaier, sc->sc_cmdi, sc->sc_srci, sc->sc_dsti, sc->sc_resi, sc->sc_cmdk, sc->sc_srck, sc->sc_dstk, sc->sc_resk, sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu); } #endif WRITE_REG_1(sc, HIFN_1_DMA_CSR, dmacsr & sc->sc_dmaier); if ((sc->sc_flags & HIFN_HAS_PUBLIC) && (dmacsr & HIFN_DMACSR_PUBDONE)) WRITE_REG_1(sc, HIFN_1_PUB_STATUS, READ_REG_1(sc, HIFN_1_PUB_STATUS) | HIFN_PUBSTS_DONE); restart = dmacsr & (HIFN_DMACSR_D_OVER | HIFN_DMACSR_R_OVER); if (restart) device_printf(sc->sc_dev, "overrun %x\n", dmacsr); if (sc->sc_flags & HIFN_IS_7811) { if (dmacsr & HIFN_DMACSR_ILLR) device_printf(sc->sc_dev, "illegal read\n"); if (dmacsr & HIFN_DMACSR_ILLW) device_printf(sc->sc_dev, "illegal write\n"); } restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT | HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT); if (restart) { device_printf(sc->sc_dev, "abort, resetting.\n"); hifnstats.hst_abort++; hifn_abort(sc); HIFN_UNLOCK(sc); return; } if ((dmacsr & HIFN_DMACSR_C_WAIT) && (sc->sc_cmdu == 0)) { /* * If no slots to process and we receive a "waiting on * command" interrupt, we disable the "waiting on command" * (by clearing it). */ sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT; WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier); } /* clear the rings */ i = sc->sc_resk; u = sc->sc_resu; while (u != 0) { HIFN_RESR_SYNC(sc, i, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if (dma->resr[i].l & htole32(HIFN_D_VALID)) { HIFN_RESR_SYNC(sc, i, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); break; } if (i != HIFN_D_RES_RSIZE) { struct hifn_command *cmd; u_int8_t *macbuf = NULL; HIFN_RES_SYNC(sc, i, BUS_DMASYNC_POSTREAD); cmd = sc->sc_hifn_commands[i]; KASSERT(cmd != NULL, ("hifn_intr: null command slot %u", i)); sc->sc_hifn_commands[i] = NULL; if (cmd->base_masks & HIFN_BASE_CMD_MAC) { macbuf = dma->result_bufs[i]; macbuf += 12; } hifn_callback(sc, cmd, macbuf); hifnstats.hst_opackets++; u--; } if (++i == (HIFN_D_RES_RSIZE + 1)) i = 0; } sc->sc_resk = i; sc->sc_resu = u; i = sc->sc_srck; u = sc->sc_srcu; while (u != 0) { if (i == HIFN_D_SRC_RSIZE) i = 0; HIFN_SRCR_SYNC(sc, i, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if (dma->srcr[i].l & htole32(HIFN_D_VALID)) { HIFN_SRCR_SYNC(sc, i, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); break; } i++, u--; } sc->sc_srck = i; sc->sc_srcu = u; i = sc->sc_cmdk; u = sc->sc_cmdu; while (u != 0) { HIFN_CMDR_SYNC(sc, i, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if (dma->cmdr[i].l & htole32(HIFN_D_VALID)) { HIFN_CMDR_SYNC(sc, i, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); break; } if (i != HIFN_D_CMD_RSIZE) { u--; HIFN_CMD_SYNC(sc, i, BUS_DMASYNC_POSTWRITE); } if (++i == (HIFN_D_CMD_RSIZE + 1)) i = 0; } sc->sc_cmdk = i; sc->sc_cmdu = u; HIFN_UNLOCK(sc); if (sc->sc_needwakeup) { /* XXX check high watermark */ int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ); #ifdef HIFN_DEBUG if (hifn_debug) device_printf(sc->sc_dev, "wakeup crypto (%x) u %d/%d/%d/%d\n", sc->sc_needwakeup, sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu); #endif sc->sc_needwakeup &= ~wakeup; crypto_unblock(sc->sc_cid, wakeup); } } /* * Allocate a new 'session' and return an encoded session id. 'sidp' * contains our registration id, and should contain an encoded session * id on successful allocation. */ static int hifn_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri) { struct hifn_softc *sc = device_get_softc(dev); struct cryptoini *c; int mac = 0, cry = 0, sesn; struct hifn_session *ses = NULL; KASSERT(sc != NULL, ("hifn_newsession: null softc")); if (sidp == NULL || cri == NULL || sc == NULL) return (EINVAL); HIFN_LOCK(sc); if (sc->sc_sessions == NULL) { ses = sc->sc_sessions = (struct hifn_session *)malloc( sizeof(*ses), M_DEVBUF, M_NOWAIT); if (ses == NULL) { HIFN_UNLOCK(sc); return (ENOMEM); } sesn = 0; sc->sc_nsessions = 1; } else { for (sesn = 0; sesn < sc->sc_nsessions; sesn++) { if (!sc->sc_sessions[sesn].hs_used) { ses = &sc->sc_sessions[sesn]; break; } } if (ses == NULL) { sesn = sc->sc_nsessions; ses = (struct hifn_session *)malloc((sesn + 1) * sizeof(*ses), M_DEVBUF, M_NOWAIT); if (ses == NULL) { HIFN_UNLOCK(sc); return (ENOMEM); } bcopy(sc->sc_sessions, ses, sesn * sizeof(*ses)); bzero(sc->sc_sessions, sesn * sizeof(*ses)); free(sc->sc_sessions, M_DEVBUF); sc->sc_sessions = ses; ses = &sc->sc_sessions[sesn]; sc->sc_nsessions++; } } HIFN_UNLOCK(sc); bzero(ses, sizeof(*ses)); ses->hs_used = 1; for (c = cri; c != NULL; c = c->cri_next) { switch (c->cri_alg) { case CRYPTO_MD5: case CRYPTO_SHA1: case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: if (mac) return (EINVAL); mac = 1; ses->hs_mlen = c->cri_mlen; if (ses->hs_mlen == 0) { switch (c->cri_alg) { case CRYPTO_MD5: case CRYPTO_MD5_HMAC: ses->hs_mlen = 16; break; case CRYPTO_SHA1: case CRYPTO_SHA1_HMAC: ses->hs_mlen = 20; break; } } break; case CRYPTO_DES_CBC: case CRYPTO_3DES_CBC: case CRYPTO_AES_CBC: /* XXX this may read fewer, does it matter? */ read_random(ses->hs_iv, c->cri_alg == CRYPTO_AES_CBC ? HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH); /*FALLTHROUGH*/ case CRYPTO_ARC4: if (cry) return (EINVAL); cry = 1; break; default: return (EINVAL); } } if (mac == 0 && cry == 0) return (EINVAL); *sidp = HIFN_SID(device_get_unit(sc->sc_dev), sesn); return (0); } /* * Deallocate a session. * XXX this routine should run a zero'd mac/encrypt key into context ram. * XXX to blow away any keys already stored there. */ static int hifn_freesession(device_t dev, u_int64_t tid) { struct hifn_softc *sc = device_get_softc(dev); int session, error; u_int32_t sid = CRYPTO_SESID2LID(tid); KASSERT(sc != NULL, ("hifn_freesession: null softc")); if (sc == NULL) return (EINVAL); HIFN_LOCK(sc); session = HIFN_SESSION(sid); if (session < sc->sc_nsessions) { bzero(&sc->sc_sessions[session], sizeof(struct hifn_session)); error = 0; } else error = EINVAL; HIFN_UNLOCK(sc); return (error); } static int hifn_process(device_t dev, struct cryptop *crp, int hint) { struct hifn_softc *sc = device_get_softc(dev); struct hifn_command *cmd = NULL; int session, err, ivlen; struct cryptodesc *crd1, *crd2, *maccrd, *enccrd; if (crp == NULL || crp->crp_callback == NULL) { hifnstats.hst_invalid++; return (EINVAL); } session = HIFN_SESSION(crp->crp_sid); if (sc == NULL || session >= sc->sc_nsessions) { err = EINVAL; goto errout; } cmd = malloc(sizeof(struct hifn_command), M_DEVBUF, M_NOWAIT | M_ZERO); if (cmd == NULL) { hifnstats.hst_nomem++; err = ENOMEM; goto errout; } if (crp->crp_flags & CRYPTO_F_IMBUF) { cmd->src_m = (struct mbuf *)crp->crp_buf; cmd->dst_m = (struct mbuf *)crp->crp_buf; } else if (crp->crp_flags & CRYPTO_F_IOV) { cmd->src_io = (struct uio *)crp->crp_buf; cmd->dst_io = (struct uio *)crp->crp_buf; } else { err = EINVAL; goto errout; /* XXX we don't handle contiguous buffers! */ } crd1 = crp->crp_desc; if (crd1 == NULL) { err = EINVAL; goto errout; } crd2 = crd1->crd_next; if (crd2 == NULL) { if (crd1->crd_alg == CRYPTO_MD5_HMAC || crd1->crd_alg == CRYPTO_SHA1_HMAC || crd1->crd_alg == CRYPTO_SHA1 || crd1->crd_alg == CRYPTO_MD5) { maccrd = crd1; enccrd = NULL; } else if (crd1->crd_alg == CRYPTO_DES_CBC || crd1->crd_alg == CRYPTO_3DES_CBC || crd1->crd_alg == CRYPTO_AES_CBC || crd1->crd_alg == CRYPTO_ARC4) { if ((crd1->crd_flags & CRD_F_ENCRYPT) == 0) cmd->base_masks |= HIFN_BASE_CMD_DECODE; maccrd = NULL; enccrd = crd1; } else { err = EINVAL; goto errout; } } else { if ((crd1->crd_alg == CRYPTO_MD5_HMAC || crd1->crd_alg == CRYPTO_SHA1_HMAC || crd1->crd_alg == CRYPTO_MD5 || crd1->crd_alg == CRYPTO_SHA1) && (crd2->crd_alg == CRYPTO_DES_CBC || crd2->crd_alg == CRYPTO_3DES_CBC || crd2->crd_alg == CRYPTO_AES_CBC || crd2->crd_alg == CRYPTO_ARC4) && ((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) { cmd->base_masks = HIFN_BASE_CMD_DECODE; maccrd = crd1; enccrd = crd2; } else if ((crd1->crd_alg == CRYPTO_DES_CBC || crd1->crd_alg == CRYPTO_ARC4 || crd1->crd_alg == CRYPTO_3DES_CBC || crd1->crd_alg == CRYPTO_AES_CBC) && (crd2->crd_alg == CRYPTO_MD5_HMAC || crd2->crd_alg == CRYPTO_SHA1_HMAC || crd2->crd_alg == CRYPTO_MD5 || crd2->crd_alg == CRYPTO_SHA1) && (crd1->crd_flags & CRD_F_ENCRYPT)) { enccrd = crd1; maccrd = crd2; } else { /* * We cannot order the 7751 as requested */ err = EINVAL; goto errout; } } if (enccrd) { cmd->enccrd = enccrd; cmd->base_masks |= HIFN_BASE_CMD_CRYPT; switch (enccrd->crd_alg) { case CRYPTO_ARC4: cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_RC4; break; case CRYPTO_DES_CBC: cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_DES | HIFN_CRYPT_CMD_MODE_CBC | HIFN_CRYPT_CMD_NEW_IV; break; case CRYPTO_3DES_CBC: cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_3DES | HIFN_CRYPT_CMD_MODE_CBC | HIFN_CRYPT_CMD_NEW_IV; break; case CRYPTO_AES_CBC: cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_AES | HIFN_CRYPT_CMD_MODE_CBC | HIFN_CRYPT_CMD_NEW_IV; break; default: err = EINVAL; goto errout; } if (enccrd->crd_alg != CRYPTO_ARC4) { ivlen = ((enccrd->crd_alg == CRYPTO_AES_CBC) ? HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH); if (enccrd->crd_flags & CRD_F_ENCRYPT) { if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) bcopy(enccrd->crd_iv, cmd->iv, ivlen); else bcopy(sc->sc_sessions[session].hs_iv, cmd->iv, ivlen); if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) { crypto_copyback(crp->crp_flags, crp->crp_buf, enccrd->crd_inject, ivlen, cmd->iv); } } else { if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) bcopy(enccrd->crd_iv, cmd->iv, ivlen); else { crypto_copydata(crp->crp_flags, crp->crp_buf, enccrd->crd_inject, ivlen, cmd->iv); } } } if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY; cmd->ck = enccrd->crd_key; cmd->cklen = enccrd->crd_klen >> 3; cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY; /* * Need to specify the size for the AES key in the masks. */ if ((cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) == HIFN_CRYPT_CMD_ALG_AES) { switch (cmd->cklen) { case 16: cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_128; break; case 24: cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_192; break; case 32: cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_256; break; default: err = EINVAL; goto errout; } } } if (maccrd) { cmd->maccrd = maccrd; cmd->base_masks |= HIFN_BASE_CMD_MAC; switch (maccrd->crd_alg) { case CRYPTO_MD5: cmd->mac_masks |= HIFN_MAC_CMD_ALG_MD5 | HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HASH | HIFN_MAC_CMD_POS_IPSEC; break; case CRYPTO_MD5_HMAC: cmd->mac_masks |= HIFN_MAC_CMD_ALG_MD5 | HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HMAC | HIFN_MAC_CMD_POS_IPSEC | HIFN_MAC_CMD_TRUNC; break; case CRYPTO_SHA1: cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 | HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HASH | HIFN_MAC_CMD_POS_IPSEC; break; case CRYPTO_SHA1_HMAC: cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 | HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HMAC | HIFN_MAC_CMD_POS_IPSEC | HIFN_MAC_CMD_TRUNC; break; } if (maccrd->crd_alg == CRYPTO_SHA1_HMAC || maccrd->crd_alg == CRYPTO_MD5_HMAC) { cmd->mac_masks |= HIFN_MAC_CMD_NEW_KEY; bcopy(maccrd->crd_key, cmd->mac, maccrd->crd_klen >> 3); bzero(cmd->mac + (maccrd->crd_klen >> 3), HIFN_MAC_KEY_LENGTH - (maccrd->crd_klen >> 3)); } } cmd->crp = crp; cmd->session_num = session; cmd->softc = sc; err = hifn_crypto(sc, cmd, crp, hint); if (!err) { return 0; } else if (err == ERESTART) { /* * There weren't enough resources to dispatch the request * to the part. Notify the caller so they'll requeue this * request and resubmit it again soon. */ #ifdef HIFN_DEBUG if (hifn_debug) device_printf(sc->sc_dev, "requeue request\n"); #endif free(cmd, M_DEVBUF); sc->sc_needwakeup |= CRYPTO_SYMQ; return (err); } errout: if (cmd != NULL) free(cmd, M_DEVBUF); if (err == EINVAL) hifnstats.hst_invalid++; else hifnstats.hst_nomem++; crp->crp_etype = err; crypto_done(crp); return (err); } static void hifn_abort(struct hifn_softc *sc) { struct hifn_dma *dma = sc->sc_dma; struct hifn_command *cmd; struct cryptop *crp; int i, u; i = sc->sc_resk; u = sc->sc_resu; while (u != 0) { cmd = sc->sc_hifn_commands[i]; KASSERT(cmd != NULL, ("hifn_abort: null command slot %u", i)); sc->sc_hifn_commands[i] = NULL; crp = cmd->crp; if ((dma->resr[i].l & htole32(HIFN_D_VALID)) == 0) { /* Salvage what we can. */ u_int8_t *macbuf; if (cmd->base_masks & HIFN_BASE_CMD_MAC) { macbuf = dma->result_bufs[i]; macbuf += 12; } else macbuf = NULL; hifnstats.hst_opackets++; hifn_callback(sc, cmd, macbuf); } else { if (cmd->src_map == cmd->dst_map) { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); } else { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_sync(sc->sc_dmat, cmd->dst_map, BUS_DMASYNC_POSTREAD); } if (cmd->src_m != cmd->dst_m) { m_freem(cmd->src_m); crp->crp_buf = (caddr_t)cmd->dst_m; } /* non-shared buffers cannot be restarted */ if (cmd->src_map != cmd->dst_map) { /* * XXX should be EAGAIN, delayed until * after the reset. */ crp->crp_etype = ENOMEM; bus_dmamap_unload(sc->sc_dmat, cmd->dst_map); bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map); } else crp->crp_etype = ENOMEM; bus_dmamap_unload(sc->sc_dmat, cmd->src_map); bus_dmamap_destroy(sc->sc_dmat, cmd->src_map); free(cmd, M_DEVBUF); if (crp->crp_etype != EAGAIN) crypto_done(crp); } if (++i == HIFN_D_RES_RSIZE) i = 0; u--; } sc->sc_resk = i; sc->sc_resu = u; hifn_reset_board(sc, 1); hifn_init_dma(sc); hifn_init_pci_registers(sc); } static void hifn_callback(struct hifn_softc *sc, struct hifn_command *cmd, u_int8_t *macbuf) { struct hifn_dma *dma = sc->sc_dma; struct cryptop *crp = cmd->crp; struct cryptodesc *crd; struct mbuf *m; int totlen, i, u, ivlen; if (cmd->src_map == cmd->dst_map) { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); } else { bus_dmamap_sync(sc->sc_dmat, cmd->src_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_sync(sc->sc_dmat, cmd->dst_map, BUS_DMASYNC_POSTREAD); } if (crp->crp_flags & CRYPTO_F_IMBUF) { if (cmd->src_m != cmd->dst_m) { crp->crp_buf = (caddr_t)cmd->dst_m; totlen = cmd->src_mapsize; for (m = cmd->dst_m; m != NULL; m = m->m_next) { if (totlen < m->m_len) { m->m_len = totlen; totlen = 0; } else totlen -= m->m_len; } cmd->dst_m->m_pkthdr.len = cmd->src_m->m_pkthdr.len; m_freem(cmd->src_m); } } if (cmd->sloplen != 0) { crypto_copyback(crp->crp_flags, crp->crp_buf, cmd->src_mapsize - cmd->sloplen, cmd->sloplen, (caddr_t)&dma->slop[cmd->slopidx]); } i = sc->sc_dstk; u = sc->sc_dstu; while (u != 0) { if (i == HIFN_D_DST_RSIZE) i = 0; bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if (dma->dstr[i].l & htole32(HIFN_D_VALID)) { bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); break; } i++, u--; } sc->sc_dstk = i; sc->sc_dstu = u; hifnstats.hst_obytes += cmd->dst_mapsize; if ((cmd->base_masks & (HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE)) == HIFN_BASE_CMD_CRYPT) { for (crd = crp->crp_desc; crd; crd = crd->crd_next) { if (crd->crd_alg != CRYPTO_DES_CBC && crd->crd_alg != CRYPTO_3DES_CBC && crd->crd_alg != CRYPTO_AES_CBC) continue; ivlen = ((crd->crd_alg == CRYPTO_AES_CBC) ? HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH); crypto_copydata(crp->crp_flags, crp->crp_buf, crd->crd_skip + crd->crd_len - ivlen, ivlen, cmd->softc->sc_sessions[cmd->session_num].hs_iv); break; } } if (macbuf != NULL) { for (crd = crp->crp_desc; crd; crd = crd->crd_next) { int len; if (crd->crd_alg != CRYPTO_MD5 && crd->crd_alg != CRYPTO_SHA1 && crd->crd_alg != CRYPTO_MD5_HMAC && crd->crd_alg != CRYPTO_SHA1_HMAC) { continue; } len = cmd->softc->sc_sessions[cmd->session_num].hs_mlen; crypto_copyback(crp->crp_flags, crp->crp_buf, crd->crd_inject, len, macbuf); break; } } if (cmd->src_map != cmd->dst_map) { bus_dmamap_unload(sc->sc_dmat, cmd->dst_map); bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map); } bus_dmamap_unload(sc->sc_dmat, cmd->src_map); bus_dmamap_destroy(sc->sc_dmat, cmd->src_map); free(cmd, M_DEVBUF); crypto_done(crp); } /* * 7811 PB3 rev/2 parts lock-up on burst writes to Group 0 * and Group 1 registers; avoid conditions that could create * burst writes by doing a read in between the writes. * * NB: The read we interpose is always to the same register; * we do this because reading from an arbitrary (e.g. last) * register may not always work. */ static void hifn_write_reg_0(struct hifn_softc *sc, bus_size_t reg, u_int32_t val) { if (sc->sc_flags & HIFN_IS_7811) { if (sc->sc_bar0_lastreg == reg - 4) bus_space_read_4(sc->sc_st0, sc->sc_sh0, HIFN_0_PUCNFG); sc->sc_bar0_lastreg = reg; } bus_space_write_4(sc->sc_st0, sc->sc_sh0, reg, val); } static void hifn_write_reg_1(struct hifn_softc *sc, bus_size_t reg, u_int32_t val) { if (sc->sc_flags & HIFN_IS_7811) { if (sc->sc_bar1_lastreg == reg - 4) bus_space_read_4(sc->sc_st1, sc->sc_sh1, HIFN_1_REVID); sc->sc_bar1_lastreg = reg; } bus_space_write_4(sc->sc_st1, sc->sc_sh1, reg, val); } #ifdef HIFN_VULCANDEV /* * this code provides support for mapping the PK engine's register * into a userspace program. * */ static int vulcanpk_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { struct hifn_softc *sc; vm_paddr_t pd; void *b; sc = dev->si_drv1; pd = rman_get_start(sc->sc_bar1res); b = rman_get_virtual(sc->sc_bar1res); #if 0 printf("vpk mmap: %p(%016llx) offset=%lld\n", b, (unsigned long long)pd, offset); hexdump(b, HIFN_1_PUB_MEMEND, "vpk", 0); #endif if (offset == 0) { *paddr = pd; return (0); } return (-1); } static struct cdevsw vulcanpk_cdevsw = { .d_version = D_VERSION, .d_mmap = vulcanpk_mmap, .d_name = "vulcanpk", }; #endif /* HIFN_VULCANDEV */ Index: head/sys/dev/hptiop/hptiop.c =================================================================== --- head/sys/dev/hptiop/hptiop.c (revision 232853) +++ head/sys/dev/hptiop/hptiop.c (revision 232854) @@ -1,2213 +1,2213 @@ /* * HighPoint RR3xxx/4xxx RAID Driver for FreeBSD * Copyright (C) 2007-2008 HighPoint Technologies, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include #include #endif #if (__FreeBSD_version <= 500043) #include #endif #include #include #include #include #include #include #include #include #if (__FreeBSD_version < 500043) #include #endif #include static char driver_name[] = "hptiop"; static char driver_version[] = "v1.3 (010208)"; static devclass_t hptiop_devclass; static int hptiop_send_sync_msg(struct hpt_iop_hba *hba, u_int32_t msg, u_int32_t millisec); static void hptiop_request_callback_itl(struct hpt_iop_hba *hba, u_int32_t req); static void hptiop_request_callback_mv(struct hpt_iop_hba *hba, u_int64_t req); static void hptiop_os_message_callback(struct hpt_iop_hba *hba, u_int32_t msg); static int hptiop_do_ioctl_itl(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams); static int hptiop_do_ioctl_mv(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams); static int hptiop_rescan_bus(struct hpt_iop_hba *hba); static int hptiop_alloc_pci_res_itl(struct hpt_iop_hba *hba); static int hptiop_alloc_pci_res_mv(struct hpt_iop_hba *hba); static int hptiop_get_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); static int hptiop_get_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); static int hptiop_set_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); static int hptiop_set_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); static int hptiop_internal_memalloc_mv(struct hpt_iop_hba *hba); static int hptiop_internal_memfree_mv(struct hpt_iop_hba *hba); static int hptiop_post_ioctl_command_itl(struct hpt_iop_hba *hba, u_int32_t req32, struct hpt_iop_ioctl_param *pParams); static int hptiop_post_ioctl_command_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams); static void hptiop_post_req_itl(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); static void hptiop_post_req_mv(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); static void hptiop_post_msg_itl(struct hpt_iop_hba *hba, u_int32_t msg); static void hptiop_post_msg_mv(struct hpt_iop_hba *hba, u_int32_t msg); static void hptiop_enable_intr_itl(struct hpt_iop_hba *hba); static void hptiop_enable_intr_mv(struct hpt_iop_hba *hba); static void hptiop_disable_intr_itl(struct hpt_iop_hba *hba); static void hptiop_disable_intr_mv(struct hpt_iop_hba *hba); static void hptiop_free_srb(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb); static int hptiop_os_query_remove_device(struct hpt_iop_hba *hba, int tid); static int hptiop_probe(device_t dev); static int hptiop_attach(device_t dev); static int hptiop_detach(device_t dev); static int hptiop_shutdown(device_t dev); static void hptiop_action(struct cam_sim *sim, union ccb *ccb); static void hptiop_poll(struct cam_sim *sim); static void hptiop_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void hptiop_pci_intr(void *arg); static void hptiop_release_resource(struct hpt_iop_hba *hba); static int hptiop_reset_adapter(struct hpt_iop_hba *hba); static d_open_t hptiop_open; static d_close_t hptiop_close; static d_ioctl_t hptiop_ioctl; static struct cdevsw hptiop_cdevsw = { .d_open = hptiop_open, .d_close = hptiop_close, .d_ioctl = hptiop_ioctl, .d_name = driver_name, #if __FreeBSD_version>=503000 .d_version = D_VERSION, #endif #if (__FreeBSD_version>=503000 && __FreeBSD_version<600034) .d_flags = D_NEEDGIANT, #endif #if __FreeBSD_version<600034 #if __FreeBSD_version>=501000 .d_maj = MAJOR_AUTO, #else .d_maj = HPT_DEV_MAJOR, #endif #endif }; #if __FreeBSD_version < 503000 #define hba_from_dev(dev) ((struct hpt_iop_hba *)(dev)->si_drv1) #else #define hba_from_dev(dev) \ ((struct hpt_iop_hba *)devclass_get_softc(hptiop_devclass, dev2unit(dev))) #endif #define BUS_SPACE_WRT4_ITL(offset, value) bus_space_write_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmu_itl, offset), (value)) #define BUS_SPACE_RD4_ITL(offset) bus_space_read_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmu_itl, offset)) #define BUS_SPACE_WRT4_MV0(offset, value) bus_space_write_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmv_regs, offset), value) #define BUS_SPACE_RD4_MV0(offset) bus_space_read_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmv_regs, offset)) #define BUS_SPACE_WRT4_MV2(offset, value) bus_space_write_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mv, offset), value) #define BUS_SPACE_RD4_MV2(offset) bus_space_read_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mv, offset)) static int hptiop_open(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t proc) { struct hpt_iop_hba *hba = hba_from_dev(dev); if (hba==NULL) return ENXIO; if (hba->flag & HPT_IOCTL_FLAG_OPEN) return EBUSY; hba->flag |= HPT_IOCTL_FLAG_OPEN; return 0; } static int hptiop_close(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t proc) { struct hpt_iop_hba *hba = hba_from_dev(dev); hba->flag &= ~(u_int32_t)HPT_IOCTL_FLAG_OPEN; return 0; } static int hptiop_ioctl(ioctl_dev_t dev, u_long cmd, caddr_t data, int flags, ioctl_thread_t proc) { int ret = EFAULT; struct hpt_iop_hba *hba = hba_from_dev(dev); #if (__FreeBSD_version >= 500000) mtx_lock(&Giant); #endif switch (cmd) { case HPT_DO_IOCONTROL: ret = hba->ops->do_ioctl(hba, (struct hpt_iop_ioctl_param *)data); break; case HPT_SCAN_BUS: ret = hptiop_rescan_bus(hba); break; } #if (__FreeBSD_version >= 500000) mtx_unlock(&Giant); #endif return ret; } static u_int64_t hptiop_mv_outbound_read(struct hpt_iop_hba *hba) { u_int64_t p; u_int32_t outbound_tail = BUS_SPACE_RD4_MV2(outbound_tail); u_int32_t outbound_head = BUS_SPACE_RD4_MV2(outbound_head); if (outbound_tail != outbound_head) { bus_space_read_region_4(hba->bar2t, hba->bar2h, offsetof(struct hpt_iopmu_mv, outbound_q[outbound_tail]), (u_int32_t *)&p, 2); outbound_tail++; if (outbound_tail == MVIOP_QUEUE_LEN) outbound_tail = 0; BUS_SPACE_WRT4_MV2(outbound_tail, outbound_tail); return p; } else return 0; } static void hptiop_mv_inbound_write(u_int64_t p, struct hpt_iop_hba *hba) { u_int32_t inbound_head = BUS_SPACE_RD4_MV2(inbound_head); u_int32_t head = inbound_head + 1; if (head == MVIOP_QUEUE_LEN) head = 0; bus_space_write_region_4(hba->bar2t, hba->bar2h, offsetof(struct hpt_iopmu_mv, inbound_q[inbound_head]), (u_int32_t *)&p, 2); BUS_SPACE_WRT4_MV2(inbound_head, head); BUS_SPACE_WRT4_MV0(inbound_doorbell, MVIOP_MU_INBOUND_INT_POSTQUEUE); } static void hptiop_post_msg_itl(struct hpt_iop_hba *hba, u_int32_t msg) { BUS_SPACE_WRT4_ITL(inbound_msgaddr0, msg); BUS_SPACE_RD4_ITL(outbound_intstatus); } static void hptiop_post_msg_mv(struct hpt_iop_hba *hba, u_int32_t msg) { BUS_SPACE_WRT4_MV2(inbound_msg, msg); BUS_SPACE_WRT4_MV0(inbound_doorbell, MVIOP_MU_INBOUND_INT_MSG); BUS_SPACE_RD4_MV0(outbound_intmask); } static int hptiop_wait_ready_itl(struct hpt_iop_hba * hba, u_int32_t millisec) { u_int32_t req=0; int i; for (i = 0; i < millisec; i++) { req = BUS_SPACE_RD4_ITL(inbound_queue); if (req != IOPMU_QUEUE_EMPTY) break; DELAY(1000); } if (req!=IOPMU_QUEUE_EMPTY) { BUS_SPACE_WRT4_ITL(outbound_queue, req); BUS_SPACE_RD4_ITL(outbound_intstatus); return 0; } return -1; } static int hptiop_wait_ready_mv(struct hpt_iop_hba * hba, u_int32_t millisec) { if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec)) return -1; return 0; } static void hptiop_request_callback_itl(struct hpt_iop_hba * hba, u_int32_t index) { struct hpt_iop_srb *srb; struct hpt_iop_request_scsi_command *req=0; union ccb *ccb; u_int8_t *cdb; u_int32_t result, temp, dxfer; u_int64_t temp64; if (index & IOPMU_QUEUE_MASK_HOST_BITS) { /*host req*/ if (hba->firmware_version > 0x01020000 || hba->interface_version > 0x01020000) { srb = hba->srb[index & ~(u_int32_t) (IOPMU_QUEUE_ADDR_HOST_BIT | IOPMU_QUEUE_REQUEST_RESULT_BIT)]; req = (struct hpt_iop_request_scsi_command *)srb; if (index & IOPMU_QUEUE_REQUEST_RESULT_BIT) result = IOP_RESULT_SUCCESS; else result = req->header.result; } else { srb = hba->srb[index & ~(u_int32_t)IOPMU_QUEUE_ADDR_HOST_BIT]; req = (struct hpt_iop_request_scsi_command *)srb; result = req->header.result; } dxfer = req->dataxfer_length; goto srb_complete; } /*iop req*/ temp = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, type)); result = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, result)); switch(temp) { case IOP_REQUEST_TYPE_IOCTL_COMMAND: { temp64 = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); wakeup((void *)((unsigned long)hba->u.itl.mu + index)); break; } case IOP_REQUEST_TYPE_SCSI_COMMAND: bus_space_read_region_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); srb = (struct hpt_iop_srb *)(unsigned long)temp64; dxfer = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_scsi_command, dataxfer_length)); srb_complete: ccb = (union ccb *)srb->ccb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; if (cdb[0] == SYNCHRONIZE_CACHE) { /* ??? */ ccb->ccb_h.status = CAM_REQ_CMP; goto scsi_done; } switch (result) { case IOP_RESULT_SUCCESS: switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; case CAM_DIR_OUT: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; } ccb->ccb_h.status = CAM_REQ_CMP; break; case IOP_RESULT_BAD_TARGET: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case IOP_RESULT_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case IOP_RESULT_FAIL: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; case IOP_RESULT_RESET: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_CHECK_CONDITION: memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); if (dxfer < ccb->csio.sense_len) ccb->csio.sense_resid = ccb->csio.sense_len - dxfer; else ccb->csio.sense_resid = 0; if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) {/*iop*/ bus_space_read_region_1(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_scsi_command, sg_list), (u_int8_t *)&ccb->csio.sense_data, MIN(dxfer, sizeof(ccb->csio.sense_data))); } else { memcpy(&ccb->csio.sense_data, &req->sg_list, MIN(dxfer, sizeof(ccb->csio.sense_data))); } ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } scsi_done: if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) BUS_SPACE_WRT4_ITL(outbound_queue, index); ccb->csio.resid = ccb->csio.dxfer_len - dxfer; hptiop_free_srb(hba, srb); xpt_done(ccb); break; } } static void hptiop_drain_outbound_queue_itl(struct hpt_iop_hba *hba) { u_int32_t req, temp; while ((req = BUS_SPACE_RD4_ITL(outbound_queue)) !=IOPMU_QUEUE_EMPTY) { if (req & IOPMU_QUEUE_MASK_HOST_BITS) hptiop_request_callback_itl(hba, req); else { struct hpt_iop_request_header *p; p = (struct hpt_iop_request_header *) ((char *)hba->u.itl.mu + req); temp = bus_space_read_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, flags)); if (temp & IOP_REQUEST_FLAG_SYNC_REQUEST) { u_int64_t temp64; bus_space_read_region_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); if (temp64) { hptiop_request_callback_itl(hba, req); } else { temp64 = 1; bus_space_write_region_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); } } else hptiop_request_callback_itl(hba, req); } } } static int hptiop_intr_itl(struct hpt_iop_hba * hba) { u_int32_t status; int ret = 0; status = BUS_SPACE_RD4_ITL(outbound_intstatus); if (status & IOPMU_OUTBOUND_INT_MSG0) { u_int32_t msg = BUS_SPACE_RD4_ITL(outbound_msgaddr0); KdPrint(("hptiop: received outbound msg %x\n", msg)); BUS_SPACE_WRT4_ITL(outbound_intstatus, IOPMU_OUTBOUND_INT_MSG0); hptiop_os_message_callback(hba, msg); ret = 1; } if (status & IOPMU_OUTBOUND_INT_POSTQUEUE) { hptiop_drain_outbound_queue_itl(hba); ret = 1; } return ret; } static void hptiop_request_callback_mv(struct hpt_iop_hba * hba, u_int64_t _tag) { u_int32_t context = (u_int32_t)_tag; if (context & MVIOP_CMD_TYPE_SCSI) { struct hpt_iop_srb *srb; struct hpt_iop_request_scsi_command *req; union ccb *ccb; u_int8_t *cdb; srb = hba->srb[context >> MVIOP_REQUEST_NUMBER_START_BIT]; req = (struct hpt_iop_request_scsi_command *)srb; ccb = (union ccb *)srb->ccb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; if (cdb[0] == SYNCHRONIZE_CACHE) { /* ??? */ ccb->ccb_h.status = CAM_REQ_CMP; goto scsi_done; } if (context & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT) req->header.result = IOP_RESULT_SUCCESS; switch (req->header.result) { case IOP_RESULT_SUCCESS: switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; case CAM_DIR_OUT: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; } ccb->ccb_h.status = CAM_REQ_CMP; break; case IOP_RESULT_BAD_TARGET: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case IOP_RESULT_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case IOP_RESULT_FAIL: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; case IOP_RESULT_RESET: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_CHECK_CONDITION: memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); if (req->dataxfer_length < ccb->csio.sense_len) ccb->csio.sense_resid = ccb->csio.sense_len - req->dataxfer_length; else ccb->csio.sense_resid = 0; memcpy(&ccb->csio.sense_data, &req->sg_list, MIN(req->dataxfer_length, sizeof(ccb->csio.sense_data))); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } scsi_done: ccb->csio.resid = ccb->csio.dxfer_len - req->dataxfer_length; hptiop_free_srb(hba, srb); xpt_done(ccb); } else if (context & MVIOP_CMD_TYPE_IOCTL) { struct hpt_iop_request_ioctl_command *req = hba->ctlcfg_ptr; if (context & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT) hba->config_done = 1; else hba->config_done = -1; wakeup(req); } else if (context & (MVIOP_CMD_TYPE_SET_CONFIG | MVIOP_CMD_TYPE_GET_CONFIG)) hba->config_done = 1; else { device_printf(hba->pcidev, "wrong callback type\n"); } } static void hptiop_drain_outbound_queue_mv(struct hpt_iop_hba * hba) { u_int64_t req; while ((req = hptiop_mv_outbound_read(hba))) { if (req & MVIOP_MU_QUEUE_ADDR_HOST_BIT) { if (req & MVIOP_MU_QUEUE_REQUEST_RETURN_CONTEXT) { hptiop_request_callback_mv(hba, req); } } } } static int hptiop_intr_mv(struct hpt_iop_hba * hba) { u_int32_t status; int ret = 0; status = BUS_SPACE_RD4_MV0(outbound_doorbell); if (status) BUS_SPACE_WRT4_MV0(outbound_doorbell, ~status); if (status & MVIOP_MU_OUTBOUND_INT_MSG) { u_int32_t msg = BUS_SPACE_RD4_MV2(outbound_msg); KdPrint(("hptiop: received outbound msg %x\n", msg)); hptiop_os_message_callback(hba, msg); ret = 1; } if (status & MVIOP_MU_OUTBOUND_INT_POSTQUEUE) { hptiop_drain_outbound_queue_mv(hba); ret = 1; } return ret; } static int hptiop_send_sync_request_itl(struct hpt_iop_hba * hba, u_int32_t req32, u_int32_t millisec) { u_int32_t i; u_int64_t temp64; BUS_SPACE_WRT4_ITL(inbound_queue, req32); BUS_SPACE_RD4_ITL(outbound_intstatus); for (i = 0; i < millisec; i++) { hptiop_intr_itl(hba); bus_space_read_region_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); if (temp64) return 0; DELAY(1000); } return -1; } static int hptiop_send_sync_request_mv(struct hpt_iop_hba *hba, void *req, u_int32_t millisec) { u_int32_t i; u_int64_t phy_addr; hba->config_done = 0; phy_addr = hba->ctlcfgcmd_phy | (u_int64_t)MVIOP_MU_QUEUE_ADDR_HOST_BIT; ((struct hpt_iop_request_get_config *)req)->header.flags |= IOP_REQUEST_FLAG_SYNC_REQUEST | IOP_REQUEST_FLAG_OUTPUT_CONTEXT; hptiop_mv_inbound_write(phy_addr, hba); BUS_SPACE_RD4_MV0(outbound_intmask); for (i = 0; i < millisec; i++) { hptiop_intr_mv(hba); if (hba->config_done) return 0; DELAY(1000); } return -1; } static int hptiop_send_sync_msg(struct hpt_iop_hba *hba, u_int32_t msg, u_int32_t millisec) { u_int32_t i; hba->msg_done = 0; hba->ops->post_msg(hba, msg); for (i=0; iops->iop_intr(hba); if (hba->msg_done) break; DELAY(1000); } return hba->msg_done? 0 : -1; } static int hptiop_get_config_itl(struct hpt_iop_hba * hba, struct hpt_iop_request_get_config * config) { u_int32_t req32; config->header.size = sizeof(struct hpt_iop_request_get_config); config->header.type = IOP_REQUEST_TYPE_GET_CONFIG; config->header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; config->header.result = IOP_RESULT_PENDING; config->header.context = 0; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return -1; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_header) >> 2); if (hptiop_send_sync_request_itl(hba, req32, 20000)) { KdPrint(("hptiop: get config send cmd failed")); return -1; } bus_space_read_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_get_config) >> 2); BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } static int hptiop_get_config_mv(struct hpt_iop_hba * hba, struct hpt_iop_request_get_config * config) { struct hpt_iop_request_get_config *req; if (!(req = hba->ctlcfg_ptr)) return -1; req->header.flags = 0; req->header.type = IOP_REQUEST_TYPE_GET_CONFIG; req->header.size = sizeof(struct hpt_iop_request_get_config); req->header.result = IOP_RESULT_PENDING; req->header.context = MVIOP_CMD_TYPE_GET_CONFIG; if (hptiop_send_sync_request_mv(hba, req, 20000)) { KdPrint(("hptiop: get config send cmd failed")); return -1; } *config = *req; return 0; } static int hptiop_set_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config) { u_int32_t req32; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return -1; config->header.size = sizeof(struct hpt_iop_request_set_config); config->header.type = IOP_REQUEST_TYPE_SET_CONFIG; config->header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; config->header.result = IOP_RESULT_PENDING; config->header.context = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_set_config) >> 2); if (hptiop_send_sync_request_itl(hba, req32, 20000)) { KdPrint(("hptiop: set config send cmd failed")); return -1; } BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } static int hptiop_set_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config) { struct hpt_iop_request_set_config *req; if (!(req = hba->ctlcfg_ptr)) return -1; memcpy((u_int8_t *)req + sizeof(struct hpt_iop_request_header), (u_int8_t *)config + sizeof(struct hpt_iop_request_header), sizeof(struct hpt_iop_request_set_config) - sizeof(struct hpt_iop_request_header)); req->header.flags = 0; req->header.type = IOP_REQUEST_TYPE_SET_CONFIG; req->header.size = sizeof(struct hpt_iop_request_set_config); req->header.result = IOP_RESULT_PENDING; req->header.context = MVIOP_CMD_TYPE_SET_CONFIG; if (hptiop_send_sync_request_mv(hba, req, 20000)) { KdPrint(("hptiop: set config send cmd failed")); return -1; } return 0; } static int hptiop_post_ioctl_command_itl(struct hpt_iop_hba *hba, u_int32_t req32, struct hpt_iop_ioctl_param *pParams) { u_int64_t temp64; struct hpt_iop_request_ioctl_command req; if ((((pParams->nInBufferSize + 3) & ~3) + pParams->nOutBufferSize) > (hba->max_request_size - offsetof(struct hpt_iop_request_ioctl_command, buf))) { device_printf(hba->pcidev, "request size beyond max value"); return -1; } req.header.size = offsetof(struct hpt_iop_request_ioctl_command, buf) + pParams->nInBufferSize; req.header.type = IOP_REQUEST_TYPE_IOCTL_COMMAND; req.header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; req.header.result = IOP_RESULT_PENDING; req.header.context = req32 + (u_int64_t)(unsigned long)hba->u.itl.mu; req.ioctl_code = HPT_CTL_CODE_BSD_TO_IOP(pParams->dwIoControlCode); req.inbuf_size = pParams->nInBufferSize; req.outbuf_size = pParams->nOutBufferSize; req.bytes_returned = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)&req, offsetof(struct hpt_iop_request_ioctl_command, buf)>>2); hptiop_lock_adapter(hba); BUS_SPACE_WRT4_ITL(inbound_queue, req32); BUS_SPACE_RD4_ITL(outbound_intstatus); bus_space_read_region_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_ioctl_command, header.context), (u_int32_t *)&temp64, 2); while (temp64) { if (hptiop_sleep(hba, (void *)((unsigned long)hba->u.itl.mu + req32), PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) break; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); bus_space_read_region_4(hba->bar0t, hba->bar0h,req32 + offsetof(struct hpt_iop_request_ioctl_command, header.context), (u_int32_t *)&temp64, 2); } hptiop_unlock_adapter(hba); return 0; } static int hptiop_bus_space_copyin(struct hpt_iop_hba *hba, u_int32_t bus, void *user, int size) { unsigned char byte; int i; for (i=0; ibar0t, hba->bar0h, bus + i, byte); } return 0; } static int hptiop_bus_space_copyout(struct hpt_iop_hba *hba, u_int32_t bus, void *user, int size) { unsigned char byte; int i; for (i=0; ibar0t, hba->bar0h, bus + i); if (copyout(&byte, (u_int8_t *)user + i, 1)) return -1; } return 0; } static int hptiop_do_ioctl_itl(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param * pParams) { u_int32_t req32; u_int32_t result; if ((pParams->Magic != HPT_IOCTL_MAGIC) && (pParams->Magic != HPT_IOCTL_MAGIC32)) return EFAULT; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return EFAULT; if (pParams->nInBufferSize) if (hptiop_bus_space_copyin(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, buf), (void *)pParams->lpInBuffer, pParams->nInBufferSize)) goto invalid; if (hptiop_post_ioctl_command_itl(hba, req32, pParams)) goto invalid; result = bus_space_read_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_ioctl_command, header.result)); if (result == IOP_RESULT_SUCCESS) { if (pParams->nOutBufferSize) if (hptiop_bus_space_copyout(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, buf) + ((pParams->nInBufferSize + 3) & ~3), (void *)pParams->lpOutBuffer, pParams->nOutBufferSize)) goto invalid; if (pParams->lpBytesReturned) { if (hptiop_bus_space_copyout(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, bytes_returned), (void *)pParams->lpBytesReturned, sizeof(unsigned long))) goto invalid; } BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } else{ invalid: BUS_SPACE_WRT4_ITL(outbound_queue, req32); return EFAULT; } } static int hptiop_post_ioctl_command_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams) { u_int64_t req_phy; int size = 0; if ((((pParams->nInBufferSize + 3) & ~3) + pParams->nOutBufferSize) > (hba->max_request_size - offsetof(struct hpt_iop_request_ioctl_command, buf))) { device_printf(hba->pcidev, "request size beyond max value"); return -1; } req->ioctl_code = HPT_CTL_CODE_BSD_TO_IOP(pParams->dwIoControlCode); req->inbuf_size = pParams->nInBufferSize; req->outbuf_size = pParams->nOutBufferSize; req->header.size = offsetof(struct hpt_iop_request_ioctl_command, buf) + pParams->nInBufferSize; req->header.context = (u_int64_t)MVIOP_CMD_TYPE_IOCTL; req->header.type = IOP_REQUEST_TYPE_IOCTL_COMMAND; req->header.result = IOP_RESULT_PENDING; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; size = req->header.size >> 8; size = size > 3 ? 3 : size; req_phy = hba->ctlcfgcmd_phy | MVIOP_MU_QUEUE_ADDR_HOST_BIT | size; hptiop_mv_inbound_write(req_phy, hba); BUS_SPACE_RD4_MV0(outbound_intmask); while (hba->config_done == 0) { if (hptiop_sleep(hba, req, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) continue; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); } return 0; } static int hptiop_do_ioctl_mv(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams) { struct hpt_iop_request_ioctl_command *req; if ((pParams->Magic != HPT_IOCTL_MAGIC) && (pParams->Magic != HPT_IOCTL_MAGIC32)) return EFAULT; req = (struct hpt_iop_request_ioctl_command *)(hba->ctlcfg_ptr); hba->config_done = 0; hptiop_lock_adapter(hba); if (pParams->nInBufferSize) if (copyin((void *)pParams->lpInBuffer, req->buf, pParams->nInBufferSize)) goto invalid; if (hptiop_post_ioctl_command_mv(hba, req, pParams)) goto invalid; if (hba->config_done == 1) { if (pParams->nOutBufferSize) if (copyout(req->buf + ((pParams->nInBufferSize + 3) & ~3), (void *)pParams->lpOutBuffer, pParams->nOutBufferSize)) goto invalid; if (pParams->lpBytesReturned) if (copyout(&req->bytes_returned, (void*)pParams->lpBytesReturned, sizeof(u_int32_t))) goto invalid; hptiop_unlock_adapter(hba); return 0; } else{ invalid: hptiop_unlock_adapter(hba); return EFAULT; } } static int hptiop_rescan_bus(struct hpt_iop_hba * hba) { union ccb *ccb; if ((ccb = xpt_alloc_ccb()) == NULL) return(ENOMEM); if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(hba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); return(0); } static bus_dmamap_callback_t hptiop_map_srb; static bus_dmamap_callback_t hptiop_post_scsi_command; static bus_dmamap_callback_t hptiop_mv_map_ctlcfg; static int hptiop_alloc_pci_res_itl(struct hpt_iop_hba *hba) { hba->bar0_rid = 0x10; hba->bar0_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar0_rid, RF_ACTIVE); if (hba->bar0_res == NULL) { device_printf(hba->pcidev, "failed to get iop base adrress.\n"); return -1; } hba->bar0t = rman_get_bustag(hba->bar0_res); hba->bar0h = rman_get_bushandle(hba->bar0_res); hba->u.itl.mu = (struct hpt_iopmu_itl *) rman_get_virtual(hba->bar0_res); if (!hba->u.itl.mu) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "alloc mem res failed\n"); return -1; } return 0; } static int hptiop_alloc_pci_res_mv(struct hpt_iop_hba *hba) { hba->bar0_rid = 0x10; hba->bar0_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar0_rid, RF_ACTIVE); if (hba->bar0_res == NULL) { device_printf(hba->pcidev, "failed to get iop bar0.\n"); return -1; } hba->bar0t = rman_get_bustag(hba->bar0_res); hba->bar0h = rman_get_bushandle(hba->bar0_res); hba->u.mv.regs = (struct hpt_iopmv_regs *) rman_get_virtual(hba->bar0_res); if (!hba->u.mv.regs) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "alloc bar0 mem res failed\n"); return -1; } hba->bar2_rid = 0x18; hba->bar2_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar2_rid, RF_ACTIVE); if (hba->bar2_res == NULL) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "failed to get iop bar2.\n"); return -1; } hba->bar2t = rman_get_bustag(hba->bar2_res); hba->bar2h = rman_get_bushandle(hba->bar2_res); hba->u.mv.mu = (struct hpt_iopmu_mv *)rman_get_virtual(hba->bar2_res); if (!hba->u.mv.mu) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); device_printf(hba->pcidev, "alloc mem bar2 res failed\n"); return -1; } return 0; } static void hptiop_release_pci_res_itl(struct hpt_iop_hba *hba) { if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); } static void hptiop_release_pci_res_mv(struct hpt_iop_hba *hba) { if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); if (hba->bar2_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); } static int hptiop_internal_memalloc_mv(struct hpt_iop_hba *hba) { if (bus_dma_tag_create(hba->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 0x800 - 0x8, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, #if __FreeBSD_version > 502000 NULL, NULL, #endif &hba->ctlcfg_dmat)) { device_printf(hba->pcidev, "alloc ctlcfg_dmat failed\n"); return -1; } if (bus_dmamem_alloc(hba->ctlcfg_dmat, (void **)&hba->ctlcfg_ptr, #if __FreeBSD_version>501000 BUS_DMA_WAITOK | BUS_DMA_COHERENT, #else BUS_DMA_WAITOK, #endif &hba->ctlcfg_dmamap) != 0) { device_printf(hba->pcidev, "bus_dmamem_alloc failed!\n"); bus_dma_tag_destroy(hba->ctlcfg_dmat); return -1; } if (bus_dmamap_load(hba->ctlcfg_dmat, hba->ctlcfg_dmamap, hba->ctlcfg_ptr, MVIOP_IOCTLCFG_SIZE, hptiop_mv_map_ctlcfg, hba, 0)) { device_printf(hba->pcidev, "bus_dmamap_load failed!\n"); if (hba->ctlcfg_dmat) bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); return -1; } return 0; } static int hptiop_internal_memfree_mv(struct hpt_iop_hba *hba) { if (hba->ctlcfg_dmat) { bus_dmamap_unload(hba->ctlcfg_dmat, hba->ctlcfg_dmamap); bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } return 0; } /* * CAM driver interface */ static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hptiop_probe), DEVMETHOD(device_attach, hptiop_attach), DEVMETHOD(device_detach, hptiop_detach), DEVMETHOD(device_shutdown, hptiop_shutdown), { 0, 0 } }; static struct hptiop_adapter_ops hptiop_itl_ops = { .iop_wait_ready = hptiop_wait_ready_itl, .internal_memalloc = 0, .internal_memfree = 0, .alloc_pci_res = hptiop_alloc_pci_res_itl, .release_pci_res = hptiop_release_pci_res_itl, .enable_intr = hptiop_enable_intr_itl, .disable_intr = hptiop_disable_intr_itl, .get_config = hptiop_get_config_itl, .set_config = hptiop_set_config_itl, .iop_intr = hptiop_intr_itl, .post_msg = hptiop_post_msg_itl, .post_req = hptiop_post_req_itl, .do_ioctl = hptiop_do_ioctl_itl, }; static struct hptiop_adapter_ops hptiop_mv_ops = { .iop_wait_ready = hptiop_wait_ready_mv, .internal_memalloc = hptiop_internal_memalloc_mv, .internal_memfree = hptiop_internal_memfree_mv, .alloc_pci_res = hptiop_alloc_pci_res_mv, .release_pci_res = hptiop_release_pci_res_mv, .enable_intr = hptiop_enable_intr_mv, .disable_intr = hptiop_disable_intr_mv, .get_config = hptiop_get_config_mv, .set_config = hptiop_set_config_mv, .iop_intr = hptiop_intr_mv, .post_msg = hptiop_post_msg_mv, .post_req = hptiop_post_req_mv, .do_ioctl = hptiop_do_ioctl_mv, }; static driver_t hptiop_pci_driver = { driver_name, driver_methods, sizeof(struct hpt_iop_hba) }; DRIVER_MODULE(hptiop, pci, hptiop_pci_driver, hptiop_devclass, 0, 0); static int hptiop_probe(device_t dev) { struct hpt_iop_hba *hba; u_int32_t id; static char buf[256]; int sas = 0; struct hptiop_adapter_ops *ops; if (pci_get_vendor(dev) != 0x1103) return (ENXIO); id = pci_get_device(dev); switch (id) { case 0x4322: case 0x4321: case 0x4320: sas = 1; case 0x3220: case 0x3320: case 0x3410: case 0x3520: case 0x3510: case 0x3511: case 0x3521: case 0x3522: case 0x3540: ops = &hptiop_itl_ops; break; case 0x3120: case 0x3122: case 0x3020: ops = &hptiop_mv_ops; break; default: return (ENXIO); } device_printf(dev, "adapter at PCI %d:%d:%d, IRQ %d\n", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), pci_get_irq(dev)); sprintf(buf, "RocketRAID %x %s Controller\n", id, sas ? "SAS" : "SATA"); device_set_desc_copy(dev, buf); hba = (struct hpt_iop_hba *)device_get_softc(dev); bzero(hba, sizeof(struct hpt_iop_hba)); hba->ops = ops; KdPrint(("hba->ops=%p\n", hba->ops)); return 0; } static int hptiop_attach(device_t dev) { struct hpt_iop_hba *hba = (struct hpt_iop_hba *)device_get_softc(dev); struct hpt_iop_request_get_config iop_config; struct hpt_iop_request_set_config set_config; int rid = 0; struct cam_devq *devq; struct ccb_setasync ccb; u_int32_t unit = device_get_unit(dev); device_printf(dev, "%d RocketRAID 3xxx/4xxx controller driver %s\n", unit, driver_version); KdPrint(("hptiop: attach(%d, %d/%d/%d) ops=%p\n", unit, pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), hba->ops)); #if __FreeBSD_version >=440000 pci_enable_busmaster(dev); #endif hba->pcidev = dev; hba->pciunit = unit; if (hba->ops->alloc_pci_res(hba)) return ENXIO; if (hba->ops->iop_wait_ready(hba, 2000)) { device_printf(dev, "adapter is not ready\n"); goto release_pci_res; } #if (__FreeBSD_version >= 500000) mtx_init(&hba->lock, "hptioplock", NULL, MTX_DEF); #endif - if (bus_dma_tag_create(NULL,/* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(dev),/* PCI parent */ 1, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ #if __FreeBSD_version>502000 NULL, /* lockfunc */ NULL, /* lockfuncarg */ #endif &hba->parent_dmat /* tag */)) { device_printf(dev, "alloc parent_dmat failed\n"); goto release_pci_res; } if (hba->ops->internal_memalloc) { if (hba->ops->internal_memalloc(hba)) { device_printf(dev, "alloc srb_dmat failed\n"); goto destroy_parent_tag; } } if (hba->ops->get_config(hba, &iop_config)) { device_printf(dev, "get iop config failed.\n"); goto get_config_failed; } hba->firmware_version = iop_config.firmware_version; hba->interface_version = iop_config.interface_version; hba->max_requests = iop_config.max_requests; hba->max_devices = iop_config.max_devices; hba->max_request_size = iop_config.request_size; hba->max_sg_count = iop_config.max_sg_count; if (bus_dma_tag_create(hba->parent_dmat,/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (hba->max_sg_count-1), /* maxsize */ hba->max_sg_count, /* nsegments */ 0x20000, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &hba->lock, /* lockfuncarg */ #endif &hba->io_dmat /* tag */)) { device_printf(dev, "alloc io_dmat failed\n"); goto get_config_failed; } if (bus_dma_tag_create(hba->parent_dmat,/* parent */ 1, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ HPT_SRB_MAX_SIZE * HPT_SRB_MAX_QUEUE_SIZE + 0x20, 1, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ #if __FreeBSD_version>502000 NULL, /* lockfunc */ NULL, /* lockfuncarg */ #endif &hba->srb_dmat /* tag */)) { device_printf(dev, "alloc srb_dmat failed\n"); goto destroy_io_dmat; } if (bus_dmamem_alloc(hba->srb_dmat, (void **)&hba->uncached_ptr, #if __FreeBSD_version>501000 BUS_DMA_WAITOK | BUS_DMA_COHERENT, #else BUS_DMA_WAITOK, #endif &hba->srb_dmamap) != 0) { device_printf(dev, "srb bus_dmamem_alloc failed!\n"); goto destroy_srb_dmat; } if (bus_dmamap_load(hba->srb_dmat, hba->srb_dmamap, hba->uncached_ptr, (HPT_SRB_MAX_SIZE * HPT_SRB_MAX_QUEUE_SIZE) + 0x20, hptiop_map_srb, hba, 0)) { device_printf(dev, "bus_dmamap_load failed!\n"); goto srb_dmamem_free; } if ((devq = cam_simq_alloc(hba->max_requests - 1 )) == NULL) { device_printf(dev, "cam_simq_alloc failed\n"); goto srb_dmamap_unload; } #if __FreeBSD_version <700000 hba->sim = cam_sim_alloc(hptiop_action, hptiop_poll, driver_name, hba, unit, hba->max_requests - 1, 1, devq); #else hba->sim = cam_sim_alloc(hptiop_action, hptiop_poll, driver_name, hba, unit, &Giant, hba->max_requests - 1, 1, devq); #endif if (!hba->sim) { device_printf(dev, "cam_sim_alloc failed\n"); cam_simq_free(devq); goto srb_dmamap_unload; } #if __FreeBSD_version <700000 if (xpt_bus_register(hba->sim, 0) != CAM_SUCCESS) #else if (xpt_bus_register(hba->sim, dev, 0) != CAM_SUCCESS) #endif { device_printf(dev, "xpt_bus_register failed\n"); goto free_cam_sim; } if (xpt_create_path(&hba->path, /*periph */ NULL, cam_sim_path(hba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "xpt_create_path failed\n"); goto deregister_xpt_bus; } bzero(&set_config, sizeof(set_config)); set_config.iop_id = unit; set_config.vbus_id = cam_sim_path(hba->sim); set_config.max_host_request_size = HPT_SRB_MAX_REQ_SIZE; if (hba->ops->set_config(hba, &set_config)) { device_printf(dev, "set iop config failed.\n"); goto free_hba_path; } xpt_setup_ccb(&ccb.ccb_h, hba->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = (AC_FOUND_DEVICE | AC_LOST_DEVICE); ccb.callback = hptiop_async; ccb.callback_arg = hba->sim; xpt_action((union ccb *)&ccb); rid = 0; if ((hba->irq_res = bus_alloc_resource(hba->pcidev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "allocate irq failed!\n"); goto free_hba_path; } #if __FreeBSD_version <700000 if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, hptiop_pci_intr, hba, &hba->irq_handle)) #else if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, NULL, hptiop_pci_intr, hba, &hba->irq_handle)) #endif { device_printf(dev, "allocate intr function failed!\n"); goto free_irq_resource; } if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) { device_printf(dev, "fail to start background task\n"); goto teartown_irq_resource; } hba->ops->enable_intr(hba); hba->ioctl_dev = make_dev(&hptiop_cdevsw, unit, UID_ROOT, GID_WHEEL /*GID_OPERATOR*/, S_IRUSR | S_IWUSR, "%s%d", driver_name, unit); #if __FreeBSD_version < 503000 hba->ioctl_dev->si_drv1 = hba; #endif return 0; teartown_irq_resource: bus_teardown_intr(dev, hba->irq_res, hba->irq_handle); free_irq_resource: bus_release_resource(dev, SYS_RES_IRQ, 0, hba->irq_res); free_hba_path: xpt_free_path(hba->path); deregister_xpt_bus: xpt_bus_deregister(cam_sim_path(hba->sim)); free_cam_sim: cam_sim_free(hba->sim, /*free devq*/ TRUE); srb_dmamap_unload: if (hba->uncached_ptr) bus_dmamap_unload(hba->srb_dmat, hba->srb_dmamap); srb_dmamem_free: if (hba->uncached_ptr) bus_dmamem_free(hba->srb_dmat, hba->uncached_ptr, hba->srb_dmamap); destroy_srb_dmat: if (hba->srb_dmat) bus_dma_tag_destroy(hba->srb_dmat); destroy_io_dmat: if (hba->io_dmat) bus_dma_tag_destroy(hba->io_dmat); get_config_failed: if (hba->ops->internal_memfree) hba->ops->internal_memfree(hba); destroy_parent_tag: if (hba->parent_dmat) bus_dma_tag_destroy(hba->parent_dmat); release_pci_res: if (hba->ops->release_pci_res) hba->ops->release_pci_res(hba); return ENXIO; } static int hptiop_detach(device_t dev) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)device_get_softc(dev); int i; int error = EBUSY; hptiop_lock_adapter(hba); for (i = 0; i < hba->max_devices; i++) if (hptiop_os_query_remove_device(hba, i)) { device_printf(dev, "%d file system is busy. id=%d", hba->pciunit, i); goto out; } if ((error = hptiop_shutdown(dev)) != 0) goto out; if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK, 60000)) goto out; hptiop_release_resource(hba); error = 0; out: hptiop_unlock_adapter(hba); return error; } static int hptiop_shutdown(device_t dev) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)device_get_softc(dev); int error = 0; if (hba->flag & HPT_IOCTL_FLAG_OPEN) { device_printf(dev, "%d device is busy", hba->pciunit); return EBUSY; } hba->ops->disable_intr(hba); if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_SHUTDOWN, 60000)) error = EBUSY; return error; } static void hptiop_pci_intr(void *arg) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)arg; hptiop_lock_adapter(hba); hba->ops->iop_intr(hba); hptiop_unlock_adapter(hba); } static void hptiop_poll(struct cam_sim *sim) { hptiop_pci_intr(cam_sim_softc(sim)); } static void hptiop_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { } static void hptiop_enable_intr_itl(struct hpt_iop_hba *hba) { BUS_SPACE_WRT4_ITL(outbound_intmask, ~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0)); } static void hptiop_enable_intr_mv(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_MV0(outbound_intmask); int_mask |= MVIOP_MU_OUTBOUND_INT_POSTQUEUE | MVIOP_MU_OUTBOUND_INT_MSG; BUS_SPACE_WRT4_MV0(outbound_intmask,int_mask); } static void hptiop_disable_intr_itl(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_ITL(outbound_intmask); int_mask |= IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0; BUS_SPACE_WRT4_ITL(outbound_intmask, int_mask); BUS_SPACE_RD4_ITL(outbound_intstatus); } static void hptiop_disable_intr_mv(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_MV0(outbound_intmask); int_mask &= ~(MVIOP_MU_OUTBOUND_INT_MSG | MVIOP_MU_OUTBOUND_INT_POSTQUEUE); BUS_SPACE_WRT4_MV0(outbound_intmask,int_mask); BUS_SPACE_RD4_MV0(outbound_intmask); } static int hptiop_reset_adapter(struct hpt_iop_hba * hba) { return hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); } static void *hptiop_get_srb(struct hpt_iop_hba * hba) { struct hpt_iop_srb * srb; if (hba->srb_list) { srb = hba->srb_list; hba->srb_list = srb->next; return srb; } return NULL; } static void hptiop_free_srb(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb) { srb->next = hba->srb_list; hba->srb_list = srb; } static void hptiop_action(struct cam_sim *sim, union ccb *ccb) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)cam_sim_softc(sim); struct hpt_iop_srb * srb; switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: hptiop_lock_adapter(hba); if (ccb->ccb_h.target_lun != 0 || ccb->ccb_h.target_id >= hba->max_devices || (ccb->ccb_h.flags & CAM_CDB_PHYS)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); goto scsi_done; } if ((srb = hptiop_get_srb(hba)) == NULL) { device_printf(hba->pcidev, "srb allocated failed"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); goto scsi_done; } srb->ccb = ccb; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) hptiop_post_scsi_command(srb, NULL, 0, 0); else if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { if ((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { int error; error = bus_dmamap_load(hba->io_dmat, srb->dma_map, ccb->csio.data_ptr, ccb->csio.dxfer_len, hptiop_post_scsi_command, srb, 0); if (error && error != EINPROGRESS) { device_printf(hba->pcidev, "%d bus_dmamap_load error %d", hba->pciunit, error); xpt_freeze_simq(hba->sim, 1); ccb->ccb_h.status = CAM_REQ_CMP_ERR; invalid: hptiop_free_srb(hba, srb); xpt_done(ccb); goto scsi_done; } } else { device_printf(hba->pcidev, "CAM_DATA_PHYS not supported"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; goto invalid; } } else { struct bus_dma_segment *segs; if ((ccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 || (ccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { device_printf(hba->pcidev, "SCSI cmd failed"); ccb->ccb_h.status=CAM_PROVIDE_FAIL; goto invalid; } segs = (struct bus_dma_segment *)ccb->csio.data_ptr; hptiop_post_scsi_command(srb, segs, ccb->csio.sglist_cnt, 0); } scsi_done: hptiop_unlock_adapter(hba); return; case XPT_RESET_BUS: device_printf(hba->pcidev, "reset adapter"); hptiop_lock_adapter(hba); hba->msg_done = 0; hptiop_reset_adapter(hba); hptiop_unlock_adapter(hba); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_CALC_GEOMETRY: #if __FreeBSD_version >= 500000 cam_calc_geometry(&ccb->ccg, 1); #else ccb->ccg.heads = 255; ccb->ccg.secs_per_track = 63; ccb->ccg.cylinders = ccb->ccg.volume_size / (ccb->ccg.heads * ccb->ccg.secs_per_track); ccb->ccb_h.status = CAM_REQ_CMP; #endif break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = hba->max_devices; cpi->max_lun = 0; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = hba->max_devices; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hptiop_post_req_itl(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs) { int idx; union ccb *ccb = srb->ccb; u_int8_t *cdb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; KdPrint(("ccb=%p %x-%x-%x\n", ccb, *(u_int32_t *)cdb, *((u_int32_t *)cdb+1), *((u_int32_t *)cdb+2))); if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) { u_int32_t iop_req32; struct hpt_iop_request_scsi_command req; iop_req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (iop_req32 == IOPMU_QUEUE_EMPTY) { device_printf(hba->pcidev, "invaild req offset\n"); ccb->ccb_h.status = CAM_BUSY; bus_dmamap_unload(hba->io_dmat, srb->dma_map); hptiop_free_srb(hba, srb); xpt_done(ccb); return; } if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req.sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } bcopy(cdb, req.cdb, ccb->csio.cdb_len); req.header.size = offsetof(struct hpt_iop_request_scsi_command, sg_list) + nsegs*sizeof(struct hpt_iopsg); req.header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req.header.flags = 0; req.header.result = IOP_RESULT_PENDING; req.header.context = (u_int64_t)(unsigned long)srb; req.dataxfer_length = ccb->csio.dxfer_len; req.channel = 0; req.target = ccb->ccb_h.target_id; req.lun = ccb->ccb_h.target_lun; bus_space_write_region_1(hba->bar0t, hba->bar0h, iop_req32, (u_int8_t *)&req, req.header.size); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); BUS_SPACE_WRT4_ITL(inbound_queue,iop_req32); } else { struct hpt_iop_request_scsi_command *req; req = (struct hpt_iop_request_scsi_command *)srb; if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req->sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } bcopy(cdb, req->cdb, ccb->csio.cdb_len); req->header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req->header.result = IOP_RESULT_PENDING; req->dataxfer_length = ccb->csio.dxfer_len; req->channel = 0; req->target = ccb->ccb_h.target_id; req->lun = ccb->ccb_h.target_lun; req->header.size = offsetof(struct hpt_iop_request_scsi_command, sg_list) + nsegs*sizeof(struct hpt_iopsg); req->header.context = (u_int64_t)srb->index | IOPMU_QUEUE_ADDR_HOST_BIT; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); }else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); } if (hba->firmware_version > 0x01020000 || hba->interface_version > 0x01020000) { u_int32_t size_bits; if (req->header.size < 256) size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT; else if (req->header.size < 512) size_bits = IOPMU_QUEUE_ADDR_HOST_BIT; else size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT | IOPMU_QUEUE_ADDR_HOST_BIT; BUS_SPACE_WRT4_ITL(inbound_queue, (u_int32_t)srb->phy_addr | size_bits); } else BUS_SPACE_WRT4_ITL(inbound_queue, (u_int32_t)srb->phy_addr |IOPMU_QUEUE_ADDR_HOST_BIT); } } static void hptiop_post_req_mv(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs) { int idx, size; union ccb *ccb = srb->ccb; u_int8_t *cdb; struct hpt_iop_request_scsi_command *req; u_int64_t req_phy; req = (struct hpt_iop_request_scsi_command *)srb; req_phy = srb->phy_addr; if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req->sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; bcopy(cdb, req->cdb, ccb->csio.cdb_len); req->header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req->header.result = IOP_RESULT_PENDING; req->dataxfer_length = ccb->csio.dxfer_len; req->channel = 0; req->target = ccb->ccb_h.target_id; req->lun = ccb->ccb_h.target_lun; req->header.size = sizeof(struct hpt_iop_request_scsi_command) - sizeof(struct hpt_iopsg) + nsegs * sizeof(struct hpt_iopsg); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); req->header.context = (u_int64_t)srb->index << MVIOP_REQUEST_NUMBER_START_BIT | MVIOP_CMD_TYPE_SCSI; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; size = req->header.size >> 8; hptiop_mv_inbound_write(req_phy | MVIOP_MU_QUEUE_ADDR_HOST_BIT | (size > 3 ? 3 : size), hba); } static void hptiop_post_scsi_command(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_srb *srb = (struct hpt_iop_srb *)arg; union ccb *ccb = srb->ccb; struct hpt_iop_hba *hba = srb->hba; if (error || nsegs > hba->max_sg_count) { KdPrint(("hptiop: func_code=%x tid=%x lun=%x nsegs=%d\n", ccb->ccb_h.func_code, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, nsegs)); ccb->ccb_h.status = CAM_BUSY; bus_dmamap_unload(hba->io_dmat, srb->dma_map); hptiop_free_srb(hba, srb); xpt_done(ccb); return; } hba->ops->post_req(hba, srb, segs, nsegs); } static void hptiop_mv_map_ctlcfg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_hba *hba = (struct hpt_iop_hba *)arg; hba->ctlcfgcmd_phy = ((u_int64_t)segs->ds_addr + 0x1F) & ~(u_int64_t)0x1F; hba->ctlcfg_ptr = (u_int8_t *)(((unsigned long)hba->ctlcfg_ptr + 0x1F) & ~0x1F); } static void hptiop_map_srb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)arg; bus_addr_t phy_addr = (segs->ds_addr + 0x1F) & ~(bus_addr_t)0x1F; struct hpt_iop_srb *srb, *tmp_srb; int i; if (error || nsegs == 0) { device_printf(hba->pcidev, "hptiop_map_srb error"); return; } /* map srb */ srb = (struct hpt_iop_srb *) (((unsigned long)hba->uncached_ptr + 0x1F) & ~(unsigned long)0x1F); for (i = 0; i < HPT_SRB_MAX_QUEUE_SIZE; i++) { tmp_srb = (struct hpt_iop_srb *) ((char *)srb + i * HPT_SRB_MAX_SIZE); if (((unsigned long)tmp_srb & 0x1F) == 0) { if (bus_dmamap_create(hba->io_dmat, 0, &tmp_srb->dma_map)) { device_printf(hba->pcidev, "dmamap create failed"); return; } bzero(tmp_srb, sizeof(struct hpt_iop_srb)); tmp_srb->hba = hba; tmp_srb->index = i; if (hba->ctlcfg_ptr == 0) {/*itl iop*/ tmp_srb->phy_addr = (u_int64_t)(u_int32_t) (phy_addr >> 5); if (phy_addr & IOPMU_MAX_MEM_SUPPORT_MASK_32G) tmp_srb->srb_flag = HPT_SRB_FLAG_HIGH_MEM_ACESS; } else { tmp_srb->phy_addr = phy_addr; } hptiop_free_srb(hba, tmp_srb); hba->srb[i] = tmp_srb; phy_addr += HPT_SRB_MAX_SIZE; } else { device_printf(hba->pcidev, "invalid alignment"); return; } } } static void hptiop_os_message_callback(struct hpt_iop_hba * hba, u_int32_t msg) { hba->msg_done = 1; } static int hptiop_os_query_remove_device(struct hpt_iop_hba * hba, int target_id) { struct cam_periph *periph = NULL; struct cam_path *path; int status, retval = 0; status = xpt_create_path(&path, NULL, hba->sim->path_id, target_id, 0); if (status == CAM_REQ_CMP) { if ((periph = cam_periph_find(path, "da")) != NULL) { if (periph->refcount >= 1) { device_printf(hba->pcidev, "%d ," "target_id=0x%x," "refcount=%d", hba->pciunit, target_id, periph->refcount); retval = -1; } } xpt_free_path(path); } return retval; } static void hptiop_release_resource(struct hpt_iop_hba *hba) { int i; if (hba->path) { struct ccb_setasync ccb; xpt_setup_ccb(&ccb.ccb_h, hba->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = 0; ccb.callback = hptiop_async; ccb.callback_arg = hba->sim; xpt_action((union ccb *)&ccb); xpt_free_path(hba->path); } if (hba->sim) { xpt_bus_deregister(cam_sim_path(hba->sim)); cam_sim_free(hba->sim, TRUE); } if (hba->ctlcfg_dmat) { bus_dmamap_unload(hba->ctlcfg_dmat, hba->ctlcfg_dmamap); bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } for (i = 0; i < HPT_SRB_MAX_QUEUE_SIZE; i++) { struct hpt_iop_srb *srb = hba->srb[i]; if (srb->dma_map) bus_dmamap_destroy(hba->io_dmat, srb->dma_map); } if (hba->srb_dmat) { bus_dmamap_unload(hba->srb_dmat, hba->srb_dmamap); bus_dmamap_destroy(hba->srb_dmat, hba->srb_dmamap); bus_dma_tag_destroy(hba->srb_dmat); } if (hba->io_dmat) bus_dma_tag_destroy(hba->io_dmat); if (hba->parent_dmat) bus_dma_tag_destroy(hba->parent_dmat); if (hba->irq_handle) bus_teardown_intr(hba->pcidev, hba->irq_res, hba->irq_handle); if (hba->irq_res) bus_release_resource(hba->pcidev, SYS_RES_IRQ, 0, hba->irq_res); if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); if (hba->bar2_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); if (hba->ioctl_dev) destroy_dev(hba->ioctl_dev); } Index: head/sys/dev/hptmv/entry.c =================================================================== --- head/sys/dev/hptmv/entry.c (revision 232853) +++ head/sys/dev/hptmv/entry.c (revision 232854) @@ -1,3120 +1,3120 @@ /* * Copyright (c) 2004-2005 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #endif #if (__FreeBSD_version >= 500000) #include #include #else #include #include #include #include #endif #ifndef __KERNEL__ #define __KERNEL__ #endif #include #include #include #include #ifdef DEBUG #ifdef DEBUG_LEVEL int hpt_dbg_level = DEBUG_LEVEL; #else int hpt_dbg_level = 0; #endif #endif #define MV_ERROR printf /* * CAM SIM entry points */ static int hpt_probe (device_t dev); static void launch_worker_thread(void); static int hpt_attach(device_t dev); static int hpt_detach(device_t dev); static int hpt_shutdown(device_t dev); static void hpt_poll(struct cam_sim *sim); static void hpt_intr(void *arg); static void hpt_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void hpt_action(struct cam_sim *sim, union ccb *ccb); static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hpt_probe), DEVMETHOD(device_attach, hpt_attach), DEVMETHOD(device_detach, hpt_detach), DEVMETHOD(device_shutdown, hpt_shutdown), DEVMETHOD_END }; static driver_t hpt_pci_driver = { __str(PROC_DIR_NAME), driver_methods, sizeof(IAL_ADAPTER_T) }; static devclass_t hpt_devclass; #define __DRIVER_MODULE(p1, p2, p3, p4, p5, p6) DRIVER_MODULE(p1, p2, p3, p4, p5, p6) __DRIVER_MODULE(PROC_DIR_NAME, pci, hpt_pci_driver, hpt_devclass, 0, 0); #define ccb_ccb_ptr spriv_ptr0 #define ccb_adapter ccb_h.spriv_ptr1 static void SetInquiryData(PINQUIRYDATA inquiryData, PVDevice pVDev); static void HPTLIBAPI OsSendCommand (_VBUS_ARG union ccb * ccb); static void HPTLIBAPI fOsCommandDone(_VBUS_ARG PCommand pCmd); static void ccb_done(union ccb *ccb); static void hpt_queue_ccb(union ccb **ccb_Q, union ccb *ccb); static void hpt_free_ccb(union ccb **ccb_Q, union ccb *ccb); static void hptmv_free_edma_queues(IAL_ADAPTER_T *pAdapter); static void hptmv_free_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static void handleEdmaError(_VBUS_ARG PCommand pCmd); static int hptmv_init_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static int fResetActiveCommands(PVBus _vbus_p); static void fRegisterVdevice(IAL_ADAPTER_T *pAdapter); static int hptmv_allocate_edma_queues(IAL_ADAPTER_T *pAdapter); static void hptmv_handle_event_disconnect(void *data); static void hptmv_handle_event_connect(void *data); static int start_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static void init_vdev_params(IAL_ADAPTER_T *pAdapter, MV_U8 channel); static int hptmv_parse_identify_results(MV_SATA_CHANNEL *pMvSataChannel); static int HPTLIBAPI fOsBuildSgl(_VBUS_ARG PCommand pCmd, FPSCAT_GATH pSg, int logical); static MV_BOOLEAN CommandCompletionCB(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channelNum, MV_COMPLETION_TYPE comp_type, MV_VOID_PTR commandId, MV_U16 responseFlags, MV_U32 timeStamp, MV_STORAGE_DEVICE_REGISTERS *registerStruct); static MV_BOOLEAN hptmv_event_notify(MV_SATA_ADAPTER *pMvSataAdapter, MV_EVENT_TYPE eventType, MV_U32 param1, MV_U32 param2); #define ccb_ccb_ptr spriv_ptr0 #define ccb_adapter ccb_h.spriv_ptr1 IAL_ADAPTER_T *gIal_Adapter = 0; IAL_ADAPTER_T *pCurAdapter = 0; static MV_SATA_CHANNEL gMvSataChannels[MAX_VBUS][MV_SATA_CHANNELS_NUM]; typedef struct st_HPT_DPC { IAL_ADAPTER_T *pAdapter; void (*dpc)(IAL_ADAPTER_T *, void *, UCHAR); void *arg; UCHAR flags; } ST_HPT_DPC; #define MAX_DPC 16 UCHAR DPC_Request_Nums = 0; static ST_HPT_DPC DpcQueue[MAX_DPC]; static int DpcQueue_First=0; static int DpcQueue_Last = 0; char DRIVER_VERSION[] = "v1.16"; #if (__FreeBSD_version >= 500000) static struct mtx driver_lock; intrmask_t lock_driver() { intrmask_t spl = 0; mtx_lock(&driver_lock); return spl; } void unlock_driver(intrmask_t spl) { mtx_unlock(&driver_lock); } #else static int driver_locked = 0; intrmask_t lock_driver() { intrmask_t spl = splcam(); loop: while (driver_locked) tsleep(&driver_locked, PRIBIO, "hptlck", hz); atomic_add_int(&driver_locked, 1); if (driver_locked>1) { atomic_subtract_int(&driver_locked, 1); goto loop; } return spl; } void unlock_driver(intrmask_t spl) { atomic_subtract_int(&driver_locked, 1); if (driver_locked==0) { wakeup(&driver_locked); } splx(spl); } #endif /******************************************************************************* * Name: hptmv_free_channel * * Description: free allocated queues for the given channel * * Parameters: pMvSataAdapter - pointer to the RR18xx controler this * channel connected to. * channelNum - channel number. * ******************************************************************************/ static void hptmv_free_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { HPT_ASSERT(channelNum < MV_SATA_CHANNELS_NUM); pAdapter->mvSataAdapter.sataChannel[channelNum] = NULL; } static void failDevice(PVDevice pVDev) { PVBus _vbus_p = pVDev->pVBus; IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)_vbus_p->OsExt; pVDev->u.disk.df_on_line = 0; pVDev->vf_online = 0; if (pVDev->pfnDeviceFailed) CallWhenIdle(_VBUS_P (DPC_PROC)pVDev->pfnDeviceFailed, pVDev); fNotifyGUI(ET_DEVICE_REMOVED, pVDev); #ifndef FOR_DEMO if (pAdapter->ver_601==2 && !pAdapter->beeping) { pAdapter->beeping = 1; BeepOn(pAdapter->mvSataAdapter.adapterIoBaseAddress); set_fail_led(&pAdapter->mvSataAdapter, pVDev->u.disk.mv->channelNumber, 1); } #endif } int MvSataResetChannel(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channel); static void handleEdmaError(_VBUS_ARG PCommand pCmd) { PDevice pDevice = &pCmd->pVDevice->u.disk; MV_SATA_ADAPTER * pSataAdapter = pDevice->mv->mvSataAdapter; if (!pDevice->df_on_line) { KdPrint(("Device is offline")); pCmd->Result = RETURN_BAD_DEVICE; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } if (pCmd->RetryCount++>5) { hpt_printk(("too many retries on channel(%d)\n", pDevice->mv->channelNumber)); failed: failDevice(pCmd->pVDevice); pCmd->Result = RETURN_IDE_ERROR; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } /* reset the channel and retry the command */ if (MvSataResetChannel(pSataAdapter, pDevice->mv->channelNumber)) goto failed; fNotifyGUI(ET_DEVICE_ERROR, Map2pVDevice(pDevice)); hpt_printk(("Retry on channel(%d)\n", pDevice->mv->channelNumber)); fDeviceSendCommand(_VBUS_P pCmd); } /**************************************************************** * Name: hptmv_init_channel * * Description: allocate request and response queues for the EDMA of the * given channel and sets other fields. * * Parameters: * pAdapter - pointer to the emulated adapter data structure * channelNum - channel number. * Return: 0 on success, otherwise on failure ****************************************************************/ static int hptmv_init_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { MV_SATA_CHANNEL *pMvSataChannel; dma_addr_t req_dma_addr; dma_addr_t rsp_dma_addr; if (channelNum >= MV_SATA_CHANNELS_NUM) { MV_ERROR("RR18xx[%d]: Bad channelNum=%d", pAdapter->mvSataAdapter.adapterId, channelNum); return -1; } pMvSataChannel = &gMvSataChannels[pAdapter->mvSataAdapter.adapterId][channelNum]; pAdapter->mvSataAdapter.sataChannel[channelNum] = pMvSataChannel; pMvSataChannel->channelNumber = channelNum; pMvSataChannel->lba48Address = MV_FALSE; pMvSataChannel->maxReadTransfer = MV_FALSE; pMvSataChannel->requestQueue = (struct mvDmaRequestQueueEntry *) (pAdapter->requestsArrayBaseAlignedAddr + (channelNum * MV_EDMA_REQUEST_QUEUE_SIZE)); req_dma_addr = pAdapter->requestsArrayBaseDmaAlignedAddr + (channelNum * MV_EDMA_REQUEST_QUEUE_SIZE); KdPrint(("requestQueue addr is 0x%llX", (HPT_U64)(ULONG_PTR)req_dma_addr)); /* check the 1K alignment of the request queue*/ if (req_dma_addr & 0x3ff) { MV_ERROR("RR18xx[%d]: request queue allocated isn't 1 K aligned," " dma_addr=%llx channel=%d\n", pAdapter->mvSataAdapter.adapterId, (HPT_U64)(ULONG_PTR)req_dma_addr, channelNum); return -1; } pMvSataChannel->requestQueuePciLowAddress = req_dma_addr; pMvSataChannel->requestQueuePciHiAddress = 0; KdPrint(("RR18xx[%d,%d]: request queue allocated: 0x%p", pAdapter->mvSataAdapter.adapterId, channelNum, pMvSataChannel->requestQueue)); pMvSataChannel->responseQueue = (struct mvDmaResponseQueueEntry *) (pAdapter->responsesArrayBaseAlignedAddr + (channelNum * MV_EDMA_RESPONSE_QUEUE_SIZE)); rsp_dma_addr = pAdapter->responsesArrayBaseDmaAlignedAddr + (channelNum * MV_EDMA_RESPONSE_QUEUE_SIZE); /* check the 256 alignment of the response queue*/ if (rsp_dma_addr & 0xff) { MV_ERROR("RR18xx[%d,%d]: response queue allocated isn't 256 byte " "aligned, dma_addr=%llx\n", pAdapter->mvSataAdapter.adapterId, channelNum, (HPT_U64)(ULONG_PTR)rsp_dma_addr); return -1; } pMvSataChannel->responseQueuePciLowAddress = rsp_dma_addr; pMvSataChannel->responseQueuePciHiAddress = 0; KdPrint(("RR18xx[%d,%d]: response queue allocated: 0x%p", pAdapter->mvSataAdapter.adapterId, channelNum, pMvSataChannel->responseQueue)); pAdapter->mvChannel[channelNum].online = MV_TRUE; return 0; } /****************************************************************************** * Name: hptmv_parse_identify_results * * Description: this functions parses the identify command results, checks * that the connected deives can be accesed by RR18xx EDMA, * and updates the channel stucture accordingly. * * Parameters: pMvSataChannel, pointer to the channel data structure. * * Returns: =0 ->success, < 0 ->failure. * ******************************************************************************/ static int hptmv_parse_identify_results(MV_SATA_CHANNEL *pMvSataChannel) { MV_U16 *iden = pMvSataChannel->identifyDevice; /*LBA addressing*/ if (! (iden[IDEN_CAPACITY_1_OFFSET] & 0x200)) { KdPrint(("IAL Error in IDENTIFY info: LBA not supported\n")); return -1; } else { KdPrint(("%25s - %s\n", "Capabilities", "LBA supported")); } /*DMA support*/ if (! (iden[IDEN_CAPACITY_1_OFFSET] & 0x100)) { KdPrint(("IAL Error in IDENTIFY info: DMA not supported\n")); return -1; } else { KdPrint(("%25s - %s\n", "Capabilities", "DMA supported")); } /* PIO */ if ((iden[IDEN_VALID] & 2) == 0) { KdPrint(("IAL Error in IDENTIFY info: not able to find PIO mode\n")); return -1; } KdPrint(("%25s - 0x%02x\n", "PIO modes supported", iden[IDEN_PIO_MODE_SPPORTED] & 0xff)); /*UDMA*/ if ((iden[IDEN_VALID] & 4) == 0) { KdPrint(("IAL Error in IDENTIFY info: not able to find UDMA mode\n")); return -1; } /* 48 bit address */ if ((iden[IDEN_SUPPORTED_COMMANDS2] & 0x400)) { KdPrint(("%25s - %s\n", "LBA48 addressing", "supported")); pMvSataChannel->lba48Address = MV_TRUE; } else { KdPrint(("%25s - %s\n", "LBA48 addressing", "Not supported")); pMvSataChannel->lba48Address = MV_FALSE; } return 0; } static void init_vdev_params(IAL_ADAPTER_T *pAdapter, MV_U8 channel) { PVDevice pVDev = &pAdapter->VDevices[channel]; MV_SATA_CHANNEL *pMvSataChannel = pAdapter->mvSataAdapter.sataChannel[channel]; MV_U16_PTR IdentifyData = pMvSataChannel->identifyDevice; pMvSataChannel->outstandingCommands = 0; pVDev->u.disk.mv = pMvSataChannel; pVDev->u.disk.df_on_line = 1; pVDev->u.disk.pVBus = &pAdapter->VBus; pVDev->pVBus = &pAdapter->VBus; #ifdef SUPPORT_48BIT_LBA if (pMvSataChannel->lba48Address == MV_TRUE) pVDev->u.disk.dDeRealCapacity = ((IdentifyData[101]<<16) | IdentifyData[100]) - 1; else #endif if(IdentifyData[53] & 1) { pVDev->u.disk.dDeRealCapacity = (((IdentifyData[58]<<16 | IdentifyData[57]) < (IdentifyData[61]<<16 | IdentifyData[60])) ? (IdentifyData[61]<<16 | IdentifyData[60]) : (IdentifyData[58]<<16 | IdentifyData[57])) - 1; } else pVDev->u.disk.dDeRealCapacity = (IdentifyData[61]<<16 | IdentifyData[60]) - 1; pVDev->u.disk.bDeUsable_Mode = pVDev->u.disk.bDeModeSetting = pAdapter->mvChannel[channel].maxPioModeSupported - MV_ATA_TRANSFER_PIO_0; if (pAdapter->mvChannel[channel].maxUltraDmaModeSupported!=0xFF) { pVDev->u.disk.bDeUsable_Mode = pVDev->u.disk.bDeModeSetting = pAdapter->mvChannel[channel].maxUltraDmaModeSupported - MV_ATA_TRANSFER_UDMA_0 + 8; } } static void device_change(IAL_ADAPTER_T *pAdapter , MV_U8 channelIndex, int plugged) { PVDevice pVDev; MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channelIndex]; if (!pMvSataChannel) return; if (plugged) { pVDev = &(pAdapter->VDevices[channelIndex]); init_vdev_params(pAdapter, channelIndex); pVDev->VDeviceType = pVDev->u.disk.df_atapi? VD_ATAPI : pVDev->u.disk.df_removable_drive? VD_REMOVABLE : VD_SINGLE_DISK; pVDev->VDeviceCapacity = pVDev->u.disk.dDeRealCapacity-SAVE_FOR_RAID_INFO; pVDev->pfnSendCommand = pfnSendCommand[pVDev->VDeviceType]; pVDev->pfnDeviceFailed = pfnDeviceFailed[pVDev->VDeviceType]; pVDev->vf_online = 1; #ifdef SUPPORT_ARRAY if(pVDev->pParent) { int iMember; for(iMember = 0; iMember < pVDev->pParent->u.array.bArnMember; iMember++) if((PVDevice)pVDev->pParent->u.array.pMember[iMember] == pVDev) pVDev->pParent->u.array.pMember[iMember] = NULL; pVDev->pParent = NULL; } #endif fNotifyGUI(ET_DEVICE_PLUGGED,pVDev); fCheckBootable(pVDev); RegisterVDevice(pVDev); #ifndef FOR_DEMO if (pAdapter->beeping) { pAdapter->beeping = 0; BeepOff(pAdapter->mvSataAdapter.adapterIoBaseAddress); } #endif } else { pVDev = &(pAdapter->VDevices[channelIndex]); failDevice(pVDev); } } static int start_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channelNum]; MV_CHANNEL *pChannelInfo = &(pAdapter->mvChannel[channelNum]); MV_U32 udmaMode,pioMode; KdPrint(("RR18xx [%d]: start channel (%d)", pMvSataAdapter->adapterId, channelNum)); /* Software reset channel */ if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Software reset\n", pMvSataAdapter->adapterId, channelNum); return -1; } /* Hardware reset channel */ if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) { /* If failed, try again - this is when trying to hardreset a channel */ /* when drive is just spinning up */ StallExec(5000000); /* wait 5 sec before trying again */ if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Hard reset\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* identify device*/ if (mvStorageDevATAIdentifyDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform ATA Identify command\n" , pMvSataAdapter->adapterId, channelNum); return -1; } if (hptmv_parse_identify_results(pMvSataChannel)) { MV_ERROR("RR18xx [%d,%d]: Error in parsing ATA Identify message\n" , pMvSataAdapter->adapterId, channelNum); return -1; } /* mvStorageDevATASetFeatures */ /* Disable 8 bit PIO in case CFA enabled */ if (pMvSataChannel->identifyDevice[86] & 4) { KdPrint(("RR18xx [%d]: Disable 8 bit PIO (CFA enabled) \n", pMvSataAdapter->adapterId)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_8_BIT_PIO, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures" " failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* Write cache */ #ifdef ENABLE_WRITE_CACHE if (pMvSataChannel->identifyDevice[82] & 0x20) { if (!(pMvSataChannel->identifyDevice[85] & 0x20)) /* if not enabled by default */ { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_ENABLE_WCACHE, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel %d, write cache enabled\n", pMvSataAdapter->adapterId, channelNum)); } else { KdPrint(("RR18xx [%d]: channel %d, write cache not supported\n", pMvSataAdapter->adapterId, channelNum)); } #else /* disable write cache */ { if (pMvSataChannel->identifyDevice[85] & 0x20) { KdPrint(("RR18xx [%d]: channel =%d, disable write cache\n", pMvSataAdapter->adapterId, channelNum)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_WCACHE, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel=%d, write cache disabled\n", pMvSataAdapter->adapterId, channelNum)); } #endif /* Set transfer mode */ KdPrint(("RR18xx [%d] Set transfer mode XFER_PIO_SLOW\n", pMvSataAdapter->adapterId)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, MV_ATA_TRANSFER_PIO_SLOW, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } if (pMvSataChannel->identifyDevice[IDEN_PIO_MODE_SPPORTED] & 1) { pioMode = MV_ATA_TRANSFER_PIO_4; } else if (pMvSataChannel->identifyDevice[IDEN_PIO_MODE_SPPORTED] & 2) { pioMode = MV_ATA_TRANSFER_PIO_3; } else { MV_ERROR("IAL Error in IDENTIFY info: PIO modes 3 and 4 not supported\n"); pioMode = MV_ATA_TRANSFER_PIO_SLOW; } KdPrint(("RR18xx [%d] Set transfer mode XFER_PIO_4\n", pMvSataAdapter->adapterId)); pAdapter->mvChannel[channelNum].maxPioModeSupported = pioMode; if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, pioMode, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } udmaMode = MV_ATA_TRANSFER_UDMA_0; if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x40) { udmaMode = MV_ATA_TRANSFER_UDMA_6; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x20) { udmaMode = MV_ATA_TRANSFER_UDMA_5; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x10) { udmaMode = MV_ATA_TRANSFER_UDMA_4; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 8) { udmaMode = MV_ATA_TRANSFER_UDMA_3; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 4) { udmaMode = MV_ATA_TRANSFER_UDMA_2; } KdPrint(("RR18xx [%d] Set transfer mode XFER_UDMA_%d\n", pMvSataAdapter->adapterId, udmaMode & 0xf)); pChannelInfo->maxUltraDmaModeSupported = udmaMode; /*if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, udmaMode, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; }*/ if (pChannelInfo->maxUltraDmaModeSupported == 0xFF) return TRUE; else do { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, pChannelInfo->maxUltraDmaModeSupported, 0, 0, 0) == MV_FALSE) { if (pChannelInfo->maxUltraDmaModeSupported > MV_ATA_TRANSFER_UDMA_0) { if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_REG_WRITE_BYTE(pMvSataAdapter->adapterIoBaseAddress, pMvSataChannel->eDmaRegsOffset + 0x11c, /* command reg */ MV_ATA_COMMAND_IDLE_IMMEDIATE); mvMicroSecondsDelay(10000); mvSataChannelHardReset(pMvSataAdapter, channelNum); if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) return FALSE; } if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) return FALSE; pChannelInfo->maxUltraDmaModeSupported--; continue; } else return FALSE; } break; }while (1); /* Read look ahead */ #ifdef ENABLE_READ_AHEAD if (pMvSataChannel->identifyDevice[82] & 0x40) { if (!(pMvSataChannel->identifyDevice[85] & 0x40)) /* if not enabled by default */ { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_ENABLE_RLA, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel=%d, read look ahead enabled\n", pMvSataAdapter->adapterId, channelNum)); } else { KdPrint(("RR18xx [%d]: channel %d, Read Look Ahead not supported\n", pMvSataAdapter->adapterId, channelNum)); } #else { if (pMvSataChannel->identifyDevice[86] & 0x20) { KdPrint(("RR18xx [%d]:channel %d, disable read look ahead\n", pMvSataAdapter->adapterId, channelNum)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_RLA, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]:channel %d: ATA Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]:channel %d, read look ahead disabled\n", pMvSataAdapter->adapterId, channelNum)); } #endif { KdPrint(("RR18xx [%d]: channel %d config EDMA, Non Queued Mode\n", pMvSataAdapter->adapterId, channelNum)); if (mvSataConfigEdmaMode(pMvSataAdapter, channelNum, MV_EDMA_MODE_NOT_QUEUED, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d Error: mvSataConfigEdmaMode failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* Enable EDMA */ if (mvSataEnableChannelDma(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d] Failed to enable DMA, channel=%d\n", pMvSataAdapter->adapterId, channelNum); return -1; } MV_ERROR("RR18xx [%d,%d]: channel started successfully\n", pMvSataAdapter->adapterId, channelNum); #ifndef FOR_DEMO set_fail_led(pMvSataAdapter, channelNum, 0); #endif return 0; } static void hptmv_handle_event(void * data, int flag) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)data; MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_U8 channelIndex; /* mvOsSemTake(&pMvSataAdapter->semaphore); */ for (channelIndex = 0; channelIndex < MV_SATA_CHANNELS_NUM; channelIndex++) { switch(pAdapter->sataEvents[channelIndex]) { case SATA_EVENT_CHANNEL_CONNECTED: /* Handle only connects */ if (flag == 1) break; KdPrint(("RR18xx [%d,%d]: new device connected\n", pMvSataAdapter->adapterId, channelIndex)); hptmv_init_channel(pAdapter, channelIndex); if (mvSataConfigureChannel( pMvSataAdapter, channelIndex) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to configure\n", pMvSataAdapter->adapterId, channelIndex); hptmv_free_channel(pAdapter, channelIndex); } else { /*mvSataChannelHardReset(pMvSataAdapter, channel);*/ if (start_channel( pAdapter, channelIndex)) { MV_ERROR("RR18xx [%d,%d]Failed to start channel\n", pMvSataAdapter->adapterId, channelIndex); hptmv_free_channel(pAdapter, channelIndex); } else { device_change(pAdapter, channelIndex, TRUE); } } pAdapter->sataEvents[channelIndex] = SATA_EVENT_NO_CHANGE; break; case SATA_EVENT_CHANNEL_DISCONNECTED: /* Handle only disconnects */ if (flag == 0) break; KdPrint(("RR18xx [%d,%d]: device disconnected\n", pMvSataAdapter->adapterId, channelIndex)); /* Flush pending commands */ if(pMvSataAdapter->sataChannel[channelIndex]) { _VBUS_INST(&pAdapter->VBus) mvSataFlushDmaQueue (pMvSataAdapter, channelIndex, MV_FLUSH_TYPE_CALLBACK); CheckPendingCall(_VBUS_P0); mvSataRemoveChannel(pMvSataAdapter,channelIndex); hptmv_free_channel(pAdapter, channelIndex); pMvSataAdapter->sataChannel[channelIndex] = NULL; KdPrint(("RR18xx [%d,%d]: channel removed\n", pMvSataAdapter->adapterId, channelIndex)); if (pAdapter->outstandingCommands==0 && DPC_Request_Nums==0) Check_Idle_Call(pAdapter); } else { KdPrint(("RR18xx [%d,%d]: channel already removed!!\n", pMvSataAdapter->adapterId, channelIndex)); } pAdapter->sataEvents[channelIndex] = SATA_EVENT_NO_CHANGE; break; case SATA_EVENT_NO_CHANGE: break; default: break; } } /* mvOsSemRelease(&pMvSataAdapter->semaphore); */ } #define EVENT_CONNECT 1 #define EVENT_DISCONNECT 0 static void hptmv_handle_event_connect(void *data) { hptmv_handle_event (data, 0); } static void hptmv_handle_event_disconnect(void *data) { hptmv_handle_event (data, 1); } static MV_BOOLEAN hptmv_event_notify(MV_SATA_ADAPTER *pMvSataAdapter, MV_EVENT_TYPE eventType, MV_U32 param1, MV_U32 param2) { IAL_ADAPTER_T *pAdapter = pMvSataAdapter->IALData; switch (eventType) { case MV_EVENT_TYPE_SATA_CABLE: { MV_U8 channel = param2; if (param1 == EVENT_CONNECT) { pAdapter->sataEvents[channel] = SATA_EVENT_CHANNEL_CONNECTED; KdPrint(("RR18xx [%d,%d]: device connected event received\n", pMvSataAdapter->adapterId, channel)); /* Delete previous timers (if multiple drives connected in the same time */ pAdapter->event_timer_connect = timeout(hptmv_handle_event_connect, pAdapter, 10*hz); } else if (param1 == EVENT_DISCONNECT) { pAdapter->sataEvents[channel] = SATA_EVENT_CHANNEL_DISCONNECTED; KdPrint(("RR18xx [%d,%d]: device disconnected event received \n", pMvSataAdapter->adapterId, channel)); device_change(pAdapter, channel, FALSE); /* Delete previous timers (if multiple drives disconnected in the same time */ /*pAdapter->event_timer_disconnect = timeout(hptmv_handle_event_disconnect, pAdapter, 10*hz); */ /*It is not necessary to wait, handle it directly*/ hptmv_handle_event_disconnect(pAdapter); } else { MV_ERROR("RR18xx: illigal value for param1(%d) at " "connect/disconect event, host=%d\n", param1, pMvSataAdapter->adapterId ); } } break; case MV_EVENT_TYPE_ADAPTER_ERROR: KdPrint(("RR18xx: DEVICE error event received, pci cause " "reg=%x, don't how to handle this\n", param1)); return MV_TRUE; default: MV_ERROR("RR18xx[%d]: unknown event type (%d)\n", pMvSataAdapter->adapterId, eventType); return MV_FALSE; } return MV_TRUE; } static int hptmv_allocate_edma_queues(IAL_ADAPTER_T *pAdapter) { pAdapter->requestsArrayBaseAddr = (MV_U8 *)contigmalloc(REQUESTS_ARRAY_SIZE, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); if (pAdapter->requestsArrayBaseAddr == NULL) { MV_ERROR("RR18xx[%d]: Failed to allocate memory for EDMA request" " queues\n", pAdapter->mvSataAdapter.adapterId); return -1; } pAdapter->requestsArrayBaseDmaAddr = fOsPhysicalAddress(pAdapter->requestsArrayBaseAddr); pAdapter->requestsArrayBaseAlignedAddr = pAdapter->requestsArrayBaseAddr; pAdapter->requestsArrayBaseAlignedAddr += MV_EDMA_REQUEST_QUEUE_SIZE; pAdapter->requestsArrayBaseAlignedAddr = (MV_U8 *) (((ULONG_PTR)pAdapter->requestsArrayBaseAlignedAddr) & ~(ULONG_PTR)(MV_EDMA_REQUEST_QUEUE_SIZE - 1)); pAdapter->requestsArrayBaseDmaAlignedAddr = pAdapter->requestsArrayBaseDmaAddr; pAdapter->requestsArrayBaseDmaAlignedAddr += MV_EDMA_REQUEST_QUEUE_SIZE; pAdapter->requestsArrayBaseDmaAlignedAddr &= ~(ULONG_PTR)(MV_EDMA_REQUEST_QUEUE_SIZE - 1); if ((pAdapter->requestsArrayBaseDmaAlignedAddr - pAdapter->requestsArrayBaseDmaAddr) != (pAdapter->requestsArrayBaseAlignedAddr - pAdapter->requestsArrayBaseAddr)) { MV_ERROR("RR18xx[%d]: Error in Request Quueues Alignment\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); return -1; } /* response queues */ pAdapter->responsesArrayBaseAddr = (MV_U8 *)contigmalloc(RESPONSES_ARRAY_SIZE, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); if (pAdapter->responsesArrayBaseAddr == NULL) { MV_ERROR("RR18xx[%d]: Failed to allocate memory for EDMA response" " queues\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); return -1; } pAdapter->responsesArrayBaseDmaAddr = fOsPhysicalAddress(pAdapter->responsesArrayBaseAddr); pAdapter->responsesArrayBaseAlignedAddr = pAdapter->responsesArrayBaseAddr; pAdapter->responsesArrayBaseAlignedAddr += MV_EDMA_RESPONSE_QUEUE_SIZE; pAdapter->responsesArrayBaseAlignedAddr = (MV_U8 *) (((ULONG_PTR)pAdapter->responsesArrayBaseAlignedAddr) & ~(ULONG_PTR)(MV_EDMA_RESPONSE_QUEUE_SIZE - 1)); pAdapter->responsesArrayBaseDmaAlignedAddr = pAdapter->responsesArrayBaseDmaAddr; pAdapter->responsesArrayBaseDmaAlignedAddr += MV_EDMA_RESPONSE_QUEUE_SIZE; pAdapter->responsesArrayBaseDmaAlignedAddr &= ~(ULONG_PTR)(MV_EDMA_RESPONSE_QUEUE_SIZE - 1); if ((pAdapter->responsesArrayBaseDmaAlignedAddr - pAdapter->responsesArrayBaseDmaAddr) != (pAdapter->responsesArrayBaseAlignedAddr - pAdapter->responsesArrayBaseAddr)) { MV_ERROR("RR18xx[%d]: Error in Response Quueues Alignment\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); contigfree(pAdapter->responsesArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); return -1; } return 0; } static void hptmv_free_edma_queues(IAL_ADAPTER_T *pAdapter) { contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); contigfree(pAdapter->responsesArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); } static PVOID AllocatePRDTable(IAL_ADAPTER_T *pAdapter) { PVOID ret; if (pAdapter->pFreePRDLink) { KdPrint(("pAdapter->pFreePRDLink:%p\n",pAdapter->pFreePRDLink)); ret = pAdapter->pFreePRDLink; pAdapter->pFreePRDLink = *(void**)ret; return ret; } return NULL; } static void FreePRDTable(IAL_ADAPTER_T *pAdapter, PVOID PRDTable) { *(void**)PRDTable = pAdapter->pFreePRDLink; pAdapter->pFreePRDLink = PRDTable; } extern PVDevice fGetFirstChild(PVDevice pLogical); extern void fResetBootMark(PVDevice pLogical); static void fRegisterVdevice(IAL_ADAPTER_T *pAdapter) { PVDevice pPhysical, pLogical; PVBus pVBus; int i,j; for(i=0;iVDevices[i]); pLogical = pPhysical; while (pLogical->pParent) pLogical = pLogical->pParent; if (pLogical->vf_online==0) { pPhysical->vf_bootmark = pLogical->vf_bootmark = 0; continue; } if (pLogical->VDeviceType==VD_SPARE || pPhysical!=fGetFirstChild(pLogical)) continue; pVBus = &pAdapter->VBus; if(pVBus) { j=0; while(jpVDevice[j]) j++; if(jpVDevice[j] = pLogical; pLogical->pVBus = pVBus; if (j>0 && pLogical->vf_bootmark) { if (pVBus->pVDevice[0]->vf_bootmark) { fResetBootMark(pLogical); } else { do { pVBus->pVDevice[j] = pVBus->pVDevice[j-1]; } while (--j); pVBus->pVDevice[0] = pLogical; } } } } } } PVDevice GetSpareDisk(_VBUS_ARG PVDevice pArray) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)pArray->pVBus->OsExt; LBA_T capacity = LongDiv(pArray->VDeviceCapacity, pArray->u.array.bArnMember-1); LBA_T thiscap, maxcap = MAX_LBA_T; PVDevice pVDevice, pFind = NULL; int i; for(i=0;iVDevices[i]; if(!pVDevice) continue; thiscap = pArray->vf_format_v2? pVDevice->u.disk.dDeRealCapacity : pVDevice->VDeviceCapacity; /* find the smallest usable spare disk */ if (pVDevice->VDeviceType==VD_SPARE && pVDevice->u.disk.df_on_line && thiscap < maxcap && thiscap >= capacity) { maxcap = pVDevice->VDeviceCapacity; pFind = pVDevice; } } return pFind; } /****************************************************************** * IO ATA Command *******************************************************************/ int HPTLIBAPI fDeReadWrite(PDevice pDev, ULONG Lba, UCHAR Cmd, void *tmpBuffer) { return mvReadWrite(pDev->mv, Lba, Cmd, tmpBuffer); } void HPTLIBAPI fDeSelectMode(PDevice pDev, UCHAR NewMode) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; UCHAR mvMode; /* 508x don't use MW-DMA? */ if (NewMode>4 && NewMode<8) NewMode = 4; pDev->bDeModeSetting = NewMode; if (NewMode<=4) mvMode = MV_ATA_TRANSFER_PIO_0 + NewMode; else mvMode = MV_ATA_TRANSFER_UDMA_0 + (NewMode-8); /*To fix 88i8030 bug*/ if (mvMode > MV_ATA_TRANSFER_UDMA_0 && mvMode < MV_ATA_TRANSFER_UDMA_4) mvMode = MV_ATA_TRANSFER_UDMA_0; mvSataDisableChannelDma(pSataAdapter, channelIndex); /* Flush pending commands */ mvSataFlushDmaQueue (pSataAdapter, channelIndex, MV_FLUSH_TYPE_NONE); if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_TRANSFER, mvMode, 0, 0, 0) == MV_FALSE) { KdPrint(("channel %d: Set Features failed\n", channelIndex)); } /* Enable EDMA */ if (mvSataEnableChannelDma(pSataAdapter, channelIndex) == MV_FALSE) KdPrint(("Failed to enable DMA, channel=%d", channelIndex)); } int HPTLIBAPI fDeSetTCQ(PDevice pDev, int enable, int depth) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if (enable) { if (pSataChannel->queuedDMA == MV_EDMA_MODE_NOT_QUEUED && (pSataChannel->identifyDevice[IDEN_SUPPORTED_COMMANDS2] & (0x2))) { UCHAR depth = ((pSataChannel->identifyDevice[IDEN_QUEUE_DEPTH]) & 0x1f) + 1; channelInfo->queueDepth = (depth==32)? 31 : depth; mvSataConfigEdmaMode(pSataAdapter, channelIndex, MV_EDMA_MODE_QUEUED, depth); ret = 1; } } else { if (pSataChannel->queuedDMA != MV_EDMA_MODE_NOT_QUEUED) { channelInfo->queueDepth = 2; mvSataConfigEdmaMode(pSataAdapter, channelIndex, MV_EDMA_MODE_NOT_QUEUED, 0); ret = 1; } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } int HPTLIBAPI fDeSetNCQ(PDevice pDev, int enable, int depth) { return 0; } int HPTLIBAPI fDeSetWriteCache(PDevice pDev, int enable) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if ((pSataChannel->identifyDevice[82] & (0x20))) { if (enable) { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_ENABLE_WCACHE, 0, 0, 0, 0)) { channelInfo->writeCacheEnabled = MV_TRUE; ret = 1; } } else { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_DISABLE_WCACHE, 0, 0, 0, 0)) { channelInfo->writeCacheEnabled = MV_FALSE; ret = 1; } } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } int HPTLIBAPI fDeSetReadAhead(PDevice pDev, int enable) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if ((pSataChannel->identifyDevice[82] & (0x40))) { if (enable) { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_ENABLE_RLA, 0, 0, 0, 0)) { channelInfo->readAheadEnabled = MV_TRUE; ret = 1; } } else { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_DISABLE_RLA, 0, 0, 0, 0)) { channelInfo->readAheadEnabled = MV_FALSE; ret = 1; } } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } #ifdef SUPPORT_ARRAY #define IdeRegisterVDevice fCheckArray #else void IdeRegisterVDevice(PDevice pDev) { PVDevice pVDev = Map2pVDevice(pDev); pVDev->VDeviceType = pDev->df_atapi? VD_ATAPI : pDev->df_removable_drive? VD_REMOVABLE : VD_SINGLE_DISK; pVDev->vf_online = 1; pVDev->VDeviceCapacity = pDev->dDeRealCapacity; pVDev->pfnSendCommand = pfnSendCommand[pVDev->VDeviceType]; pVDev->pfnDeviceFailed = pfnDeviceFailed[pVDev->VDeviceType]; } #endif static __inline PBUS_DMAMAP dmamap_get(struct IALAdapter * pAdapter) { PBUS_DMAMAP p = pAdapter->pbus_dmamap_list; if (p) pAdapter->pbus_dmamap_list = p-> next; return p; } static __inline void dmamap_put(PBUS_DMAMAP p) { p->next = p->pAdapter->pbus_dmamap_list; p->pAdapter->pbus_dmamap_list = p; } /*Since mtx not provide the initialize when declare, so we Final init here to initialize the global mtx*/ #if __FreeBSD_version >= 500000 #define override_kernel_driver() static void hpt_init(void *dummy) { override_kernel_driver(); mtx_init(&driver_lock, "hptsleeplock", NULL, MTX_DEF); } SYSINIT(hptinit, SI_SUB_CONFIGURE, SI_ORDER_FIRST, hpt_init, NULL); #endif static int num_adapters = 0; static int init_adapter(IAL_ADAPTER_T *pAdapter) { PVBus _vbus_p = &pAdapter->VBus; MV_SATA_ADAPTER *pMvSataAdapter; int i, channel, rid; PVDevice pVDev; intrmask_t oldspl = lock_driver(); pAdapter->next = 0; if(gIal_Adapter == 0){ gIal_Adapter = pAdapter; pCurAdapter = gIal_Adapter; } else { pCurAdapter->next = pAdapter; pCurAdapter = pAdapter; } pAdapter->outstandingCommands = 0; pMvSataAdapter = &(pAdapter->mvSataAdapter); _vbus_p->OsExt = (void *)pAdapter; pMvSataAdapter->IALData = pAdapter; - if (bus_dma_tag_create(NULL,/* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(pAdapter->hpt_dev),/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (MAX_SG_DESCRIPTORS-1), /* maxsize */ MAX_SG_DESCRIPTORS, /* nsegments */ 0x10000, /* maxsegsize */ BUS_DMA_WAITOK, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &driver_lock, /* lockfuncarg */ #endif &pAdapter->io_dma_parent /* tag */)) { return ENXIO; } if (hptmv_allocate_edma_queues(pAdapter)) { MV_ERROR("RR18xx: Failed to allocate memory for EDMA queues\n"); unlock_driver(oldspl); return ENOMEM; } /* also map EPROM address */ rid = 0x10; if (!(pAdapter->mem_res = bus_alloc_resource(pAdapter->hpt_dev, SYS_RES_MEMORY, &rid, 0, ~0, MV_SATA_PCI_BAR0_SPACE_SIZE+0x40000, RF_ACTIVE)) || !(pMvSataAdapter->adapterIoBaseAddress = rman_get_virtual(pAdapter->mem_res))) { MV_ERROR("RR18xx: Failed to remap memory space\n"); hptmv_free_edma_queues(pAdapter); unlock_driver(oldspl); return ENXIO; } else { KdPrint(("RR18xx: io base address 0x%p\n", pMvSataAdapter->adapterIoBaseAddress)); } pMvSataAdapter->adapterId = num_adapters++; /* get the revision ID */ pMvSataAdapter->pciConfigRevisionId = pci_read_config(pAdapter->hpt_dev, PCIR_REVID, 1); pMvSataAdapter->pciConfigDeviceId = pci_get_device(pAdapter->hpt_dev); /* init RR18xx */ pMvSataAdapter->intCoalThre[0]= 1; pMvSataAdapter->intCoalThre[1]= 1; pMvSataAdapter->intTimeThre[0] = 1; pMvSataAdapter->intTimeThre[1] = 1; pMvSataAdapter->pciCommand = 0x0107E371; pMvSataAdapter->pciSerrMask = 0xd77fe6ul; pMvSataAdapter->pciInterruptMask = 0xd77fe6ul; pMvSataAdapter->mvSataEventNotify = hptmv_event_notify; if (mvSataInitAdapter(pMvSataAdapter) == MV_FALSE) { MV_ERROR("RR18xx[%d]: core failed to initialize the adapter\n", pMvSataAdapter->adapterId); unregister: bus_release_resource(pAdapter->hpt_dev, SYS_RES_MEMORY, rid, pAdapter->mem_res); hptmv_free_edma_queues(pAdapter); unlock_driver(oldspl); return ENXIO; } pAdapter->ver_601 = pMvSataAdapter->pcbVersion; #ifndef FOR_DEMO set_fail_leds(pMvSataAdapter, 0); #endif /* setup command blocks */ KdPrint(("Allocate command blocks\n")); _vbus_(pFreeCommands) = 0; pAdapter->pCommandBlocks = malloc(sizeof(struct _Command) * MAX_COMMAND_BLOCKS_FOR_EACH_VBUS, M_DEVBUF, M_NOWAIT); KdPrint(("pCommandBlocks:%p\n",pAdapter->pCommandBlocks)); if (!pAdapter->pCommandBlocks) { MV_ERROR("insufficient memory\n"); goto unregister; } for (i=0; ipCommandBlocks[i])); } /*Set up the bus_dmamap*/ pAdapter->pbus_dmamap = (PBUS_DMAMAP)malloc (sizeof(struct _BUS_DMAMAP) * MAX_QUEUE_COMM, M_DEVBUF, M_NOWAIT); if(!pAdapter->pbus_dmamap) { MV_ERROR("insufficient memory\n"); free(pAdapter->pCommandBlocks, M_DEVBUF); goto unregister; } memset((void *)pAdapter->pbus_dmamap, 0, sizeof(struct _BUS_DMAMAP) * MAX_QUEUE_COMM); pAdapter->pbus_dmamap_list = 0; for (i=0; i < MAX_QUEUE_COMM; i++) { PBUS_DMAMAP pmap = &(pAdapter->pbus_dmamap[i]); pmap->pAdapter = pAdapter; dmamap_put(pmap); if(bus_dmamap_create(pAdapter->io_dma_parent, 0, &pmap->dma_map)) { MV_ERROR("Can not allocate dma map\n"); free(pAdapter->pCommandBlocks, M_DEVBUF); free(pAdapter->pbus_dmamap, M_DEVBUF); goto unregister; } } /* setup PRD Tables */ KdPrint(("Allocate PRD Tables\n")); pAdapter->pFreePRDLink = 0; pAdapter->prdTableAddr = (PUCHAR)contigmalloc( (PRD_ENTRIES_SIZE*PRD_TABLES_FOR_VBUS + 32), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); KdPrint(("prdTableAddr:%p\n",pAdapter->prdTableAddr)); if (!pAdapter->prdTableAddr) { MV_ERROR("insufficient PRD Tables\n"); goto unregister; } pAdapter->prdTableAlignedAddr = (PUCHAR)(((ULONG_PTR)pAdapter->prdTableAddr + 0x1f) & ~(ULONG_PTR)0x1fL); { PUCHAR PRDTable = pAdapter->prdTableAlignedAddr; for (i=0; ipFreePRDLink=%p\n",i,pAdapter->pFreePRDLink)); */ FreePRDTable(pAdapter, PRDTable); PRDTable += PRD_ENTRIES_SIZE; } } /* enable the adapter interrupts */ /* configure and start the connected channels*/ for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) { pAdapter->mvChannel[channel].online = MV_FALSE; if (mvSataIsStorageDeviceConnected(pMvSataAdapter, channel) == MV_TRUE) { KdPrint(("RR18xx[%d]: channel %d is connected\n", pMvSataAdapter->adapterId, channel)); if (hptmv_init_channel(pAdapter, channel) == 0) { if (mvSataConfigureChannel(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx[%d]: Failed to configure channel" " %d\n",pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); } else { if (start_channel(pAdapter, channel)) { MV_ERROR("RR18xx[%d]: Failed to start channel," " channel=%d\n",pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); } pAdapter->mvChannel[channel].online = MV_TRUE; /* mvSataChannelSetEdmaLoopBackMode(pMvSataAdapter, channel, MV_TRUE);*/ } } } KdPrint(("pAdapter->mvChannel[channel].online:%x, channel:%d\n", pAdapter->mvChannel[channel].online, channel)); } #ifdef SUPPORT_ARRAY for(i = MAX_ARRAY_DEVICE - 1; i >= 0; i--) { pVDev = ArrayTables(i); mArFreeArrayTable(pVDev); } #endif KdPrint(("Initialize Devices\n")); for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) { MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channel]; if (pMvSataChannel) { init_vdev_params(pAdapter, channel); IdeRegisterVDevice(&pAdapter->VDevices[channel].u.disk); } } #ifdef SUPPORT_ARRAY CheckArrayCritical(_VBUS_P0); #endif _vbus_p->nInstances = 1; fRegisterVdevice(pAdapter); for (channel=0;channelpVDevice[channel]; if (pVDev && pVDev->vf_online) fCheckBootable(pVDev); } #if defined(SUPPORT_ARRAY) && defined(_RAID5N_) init_raid5_memory(_VBUS_P0); _vbus_(r5).enable_write_back = 1; printf("RR18xx: RAID5 write-back %s\n", _vbus_(r5).enable_write_back? "enabled" : "disabled"); #endif mvSataUnmaskAdapterInterrupt(pMvSataAdapter); unlock_driver(oldspl); return 0; } int MvSataResetChannel(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channel) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)pMvSataAdapter->IALData; mvSataDisableChannelDma(pMvSataAdapter, channel); /* Flush pending commands */ mvSataFlushDmaQueue (pMvSataAdapter, channel, MV_FLUSH_TYPE_CALLBACK); /* Software reset channel */ if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Software reset\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; } /* Hardware reset channel */ if (mvSataChannelHardReset(pMvSataAdapter, channel)== MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to Hard reser the SATA channel\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; } if (mvSataIsStorageDeviceConnected(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to Connect Device\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; }else { MV_ERROR("channel %d: perform recalibrate command", channel); if (!mvStorageDevATAExecuteNonUDMACommand(pMvSataAdapter, channel, MV_NON_UDMA_PROTOCOL_NON_DATA, MV_FALSE, NULL, /* pBuffer*/ 0, /* count */ 0, /*features*/ /* sectorCount */ 0, 0, /* lbaLow */ 0, /* lbaMid */ /* lbaHigh */ 0, 0, /* device */ /* command */ 0x10)) MV_ERROR("channel %d: recalibrate failed", channel); /* Set transfer mode */ if((mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, MV_ATA_TRANSFER_PIO_SLOW, 0, 0, 0) == MV_FALSE) || (mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, pAdapter->mvChannel[channel].maxPioModeSupported, 0, 0, 0) == MV_FALSE) || (mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, pAdapter->mvChannel[channel].maxUltraDmaModeSupported, 0, 0, 0) == MV_FALSE) ) { MV_ERROR("channel %d: Set Features failed", channel); hptmv_free_channel(pAdapter, channel); return -1; } /* Enable EDMA */ if (mvSataEnableChannelDma(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("Failed to enable DMA, channel=%d", channel); hptmv_free_channel(pAdapter, channel); return -1; } } return 0; } static int fResetActiveCommands(PVBus _vbus_p) { MV_SATA_ADAPTER *pMvSataAdapter = &((IAL_ADAPTER_T *)_vbus_p->OsExt)->mvSataAdapter; MV_U8 channel; for (channel=0;channel< MV_SATA_CHANNELS_NUM;channel++) { if (pMvSataAdapter->sataChannel[channel] && pMvSataAdapter->sataChannel[channel]->outstandingCommands) MvSataResetChannel(pMvSataAdapter,channel); } return 0; } void fCompleteAllCommandsSynchronously(PVBus _vbus_p) { UINT cont; ULONG ticks = 0; MV_U8 channel; MV_SATA_ADAPTER *pMvSataAdapter = &((IAL_ADAPTER_T *)_vbus_p->OsExt)->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel; do { check_cmds: cont = 0; CheckPendingCall(_VBUS_P0); #ifdef _RAID5N_ dataxfer_poll(); xor_poll(); #endif for (channel=0;channel< MV_SATA_CHANNELS_NUM;channel++) { pMvSataChannel = pMvSataAdapter->sataChannel[channel]; if (pMvSataChannel && pMvSataChannel->outstandingCommands) { while (pMvSataChannel->outstandingCommands) { if (!mvSataInterruptServiceRoutine(pMvSataAdapter)) { StallExec(1000); if (ticks++ > 3000) { MvSataResetChannel(pMvSataAdapter,channel); goto check_cmds; } } else ticks = 0; } cont = 1; } } } while (cont); } void fResetVBus(_VBUS_ARG0) { KdPrint(("fMvResetBus(%p)", _vbus_p)); /* some commands may already finished. */ CheckPendingCall(_VBUS_P0); fResetActiveCommands(_vbus_p); /* * the other pending commands may still be finished successfully. */ fCompleteAllCommandsSynchronously(_vbus_p); /* Now there should be no pending commands. No more action needed. */ CheckIdleCall(_VBUS_P0); KdPrint(("fMvResetBus() done")); } /*No rescan function*/ void fRescanAllDevice(_VBUS_ARG0) { } static MV_BOOLEAN CommandCompletionCB(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channelNum, MV_COMPLETION_TYPE comp_type, MV_VOID_PTR commandId, MV_U16 responseFlags, MV_U32 timeStamp, MV_STORAGE_DEVICE_REGISTERS *registerStruct) { PCommand pCmd = (PCommand) commandId; _VBUS_INST(pCmd->pVDevice->pVBus) if (pCmd->uScratch.sata_param.prdAddr) FreePRDTable(pMvSataAdapter->IALData,pCmd->uScratch.sata_param.prdAddr); switch (comp_type) { case MV_COMPLETION_TYPE_NORMAL: pCmd->Result = RETURN_SUCCESS; break; case MV_COMPLETION_TYPE_ABORT: pCmd->Result = RETURN_BUS_RESET; break; case MV_COMPLETION_TYPE_ERROR: MV_ERROR("IAL: COMPLETION ERROR, adapter %d, channel %d, flags=%x\n", pMvSataAdapter->adapterId, channelNum, responseFlags); if (responseFlags & 4) { MV_ERROR("ATA regs: error %x, sector count %x, LBA low %x, LBA mid %x," " LBA high %x, device %x, status %x\n", registerStruct->errorRegister, registerStruct->sectorCountRegister, registerStruct->lbaLowRegister, registerStruct->lbaMidRegister, registerStruct->lbaHighRegister, registerStruct->deviceRegister, registerStruct->statusRegister); } /*We can't do handleEdmaError directly here, because CommandCompletionCB is called by * mv's ISR, if we retry the command, than the internel data structure may be destroyed*/ pCmd->uScratch.sata_param.responseFlags = responseFlags; pCmd->uScratch.sata_param.bIdeStatus = registerStruct->statusRegister; pCmd->uScratch.sata_param.errorRegister = registerStruct->errorRegister; pCmd->pVDevice->u.disk.QueueLength--; CallAfterReturn(_VBUS_P (DPC_PROC)handleEdmaError,pCmd); return TRUE; default: MV_ERROR(" Unknown completion type (%d)\n", comp_type); return MV_FALSE; } if (pCmd->uCmd.Ide.Command == IDE_COMMAND_VERIFY && pCmd->uScratch.sata_param.cmd_priv > 1) { pCmd->uScratch.sata_param.cmd_priv --; return TRUE; } pCmd->pVDevice->u.disk.QueueLength--; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return TRUE; } void fDeviceSendCommand(_VBUS_ARG PCommand pCmd) { MV_SATA_EDMA_PRD_ENTRY *pPRDTable = 0; MV_SATA_ADAPTER *pMvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel; PVDevice pVDevice = pCmd->pVDevice; PDevice pDevice = &pVDevice->u.disk; LBA_T Lba = pCmd->uCmd.Ide.Lba; USHORT nSector = pCmd->uCmd.Ide.nSectors; MV_QUEUE_COMMAND_RESULT result; MV_QUEUE_COMMAND_INFO commandInfo; MV_UDMA_COMMAND_PARAMS *pUdmaParams = &commandInfo.commandParams.udmaCommand; MV_NONE_UDMA_COMMAND_PARAMS *pNoUdmaParams = &commandInfo.commandParams.NoneUdmaCommand; MV_BOOLEAN is48bit; MV_U8 channel; int i=0; DECLARE_BUFFER(FPSCAT_GATH, tmpSg); if (!pDevice->df_on_line) { MV_ERROR("Device is offline"); pCmd->Result = RETURN_BAD_DEVICE; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } pDevice->HeadPosition = pCmd->uCmd.Ide.Lba + pCmd->uCmd.Ide.nSectors; pMvSataChannel = pDevice->mv; pMvSataAdapter = pMvSataChannel->mvSataAdapter; channel = pMvSataChannel->channelNumber; /* old RAID0 has hidden lba. Remember to clear dDeHiddenLba when delete array! */ Lba += pDevice->dDeHiddenLba; /* check LBA */ if (Lba+nSector-1 > pDevice->dDeRealCapacity) { pCmd->Result = RETURN_INVALID_REQUEST; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } /* * always use 48bit LBA if drive supports it. * Some Seagate drives report error if you use a 28-bit command * to access sector 0xfffffff. */ is48bit = pMvSataChannel->lba48Address; switch (pCmd->uCmd.Ide.Command) { case IDE_COMMAND_READ: case IDE_COMMAND_WRITE: if (pDevice->bDeModeSetting<8) goto pio; commandInfo.type = MV_QUEUED_COMMAND_TYPE_UDMA; pUdmaParams->isEXT = is48bit; pUdmaParams->numOfSectors = nSector; pUdmaParams->lowLBAAddress = Lba; pUdmaParams->highLBAAddress = 0; pUdmaParams->prdHighAddr = 0; pUdmaParams->callBack = CommandCompletionCB; pUdmaParams->commandId = (MV_VOID_PTR )pCmd; if(pCmd->uCmd.Ide.Command == IDE_COMMAND_READ) pUdmaParams->readWrite = MV_UDMA_TYPE_READ; else pUdmaParams->readWrite = MV_UDMA_TYPE_WRITE; if (pCmd->pSgTable && pCmd->cf_physical_sg) { FPSCAT_GATH sg1=tmpSg, sg2=pCmd->pSgTable; do { *sg1++=*sg2; } while ((sg2++->wSgFlag & SG_FLAG_EOT)==0); } else { if (!pCmd->pfnBuildSgl || !pCmd->pfnBuildSgl(_VBUS_P pCmd, tmpSg, 0)) { pio: mvSataDisableChannelDma(pMvSataAdapter, channel); mvSataFlushDmaQueue(pMvSataAdapter, channel, MV_FLUSH_TYPE_CALLBACK); if (pCmd->pSgTable && pCmd->cf_physical_sg==0) { FPSCAT_GATH sg1=tmpSg, sg2=pCmd->pSgTable; do { *sg1++=*sg2; } while ((sg2++->wSgFlag & SG_FLAG_EOT)==0); } else { if (!pCmd->pfnBuildSgl || !pCmd->pfnBuildSgl(_VBUS_P pCmd, tmpSg, 1)) { pCmd->Result = RETURN_NEED_LOGICAL_SG; goto finish_cmd; } } do { ULONG size = tmpSg->wSgSize? tmpSg->wSgSize : 0x10000; ULONG_PTR addr = tmpSg->dSgAddress; if (size & 0x1ff) { pCmd->Result = RETURN_INVALID_REQUEST; goto finish_cmd; } if (mvStorageDevATAExecuteNonUDMACommand(pMvSataAdapter, channel, (pCmd->cf_data_out)?MV_NON_UDMA_PROTOCOL_PIO_DATA_OUT:MV_NON_UDMA_PROTOCOL_PIO_DATA_IN, is48bit, (MV_U16_PTR)addr, size >> 1, /* count */ 0, /* features N/A */ (MV_U16)(size>>9), /*sector count*/ (MV_U16)( (is48bit? (MV_U16)((Lba >> 16) & 0xFF00) : 0 ) | (UCHAR)(Lba & 0xFF) ), /*lbalow*/ (MV_U16)((Lba >> 8) & 0xFF), /* lbaMid */ (MV_U16)((Lba >> 16) & 0xFF),/* lbaHigh */ (MV_U8)(0x40 | (is48bit ? 0 : (UCHAR)(Lba >> 24) & 0xFF )),/* device */ (MV_U8)(is48bit ? (pCmd->cf_data_in?IDE_COMMAND_READ_EXT:IDE_COMMAND_WRITE_EXT):pCmd->uCmd.Ide.Command) )==MV_FALSE) { pCmd->Result = RETURN_IDE_ERROR; goto finish_cmd; } Lba += size>>9; if(Lba & 0xF0000000) is48bit = MV_TRUE; } while ((tmpSg++->wSgFlag & SG_FLAG_EOT)==0); pCmd->Result = RETURN_SUCCESS; finish_cmd: mvSataEnableChannelDma(pMvSataAdapter,channel); CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } } pPRDTable = (MV_SATA_EDMA_PRD_ENTRY *) AllocatePRDTable(pMvSataAdapter->IALData); KdPrint(("pPRDTable:%p\n",pPRDTable)); if (!pPRDTable) { pCmd->Result = RETURN_DEVICE_BUSY; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); HPT_ASSERT(0); return; } do{ pPRDTable[i].highBaseAddr = (sizeof(tmpSg->dSgAddress)>4 ? (MV_U32)(tmpSg->dSgAddress>>32) : 0); pPRDTable[i].flags = (MV_U16)tmpSg->wSgFlag; pPRDTable[i].byteCount = (MV_U16)tmpSg->wSgSize; pPRDTable[i].lowBaseAddr = (MV_U32)tmpSg->dSgAddress; pPRDTable[i].reserved = 0; i++; }while((tmpSg++->wSgFlag & SG_FLAG_EOT)==0); pUdmaParams->prdLowAddr = (ULONG)fOsPhysicalAddress(pPRDTable); if ((pUdmaParams->numOfSectors == 256) && (pMvSataChannel->lba48Address == MV_FALSE)) { pUdmaParams->numOfSectors = 0; } pCmd->uScratch.sata_param.prdAddr = (PVOID)pPRDTable; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); if (result != MV_QUEUE_COMMAND_RESULT_OK) { queue_failed: switch (result) { case MV_QUEUE_COMMAND_RESULT_BAD_LBA_ADDRESS: MV_ERROR("IAL Error: Edma Queue command failed. Bad LBA " "LBA[31:0](0x%08x)\n", pUdmaParams->lowLBAAddress); pCmd->Result = RETURN_IDE_ERROR; break; case MV_QUEUE_COMMAND_RESULT_QUEUED_MODE_DISABLED: MV_ERROR("IAL Error: Edma Queue command failed. EDMA" " disabled adapter %d channel %d\n", pMvSataAdapter->adapterId, channel); mvSataEnableChannelDma(pMvSataAdapter,channel); pCmd->Result = RETURN_IDE_ERROR; break; case MV_QUEUE_COMMAND_RESULT_FULL: MV_ERROR("IAL Error: Edma Queue command failed. Queue is" " Full adapter %d channel %d\n", pMvSataAdapter->adapterId, channel); pCmd->Result = RETURN_DEVICE_BUSY; break; case MV_QUEUE_COMMAND_RESULT_BAD_PARAMS: MV_ERROR("IAL Error: Edma Queue command failed. (Bad " "Params), pMvSataAdapter: %p, pSataChannel: %p.\n", pMvSataAdapter, pMvSataAdapter->sataChannel[channel]); pCmd->Result = RETURN_IDE_ERROR; break; default: MV_ERROR("IAL Error: Bad result value (%d) from queue" " command\n", result); pCmd->Result = RETURN_IDE_ERROR; } if(pPRDTable) FreePRDTable(pMvSataAdapter->IALData,pPRDTable); CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); } pDevice->QueueLength++; return; case IDE_COMMAND_VERIFY: commandInfo.type = MV_QUEUED_COMMAND_TYPE_NONE_UDMA; pNoUdmaParams->bufPtr = NULL; pNoUdmaParams->callBack = CommandCompletionCB; pNoUdmaParams->commandId = (MV_VOID_PTR)pCmd; pNoUdmaParams->count = 0; pNoUdmaParams->features = 0; pNoUdmaParams->protocolType = MV_NON_UDMA_PROTOCOL_NON_DATA; pCmd->uScratch.sata_param.cmd_priv = 1; if (pMvSataChannel->lba48Address == MV_TRUE){ pNoUdmaParams->command = MV_ATA_COMMAND_READ_VERIFY_SECTORS_EXT; pNoUdmaParams->isEXT = MV_TRUE; pNoUdmaParams->lbaHigh = (MV_U16)((Lba & 0xff0000) >> 16); pNoUdmaParams->lbaMid = (MV_U16)((Lba & 0xff00) >> 8); pNoUdmaParams->lbaLow = (MV_U16)(((Lba & 0xff000000) >> 16)| (Lba & 0xff)); pNoUdmaParams->sectorCount = nSector; pNoUdmaParams->device = 0x40; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); if (result != MV_QUEUE_COMMAND_RESULT_OK){ goto queue_failed; } return; } else{ pNoUdmaParams->command = MV_ATA_COMMAND_READ_VERIFY_SECTORS; pNoUdmaParams->isEXT = MV_FALSE; pNoUdmaParams->lbaHigh = (MV_U16)((Lba & 0xff0000) >> 16); pNoUdmaParams->lbaMid = (MV_U16)((Lba & 0xff00) >> 8); pNoUdmaParams->lbaLow = (MV_U16)(Lba & 0xff); pNoUdmaParams->sectorCount = 0xff & nSector; pNoUdmaParams->device = (MV_U8)(0x40 | ((Lba & 0xf000000) >> 24)); pNoUdmaParams->callBack = CommandCompletionCB; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); /*FIXME: how about the commands already queued? but marvel also forgets to consider this*/ if (result != MV_QUEUE_COMMAND_RESULT_OK){ goto queue_failed; } } break; default: pCmd->Result = RETURN_INVALID_REQUEST; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); break; } } /********************************************************** * * Probe the hostadapter. * **********************************************************/ static int hpt_probe(device_t dev) { if ((pci_get_vendor(dev) == MV_SATA_VENDOR_ID) && (pci_get_device(dev) == MV_SATA_DEVICE_ID_5081 #ifdef FOR_DEMO || pci_get_device(dev) == MV_SATA_DEVICE_ID_5080 #endif )) { KdPrintI((CONTROLLER_NAME " found\n")); device_set_desc(dev, CONTROLLER_NAME); return 0; } else return(ENXIO); } /*********************************************************** * * Auto configuration: attach and init a host adapter. * ***********************************************************/ static int hpt_attach(device_t dev) { IAL_ADAPTER_T * pAdapter = device_get_softc(dev); int rid; union ccb *ccb; struct cam_devq *devq; struct cam_sim *hpt_vsim; printf("%s Version %s \n", DRIVER_NAME, DRIVER_VERSION); if (!pAdapter) { pAdapter = (IAL_ADAPTER_T *)malloc(sizeof (IAL_ADAPTER_T), M_DEVBUF, M_NOWAIT); #if __FreeBSD_version > 410000 device_set_softc(dev, (void *)pAdapter); #else device_set_driver(dev, (driver_t *)pAdapter); #endif } if (!pAdapter) return (ENOMEM); bzero(pAdapter, sizeof(IAL_ADAPTER_T)); pAdapter->hpt_dev = dev; rid = init_adapter(pAdapter); if (rid) return rid; rid = 0; if ((pAdapter->hpt_irq = bus_alloc_resource(pAdapter->hpt_dev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { hpt_printk(("can't allocate interrupt\n")); return(ENXIO); } #if __FreeBSD_version <700000 if (bus_setup_intr(pAdapter->hpt_dev, pAdapter->hpt_irq, INTR_TYPE_CAM, hpt_intr, pAdapter, &pAdapter->hpt_intr)) #else if (bus_setup_intr(pAdapter->hpt_dev, pAdapter->hpt_irq, INTR_TYPE_CAM, NULL, hpt_intr, pAdapter, &pAdapter->hpt_intr)) #endif { hpt_printk(("can't set up interrupt\n")); free(pAdapter, M_DEVBUF); return(ENXIO); } if((ccb = (union ccb *)malloc(sizeof(*ccb), M_DEVBUF, M_WAITOK)) != (union ccb*)NULL) { bzero(ccb, sizeof(*ccb)); ccb->ccb_h.pinfo.priority = 1; ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX; } else { return ENOMEM; } /* * Create the device queue for our SIM(s). */ if((devq = cam_simq_alloc(8/*MAX_QUEUE_COMM*/)) == NULL) { KdPrint(("ENXIO\n")); return ENOMEM; } /* * Construct our SIM entry */ #if __FreeBSD_version <700000 hpt_vsim = cam_sim_alloc(hpt_action, hpt_poll, __str(PROC_DIR_NAME), pAdapter, device_get_unit(pAdapter->hpt_dev), 1, 8, devq); #else hpt_vsim = cam_sim_alloc(hpt_action, hpt_poll, __str(PROC_DIR_NAME), pAdapter, device_get_unit(pAdapter->hpt_dev), &Giant, 1, 8, devq); #endif if (hpt_vsim == NULL) { cam_simq_free(devq); return ENOMEM; } #if __FreeBSD_version <700000 if (xpt_bus_register(hpt_vsim, 0) != CAM_SUCCESS) #else if (xpt_bus_register(hpt_vsim, dev, 0) != CAM_SUCCESS) #endif { cam_sim_free(hpt_vsim, /*free devq*/ TRUE); hpt_vsim = NULL; return ENXIO; } if(xpt_create_path(&pAdapter->path, /*periph */ NULL, cam_sim_path(hpt_vsim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(hpt_vsim)); cam_sim_free(hpt_vsim, /*free_devq*/TRUE); hpt_vsim = NULL; return ENXIO; } xpt_setup_ccb(&(ccb->ccb_h), pAdapter->path, /*priority*/5); ccb->ccb_h.func_code = XPT_SASYNC_CB; ccb->csa.event_enable = AC_LOST_DEVICE; ccb->csa.callback = hpt_async; ccb->csa.callback_arg = hpt_vsim; xpt_action((union ccb *)ccb); free(ccb, M_DEVBUF); if (device_get_unit(dev) == 0) { /* Start the work thread. XXX */ launch_worker_thread(); } return 0; } static int hpt_detach(device_t dev) { return (EBUSY); } /*************************************************************** * The poll function is used to simulate the interrupt when * the interrupt subsystem is not functioning. * ***************************************************************/ static void hpt_poll(struct cam_sim *sim) { hpt_intr((void *)cam_sim_softc(sim)); } /**************************************************************** * Name: hpt_intr * Description: Interrupt handler. ****************************************************************/ static void hpt_intr(void *arg) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)arg; intrmask_t oldspl = lock_driver(); /* KdPrintI(("----- Entering Isr() -----\n")); */ if (mvSataInterruptServiceRoutine(&pAdapter->mvSataAdapter) == MV_TRUE) { _VBUS_INST(&pAdapter->VBus) CheckPendingCall(_VBUS_P0); } /* KdPrintI(("----- Leaving Isr() -----\n")); */ unlock_driver(oldspl); } /********************************************************** * Asynchronous Events *********************************************************/ #if (!defined(UNREFERENCED_PARAMETER)) #define UNREFERENCED_PARAMETER(x) (void)(x) #endif static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { /* debug XXXX */ panic("Here"); UNREFERENCED_PARAMETER(callback_arg); UNREFERENCED_PARAMETER(code); UNREFERENCED_PARAMETER(path); UNREFERENCED_PARAMETER(arg); } static void FlushAdapter(IAL_ADAPTER_T *pAdapter) { int i; hpt_printk(("flush all devices\n")); /* flush all devices */ for (i=0; iVBus.pVDevice[i]; if(pVDev) fFlushVDev(pVDev); } } static int hpt_shutdown(device_t dev) { IAL_ADAPTER_T *pAdapter; pAdapter = device_get_softc(dev); if (pAdapter == NULL) return (EINVAL); EVENTHANDLER_DEREGISTER(shutdown_final, pAdapter->eh); FlushAdapter(pAdapter); /* give the flush some time to happen, *otherwise "shutdown -p now" will make file system corrupted */ DELAY(1000 * 1000 * 5); return 0; } void Check_Idle_Call(IAL_ADAPTER_T *pAdapter) { _VBUS_INST(&pAdapter->VBus) if (mWaitingForIdle(_VBUS_P0)) { CheckIdleCall(_VBUS_P0); #ifdef SUPPORT_ARRAY { int i; PVDevice pArray; for(i = 0; i < MAX_ARRAY_PER_VBUS; i++){ if ((pArray=ArrayTables(i))->u.array.dArStamp==0) continue; else if (pArray->u.array.rf_auto_rebuild) { KdPrint(("auto rebuild.\n")); pArray->u.array.rf_auto_rebuild = 0; hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, DUPLICATE); } } } #endif } /* launch the awaiting commands blocked by mWaitingForIdle */ while(pAdapter->pending_Q!= NULL) { _VBUS_INST(&pAdapter->VBus) union ccb *ccb = (union ccb *)pAdapter->pending_Q->ccb_h.ccb_ccb_ptr; hpt_free_ccb(&pAdapter->pending_Q, ccb); CallAfterReturn(_VBUS_P (DPC_PROC)OsSendCommand, ccb); } } static void ccb_done(union ccb *ccb) { PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T * pAdapter = pmap->pAdapter; KdPrintI(("ccb_done: ccb %p status %x\n", ccb, ccb->ccb_h.status)); dmamap_put(pmap); xpt_done(ccb); pAdapter->outstandingCommands--; if (pAdapter->outstandingCommands == 0) { if(DPC_Request_Nums == 0) Check_Idle_Call(pAdapter); } } /**************************************************************** * Name: hpt_action * Description: Process a queued command from the CAM layer. * Parameters: sim - Pointer to SIM object * ccb - Pointer to SCSI command structure. ****************************************************************/ void hpt_action(struct cam_sim *sim, union ccb *ccb) { intrmask_t oldspl; IAL_ADAPTER_T * pAdapter = (IAL_ADAPTER_T *) cam_sim_softc(sim); PBUS_DMAMAP pmap; _VBUS_INST(&pAdapter->VBus) CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("hpt_action\n")); KdPrint(("hpt_action(%lx,%lx{%x})\n", (u_long)sim, (u_long)ccb, ccb->ccb_h.func_code)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ { /* ccb->ccb_h.path_id is not our bus id - don't check it */ if (ccb->ccb_h.target_lun) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (ccb->ccb_h.target_id >= MAX_VDEVICE_PER_VBUS || pAdapter->VBus.pVDevice[ccb->ccb_h.target_id]==0) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } oldspl = lock_driver(); if (pAdapter->outstandingCommands==0 && DPC_Request_Nums==0) Check_Idle_Call(pAdapter); pmap = dmamap_get(pAdapter); HPT_ASSERT(pmap); ccb->ccb_adapter = pmap; memset((void *)pmap->psg, 0, sizeof(pmap->psg)); if (mWaitingForIdle(_VBUS_P0)) hpt_queue_ccb(&pAdapter->pending_Q, ccb); else OsSendCommand(_VBUS_P ccb); unlock_driver(oldspl); /* KdPrint(("leave scsiio\n")); */ break; } case XPT_RESET_BUS: KdPrint(("reset bus\n")); oldspl = lock_driver(); fResetVBus(_VBUS_P0); unlock_driver(oldspl); xpt_done(ccb); break; case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: /* XXX Implement */ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: #if __FreeBSD_version >= 500000 cam_calc_geometry(&ccb->ccg, 1); #else { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb > 1024 ) { ccg->heads = 255; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; } #endif xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; /* Not necessary to reset bus */ cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = MAX_VDEVICE_PER_VBUS; cpi->max_lun = 0; cpi->initiator_id = MAX_VDEVICE_PER_VBUS; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: KdPrint(("invalid cmd\n")); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } /* KdPrint(("leave hpt_action..............\n")); */ } /* shall be called at lock_driver() */ static void hpt_queue_ccb(union ccb **ccb_Q, union ccb *ccb) { if(*ccb_Q == NULL) ccb->ccb_h.ccb_ccb_ptr = ccb; else { ccb->ccb_h.ccb_ccb_ptr = (*ccb_Q)->ccb_h.ccb_ccb_ptr; (*ccb_Q)->ccb_h.ccb_ccb_ptr = (char *)ccb; } *ccb_Q = ccb; } /* shall be called at lock_driver() */ static void hpt_free_ccb(union ccb **ccb_Q, union ccb *ccb) { union ccb *TempCCB; TempCCB = *ccb_Q; if(ccb->ccb_h.ccb_ccb_ptr == ccb) /*it means SCpnt is the last one in CURRCMDs*/ *ccb_Q = NULL; else { while(TempCCB->ccb_h.ccb_ccb_ptr != (char *)ccb) TempCCB = (union ccb *)TempCCB->ccb_h.ccb_ccb_ptr; TempCCB->ccb_h.ccb_ccb_ptr = ccb->ccb_h.ccb_ccb_ptr; if(*ccb_Q == ccb) *ccb_Q = TempCCB; } } #ifdef SUPPORT_ARRAY /*************************************************************************** * Function: hpt_worker_thread * Description: Do background rebuilding. Execute in kernel thread context. * Returns: None ***************************************************************************/ static void hpt_worker_thread(void) { intrmask_t oldspl; for(;;) { while (DpcQueue_First!=DpcQueue_Last) { ST_HPT_DPC p; oldspl = lock_driver(); p = DpcQueue[DpcQueue_First]; DpcQueue_First++; DpcQueue_First %= MAX_DPC; DPC_Request_Nums++; unlock_driver(oldspl); p.dpc(p.pAdapter, p.arg, p.flags); oldspl = lock_driver(); DPC_Request_Nums--; /* since we may have prevented Check_Idle_Call, do it here */ if (DPC_Request_Nums==0) { if (p.pAdapter->outstandingCommands == 0) { _VBUS_INST(&p.pAdapter->VBus); Check_Idle_Call(p.pAdapter); CheckPendingCall(_VBUS_P0); } } unlock_driver(oldspl); /*Schedule out*/ #if (__FreeBSD_version < 500000) YIELD_THREAD; #else #if (__FreeBSD_version > 700033) pause("sched", 1); #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "sched", 1); #endif #endif if (SIGISMEMBER(curproc->p_siglist, SIGSTOP)) { /* abort rebuilding process. */ IAL_ADAPTER_T *pAdapter; PVDevice pArray; PVBus _vbus_p; int i; pAdapter = gIal_Adapter; while(pAdapter != 0){ _vbus_p = &pAdapter->VBus; for (i=0;iu.array.dArStamp==0) continue; else if (pArray->u.array.rf_rebuilding || pArray->u.array.rf_verifying || pArray->u.array.rf_initializing) { pArray->u.array.rf_abort_rebuild = 1; } } pAdapter = pAdapter->next; } } } /*Remove this debug option*/ /* #ifdef DEBUG if (SIGISMEMBER(curproc->p_siglist, SIGSTOP)) #if (__FreeBSD_version > 700033) pause("hptrdy", 2*hz); #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "hptrdy", 2*hz); #endif #endif */ #if (__FreeBSD_version >= 800002) kproc_suspend_check(curproc); #elif (__FreeBSD_version >= 500043) kthread_suspend_check(curproc); #else kproc_suspend_loop(curproc); #endif #if (__FreeBSD_version > 700033) pause("hptrdy", 2*hz); /* wait for something to do */ #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "hptrdy", 2*hz); /* wait for something to do */ #endif } } static struct proc *hptdaemonproc; static struct kproc_desc hpt_kp = { "hpt_wt", hpt_worker_thread, &hptdaemonproc }; /*Start this thread in the hpt_attach, to prevent kernel from loading it without our controller.*/ static void launch_worker_thread(void) { IAL_ADAPTER_T *pAdapTemp; kproc_start(&hpt_kp); for (pAdapTemp = gIal_Adapter; pAdapTemp; pAdapTemp = pAdapTemp->next) { _VBUS_INST(&pAdapTemp->VBus) int i; PVDevice pVDev; for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) if ((pVDev=ArrayTables(i))->u.array.dArStamp==0) continue; else{ if (pVDev->u.array.rf_need_rebuild && !pVDev->u.array.rf_rebuilding) hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapTemp, pVDev, (UCHAR)((pVDev->u.array.CriticalMembers || pVDev->VDeviceType == VD_RAID_1)? DUPLICATE : REBUILD_PARITY)); } } /* * hpt_worker_thread needs to be suspended after shutdown sync, when fs sync finished. */ #if (__FreeBSD_version < 500043) EVENTHANDLER_REGISTER(shutdown_post_sync, shutdown_kproc, hptdaemonproc, SHUTDOWN_PRI_FIRST); #else EVENTHANDLER_REGISTER(shutdown_post_sync, kproc_shutdown, hptdaemonproc, SHUTDOWN_PRI_FIRST); #endif } /* *SYSINIT(hptwt, SI_SUB_KTHREAD_IDLE, SI_ORDER_FIRST, launch_worker_thread, NULL); */ #endif /********************************************************************************/ int HPTLIBAPI fOsBuildSgl(_VBUS_ARG PCommand pCmd, FPSCAT_GATH pSg, int logical) { union ccb *ccb = (union ccb *)pCmd->pOrgCommand; bus_dma_segment_t *sgList = (bus_dma_segment_t *)ccb->csio.data_ptr; int idx; if(logical) { if (ccb->ccb_h.flags & CAM_DATA_PHYS) panic("physical address unsupported"); if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { if (ccb->ccb_h.flags & CAM_SG_LIST_PHYS) panic("physical address unsupported"); for (idx = 0; idx < ccb->csio.sglist_cnt; idx++) { pSg[idx].dSgAddress = (ULONG_PTR)(UCHAR *)sgList[idx].ds_addr; pSg[idx].wSgSize = sgList[idx].ds_len; pSg[idx].wSgFlag = (idx==ccb->csio.sglist_cnt-1)? SG_FLAG_EOT : 0; } } else { pSg->dSgAddress = (ULONG_PTR)(UCHAR *)ccb->csio.data_ptr; pSg->wSgSize = ccb->csio.dxfer_len; pSg->wSgFlag = SG_FLAG_EOT; } return TRUE; } /* since we have provided physical sg, nobody will ask us to build physical sg */ HPT_ASSERT(0); return FALSE; } /*******************************************************************************/ ULONG HPTLIBAPI GetStamp(void) { /* * the system variable, ticks, can't be used since it hasn't yet been active * when our driver starts (ticks==0, it's a invalid stamp value) */ ULONG stamp; do { stamp = random(); } while (stamp==0); return stamp; } static void SetInquiryData(PINQUIRYDATA inquiryData, PVDevice pVDev) { int i; IDENTIFY_DATA2 *pIdentify = (IDENTIFY_DATA2*)pVDev->u.disk.mv->identifyDevice; inquiryData->DeviceType = T_DIRECT; /*DIRECT_ACCESS_DEVICE*/ inquiryData->AdditionalLength = (UCHAR)(sizeof(INQUIRYDATA) - 5); #ifndef SERIAL_CMDS inquiryData->CommandQueue = 1; #endif switch(pVDev->VDeviceType) { case VD_SINGLE_DISK: case VD_ATAPI: case VD_REMOVABLE: /* Set the removable bit, if applicable. */ if ((pVDev->u.disk.df_removable_drive) || (pIdentify->GeneralConfiguration & 0x80)) inquiryData->RemovableMedia = 1; /* Fill in vendor identification fields. */ for (i = 0; i < 20; i += 2) { inquiryData->VendorId[i] = ((PUCHAR)pIdentify->ModelNumber)[i + 1]; inquiryData->VendorId[i+1] = ((PUCHAR)pIdentify->ModelNumber)[i]; } /* Initialize unused portion of product id. */ for (i = 0; i < 4; i++) inquiryData->ProductId[12+i] = ' '; /* firmware revision */ for (i = 0; i < 4; i += 2) { inquiryData->ProductRevisionLevel[i] = ((PUCHAR)pIdentify->FirmwareRevision)[i+1]; inquiryData->ProductRevisionLevel[i+1] = ((PUCHAR)pIdentify->FirmwareRevision)[i]; } break; default: memcpy(&inquiryData->VendorId, "RR18xx ", 8); #ifdef SUPPORT_ARRAY switch(pVDev->VDeviceType){ case VD_RAID_0: if ((pVDev->u.array.pMember[0] && mIsArray(pVDev->u.array.pMember[0])) || (pVDev->u.array.pMember[1] && mIsArray(pVDev->u.array.pMember[1]))) memcpy(&inquiryData->ProductId, "RAID 1/0 Array ", 16); else memcpy(&inquiryData->ProductId, "RAID 0 Array ", 16); break; case VD_RAID_1: if ((pVDev->u.array.pMember[0] && mIsArray(pVDev->u.array.pMember[0])) || (pVDev->u.array.pMember[1] && mIsArray(pVDev->u.array.pMember[1]))) memcpy(&inquiryData->ProductId, "RAID 0/1 Array ", 16); else memcpy(&inquiryData->ProductId, "RAID 1 Array ", 16); break; case VD_RAID_5: memcpy(&inquiryData->ProductId, "RAID 5 Array ", 16); break; case VD_JBOD: memcpy(&inquiryData->ProductId, "JBOD Array ", 16); break; } #endif memcpy(&inquiryData->ProductRevisionLevel, "3.00", 4); break; } } static void hpt_timeout(void *arg) { _VBUS_INST(&((PBUS_DMAMAP)((union ccb *)arg)->ccb_adapter)->pAdapter->VBus) intrmask_t oldspl = lock_driver(); fResetVBus(_VBUS_P0); unlock_driver(oldspl); } static void hpt_io_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { PCommand pCmd = (PCommand)arg; union ccb *ccb = pCmd->pOrgCommand; struct ccb_hdr *ccb_h = &ccb->ccb_h; PBUS_DMAMAP pmap = (PBUS_DMAMAP) ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; PVDevice pVDev = pAdapter->VBus.pVDevice[ccb_h->target_id]; FPSCAT_GATH psg = pCmd->pSgTable; int idx; _VBUS_INST(pVDev->pVBus) HPT_ASSERT(pCmd->cf_physical_sg); if (error || nsegs == 0) panic("busdma error"); HPT_ASSERT(nsegs<= MAX_SG_DESCRIPTORS); for (idx = 0; idx < nsegs; idx++, psg++) { psg->dSgAddress = (ULONG_PTR)(UCHAR *)segs[idx].ds_addr; psg->wSgSize = segs[idx].ds_len; psg->wSgFlag = (idx == nsegs-1)? SG_FLAG_EOT: 0; /* KdPrint(("psg[%d]:add=%p,size=%x,flag=%x\n", idx, psg->dSgAddress,psg->wSgSize,psg->wSgFlag)); */ } /* psg[-1].wSgFlag = SG_FLAG_EOT; */ if (pCmd->cf_data_in) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_PREREAD); } else if (pCmd->cf_data_out) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_PREWRITE); } ccb->ccb_h.timeout_ch = timeout(hpt_timeout, (caddr_t)ccb, 20*hz); pVDev->pfnSendCommand(_VBUS_P pCmd); CheckPendingCall(_VBUS_P0); } static void HPTLIBAPI OsSendCommand(_VBUS_ARG union ccb *ccb) { PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; struct ccb_hdr *ccb_h = &ccb->ccb_h; struct ccb_scsiio *csio = &ccb->csio; PVDevice pVDev = pAdapter->VBus.pVDevice[ccb_h->target_id]; KdPrintI(("OsSendCommand: ccb %p cdb %x-%x-%x\n", ccb, *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[0], *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[4], *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[8] )); pAdapter->outstandingCommands++; if (pVDev == NULL || pVDev->vf_online == 0) { ccb->ccb_h.status = CAM_REQ_INVALID; ccb_done(ccb); goto Command_Complished; } switch(ccb->csio.cdb_io.cdb_bytes[0]) { case TEST_UNIT_READY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: /* FALLTHROUGH */ ccb->ccb_h.status = CAM_REQ_CMP; break; case INQUIRY: ZeroMemory(ccb->csio.data_ptr, ccb->csio.dxfer_len); SetInquiryData((PINQUIRYDATA)ccb->csio.data_ptr, pVDev); ccb_h->status = CAM_REQ_CMP; break; case READ_CAPACITY: { UCHAR *rbuf=csio->data_ptr; unsigned int cap; if (pVDev->VDeviceCapacity > 0xfffffffful) { cap = 0xfffffffful; } else { cap = pVDev->VDeviceCapacity - 1; } rbuf[0] = (UCHAR)(cap>>24); rbuf[1] = (UCHAR)(cap>>16); rbuf[2] = (UCHAR)(cap>>8); rbuf[3] = (UCHAR)cap; /* Claim 512 byte blocks (big-endian). */ rbuf[4] = 0; rbuf[5] = 0; rbuf[6] = 2; rbuf[7] = 0; ccb_h->status = CAM_REQ_CMP; break; } case 0x9e: /*SERVICE_ACTION_IN*/ { UCHAR *rbuf = csio->data_ptr; LBA_T cap = pVDev->VDeviceCapacity - 1; rbuf[0] = (UCHAR)(cap>>56); rbuf[1] = (UCHAR)(cap>>48); rbuf[2] = (UCHAR)(cap>>40); rbuf[3] = (UCHAR)(cap>>32); rbuf[4] = (UCHAR)(cap>>24); rbuf[5] = (UCHAR)(cap>>16); rbuf[6] = (UCHAR)(cap>>8); rbuf[7] = (UCHAR)cap; rbuf[8] = 0; rbuf[9] = 0; rbuf[10] = 2; rbuf[11] = 0; ccb_h->status = CAM_REQ_CMP; break; } case READ_6: case WRITE_6: case READ_10: case WRITE_10: case 0x88: /* READ_16 */ case 0x8a: /* WRITE_16 */ case 0x13: case 0x2f: { UCHAR Cdb[16]; UCHAR CdbLength; _VBUS_INST(pVDev->pVBus) PCommand pCmd = AllocateCommand(_VBUS_P0); HPT_ASSERT(pCmd); CdbLength = csio->cdb_len; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(csio->cdb_io.cdb_ptr, Cdb, CdbLength); } else { KdPrintE(("ERROR!!!\n")); ccb->ccb_h.status = CAM_REQ_INVALID; break; } } else { bcopy(csio->cdb_io.cdb_bytes, Cdb, CdbLength); } pCmd->pOrgCommand = ccb; pCmd->pVDevice = pVDev; pCmd->pfnCompletion = fOsCommandDone; pCmd->pfnBuildSgl = fOsBuildSgl; pCmd->pSgTable = pmap->psg; switch (Cdb[0]) { case READ_6: case WRITE_6: case 0x13: pCmd->uCmd.Ide.Lba = ((ULONG)Cdb[1] << 16) | ((ULONG)Cdb[2] << 8) | (ULONG)Cdb[3]; pCmd->uCmd.Ide.nSectors = (USHORT) Cdb[4]; break; case 0x88: /* READ_16 */ case 0x8a: /* WRITE_16 */ pCmd->uCmd.Ide.Lba = (HPT_U64)Cdb[2] << 56 | (HPT_U64)Cdb[3] << 48 | (HPT_U64)Cdb[4] << 40 | (HPT_U64)Cdb[5] << 32 | (HPT_U64)Cdb[6] << 24 | (HPT_U64)Cdb[7] << 16 | (HPT_U64)Cdb[8] << 8 | (HPT_U64)Cdb[9]; pCmd->uCmd.Ide.nSectors = (USHORT)Cdb[12] << 8 | (USHORT)Cdb[13]; break; default: pCmd->uCmd.Ide.Lba = (ULONG)Cdb[5] | ((ULONG)Cdb[4] << 8) | ((ULONG)Cdb[3] << 16) | ((ULONG)Cdb[2] << 24); pCmd->uCmd.Ide.nSectors = (USHORT) Cdb[8] | ((USHORT)Cdb[7]<<8); break; } switch (Cdb[0]) { case READ_6: case READ_10: case 0x88: /* READ_16 */ pCmd->uCmd.Ide.Command = IDE_COMMAND_READ; pCmd->cf_data_in = 1; break; case WRITE_6: case WRITE_10: case 0x8a: /* WRITE_16 */ pCmd->uCmd.Ide.Command = IDE_COMMAND_WRITE; pCmd->cf_data_out = 1; break; case 0x13: case 0x2f: pCmd->uCmd.Ide.Command = IDE_COMMAND_VERIFY; break; } /*///////////////////////// */ if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { int idx; bus_dma_segment_t *sgList = (bus_dma_segment_t *)ccb->csio.data_ptr; if (ccb->ccb_h.flags & CAM_SG_LIST_PHYS) pCmd->cf_physical_sg = 1; for (idx = 0; idx < ccb->csio.sglist_cnt; idx++) { pCmd->pSgTable[idx].dSgAddress = (ULONG_PTR)(UCHAR *)sgList[idx].ds_addr; pCmd->pSgTable[idx].wSgSize = sgList[idx].ds_len; pCmd->pSgTable[idx].wSgFlag= (idx==ccb->csio.sglist_cnt-1)?SG_FLAG_EOT: 0; } ccb->ccb_h.timeout_ch = timeout(hpt_timeout, (caddr_t)ccb, 20*hz); pVDev->pfnSendCommand(_VBUS_P pCmd); } else { int error; pCmd->cf_physical_sg = 1; error = bus_dmamap_load(pAdapter->io_dma_parent, pmap->dma_map, ccb->csio.data_ptr, ccb->csio.dxfer_len, hpt_io_dmamap_callback, pCmd, BUS_DMA_WAITOK ); KdPrint(("bus_dmamap_load return %d\n", error)); if (error && error!=EINPROGRESS) { hpt_printk(("bus_dmamap_load error %d\n", error)); FreeCommand(_VBUS_P pCmd); ccb->ccb_h.status = CAM_REQ_CMP_ERR; dmamap_put(pmap); pAdapter->outstandingCommands--; xpt_done(ccb); } } goto Command_Complished; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } ccb_done(ccb); Command_Complished: CheckPendingCall(_VBUS_P0); return; } static void HPTLIBAPI fOsCommandDone(_VBUS_ARG PCommand pCmd) { union ccb *ccb = pCmd->pOrgCommand; PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; KdPrint(("fOsCommandDone(pcmd=%p, result=%d)\n", pCmd, pCmd->Result)); untimeout(hpt_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch); switch(pCmd->Result) { case RETURN_SUCCESS: ccb->ccb_h.status = CAM_REQ_CMP; break; case RETURN_BAD_DEVICE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case RETURN_DEVICE_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case RETURN_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case RETURN_SELECTION_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case RETURN_RETRY: ccb->ccb_h.status = CAM_BUSY; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } if (pCmd->cf_data_in) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_POSTREAD); } else if (pCmd->cf_data_in) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(pAdapter->io_dma_parent, pmap->dma_map); FreeCommand(_VBUS_P pCmd); ccb_done(ccb); } int hpt_queue_dpc(HPT_DPC dpc, IAL_ADAPTER_T * pAdapter, void *arg, UCHAR flags) { int p; p = (DpcQueue_Last + 1) % MAX_DPC; if (p==DpcQueue_First) { KdPrint(("DPC Queue full!\n")); return -1; } DpcQueue[DpcQueue_Last].dpc = dpc; DpcQueue[DpcQueue_Last].pAdapter = pAdapter; DpcQueue[DpcQueue_Last].arg = arg; DpcQueue[DpcQueue_Last].flags = flags; DpcQueue_Last = p; return 0; } #ifdef _RAID5N_ /* * Allocate memory above 16M, otherwise we may eat all low memory for ISA devices. * How about the memory for 5081 request/response array and PRD table? */ void *os_alloc_page(_VBUS_ARG0) { return (void *)contigmalloc(0x1000, M_DEVBUF, M_NOWAIT, 0x1000000, 0xffffffff, PAGE_SIZE, 0ul); } void *os_alloc_dma_page(_VBUS_ARG0) { return (void *)contigmalloc(0x1000, M_DEVBUF, M_NOWAIT, 0x1000000, 0xffffffff, PAGE_SIZE, 0ul); } void os_free_page(_VBUS_ARG void *p) { contigfree(p, 0x1000, M_DEVBUF); } void os_free_dma_page(_VBUS_ARG void *p) { contigfree(p, 0x1000, M_DEVBUF); } void DoXor1(ULONG *p0, ULONG *p1, ULONG *p2, UINT nBytes) { UINT i; for (i = 0; i < nBytes / 4; i++) *p0++ = *p1++ ^ *p2++; } void DoXor2(ULONG *p0, ULONG *p2, UINT nBytes) { UINT i; for (i = 0; i < nBytes / 4; i++) *p0++ ^= *p2++; } #endif Index: head/sys/dev/ida/ida_pci.c =================================================================== --- head/sys/dev/ida/ida_pci.c (revision 232853) +++ head/sys/dev/ida/ida_pci.c (revision 232854) @@ -1,317 +1,317 @@ /*- * Copyright (c) 1999,2000 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IDA_PCI_MAX_DMA_ADDR 0xFFFFFFFF #define IDA_PCI_MAX_DMA_COUNT 0xFFFFFFFF #define IDA_PCI_MEMADDR PCIR_BAR(1) /* Mem I/O Address */ #define IDA_DEVICEID_SMART 0xAE100E11 #define IDA_DEVICEID_DEC_SMART 0x00461011 #define IDA_DEVICEID_NCR_53C1510 0x00101000 static int ida_v3_fifo_full(struct ida_softc *ida) { return (ida_inl(ida, R_CMD_FIFO) == 0); } static void ida_v3_submit(struct ida_softc *ida, struct ida_qcb *qcb) { ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr); } static bus_addr_t ida_v3_done(struct ida_softc *ida) { bus_addr_t completed; completed = ida_inl(ida, R_DONE_FIFO); if (completed == -1) { return (0); /* fifo is empty */ } return (completed); } static int ida_v3_int_pending(struct ida_softc *ida) { return (ida_inl(ida, R_INT_PENDING)); } static void ida_v3_int_enable(struct ida_softc *ida, int enable) { if (enable) ida->flags |= IDA_INTERRUPTS; else ida->flags &= ~IDA_INTERRUPTS; ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE); } static int ida_v4_fifo_full(struct ida_softc *ida) { return (ida_inl(ida, R_42XX_REQUEST) != 0); } static void ida_v4_submit(struct ida_softc *ida, struct ida_qcb *qcb) { ida_outl(ida, R_42XX_REQUEST, qcb->hwqcb_busaddr); } static bus_addr_t ida_v4_done(struct ida_softc *ida) { bus_addr_t completed; completed = ida_inl(ida, R_42XX_REPLY); if (completed == -1) return (0); /* fifo is empty */ ida_outl(ida, R_42XX_REPLY, 0); /* confirm read */ return (completed); } static int ida_v4_int_pending(struct ida_softc *ida) { return (ida_inl(ida, R_42XX_STATUS) & STATUS_42XX_INT_PENDING); } static void ida_v4_int_enable(struct ida_softc *ida, int enable) { if (enable) ida->flags |= IDA_INTERRUPTS; else ida->flags &= ~IDA_INTERRUPTS; ida_outl(ida, R_42XX_INT_MASK, enable ? INT_ENABLE_42XX : INT_DISABLE_42XX); } static struct ida_access ida_v3_access = { ida_v3_fifo_full, ida_v3_submit, ida_v3_done, ida_v3_int_pending, ida_v3_int_enable, }; static struct ida_access ida_v4_access = { ida_v4_fifo_full, ida_v4_submit, ida_v4_done, ida_v4_int_pending, ida_v4_int_enable, }; static struct ida_board board_id[] = { { 0x40300E11, "Compaq SMART-2/P array controller", &ida_v3_access, 0 }, { 0x40310E11, "Compaq SMART-2SL array controller", &ida_v3_access, 0 }, { 0x40320E11, "Compaq Smart Array 3200 controller", &ida_v3_access, 0 }, { 0x40330E11, "Compaq Smart Array 3100ES controller", &ida_v3_access, 0 }, { 0x40340E11, "Compaq Smart Array 221 controller", &ida_v3_access, 0 }, { 0x40400E11, "Compaq Integrated Array controller", &ida_v4_access, IDA_FIRMWARE }, { 0x40480E11, "Compaq RAID LC2 controller", &ida_v4_access, IDA_FIRMWARE }, { 0x40500E11, "Compaq Smart Array 4200 controller", &ida_v4_access, 0 }, { 0x40510E11, "Compaq Smart Array 4250ES controller", &ida_v4_access, 0 }, { 0x40580E11, "Compaq Smart Array 431 controller", &ida_v4_access, 0 }, { 0, "", 0, 0 }, }; static int ida_pci_probe(device_t dev); static int ida_pci_attach(device_t dev); static device_method_t ida_pci_methods[] = { DEVMETHOD(device_probe, ida_pci_probe), DEVMETHOD(device_attach, ida_pci_attach), DEVMETHOD(device_detach, ida_detach), DEVMETHOD_END }; static driver_t ida_pci_driver = { "ida", ida_pci_methods, sizeof(struct ida_softc) }; static devclass_t ida_devclass; static struct ida_board * ida_pci_match(device_t dev) { int i; u_int32_t id, sub_id; id = pci_get_devid(dev); sub_id = pci_get_subdevice(dev) << 16 | pci_get_subvendor(dev); if (id == IDA_DEVICEID_SMART || id == IDA_DEVICEID_DEC_SMART || id == IDA_DEVICEID_NCR_53C1510) { for (i = 0; board_id[i].board; i++) if (board_id[i].board == sub_id) return (&board_id[i]); } return (NULL); } static int ida_pci_probe(device_t dev) { struct ida_board *board = ida_pci_match(dev); if (board != NULL) { device_set_desc(dev, board->desc); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int ida_pci_attach(device_t dev) { struct ida_board *board = ida_pci_match(dev); u_int32_t id = pci_get_devid(dev); struct ida_softc *ida; u_int command; int error, rid; command = pci_read_config(dev, PCIR_COMMAND, 1); /* * it appears that this board only does MEMIO access. */ if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "Only memory mapped I/O is supported\n"); return (ENXIO); } ida = (struct ida_softc *)device_get_softc(dev); ida->dev = dev; ida->cmd = *board->accessor; ida->flags = board->flags; ida->regs_res_type = SYS_RES_MEMORY; ida->regs_res_id = IDA_PCI_MEMADDR; if (id == IDA_DEVICEID_DEC_SMART) ida->regs_res_id = PCIR_BAR(0); ida->regs = bus_alloc_resource_any(dev, ida->regs_res_type, &ida->regs_res_id, RF_ACTIVE); if (ida->regs == NULL) { device_printf(dev, "can't allocate memory resources\n"); return (ENOMEM); } error = bus_dma_tag_create( - /* parent */ NULL, + /* parent */ bus_get_dma_tag(dev), /* alignment */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ MAXBSIZE, /* nsegments */ IDA_NSEG, /* maxsegsize */ BUS_SPACE_MAXSIZE_32BIT, /* flags */ BUS_DMA_ALLOCNOW, /* lockfunc */ NULL, /* lockarg */ NULL, &ida->parent_dmat); if (error != 0) { device_printf(dev, "can't allocate DMA tag\n"); ida_free(ida); return (ENOMEM); } rid = 0; ida->irq_res_type = SYS_RES_IRQ; ida->irq = bus_alloc_resource_any(dev, ida->irq_res_type, &rid, RF_ACTIVE | RF_SHAREABLE); if (ida->irq == NULL) { ida_free(ida); return (ENOMEM); } error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, ida_intr, ida, &ida->ih); if (error) { device_printf(dev, "can't setup interrupt\n"); ida_free(ida); return (ENOMEM); } error = ida_init(ida); if (error) { ida_free(ida); return (error); } ida_attach(ida); ida->flags |= IDA_ATTACHED; return (0); } DRIVER_MODULE(ida, pci, ida_pci_driver, ida_devclass, 0, 0); Index: head/sys/dev/if_ndis/if_ndis_pci.c =================================================================== --- head/sys/dev/if_ndis/if_ndis_pci.c (revision 232853) +++ head/sys/dev/if_ndis/if_ndis_pci.c (revision 232854) @@ -1,349 +1,349 @@ /*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(ndis, pci, 1, 1, 1); static int ndis_probe_pci (device_t); static int ndis_attach_pci (device_t); static struct resource_list *ndis_get_resource_list (device_t, device_t); static int ndis_devcompare (interface_type, struct ndis_pci_type *, device_t); extern int ndisdrv_modevent (module_t, int, void *); extern int ndis_attach (device_t); extern int ndis_shutdown (device_t); extern int ndis_detach (device_t); extern int ndis_suspend (device_t); extern int ndis_resume (device_t); static device_method_t ndis_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ndis_probe_pci), DEVMETHOD(device_attach, ndis_attach_pci), DEVMETHOD(device_detach, ndis_detach), DEVMETHOD(device_shutdown, ndis_shutdown), DEVMETHOD(device_suspend, ndis_suspend), DEVMETHOD(device_resume, ndis_resume), /* Bus interface */ DEVMETHOD(bus_get_resource_list, ndis_get_resource_list), { 0, 0 } }; static driver_t ndis_driver = { "ndis", ndis_methods, sizeof(struct ndis_softc) }; static devclass_t ndis_devclass; DRIVER_MODULE(ndis, pci, ndis_driver, ndis_devclass, ndisdrv_modevent, 0); static int ndis_devcompare(bustype, t, dev) interface_type bustype; struct ndis_pci_type *t; device_t dev; { uint16_t vid, did; uint32_t subsys; if (bustype != PCIBus) return(FALSE); vid = pci_get_vendor(dev); did = pci_get_device(dev); subsys = pci_get_subdevice(dev); subsys = (subsys << 16) | pci_get_subvendor(dev); while(t->ndis_name != NULL) { if ((t->ndis_vid == vid) && (t->ndis_did == did) && (t->ndis_subsys == subsys || t->ndis_subsys == 0)) { device_set_desc(dev, t->ndis_name); return(TRUE); } t++; } return(FALSE); } /* * Probe for an NDIS device. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int ndis_probe_pci(dev) device_t dev; { driver_object *drv; struct drvdb_ent *db; drv = windrv_lookup(0, "PCI Bus"); if (drv == NULL) return(ENXIO); db = windrv_match((matchfuncptr)ndis_devcompare, dev); if (db != NULL) { /* Create PDO for this device instance */ windrv_create_pdo(drv, dev); return(0); } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int ndis_attach_pci(dev) device_t dev; { struct ndis_softc *sc; int unit, error = 0, rid; struct ndis_pci_type *t; int devidx = 0, defidx = 0; struct resource_list *rl; struct resource_list_entry *rle; struct drvdb_ent *db; uint16_t vid, did; uint32_t subsys; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ndis_dev = dev; db = windrv_match((matchfuncptr)ndis_devcompare, dev); if (db == NULL) return (ENXIO); sc->ndis_dobj = db->windrv_object; sc->ndis_regvals = db->windrv_regvals; /* * Map control/status registers. */ pci_enable_busmaster(dev); rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); if (rl != NULL) { STAILQ_FOREACH(rle, rl, link) { switch (rle->type) { case SYS_RES_IOPORT: sc->ndis_io_rid = rle->rid; sc->ndis_res_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->ndis_io_rid, RF_ACTIVE); if (sc->ndis_res_io == NULL) { device_printf(dev, "couldn't map iospace\n"); error = ENXIO; goto fail; } break; case SYS_RES_MEMORY: if (sc->ndis_res_altmem != NULL && sc->ndis_res_mem != NULL) { device_printf(dev, "too many memory resources\n"); error = ENXIO; goto fail; } if (sc->ndis_res_mem) { sc->ndis_altmem_rid = rle->rid; sc->ndis_res_altmem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->ndis_altmem_rid, RF_ACTIVE); if (sc->ndis_res_altmem == NULL) { device_printf(dev, "couldn't map alt " "memory\n"); error = ENXIO; goto fail; } } else { sc->ndis_mem_rid = rle->rid; sc->ndis_res_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->ndis_mem_rid, RF_ACTIVE); if (sc->ndis_res_mem == NULL) { device_printf(dev, "couldn't map memory\n"); error = ENXIO; goto fail; } } break; case SYS_RES_IRQ: rid = rle->rid; sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->ndis_irq == NULL) { device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } break; default: break; } sc->ndis_rescnt++; } } /* * If the BIOS did not set up an interrupt for this device, * the resource traversal code above will fail to set up * an IRQ resource. This is usually a bad thing, so try to * force the allocation of an interrupt here. If one was * not assigned to us by the BIOS, bus_alloc_resource() * should route one for us. */ if (sc->ndis_irq == NULL) { rid = 0; sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->ndis_irq == NULL) { device_printf(dev, "couldn't route interrupt\n"); error = ENXIO; goto fail; } sc->ndis_rescnt++; } /* * Allocate the parent bus DMA tag appropriate for PCI. */ #define NDIS_NSEG_NEW 32 - error = bus_dma_tag_create(NULL, /* parent */ + error = bus_dma_tag_create(bus_get_dma_tag(dev),/* PCI parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, NDIS_NSEG_NEW,/* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ndis_parent_tag); if (error) goto fail; sc->ndis_iftype = PCIBus; /* Figure out exactly which device we matched. */ vid = pci_get_vendor(dev); did = pci_get_device(dev); subsys = pci_get_subdevice(dev); subsys = (subsys << 16) | pci_get_subvendor(dev); t = db->windrv_devlist; while(t->ndis_name != NULL) { if (t->ndis_vid == vid && t->ndis_did == did) { if (t->ndis_subsys == 0) defidx = devidx; else if (t->ndis_subsys == subsys) break; } t++; devidx++; } if (t->ndis_name == NULL) sc->ndis_devidx = defidx; else sc->ndis_devidx = devidx; error = ndis_attach(dev); fail: return(error); } static struct resource_list * ndis_get_resource_list(dev, child) device_t dev; device_t child; { struct ndis_softc *sc; sc = device_get_softc(dev); return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); } Index: head/sys/dev/iir/iir_pci.c =================================================================== --- head/sys/dev/iir/iir_pci.c (revision 232853) +++ head/sys/dev/iir/iir_pci.c (revision 232854) @@ -1,483 +1,484 @@ /*- * Copyright (c) 2000-03 ICP vortex GmbH * Copyright (c) 2002-03 Intel Corporation * Copyright (c) 2003 Adaptec Inc. * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * iir_pci.c: PCI Bus Attachment for Intel Integrated RAID Controller driver * * Written by: Achim Leubner * Written by: Achim Leubner * Fixes/Additions: Boji Tony Kannanthanam * * TODO: */ /* #include "opt_iir.h" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Mapping registers for various areas */ #define PCI_DPMEM PCIR_BAR(0) /* Product numbers for Fibre-Channel are greater than or equal to 0x200 */ #define GDT_PCI_PRODUCT_FC 0x200 /* PCI SRAM structure */ #define GDT_MAGIC 0x00 /* u_int32_t, controller ID from BIOS */ #define GDT_NEED_DEINIT 0x04 /* u_int16_t, switch between BIOS/driver */ #define GDT_SWITCH_SUPPORT 0x06 /* u_int8_t, see GDT_NEED_DEINIT */ #define GDT_OS_USED 0x10 /* u_int8_t [16], OS code per service */ #define GDT_FW_MAGIC 0x3c /* u_int8_t, controller ID from firmware */ #define GDT_SRAM_SZ 0x40 /* DPRAM PCI controllers */ #define GDT_DPR_IF 0x00 /* interface area */ #define GDT_6SR (0xff0 - GDT_SRAM_SZ) #define GDT_SEMA1 0xff1 /* volatile u_int8_t, command semaphore */ #define GDT_IRQEN 0xff5 /* u_int8_t, board interrupts enable */ #define GDT_EVENT 0xff8 /* u_int8_t, release event */ #define GDT_IRQDEL 0xffc /* u_int8_t, acknowledge board interrupt */ #define GDT_DPRAM_SZ 0x1000 /* PLX register structure (new PCI controllers) */ #define GDT_CFG_REG 0x00 /* u_int8_t, DPRAM cfg. (2: < 1MB, 0: any) */ #define GDT_SEMA0_REG 0x40 /* volatile u_int8_t, command semaphore */ #define GDT_SEMA1_REG 0x41 /* volatile u_int8_t, status semaphore */ #define GDT_PLX_STATUS 0x44 /* volatile u_int16_t, command status */ #define GDT_PLX_SERVICE 0x46 /* u_int16_t, service */ #define GDT_PLX_INFO 0x48 /* u_int32_t [2], additional info */ #define GDT_LDOOR_REG 0x60 /* u_int8_t, PCI to local doorbell */ #define GDT_EDOOR_REG 0x64 /* volatile u_int8_t, local to PCI doorbell */ #define GDT_CONTROL0 0x68 /* u_int8_t, control0 register (unused) */ #define GDT_CONTROL1 0x69 /* u_int8_t, board interrupts enable */ #define GDT_PLX_SZ 0x80 /* DPRAM new PCI controllers */ #define GDT_IC 0x00 /* interface */ #define GDT_PCINEW_6SR (0x4000 - GDT_SRAM_SZ) /* SRAM structure */ #define GDT_PCINEW_SZ 0x4000 /* i960 register structure (PCI MPR controllers) */ #define GDT_MPR_SEMA0 0x10 /* volatile u_int8_t, command semaphore */ #define GDT_MPR_SEMA1 0x12 /* volatile u_int8_t, status semaphore */ #define GDT_MPR_STATUS 0x14 /* volatile u_int16_t, command status */ #define GDT_MPR_SERVICE 0x16 /* u_int16_t, service */ #define GDT_MPR_INFO 0x18 /* u_int32_t [2], additional info */ #define GDT_MPR_LDOOR 0x20 /* u_int8_t, PCI to local doorbell */ #define GDT_MPR_EDOOR 0x2c /* volatile u_int8_t, locl to PCI doorbell */ #define GDT_EDOOR_EN 0x34 /* u_int8_t, board interrupts enable */ #define GDT_SEVERITY 0xefc /* u_int8_t, event severity */ #define GDT_EVT_BUF 0xf00 /* u_int8_t [256], event buffer */ #define GDT_I960_SZ 0x1000 /* DPRAM PCI MPR controllers */ #define GDT_I960R 0x00 /* 4KB i960 registers */ #define GDT_MPR_IC GDT_I960_SZ /* i960 register area */ #define GDT_MPR_6SR (GDT_I960_SZ + 0x3000 - GDT_SRAM_SZ) /* DPRAM struct. */ #define GDT_MPR_SZ (0x3000 - GDT_SRAM_SZ) static int iir_pci_probe(device_t dev); static int iir_pci_attach(device_t dev); void gdt_pci_enable_intr(struct gdt_softc *); void gdt_mpr_copy_cmd(struct gdt_softc *, struct gdt_ccb *); u_int8_t gdt_mpr_get_status(struct gdt_softc *); void gdt_mpr_intr(struct gdt_softc *, struct gdt_intr_ctx *); void gdt_mpr_release_event(struct gdt_softc *); void gdt_mpr_set_sema0(struct gdt_softc *); int gdt_mpr_test_busy(struct gdt_softc *); static device_method_t iir_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iir_pci_probe), DEVMETHOD(device_attach, iir_pci_attach), { 0, 0} }; static driver_t iir_pci_driver = { "iir", iir_pci_methods, sizeof(struct gdt_softc) }; static devclass_t iir_devclass; DRIVER_MODULE(iir, pci, iir_pci_driver, iir_devclass, 0, 0); MODULE_DEPEND(iir, pci, 1, 1, 1); MODULE_DEPEND(iir, cam, 1, 1, 1); static int iir_pci_probe(device_t dev) { if (pci_get_vendor(dev) == INTEL_VENDOR_ID && pci_get_device(dev) == INTEL_DEVICE_ID_IIR) { device_set_desc(dev, "Intel Integrated RAID Controller"); return (BUS_PROBE_DEFAULT); } if (pci_get_vendor(dev) == GDT_VENDOR_ID && ((pci_get_device(dev) >= GDT_DEVICE_ID_MIN && pci_get_device(dev) <= GDT_DEVICE_ID_MAX) || pci_get_device(dev) == GDT_DEVICE_ID_NEWRX)) { device_set_desc(dev, "ICP Disk Array Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int iir_pci_attach(device_t dev) { struct gdt_softc *gdt; struct resource *io = NULL, *irq = NULL; int retries, rid, error = 0; void *ih; u_int8_t protocol; /* map DPMEM */ rid = PCI_DPMEM; io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (io == NULL) { device_printf(dev, "can't allocate register resources\n"); error = ENOMEM; goto err; } /* get IRQ */ rid = 0; irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (irq == NULL) { device_printf(dev, "can't find IRQ value\n"); error = ENOMEM; goto err; } gdt = device_get_softc(dev); gdt->sc_devnode = dev; gdt->sc_init_level = 0; gdt->sc_dpmemt = rman_get_bustag(io); gdt->sc_dpmemh = rman_get_bushandle(io); gdt->sc_dpmembase = rman_get_start(io); gdt->sc_hanum = device_get_unit(dev); gdt->sc_bus = pci_get_bus(dev); gdt->sc_slot = pci_get_slot(dev); gdt->sc_vendor = pci_get_vendor(dev); gdt->sc_device = pci_get_device(dev); gdt->sc_subdevice = pci_get_subdevice(dev); gdt->sc_class = GDT_MPR; /* no FC ctr. if (gdt->sc_device >= GDT_PCI_PRODUCT_FC) gdt->sc_class |= GDT_FC; */ /* initialize RP controller */ /* check and reset interface area */ bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC, htole32(GDT_MPR_MAGIC)); if (bus_space_read_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC) != htole32(GDT_MPR_MAGIC)) { printf("cannot access DPMEM at 0x%jx (shadowed?)\n", (uintmax_t)gdt->sc_dpmembase); error = ENXIO; goto err; } bus_space_set_region_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_I960_SZ, htole32(0), GDT_MPR_SZ >> 2); /* Disable everything */ bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_EDOOR_EN, bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_EDOOR_EN) | 4); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_EDOOR, 0xff); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_STATUS, 0); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_CMD_INDEX, 0); bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO, htole32(gdt->sc_dpmembase)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_CMD_INDX, 0xff); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_LDOOR, 1); DELAY(20); retries = GDT_RETRIES; while (bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_STATUS) != 0xff) { if (--retries == 0) { printf("DEINIT failed\n"); error = ENXIO; goto err; } DELAY(1); } protocol = (uint8_t)le32toh(bus_space_read_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_STATUS, 0); if (protocol != GDT_PROTOCOL_VERSION) { printf("unsupported protocol %d\n", protocol); error = ENXIO; goto err; } /* special commnd to controller BIOS */ bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO, htole32(0)); bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO + sizeof (u_int32_t), htole32(0)); bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO + 2 * sizeof (u_int32_t), htole32(1)); bus_space_write_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_INFO + 3 * sizeof (u_int32_t), htole32(0)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_CMD_INDX, 0xfe); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_LDOOR, 1); DELAY(20); retries = GDT_RETRIES; while (bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_STATUS) != 0xfe) { if (--retries == 0) { printf("initialization error\n"); error = ENXIO; goto err; } DELAY(1); } bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_S_STATUS, 0); gdt->sc_ic_all_size = GDT_MPR_SZ; gdt->sc_copy_cmd = gdt_mpr_copy_cmd; gdt->sc_get_status = gdt_mpr_get_status; gdt->sc_intr = gdt_mpr_intr; gdt->sc_release_event = gdt_mpr_release_event; gdt->sc_set_sema0 = gdt_mpr_set_sema0; gdt->sc_test_busy = gdt_mpr_test_busy; /* Allocate a dmatag representing the capabilities of this attachment */ /* XXX Should be a child of the PCI bus dma tag */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignemnt*/1, /*boundary*/0, + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), + /*alignemnt*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/BUS_SPACE_MAXSIZE_32BIT, /*nsegments*/GDT_MAXSG, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, &gdt->sc_parent_dmat) != 0) { error = ENXIO; goto err; } gdt->sc_init_level++; if (iir_init(gdt) != 0) { iir_free(gdt); error = ENXIO; goto err; } /* Register with the XPT */ iir_attach(gdt); /* associate interrupt handler */ if (bus_setup_intr( dev, irq, INTR_TYPE_CAM, NULL, iir_intr, gdt, &ih )) { device_printf(dev, "Unable to register interrupt handler\n"); error = ENXIO; goto err; } gdt_pci_enable_intr(gdt); return (0); err: if (irq) bus_release_resource( dev, SYS_RES_IRQ, 0, irq ); /* if (io) bus_release_resource( dev, SYS_RES_MEMORY, rid, io ); */ return (error); } /* Enable interrupts */ void gdt_pci_enable_intr(struct gdt_softc *gdt) { GDT_DPRINTF(GDT_D_INTR, ("gdt_pci_enable_intr(%p) ", gdt)); switch(GDT_CLASS(gdt)) { case GDT_MPR: bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_EDOOR, 0xff); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_EDOOR_EN, bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_EDOOR_EN) & ~4); break; } } /* * MPR PCI controller-specific functions */ void gdt_mpr_copy_cmd(struct gdt_softc *gdt, struct gdt_ccb *gccb) { u_int16_t cp_count = roundup(gccb->gc_cmd_len, sizeof (u_int32_t)); u_int16_t dp_offset = gdt->sc_cmd_off; u_int16_t cmd_no = gdt->sc_cmd_cnt++; GDT_DPRINTF(GDT_D_CMD, ("gdt_mpr_copy_cmd(%p) ", gdt)); gdt->sc_cmd_off += cp_count; bus_space_write_region_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_DPR_CMD + dp_offset, (u_int32_t *)gccb->gc_cmd, cp_count >> 2); bus_space_write_2(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_COMM_QUEUE + cmd_no * GDT_COMM_Q_SZ + GDT_OFFSET, htole16(GDT_DPMEM_COMMAND_OFFSET + dp_offset)); bus_space_write_2(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_IC + GDT_COMM_QUEUE + cmd_no * GDT_COMM_Q_SZ + GDT_SERV_ID, htole16(gccb->gc_service)); } u_int8_t gdt_mpr_get_status(struct gdt_softc *gdt) { GDT_DPRINTF(GDT_D_MISC, ("gdt_mpr_get_status(%p) ", gdt)); return bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_EDOOR); } void gdt_mpr_intr(struct gdt_softc *gdt, struct gdt_intr_ctx *ctx) { int i; GDT_DPRINTF(GDT_D_INTR, ("gdt_mpr_intr(%p) ", gdt)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_EDOOR, 0xff); if (ctx->istatus & 0x80) { /* error flag */ ctx->istatus &= ~0x80; ctx->cmd_status = bus_space_read_2(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_STATUS); } else /* no error */ ctx->cmd_status = GDT_S_OK; ctx->info = bus_space_read_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_INFO); ctx->service = bus_space_read_2(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_SERVICE); ctx->info2 = bus_space_read_4(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_INFO + sizeof (u_int32_t)); /* event string */ if (ctx->istatus == GDT_ASYNCINDEX) { if (ctx->service != GDT_SCREENSERVICE && (gdt->sc_fw_vers & 0xff) >= 0x1a) { gdt->sc_dvr.severity = bus_space_read_1(gdt->sc_dpmemt,gdt->sc_dpmemh, GDT_SEVERITY); for (i = 0; i < 256; ++i) { gdt->sc_dvr.event_string[i] = bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_EVT_BUF + i); if (gdt->sc_dvr.event_string[i] == 0) break; } } } bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_SEMA1, 0); } void gdt_mpr_release_event(struct gdt_softc *gdt) { GDT_DPRINTF(GDT_D_MISC, ("gdt_mpr_release_event(%p) ", gdt)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_LDOOR, 1); } void gdt_mpr_set_sema0(struct gdt_softc *gdt) { GDT_DPRINTF(GDT_D_MISC, ("gdt_mpr_set_sema0(%p) ", gdt)); bus_space_write_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_SEMA0, 1); } int gdt_mpr_test_busy(struct gdt_softc *gdt) { GDT_DPRINTF(GDT_D_MISC, ("gdt_mpr_test_busy(%p) ", gdt)); return (bus_space_read_1(gdt->sc_dpmemt, gdt->sc_dpmemh, GDT_MPR_SEMA0) & 1); } Index: head/sys/dev/ips/ips_pci.c =================================================================== --- head/sys/dev/ips/ips_pci.c (revision 232853) +++ head/sys/dev/ips/ips_pci.c (revision 232854) @@ -1,238 +1,238 @@ /*- * Copyright (c) 2002 Adaptec Inc. * All rights reserved. * * Written by: David Jeffery * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include static int ips_pci_free(ips_softc_t *sc); static void ips_intrhook(void *arg); static int ips_pci_probe(device_t dev) { if ((pci_get_vendor(dev) == IPS_VENDOR_ID) && (pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)) { device_set_desc(dev, "IBM ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } else if ((pci_get_vendor(dev) == IPS_VENDOR_ID) && (pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID)) { device_set_desc(dev, "IBM ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } else if ((pci_get_vendor(dev) == IPS_VENDOR_ID_ADAPTEC) && (pci_get_device(dev) == IPS_MARCO_DEVICE_ID)) { device_set_desc(dev, "Adaptec ServeRAID Adapter"); return (BUS_PROBE_DEFAULT); } return(ENXIO); } static int ips_pci_attach(device_t dev) { u_int32_t command; ips_softc_t *sc; if (resource_disabled(device_get_name(dev), device_get_unit(dev))) { device_printf(dev, "device is disabled\n"); /* but return 0 so the !$)$)*!$*) unit isn't reused */ return (0); } DEVICE_PRINTF(1, dev, "in attach.\n"); sc = (ips_softc_t *)device_get_softc(dev); if(!sc){ printf("how is sc NULL?!\n"); return (ENXIO); } bzero(sc, sizeof(ips_softc_t)); sc->dev = dev; if(pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID){ sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; sc->ips_poll_cmd = ips_morpheus_poll; } else if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID){ sc->ips_adapter_reinit = ips_copperhead_reinit; sc->ips_adapter_intr = ips_copperhead_intr; sc->ips_issue_cmd = ips_issue_copperhead_cmd; sc->ips_poll_cmd = ips_copperhead_poll; } else if (pci_get_device(dev) == IPS_MARCO_DEVICE_ID){ sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; sc->ips_poll_cmd = ips_morpheus_poll; } else goto error; /* make sure busmastering is on */ command = pci_read_config(dev, PCIR_COMMAND, 1); command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 1); /* seting up io space */ sc->iores = NULL; if(command & PCIM_CMD_MEMEN){ PRINTF(10, "trying MEMIO\n"); if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID) sc->rid = PCIR_BAR(1); else sc->rid = PCIR_BAR(0); sc->iotype = SYS_RES_MEMORY; sc->iores = bus_alloc_resource_any(dev, sc->iotype, &sc->rid, RF_ACTIVE); } if(!sc->iores && command & PCIM_CMD_PORTEN){ PRINTF(10, "trying PORTIO\n"); sc->rid = PCIR_BAR(0); sc->iotype = SYS_RES_IOPORT; sc->iores = bus_alloc_resource_any(dev, sc->iotype, &sc->rid, RF_ACTIVE); } if(sc->iores == NULL){ device_printf(dev, "resource allocation failed\n"); return (ENXIO); } sc->bustag = rman_get_bustag(sc->iores); sc->bushandle = rman_get_bushandle(sc->iores); /*allocate an interrupt. when does the irq become active? after leaving attach? */ sc->irqrid = 0; if(!(sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, RF_SHAREABLE | RF_ACTIVE))){ device_printf(dev, "irq allocation failed\n"); goto error; } if(bus_setup_intr(dev, sc->irqres, INTR_TYPE_BIO|INTR_MPSAFE, NULL, sc->ips_adapter_intr, sc, &sc->irqcookie)){ device_printf(dev, "irq setup failed\n"); goto error; } - if (bus_dma_tag_create( /* parent */ NULL, + if (bus_dma_tag_create( /* PCI parent */bus_get_dma_tag(dev), /* alignemnt */ 1, /* boundary */ 0, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ BUS_SPACE_MAXSIZE_32BIT, /* numsegs */ IPS_MAX_SG_ELEMENTS, /* maxsegsize*/ BUS_SPACE_MAXSIZE_32BIT, /* flags */ 0, /* lockfunc */ NULL, /* lockarg */ NULL, &sc->adapter_dmatag) != 0) { printf("IPS can't alloc dma tag\n"); goto error; } sc->ips_ich.ich_func = ips_intrhook; sc->ips_ich.ich_arg = sc; mtx_init(&sc->queue_mtx, "IPS bioqueue lock", NULL, MTX_DEF); sema_init(&sc->cmd_sema, 0, "IPS Command Semaphore"); bioq_init(&sc->queue); if (config_intrhook_establish(&sc->ips_ich) != 0) { printf("IPS can't establish configuration hook\n"); goto error; } return 0; error: ips_pci_free(sc); return (ENXIO); } static void ips_intrhook(void *arg) { struct ips_softc *sc = (struct ips_softc *)arg; config_intrhook_disestablish(&sc->ips_ich); if (ips_adapter_init(sc)) ips_pci_free(sc); else sc->configured = 1; } static int ips_pci_free(ips_softc_t *sc) { if(sc->adapter_dmatag) bus_dma_tag_destroy(sc->adapter_dmatag); if(sc->irqcookie) bus_teardown_intr(sc->dev, sc->irqres, sc->irqcookie); if(sc->irqres) bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqrid, sc->irqres); if(sc->iores) bus_release_resource(sc->dev, sc->iotype, sc->rid, sc->iores); sc->configured = 0; mtx_destroy(&sc->queue_mtx); sema_destroy(&sc->cmd_sema); return 0; } static int ips_pci_detach(device_t dev) { ips_softc_t *sc; DEVICE_PRINTF(1, dev, "detaching ServeRaid\n"); sc = (ips_softc_t *) device_get_softc(dev); if (sc->configured) { sc->configured = 0; ips_flush_cache(sc); if(ips_adapter_free(sc)) return EBUSY; ips_pci_free(sc); bioq_flush(&sc->queue, NULL, ENXIO); } return 0; } static int ips_pci_shutdown(device_t dev) { ips_softc_t *sc = (ips_softc_t *) device_get_softc(dev); if (sc->configured) { ips_flush_cache(sc); } return 0; } static device_method_t ips_driver_methods[] = { DEVMETHOD(device_probe, ips_pci_probe), DEVMETHOD(device_attach, ips_pci_attach), DEVMETHOD(device_detach, ips_pci_detach), DEVMETHOD(device_shutdown, ips_pci_shutdown), {0,0} }; static driver_t ips_pci_driver = { "ips", ips_driver_methods, sizeof(ips_softc_t), }; static devclass_t ips_devclass; DRIVER_MODULE(ips, pci, ips_pci_driver, ips_devclass, 0, 0); Index: head/sys/dev/mfi/mfi_pci.c =================================================================== --- head/sys/dev/mfi/mfi_pci.c (revision 232853) +++ head/sys/dev/mfi/mfi_pci.c (revision 232854) @@ -1,320 +1,320 @@ /*- * Copyright (c) 2006 IronPort Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2007 LSI Corp. * Copyright (c) 2007 Rajesh Prabhakaran. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* PCI/PCI-X/PCIe bus interface for the LSI MegaSAS controllers */ #include "opt_mfi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int mfi_pci_probe(device_t); static int mfi_pci_attach(device_t); static int mfi_pci_detach(device_t); static int mfi_pci_suspend(device_t); static int mfi_pci_resume(device_t); static void mfi_pci_free(struct mfi_softc *); static device_method_t mfi_methods[] = { DEVMETHOD(device_probe, mfi_pci_probe), DEVMETHOD(device_attach, mfi_pci_attach), DEVMETHOD(device_detach, mfi_pci_detach), DEVMETHOD(device_suspend, mfi_pci_suspend), DEVMETHOD(device_resume, mfi_pci_resume), DEVMETHOD_END }; static driver_t mfi_pci_driver = { "mfi", mfi_methods, sizeof(struct mfi_softc) }; static devclass_t mfi_devclass; DRIVER_MODULE(mfi, pci, mfi_pci_driver, mfi_devclass, 0, 0); MODULE_VERSION(mfi, 1); static int mfi_msi = 0; TUNABLE_INT("hw.mfi.msi", &mfi_msi); SYSCTL_INT(_hw_mfi, OID_AUTO, msi, CTLFLAG_RDTUN, &mfi_msi, 0, "Enable use of MSI interrupts"); struct mfi_ident { uint16_t vendor; uint16_t device; uint16_t subvendor; uint16_t subdevice; int flags; const char *desc; } mfi_identifiers[] = { {0x1000, 0x0060, 0x1028, 0xffff, MFI_FLAGS_1078, "Dell PERC 6"}, {0x1000, 0x0060, 0xffff, 0xffff, MFI_FLAGS_1078, "LSI MegaSAS 1078"}, {0x1000, 0x0078, 0xffff, 0xffff, MFI_FLAGS_GEN2, "LSI MegaSAS Gen2"}, {0x1000, 0x0079, 0x1028, 0x1f15, MFI_FLAGS_GEN2, "Dell PERC H800 Adapter"}, {0x1000, 0x0079, 0x1028, 0x1f16, MFI_FLAGS_GEN2, "Dell PERC H700 Adapter"}, {0x1000, 0x0079, 0x1028, 0x1f17, MFI_FLAGS_GEN2, "Dell PERC H700 Integrated"}, {0x1000, 0x0079, 0x1028, 0x1f18, MFI_FLAGS_GEN2, "Dell PERC H700 Modular"}, {0x1000, 0x0079, 0x1028, 0x1f19, MFI_FLAGS_GEN2, "Dell PERC H700"}, {0x1000, 0x0079, 0x1028, 0x1f1b, MFI_FLAGS_GEN2, "Dell PERC H800"}, {0x1000, 0x0079, 0x1028, 0xffff, MFI_FLAGS_GEN2, "Dell PERC Gen2"}, {0x1000, 0x0079, 0xffff, 0xffff, MFI_FLAGS_GEN2, "LSI MegaSAS Gen2"}, {0x1000, 0x007c, 0xffff, 0xffff, MFI_FLAGS_1078, "LSI MegaSAS 1078"}, {0x1000, 0x0411, 0xffff, 0xffff, MFI_FLAGS_1064R, "LSI MegaSAS 1064R"}, /* Brocton IOP */ {0x1000, 0x0413, 0xffff, 0xffff, MFI_FLAGS_1064R, "LSI MegaSAS 1064R"}, /* Verde ZCR */ {0x1028, 0x0015, 0xffff, 0xffff, MFI_FLAGS_1064R, "Dell PERC 5/i"}, {0, 0, 0, 0, 0, NULL} }; static struct mfi_ident * mfi_find_ident(device_t dev) { struct mfi_ident *m; for (m = mfi_identifiers; m->vendor != 0; m++) { if ((m->vendor == pci_get_vendor(dev)) && (m->device == pci_get_device(dev)) && ((m->subvendor == pci_get_subvendor(dev)) || (m->subvendor == 0xffff)) && ((m->subdevice == pci_get_subdevice(dev)) || (m->subdevice == 0xffff))) return (m); } return (NULL); } static int mfi_pci_probe(device_t dev) { struct mfi_ident *id; if ((id = mfi_find_ident(dev)) != NULL) { device_set_desc(dev, id->desc); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int mfi_pci_attach(device_t dev) { struct mfi_softc *sc; struct mfi_ident *m; uint32_t command; int count, error; sc = device_get_softc(dev); bzero(sc, sizeof(*sc)); sc->mfi_dev = dev; m = mfi_find_ident(dev); sc->mfi_flags = m->flags; /* Verify that the adapter can be set up in PCI space */ command = pci_read_config(dev, PCIR_COMMAND, 2); command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_BUSMASTEREN) == 0) { device_printf(dev, "Can't enable PCI busmaster\n"); return (ENXIO); } if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "PCI memory window not available\n"); return (ENXIO); } /* Allocate PCI registers */ if ((sc->mfi_flags & MFI_FLAGS_1064R) || (sc->mfi_flags & MFI_FLAGS_1078)) { /* 1068/1078: Memory mapped BAR is at offset 0x10 */ sc->mfi_regs_rid = PCIR_BAR(0); } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { /* GEN2: Memory mapped BAR is at offset 0x14 */ sc->mfi_regs_rid = PCIR_BAR(1); } if ((sc->mfi_regs_resource = bus_alloc_resource_any(sc->mfi_dev, SYS_RES_MEMORY, &sc->mfi_regs_rid, RF_ACTIVE)) == NULL) { device_printf(dev, "Cannot allocate PCI registers\n"); return (ENXIO); } sc->mfi_btag = rman_get_bustag(sc->mfi_regs_resource); sc->mfi_bhandle = rman_get_bushandle(sc->mfi_regs_resource); error = ENOMEM; /* Allocate parent DMA tag */ - if (bus_dma_tag_create( NULL, /* parent */ + if (bus_dma_tag_create( bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mfi_parent_dmat)) { device_printf(dev, "Cannot allocate parent DMA tag\n"); goto out; } /* Allocate IRQ resource. */ sc->mfi_irq_rid = 0; count = 1; if (mfi_msi && pci_alloc_msi(sc->mfi_dev, &count) == 0) { device_printf(sc->mfi_dev, "Using MSI\n"); sc->mfi_irq_rid = 1; } if ((sc->mfi_irq = bus_alloc_resource_any(sc->mfi_dev, SYS_RES_IRQ, &sc->mfi_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(sc->mfi_dev, "Cannot allocate interrupt\n"); error = EINVAL; goto out; } error = mfi_attach(sc); out: if (error) { mfi_free(sc); mfi_pci_free(sc); } return (error); } static int mfi_pci_detach(device_t dev) { struct mfi_softc *sc; struct mfi_disk *ld; int error; sc = device_get_softc(dev); sx_xlock(&sc->mfi_config_lock); mtx_lock(&sc->mfi_io_lock); if ((sc->mfi_flags & MFI_FLAGS_OPEN) != 0) { mtx_unlock(&sc->mfi_io_lock); sx_xunlock(&sc->mfi_config_lock); return (EBUSY); } sc->mfi_detaching = 1; mtx_unlock(&sc->mfi_io_lock); while ((ld = TAILQ_FIRST(&sc->mfi_ld_tqh)) != NULL) { if ((error = device_delete_child(dev, ld->ld_dev)) != 0) { sc->mfi_detaching = 0; sx_xunlock(&sc->mfi_config_lock); return (error); } } sx_xunlock(&sc->mfi_config_lock); EVENTHANDLER_DEREGISTER(shutdown_final, sc->mfi_eh); mfi_shutdown(sc); mfi_free(sc); mfi_pci_free(sc); return (0); } static void mfi_pci_free(struct mfi_softc *sc) { if (sc->mfi_regs_resource != NULL) { bus_release_resource(sc->mfi_dev, SYS_RES_MEMORY, sc->mfi_regs_rid, sc->mfi_regs_resource); } if (sc->mfi_irq_rid != 0) pci_release_msi(sc->mfi_dev); return; } static int mfi_pci_suspend(device_t dev) { return (EINVAL); } static int mfi_pci_resume(device_t dev) { return (EINVAL); } Index: head/sys/dev/mlx/mlx_pci.c =================================================================== --- head/sys/dev/mlx/mlx_pci.c (revision 232853) +++ head/sys/dev/mlx/mlx_pci.c (revision 232854) @@ -1,223 +1,223 @@ /*- * Copyright (c) 1999 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int mlx_pci_probe(device_t dev); static int mlx_pci_attach(device_t dev); static device_method_t mlx_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mlx_pci_probe), DEVMETHOD(device_attach, mlx_pci_attach), DEVMETHOD(device_detach, mlx_detach), DEVMETHOD(device_shutdown, mlx_shutdown), DEVMETHOD(device_suspend, mlx_suspend), DEVMETHOD(device_resume, mlx_resume), DEVMETHOD_END }; static driver_t mlx_pci_driver = { "mlx", mlx_methods, sizeof(struct mlx_softc) }; DRIVER_MODULE(mlx, pci, mlx_pci_driver, mlx_devclass, 0, 0); struct mlx_ident { u_int16_t vendor; u_int16_t device; u_int16_t subvendor; u_int16_t subdevice; int iftype; char *desc; } mlx_identifiers[] = { {0x1069, 0x0001, 0x0000, 0x0000, MLX_IFTYPE_2, "Mylex version 2 RAID interface"}, {0x1069, 0x0002, 0x0000, 0x0000, MLX_IFTYPE_3, "Mylex version 3 RAID interface"}, {0x1069, 0x0010, 0x0000, 0x0000, MLX_IFTYPE_4, "Mylex version 4 RAID interface"}, {0x1011, 0x1065, 0x1069, 0x0020, MLX_IFTYPE_5, "Mylex version 5 RAID interface"}, {0, 0, 0, 0, 0, 0} }; static int mlx_pci_probe(device_t dev) { struct mlx_ident *m; debug_called(1); for (m = mlx_identifiers; m->vendor != 0; m++) { if ((m->vendor == pci_get_vendor(dev)) && (m->device == pci_get_device(dev)) && ((m->subvendor == 0) || ((m->subvendor == pci_get_subvendor(dev)) && (m->subdevice == pci_get_subdevice(dev))))) { device_set_desc(dev, m->desc); return(BUS_PROBE_DEFAULT); } } return(ENXIO); } static int mlx_pci_attach(device_t dev) { struct mlx_softc *sc; int i, error; u_int32_t command; debug_called(1); /* * Make sure we are going to be able to talk to this board. */ command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_MEMEN) == 0) { device_printf(dev, "memory window not available\n"); return(ENXIO); } /* force the busmaster enable bit on */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); /* * Initialise softc. */ sc = device_get_softc(dev); bzero(sc, sizeof(*sc)); sc->mlx_dev = dev; /* * Work out what sort of adapter this is (we need to know this in order * to map the appropriate interface resources). */ sc->mlx_iftype = 0; for (i = 0; mlx_identifiers[i].vendor != 0; i++) { if ((mlx_identifiers[i].vendor == pci_get_vendor(dev)) && (mlx_identifiers[i].device == pci_get_device(dev))) { sc->mlx_iftype = mlx_identifiers[i].iftype; break; } } if (sc->mlx_iftype == 0) /* shouldn't happen */ return(ENXIO); /* * Allocate the PCI register window. */ /* type 2/3 adapters have an I/O region we don't prefer at base 0 */ switch(sc->mlx_iftype) { case MLX_IFTYPE_2: case MLX_IFTYPE_3: sc->mlx_mem_type = SYS_RES_MEMORY; sc->mlx_mem_rid = MLX_CFG_BASE1; sc->mlx_mem = bus_alloc_resource_any(dev, sc->mlx_mem_type, &sc->mlx_mem_rid, RF_ACTIVE); if (sc->mlx_mem == NULL) { sc->mlx_mem_type = SYS_RES_IOPORT; sc->mlx_mem_rid = MLX_CFG_BASE0; sc->mlx_mem = bus_alloc_resource_any(dev, sc->mlx_mem_type, &sc->mlx_mem_rid, RF_ACTIVE); } break; case MLX_IFTYPE_4: case MLX_IFTYPE_5: sc->mlx_mem_type = SYS_RES_MEMORY; sc->mlx_mem_rid = MLX_CFG_BASE0; sc->mlx_mem = bus_alloc_resource_any(dev, sc->mlx_mem_type, &sc->mlx_mem_rid, RF_ACTIVE); break; } if (sc->mlx_mem == NULL) { device_printf(sc->mlx_dev, "couldn't allocate mailbox window\n"); mlx_free(sc); return(ENXIO); } sc->mlx_btag = rman_get_bustag(sc->mlx_mem); sc->mlx_bhandle = rman_get_bushandle(sc->mlx_mem); /* * Allocate the parent bus DMA tag appropriate for PCI. */ - error = bus_dma_tag_create(NULL, /* parent */ + error = bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, MLX_NSEG, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->mlx_parent_dmat); if (error != 0) { device_printf(dev, "can't allocate parent DMA tag\n"); mlx_free(sc); return(ENOMEM); } /* * Do bus-independant initialisation. */ error = mlx_attach(sc); if (error != 0) { mlx_free(sc); return(error); } /* * Start the controller. */ mlx_startup(sc); return(0); } Index: head/sys/dev/mly/mly.c =================================================================== --- head/sys/dev/mly/mly.c (revision 232853) +++ head/sys/dev/mly/mly.c (revision 232854) @@ -1,2994 +1,2994 @@ /*- * Copyright (c) 2000, 2001 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int mly_probe(device_t dev); static int mly_attach(device_t dev); static int mly_pci_attach(struct mly_softc *sc); static int mly_detach(device_t dev); static int mly_shutdown(device_t dev); static void mly_intr(void *arg); static int mly_sg_map(struct mly_softc *sc); static void mly_sg_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int mly_mmbox_map(struct mly_softc *sc); static void mly_mmbox_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); static void mly_free(struct mly_softc *sc); static int mly_get_controllerinfo(struct mly_softc *sc); static void mly_scan_devices(struct mly_softc *sc); static void mly_rescan_btl(struct mly_softc *sc, int bus, int target); static void mly_complete_rescan(struct mly_command *mc); static int mly_get_eventstatus(struct mly_softc *sc); static int mly_enable_mmbox(struct mly_softc *sc); static int mly_flush(struct mly_softc *sc); static int mly_ioctl(struct mly_softc *sc, struct mly_command_ioctl *ioctl, void **data, size_t datasize, u_int8_t *status, void *sense_buffer, size_t *sense_length); static void mly_check_event(struct mly_softc *sc); static void mly_fetch_event(struct mly_softc *sc); static void mly_complete_event(struct mly_command *mc); static void mly_process_event(struct mly_softc *sc, struct mly_event *me); static void mly_periodic(void *data); static int mly_immediate_command(struct mly_command *mc); static int mly_start(struct mly_command *mc); static void mly_done(struct mly_softc *sc); static void mly_complete(void *context, int pending); static int mly_alloc_command(struct mly_softc *sc, struct mly_command **mcp); static void mly_release_command(struct mly_command *mc); static void mly_alloc_commands_map(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int mly_alloc_commands(struct mly_softc *sc); static void mly_release_commands(struct mly_softc *sc); static void mly_map_command(struct mly_command *mc); static void mly_unmap_command(struct mly_command *mc); static int mly_cam_attach(struct mly_softc *sc); static void mly_cam_detach(struct mly_softc *sc); static void mly_cam_rescan_btl(struct mly_softc *sc, int bus, int target); static void mly_cam_action(struct cam_sim *sim, union ccb *ccb); static int mly_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio); static void mly_cam_poll(struct cam_sim *sim); static void mly_cam_complete(struct mly_command *mc); static struct cam_periph *mly_find_periph(struct mly_softc *sc, int bus, int target); static int mly_name_device(struct mly_softc *sc, int bus, int target); static int mly_fwhandshake(struct mly_softc *sc); static void mly_describe_controller(struct mly_softc *sc); #ifdef MLY_DEBUG static void mly_printstate(struct mly_softc *sc); static void mly_print_command(struct mly_command *mc); static void mly_print_packet(struct mly_command *mc); static void mly_panic(struct mly_softc *sc, char *reason); static int mly_timeout(struct mly_softc *sc); #endif void mly_print_controller(int controller); static d_open_t mly_user_open; static d_close_t mly_user_close; static d_ioctl_t mly_user_ioctl; static int mly_user_command(struct mly_softc *sc, struct mly_user_command *uc); static int mly_user_health(struct mly_softc *sc, struct mly_user_health *uh); #define MLY_CMD_TIMEOUT 20 static device_method_t mly_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mly_probe), DEVMETHOD(device_attach, mly_attach), DEVMETHOD(device_detach, mly_detach), DEVMETHOD(device_shutdown, mly_shutdown), { 0, 0 } }; static driver_t mly_pci_driver = { "mly", mly_methods, sizeof(struct mly_softc) }; static devclass_t mly_devclass; DRIVER_MODULE(mly, pci, mly_pci_driver, mly_devclass, 0, 0); MODULE_DEPEND(mly, pci, 1, 1, 1); MODULE_DEPEND(mly, cam, 1, 1, 1); static struct cdevsw mly_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = mly_user_open, .d_close = mly_user_close, .d_ioctl = mly_user_ioctl, .d_name = "mly", }; /******************************************************************************** ******************************************************************************** Device Interface ******************************************************************************** ********************************************************************************/ static struct mly_ident { u_int16_t vendor; u_int16_t device; u_int16_t subvendor; u_int16_t subdevice; int hwif; char *desc; } mly_identifiers[] = { {0x1069, 0xba56, 0x1069, 0x0040, MLY_HWIF_STRONGARM, "Mylex eXtremeRAID 2000"}, {0x1069, 0xba56, 0x1069, 0x0030, MLY_HWIF_STRONGARM, "Mylex eXtremeRAID 3000"}, {0x1069, 0x0050, 0x1069, 0x0050, MLY_HWIF_I960RX, "Mylex AcceleRAID 352"}, {0x1069, 0x0050, 0x1069, 0x0052, MLY_HWIF_I960RX, "Mylex AcceleRAID 170"}, {0x1069, 0x0050, 0x1069, 0x0054, MLY_HWIF_I960RX, "Mylex AcceleRAID 160"}, {0, 0, 0, 0, 0, 0} }; /******************************************************************************** * Compare the provided PCI device with the list we support. */ static int mly_probe(device_t dev) { struct mly_ident *m; debug_called(1); for (m = mly_identifiers; m->vendor != 0; m++) { if ((m->vendor == pci_get_vendor(dev)) && (m->device == pci_get_device(dev)) && ((m->subvendor == 0) || ((m->subvendor == pci_get_subvendor(dev)) && (m->subdevice == pci_get_subdevice(dev))))) { device_set_desc(dev, m->desc); return(BUS_PROBE_DEFAULT); /* allow room to be overridden */ } } return(ENXIO); } /******************************************************************************** * Initialise the controller and softc */ static int mly_attach(device_t dev) { struct mly_softc *sc = device_get_softc(dev); int error; debug_called(1); sc->mly_dev = dev; #ifdef MLY_DEBUG if (device_get_unit(sc->mly_dev) == 0) mly_softc0 = sc; #endif /* * Do PCI-specific initialisation. */ if ((error = mly_pci_attach(sc)) != 0) goto out; /* * Initialise per-controller queues. */ mly_initq_free(sc); mly_initq_busy(sc); mly_initq_complete(sc); /* * Initialise command-completion task. */ TASK_INIT(&sc->mly_task_complete, 0, mly_complete, sc); /* disable interrupts before we start talking to the controller */ MLY_MASK_INTERRUPTS(sc); /* * Wait for the controller to come ready, handshake with the firmware if required. * This is typically only necessary on platforms where the controller BIOS does not * run. */ if ((error = mly_fwhandshake(sc))) goto out; /* * Allocate initial command buffers. */ if ((error = mly_alloc_commands(sc))) goto out; /* * Obtain controller feature information */ if ((error = mly_get_controllerinfo(sc))) goto out; /* * Reallocate command buffers now we know how many we want. */ mly_release_commands(sc); if ((error = mly_alloc_commands(sc))) goto out; /* * Get the current event counter for health purposes, populate the initial * health status buffer. */ if ((error = mly_get_eventstatus(sc))) goto out; /* * Enable memory-mailbox mode. */ if ((error = mly_enable_mmbox(sc))) goto out; /* * Attach to CAM. */ if ((error = mly_cam_attach(sc))) goto out; /* * Print a little information about the controller */ mly_describe_controller(sc); /* * Mark all attached devices for rescan. */ mly_scan_devices(sc); /* * Instigate the first status poll immediately. Rescan completions won't * happen until interrupts are enabled, which should still be before * the SCSI subsystem gets to us, courtesy of the "SCSI settling delay". */ mly_periodic((void *)sc); /* * Create the control device. */ sc->mly_dev_t = make_dev(&mly_cdevsw, 0, UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "mly%d", device_get_unit(sc->mly_dev)); sc->mly_dev_t->si_drv1 = sc; /* enable interrupts now */ MLY_UNMASK_INTERRUPTS(sc); #ifdef MLY_DEBUG timeout((timeout_t *)mly_timeout, sc, MLY_CMD_TIMEOUT * hz); #endif out: if (error != 0) mly_free(sc); return(error); } /******************************************************************************** * Perform PCI-specific initialisation. */ static int mly_pci_attach(struct mly_softc *sc) { int i, error; u_int32_t command; debug_called(1); /* assume failure is 'not configured' */ error = ENXIO; /* * Verify that the adapter is correctly set up in PCI space. * * XXX we shouldn't do this; the PCI code should. */ command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2); command |= PCIM_CMD_BUSMASTEREN; pci_write_config(sc->mly_dev, PCIR_COMMAND, command, 2); command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2); if (!(command & PCIM_CMD_BUSMASTEREN)) { mly_printf(sc, "can't enable busmaster feature\n"); goto fail; } if ((command & PCIM_CMD_MEMEN) == 0) { mly_printf(sc, "memory window not available\n"); goto fail; } /* * Allocate the PCI register window. */ sc->mly_regs_rid = PCIR_BAR(0); /* first base address register */ if ((sc->mly_regs_resource = bus_alloc_resource_any(sc->mly_dev, SYS_RES_MEMORY, &sc->mly_regs_rid, RF_ACTIVE)) == NULL) { mly_printf(sc, "can't allocate register window\n"); goto fail; } sc->mly_btag = rman_get_bustag(sc->mly_regs_resource); sc->mly_bhandle = rman_get_bushandle(sc->mly_regs_resource); /* * Allocate and connect our interrupt. */ sc->mly_irq_rid = 0; if ((sc->mly_irq = bus_alloc_resource_any(sc->mly_dev, SYS_RES_IRQ, &sc->mly_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { mly_printf(sc, "can't allocate interrupt\n"); goto fail; } if (bus_setup_intr(sc->mly_dev, sc->mly_irq, INTR_TYPE_CAM | INTR_ENTROPY, NULL, mly_intr, sc, &sc->mly_intr)) { mly_printf(sc, "can't set up interrupt\n"); goto fail; } /* assume failure is 'out of memory' */ error = ENOMEM; /* * Allocate the parent bus DMA tag appropriate for our PCI interface. * * Note that all of these controllers are 64-bit capable. */ - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(sc->mly_dev),/* PCI parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, MLY_MAX_SGENTRIES, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->mly_parent_dmat)) { mly_printf(sc, "can't allocate parent DMA tag\n"); goto fail; } /* * Create DMA tag for mapping buffers into controller-addressable space. */ if (bus_dma_tag_create(sc->mly_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, MLY_MAX_SGENTRIES, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &Giant, /* lockarg */ &sc->mly_buffer_dmat)) { mly_printf(sc, "can't allocate buffer DMA tag\n"); goto fail; } /* * Initialise the DMA tag for command packets. */ if (bus_dma_tag_create(sc->mly_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(union mly_command_packet) * MLY_MAX_COMMANDS, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mly_packet_dmat)) { mly_printf(sc, "can't allocate command packet DMA tag\n"); goto fail; } /* * Detect the hardware interface version */ for (i = 0; mly_identifiers[i].vendor != 0; i++) { if ((mly_identifiers[i].vendor == pci_get_vendor(sc->mly_dev)) && (mly_identifiers[i].device == pci_get_device(sc->mly_dev))) { sc->mly_hwif = mly_identifiers[i].hwif; switch(sc->mly_hwif) { case MLY_HWIF_I960RX: debug(1, "set hardware up for i960RX"); sc->mly_doorbell_true = 0x00; sc->mly_command_mailbox = MLY_I960RX_COMMAND_MAILBOX; sc->mly_status_mailbox = MLY_I960RX_STATUS_MAILBOX; sc->mly_idbr = MLY_I960RX_IDBR; sc->mly_odbr = MLY_I960RX_ODBR; sc->mly_error_status = MLY_I960RX_ERROR_STATUS; sc->mly_interrupt_status = MLY_I960RX_INTERRUPT_STATUS; sc->mly_interrupt_mask = MLY_I960RX_INTERRUPT_MASK; break; case MLY_HWIF_STRONGARM: debug(1, "set hardware up for StrongARM"); sc->mly_doorbell_true = 0xff; /* doorbell 'true' is 0 */ sc->mly_command_mailbox = MLY_STRONGARM_COMMAND_MAILBOX; sc->mly_status_mailbox = MLY_STRONGARM_STATUS_MAILBOX; sc->mly_idbr = MLY_STRONGARM_IDBR; sc->mly_odbr = MLY_STRONGARM_ODBR; sc->mly_error_status = MLY_STRONGARM_ERROR_STATUS; sc->mly_interrupt_status = MLY_STRONGARM_INTERRUPT_STATUS; sc->mly_interrupt_mask = MLY_STRONGARM_INTERRUPT_MASK; break; } break; } } /* * Create the scatter/gather mappings. */ if ((error = mly_sg_map(sc))) goto fail; /* * Allocate and map the memory mailbox */ if ((error = mly_mmbox_map(sc))) goto fail; error = 0; fail: return(error); } /******************************************************************************** * Shut the controller down and detach all our resources. */ static int mly_detach(device_t dev) { int error; if ((error = mly_shutdown(dev)) != 0) return(error); mly_free(device_get_softc(dev)); return(0); } /******************************************************************************** * Bring the controller to a state where it can be safely left alone. * * Note that it should not be necessary to wait for any outstanding commands, * as they should be completed prior to calling here. * * XXX this applies for I/O, but not status polls; we should beware of * the case where a status command is running while we detach. */ static int mly_shutdown(device_t dev) { struct mly_softc *sc = device_get_softc(dev); debug_called(1); if (sc->mly_state & MLY_STATE_OPEN) return(EBUSY); /* kill the periodic event */ untimeout(mly_periodic, sc, sc->mly_periodic); /* flush controller */ mly_printf(sc, "flushing cache..."); printf("%s\n", mly_flush(sc) ? "failed" : "done"); MLY_MASK_INTERRUPTS(sc); return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ static void mly_intr(void *arg) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(2); mly_done(sc); }; /******************************************************************************** ******************************************************************************** Bus-dependant Resource Management ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Allocate memory for the scatter/gather tables */ static int mly_sg_map(struct mly_softc *sc) { size_t segsize; debug_called(1); /* * Create a single tag describing a region large enough to hold all of * the s/g lists we will need. */ segsize = sizeof(struct mly_sg_entry) * MLY_MAX_COMMANDS *MLY_MAX_SGENTRIES; if (bus_dma_tag_create(sc->mly_parent_dmat, /* parent */ 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ segsize, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mly_sg_dmat)) { mly_printf(sc, "can't allocate scatter/gather DMA tag\n"); return(ENOMEM); } /* * Allocate enough s/g maps for all commands and permanently map them into * controller-visible space. * * XXX this assumes we can get enough space for all the s/g maps in one * contiguous slab. */ if (bus_dmamem_alloc(sc->mly_sg_dmat, (void **)&sc->mly_sg_table, BUS_DMA_NOWAIT, &sc->mly_sg_dmamap)) { mly_printf(sc, "can't allocate s/g table\n"); return(ENOMEM); } if (bus_dmamap_load(sc->mly_sg_dmat, sc->mly_sg_dmamap, sc->mly_sg_table, segsize, mly_sg_map_helper, sc, BUS_DMA_NOWAIT) != 0) return (ENOMEM); return(0); } /******************************************************************************** * Save the physical address of the base of the s/g table. */ static void mly_sg_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(1); /* save base of s/g table's address in bus space */ sc->mly_sg_busaddr = segs->ds_addr; } /******************************************************************************** * Allocate memory for the memory-mailbox interface */ static int mly_mmbox_map(struct mly_softc *sc) { /* * Create a DMA tag for a single contiguous region large enough for the * memory mailbox structure. */ if (bus_dma_tag_create(sc->mly_parent_dmat, /* parent */ 1, 0, /* alignment,boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(struct mly_mmbox), 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mly_mmbox_dmat)) { mly_printf(sc, "can't allocate memory mailbox DMA tag\n"); return(ENOMEM); } /* * Allocate the buffer */ if (bus_dmamem_alloc(sc->mly_mmbox_dmat, (void **)&sc->mly_mmbox, BUS_DMA_NOWAIT, &sc->mly_mmbox_dmamap)) { mly_printf(sc, "can't allocate memory mailbox\n"); return(ENOMEM); } if (bus_dmamap_load(sc->mly_mmbox_dmat, sc->mly_mmbox_dmamap, sc->mly_mmbox, sizeof(struct mly_mmbox), mly_mmbox_map_helper, sc, BUS_DMA_NOWAIT) != 0) return (ENOMEM); bzero(sc->mly_mmbox, sizeof(*sc->mly_mmbox)); return(0); } /******************************************************************************** * Save the physical address of the memory mailbox */ static void mly_mmbox_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(1); sc->mly_mmbox_busaddr = segs->ds_addr; } /******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ static void mly_free(struct mly_softc *sc) { debug_called(1); /* Remove the management device */ destroy_dev(sc->mly_dev_t); /* detach from CAM */ mly_cam_detach(sc); /* release command memory */ mly_release_commands(sc); /* throw away the controllerinfo structure */ if (sc->mly_controllerinfo != NULL) free(sc->mly_controllerinfo, M_DEVBUF); /* throw away the controllerparam structure */ if (sc->mly_controllerparam != NULL) free(sc->mly_controllerparam, M_DEVBUF); /* destroy data-transfer DMA tag */ if (sc->mly_buffer_dmat) bus_dma_tag_destroy(sc->mly_buffer_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->mly_sg_table) { bus_dmamap_unload(sc->mly_sg_dmat, sc->mly_sg_dmamap); bus_dmamem_free(sc->mly_sg_dmat, sc->mly_sg_table, sc->mly_sg_dmamap); } if (sc->mly_sg_dmat) bus_dma_tag_destroy(sc->mly_sg_dmat); /* free and destroy DMA memory and tag for memory mailbox */ if (sc->mly_mmbox) { bus_dmamap_unload(sc->mly_mmbox_dmat, sc->mly_mmbox_dmamap); bus_dmamem_free(sc->mly_mmbox_dmat, sc->mly_mmbox, sc->mly_mmbox_dmamap); } if (sc->mly_mmbox_dmat) bus_dma_tag_destroy(sc->mly_mmbox_dmat); /* disconnect the interrupt handler */ if (sc->mly_intr) bus_teardown_intr(sc->mly_dev, sc->mly_irq, sc->mly_intr); if (sc->mly_irq != NULL) bus_release_resource(sc->mly_dev, SYS_RES_IRQ, sc->mly_irq_rid, sc->mly_irq); /* destroy the parent DMA tag */ if (sc->mly_parent_dmat) bus_dma_tag_destroy(sc->mly_parent_dmat); /* release the register window mapping */ if (sc->mly_regs_resource != NULL) bus_release_resource(sc->mly_dev, SYS_RES_MEMORY, sc->mly_regs_rid, sc->mly_regs_resource); } /******************************************************************************** ******************************************************************************** Command Wrappers ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Fill in the mly_controllerinfo and mly_controllerparam fields in the softc. */ static int mly_get_controllerinfo(struct mly_softc *sc) { struct mly_command_ioctl mci; u_int8_t status; int error; debug_called(1); if (sc->mly_controllerinfo != NULL) free(sc->mly_controllerinfo, M_DEVBUF); /* build the getcontrollerinfo ioctl and send it */ bzero(&mci, sizeof(mci)); sc->mly_controllerinfo = NULL; mci.sub_ioctl = MDACIOCTL_GETCONTROLLERINFO; if ((error = mly_ioctl(sc, &mci, (void **)&sc->mly_controllerinfo, sizeof(*sc->mly_controllerinfo), &status, NULL, NULL))) return(error); if (status != 0) return(EIO); if (sc->mly_controllerparam != NULL) free(sc->mly_controllerparam, M_DEVBUF); /* build the getcontrollerparameter ioctl and send it */ bzero(&mci, sizeof(mci)); sc->mly_controllerparam = NULL; mci.sub_ioctl = MDACIOCTL_GETCONTROLLERPARAMETER; if ((error = mly_ioctl(sc, &mci, (void **)&sc->mly_controllerparam, sizeof(*sc->mly_controllerparam), &status, NULL, NULL))) return(error); if (status != 0) return(EIO); return(0); } /******************************************************************************** * Schedule all possible devices for a rescan. * */ static void mly_scan_devices(struct mly_softc *sc) { int bus, target; debug_called(1); /* * Clear any previous BTL information. */ bzero(&sc->mly_btl, sizeof(sc->mly_btl)); /* * Mark all devices as requiring a rescan, and let the next * periodic scan collect them. */ for (bus = 0; bus < sc->mly_cam_channels; bus++) if (MLY_BUS_IS_VALID(sc, bus)) for (target = 0; target < MLY_MAX_TARGETS; target++) sc->mly_btl[bus][target].mb_flags = MLY_BTL_RESCAN; } /******************************************************************************** * Rescan a device, possibly as a consequence of getting an event which suggests * that it may have changed. * * If we suffer resource starvation, we can abandon the rescan as we'll be * retried. */ static void mly_rescan_btl(struct mly_softc *sc, int bus, int target) { struct mly_command *mc; struct mly_command_ioctl *mci; debug_called(1); /* check that this bus is valid */ if (!MLY_BUS_IS_VALID(sc, bus)) return; /* get a command */ if (mly_alloc_command(sc, &mc)) return; /* set up the data buffer */ if ((mc->mc_data = malloc(sizeof(union mly_devinfo), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { mly_release_command(mc); return; } mc->mc_flags |= MLY_CMD_DATAIN; mc->mc_complete = mly_complete_rescan; /* * Build the ioctl. */ mci = (struct mly_command_ioctl *)&mc->mc_packet->ioctl; mci->opcode = MDACMD_IOCTL; mci->addr.phys.controller = 0; mci->timeout.value = 30; mci->timeout.scale = MLY_TIMEOUT_SECONDS; if (MLY_BUS_IS_VIRTUAL(sc, bus)) { mc->mc_length = mci->data_size = sizeof(struct mly_ioctl_getlogdevinfovalid); mci->sub_ioctl = MDACIOCTL_GETLOGDEVINFOVALID; mci->addr.log.logdev = MLY_LOGDEV_ID(sc, bus, target); debug(1, "logical device %d", mci->addr.log.logdev); } else { mc->mc_length = mci->data_size = sizeof(struct mly_ioctl_getphysdevinfovalid); mci->sub_ioctl = MDACIOCTL_GETPHYSDEVINFOVALID; mci->addr.phys.lun = 0; mci->addr.phys.target = target; mci->addr.phys.channel = bus; debug(1, "physical device %d:%d", mci->addr.phys.channel, mci->addr.phys.target); } /* * Dispatch the command. If we successfully send the command, clear the rescan * bit. */ if (mly_start(mc) != 0) { mly_release_command(mc); } else { sc->mly_btl[bus][target].mb_flags &= ~MLY_BTL_RESCAN; /* success */ } } /******************************************************************************** * Handle the completion of a rescan operation */ static void mly_complete_rescan(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; struct mly_ioctl_getlogdevinfovalid *ldi; struct mly_ioctl_getphysdevinfovalid *pdi; struct mly_command_ioctl *mci; struct mly_btl btl, *btlp; int bus, target, rescan; debug_called(1); /* * Recover the bus and target from the command. We need these even in * the case where we don't have a useful response. */ mci = (struct mly_command_ioctl *)&mc->mc_packet->ioctl; if (mci->sub_ioctl == MDACIOCTL_GETLOGDEVINFOVALID) { bus = MLY_LOGDEV_BUS(sc, mci->addr.log.logdev); target = MLY_LOGDEV_TARGET(sc, mci->addr.log.logdev); } else { bus = mci->addr.phys.channel; target = mci->addr.phys.target; } /* XXX validate bus/target? */ /* the default result is 'no device' */ bzero(&btl, sizeof(btl)); /* if the rescan completed OK, we have possibly-new BTL data */ if (mc->mc_status == 0) { if (mc->mc_length == sizeof(*ldi)) { ldi = (struct mly_ioctl_getlogdevinfovalid *)mc->mc_data; if ((MLY_LOGDEV_BUS(sc, ldi->logical_device_number) != bus) || (MLY_LOGDEV_TARGET(sc, ldi->logical_device_number) != target)) { mly_printf(sc, "WARNING: BTL rescan for %d:%d returned data for %d:%d instead\n", bus, target, MLY_LOGDEV_BUS(sc, ldi->logical_device_number), MLY_LOGDEV_TARGET(sc, ldi->logical_device_number)); /* XXX what can we do about this? */ } btl.mb_flags = MLY_BTL_LOGICAL; btl.mb_type = ldi->raid_level; btl.mb_state = ldi->state; debug(1, "BTL rescan for %d returns %s, %s", ldi->logical_device_number, mly_describe_code(mly_table_device_type, ldi->raid_level), mly_describe_code(mly_table_device_state, ldi->state)); } else if (mc->mc_length == sizeof(*pdi)) { pdi = (struct mly_ioctl_getphysdevinfovalid *)mc->mc_data; if ((pdi->channel != bus) || (pdi->target != target)) { mly_printf(sc, "WARNING: BTL rescan for %d:%d returned data for %d:%d instead\n", bus, target, pdi->channel, pdi->target); /* XXX what can we do about this? */ } btl.mb_flags = MLY_BTL_PHYSICAL; btl.mb_type = MLY_DEVICE_TYPE_PHYSICAL; btl.mb_state = pdi->state; btl.mb_speed = pdi->speed; btl.mb_width = pdi->width; if (pdi->state != MLY_DEVICE_STATE_UNCONFIGURED) sc->mly_btl[bus][target].mb_flags |= MLY_BTL_PROTECTED; debug(1, "BTL rescan for %d:%d returns %s", bus, target, mly_describe_code(mly_table_device_state, pdi->state)); } else { mly_printf(sc, "BTL rescan result invalid\n"); } } free(mc->mc_data, M_DEVBUF); mly_release_command(mc); /* * Decide whether we need to rescan the device. */ rescan = 0; /* device type changes (usually between 'nothing' and 'something') */ btlp = &sc->mly_btl[bus][target]; if (btl.mb_flags != btlp->mb_flags) { debug(1, "flags changed, rescanning"); rescan = 1; } /* XXX other reasons? */ /* * Update BTL information. */ *btlp = btl; /* * Perform CAM rescan if required. */ if (rescan) mly_cam_rescan_btl(sc, bus, target); } /******************************************************************************** * Get the current health status and set the 'next event' counter to suit. */ static int mly_get_eventstatus(struct mly_softc *sc) { struct mly_command_ioctl mci; struct mly_health_status *mh; u_int8_t status; int error; /* build the gethealthstatus ioctl and send it */ bzero(&mci, sizeof(mci)); mh = NULL; mci.sub_ioctl = MDACIOCTL_GETHEALTHSTATUS; if ((error = mly_ioctl(sc, &mci, (void **)&mh, sizeof(*mh), &status, NULL, NULL))) return(error); if (status != 0) return(EIO); /* get the event counter */ sc->mly_event_change = mh->change_counter; sc->mly_event_waiting = mh->next_event; sc->mly_event_counter = mh->next_event; /* save the health status into the memory mailbox */ bcopy(mh, &sc->mly_mmbox->mmm_health.status, sizeof(*mh)); debug(1, "initial change counter %d, event counter %d", mh->change_counter, mh->next_event); free(mh, M_DEVBUF); return(0); } /******************************************************************************** * Enable the memory mailbox mode. */ static int mly_enable_mmbox(struct mly_softc *sc) { struct mly_command_ioctl mci; u_int8_t *sp, status; int error; debug_called(1); /* build the ioctl and send it */ bzero(&mci, sizeof(mci)); mci.sub_ioctl = MDACIOCTL_SETMEMORYMAILBOX; /* set buffer addresses */ mci.param.setmemorymailbox.command_mailbox_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_command); mci.param.setmemorymailbox.status_mailbox_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_status); mci.param.setmemorymailbox.health_buffer_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_health); /* set buffer sizes - abuse of data_size field is revolting */ sp = (u_int8_t *)&mci.data_size; sp[0] = ((sizeof(union mly_command_packet) * MLY_MMBOX_COMMANDS) / 1024); sp[1] = (sizeof(union mly_status_packet) * MLY_MMBOX_STATUS) / 1024; mci.param.setmemorymailbox.health_buffer_size = sizeof(union mly_health_region) / 1024; debug(1, "memory mailbox at %p (0x%llx/%d 0x%llx/%d 0x%llx/%d", sc->mly_mmbox, mci.param.setmemorymailbox.command_mailbox_physaddr, sp[0], mci.param.setmemorymailbox.status_mailbox_physaddr, sp[1], mci.param.setmemorymailbox.health_buffer_physaddr, mci.param.setmemorymailbox.health_buffer_size); if ((error = mly_ioctl(sc, &mci, NULL, 0, &status, NULL, NULL))) return(error); if (status != 0) return(EIO); sc->mly_state |= MLY_STATE_MMBOX_ACTIVE; debug(1, "memory mailbox active"); return(0); } /******************************************************************************** * Flush all pending I/O from the controller. */ static int mly_flush(struct mly_softc *sc) { struct mly_command_ioctl mci; u_int8_t status; int error; debug_called(1); /* build the ioctl */ bzero(&mci, sizeof(mci)); mci.sub_ioctl = MDACIOCTL_FLUSHDEVICEDATA; mci.param.deviceoperation.operation_device = MLY_OPDEVICE_PHYSICAL_CONTROLLER; /* pass it off to the controller */ if ((error = mly_ioctl(sc, &mci, NULL, 0, &status, NULL, NULL))) return(error); return((status == 0) ? 0 : EIO); } /******************************************************************************** * Perform an ioctl command. * * If (data) is not NULL, the command requires data transfer. If (*data) is NULL * the command requires data transfer from the controller, and we will allocate * a buffer for it. If (*data) is not NULL, the command requires data transfer * to the controller. * * XXX passing in the whole ioctl structure is ugly. Better ideas? * * XXX we don't even try to handle the case where datasize > 4k. We should. */ static int mly_ioctl(struct mly_softc *sc, struct mly_command_ioctl *ioctl, void **data, size_t datasize, u_int8_t *status, void *sense_buffer, size_t *sense_length) { struct mly_command *mc; struct mly_command_ioctl *mci; int error; debug_called(1); mc = NULL; if (mly_alloc_command(sc, &mc)) { error = ENOMEM; goto out; } /* copy the ioctl structure, but save some important fields and then fixup */ mci = &mc->mc_packet->ioctl; ioctl->sense_buffer_address = mci->sense_buffer_address; ioctl->maximum_sense_size = mci->maximum_sense_size; *mci = *ioctl; mci->opcode = MDACMD_IOCTL; mci->timeout.value = 30; mci->timeout.scale = MLY_TIMEOUT_SECONDS; /* handle the data buffer */ if (data != NULL) { if (*data == NULL) { /* allocate data buffer */ if ((mc->mc_data = malloc(datasize, M_DEVBUF, M_NOWAIT)) == NULL) { error = ENOMEM; goto out; } mc->mc_flags |= MLY_CMD_DATAIN; } else { mc->mc_data = *data; mc->mc_flags |= MLY_CMD_DATAOUT; } mc->mc_length = datasize; mc->mc_packet->generic.data_size = datasize; } /* run the command */ if ((error = mly_immediate_command(mc))) goto out; /* clean up and return any data */ *status = mc->mc_status; if ((mc->mc_sense > 0) && (sense_buffer != NULL)) { bcopy(mc->mc_packet, sense_buffer, mc->mc_sense); *sense_length = mc->mc_sense; goto out; } /* should we return a data pointer? */ if ((data != NULL) && (*data == NULL)) *data = mc->mc_data; /* command completed OK */ error = 0; out: if (mc != NULL) { /* do we need to free a data buffer we allocated? */ if (error && (mc->mc_data != NULL) && (*data == NULL)) free(mc->mc_data, M_DEVBUF); mly_release_command(mc); } return(error); } /******************************************************************************** * Check for event(s) outstanding in the controller. */ static void mly_check_event(struct mly_softc *sc) { /* * The controller may have updated the health status information, * so check for it here. Note that the counters are all in host memory, * so this check is very cheap. Also note that we depend on checking on * completion */ if (sc->mly_mmbox->mmm_health.status.change_counter != sc->mly_event_change) { sc->mly_event_change = sc->mly_mmbox->mmm_health.status.change_counter; debug(1, "event change %d, event status update, %d -> %d", sc->mly_event_change, sc->mly_event_waiting, sc->mly_mmbox->mmm_health.status.next_event); sc->mly_event_waiting = sc->mly_mmbox->mmm_health.status.next_event; /* wake up anyone that might be interested in this */ wakeup(&sc->mly_event_change); } if (sc->mly_event_counter != sc->mly_event_waiting) mly_fetch_event(sc); } /******************************************************************************** * Fetch one event from the controller. * * If we fail due to resource starvation, we'll be retried the next time a * command completes. */ static void mly_fetch_event(struct mly_softc *sc) { struct mly_command *mc; struct mly_command_ioctl *mci; int s; u_int32_t event; debug_called(1); /* get a command */ if (mly_alloc_command(sc, &mc)) return; /* set up the data buffer */ if ((mc->mc_data = malloc(sizeof(struct mly_event), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { mly_release_command(mc); return; } mc->mc_length = sizeof(struct mly_event); mc->mc_flags |= MLY_CMD_DATAIN; mc->mc_complete = mly_complete_event; /* * Get an event number to fetch. It's possible that we've raced with another * context for the last event, in which case there will be no more events. */ s = splcam(); if (sc->mly_event_counter == sc->mly_event_waiting) { mly_release_command(mc); splx(s); return; } event = sc->mly_event_counter++; splx(s); /* * Build the ioctl. * * At this point we are committed to sending this request, as it * will be the only one constructed for this particular event number. */ mci = (struct mly_command_ioctl *)&mc->mc_packet->ioctl; mci->opcode = MDACMD_IOCTL; mci->data_size = sizeof(struct mly_event); mci->addr.phys.lun = (event >> 16) & 0xff; mci->addr.phys.target = (event >> 24) & 0xff; mci->addr.phys.channel = 0; mci->addr.phys.controller = 0; mci->timeout.value = 30; mci->timeout.scale = MLY_TIMEOUT_SECONDS; mci->sub_ioctl = MDACIOCTL_GETEVENT; mci->param.getevent.sequence_number_low = event & 0xffff; debug(1, "fetch event %u", event); /* * Submit the command. * * Note that failure of mly_start() will result in this event never being * fetched. */ if (mly_start(mc) != 0) { mly_printf(sc, "couldn't fetch event %u\n", event); mly_release_command(mc); } } /******************************************************************************** * Handle the completion of an event poll. */ static void mly_complete_event(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; struct mly_event *me = (struct mly_event *)mc->mc_data; debug_called(1); /* * If the event was successfully fetched, process it. */ if (mc->mc_status == SCSI_STATUS_OK) { mly_process_event(sc, me); free(me, M_DEVBUF); } mly_release_command(mc); /* * Check for another event. */ mly_check_event(sc); } /******************************************************************************** * Process a controller event. */ static void mly_process_event(struct mly_softc *sc, struct mly_event *me) { struct scsi_sense_data_fixed *ssd; char *fp, *tp; int bus, target, event, class, action; ssd = (struct scsi_sense_data_fixed *)&me->sense[0]; /* * Errors can be reported using vendor-unique sense data. In this case, the * event code will be 0x1c (Request sense data present), the sense key will * be 0x09 (vendor specific), the MSB of the ASC will be set, and the * actual event code will be a 16-bit value comprised of the ASCQ (low byte) * and low seven bits of the ASC (low seven bits of the high byte). */ if ((me->code == 0x1c) && ((ssd->flags & SSD_KEY) == SSD_KEY_Vendor_Specific) && (ssd->add_sense_code & 0x80)) { event = ((int)(ssd->add_sense_code & ~0x80) << 8) + ssd->add_sense_code_qual; } else { event = me->code; } /* look up event, get codes */ fp = mly_describe_code(mly_table_event, event); debug(1, "Event %d code 0x%x", me->sequence_number, me->code); /* quiet event? */ class = fp[0]; if (isupper(class) && bootverbose) class = tolower(class); /* get action code, text string */ action = fp[1]; tp = &fp[2]; /* * Print some information about the event. * * This code uses a table derived from the corresponding portion of the Linux * driver, and thus the parser is very similar. */ switch(class) { case 'p': /* error on physical device */ mly_printf(sc, "physical device %d:%d %s\n", me->channel, me->target, tp); if (action == 'r') sc->mly_btl[me->channel][me->target].mb_flags |= MLY_BTL_RESCAN; break; case 'l': /* error on logical unit */ case 'm': /* message about logical unit */ bus = MLY_LOGDEV_BUS(sc, me->lun); target = MLY_LOGDEV_TARGET(sc, me->lun); mly_name_device(sc, bus, target); mly_printf(sc, "logical device %d (%s) %s\n", me->lun, sc->mly_btl[bus][target].mb_name, tp); if (action == 'r') sc->mly_btl[bus][target].mb_flags |= MLY_BTL_RESCAN; break; break; case 's': /* report of sense data */ if (((ssd->flags & SSD_KEY) == SSD_KEY_NO_SENSE) || (((ssd->flags & SSD_KEY) == SSD_KEY_NOT_READY) && (ssd->add_sense_code == 0x04) && ((ssd->add_sense_code_qual == 0x01) || (ssd->add_sense_code_qual == 0x02)))) break; /* ignore NO_SENSE or NOT_READY in one case */ mly_printf(sc, "physical device %d:%d %s\n", me->channel, me->target, tp); mly_printf(sc, " sense key %d asc %02x ascq %02x\n", ssd->flags & SSD_KEY, ssd->add_sense_code, ssd->add_sense_code_qual); mly_printf(sc, " info %4D csi %4D\n", ssd->info, "", ssd->cmd_spec_info, ""); if (action == 'r') sc->mly_btl[me->channel][me->target].mb_flags |= MLY_BTL_RESCAN; break; case 'e': mly_printf(sc, tp, me->target, me->lun); printf("\n"); break; case 'c': mly_printf(sc, "controller %s\n", tp); break; case '?': mly_printf(sc, "%s - %d\n", tp, me->code); break; default: /* probably a 'noisy' event being ignored */ break; } } /******************************************************************************** * Perform periodic activities. */ static void mly_periodic(void *data) { struct mly_softc *sc = (struct mly_softc *)data; int bus, target; debug_called(2); /* * Scan devices. */ for (bus = 0; bus < sc->mly_cam_channels; bus++) { if (MLY_BUS_IS_VALID(sc, bus)) { for (target = 0; target < MLY_MAX_TARGETS; target++) { /* ignore the controller in this scan */ if (target == sc->mly_controllerparam->initiator_id) continue; /* perform device rescan? */ if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_RESCAN) mly_rescan_btl(sc, bus, target); } } } /* check for controller events */ mly_check_event(sc); /* reschedule ourselves */ sc->mly_periodic = timeout(mly_periodic, sc, MLY_PERIODIC_INTERVAL * hz); } /******************************************************************************** ******************************************************************************** Command Processing ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Run a command and wait for it to complete. * */ static int mly_immediate_command(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; int error, s; debug_called(1); /* spinning at splcam is ugly, but we're only used during controller init */ s = splcam(); if ((error = mly_start(mc))) { splx(s); return(error); } if (sc->mly_state & MLY_STATE_INTERRUPTS_ON) { /* sleep on the command */ while(!(mc->mc_flags & MLY_CMD_COMPLETE)) { tsleep(mc, PRIBIO, "mlywait", 0); } } else { /* spin and collect status while we do */ while(!(mc->mc_flags & MLY_CMD_COMPLETE)) { mly_done(mc->mc_sc); } } splx(s); return(0); } /******************************************************************************** * Deliver a command to the controller. * * XXX it would be good to just queue commands that we can't submit immediately * and send them later, but we probably want a wrapper for that so that * we don't hang on a failed submission for an immediate command. */ static int mly_start(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; union mly_command_packet *pkt; int s; debug_called(2); /* * Set the command up for delivery to the controller. */ mly_map_command(mc); mc->mc_packet->generic.command_id = mc->mc_slot; #ifdef MLY_DEBUG mc->mc_timestamp = time_second; #endif s = splcam(); /* * Do we have to use the hardware mailbox? */ if (!(sc->mly_state & MLY_STATE_MMBOX_ACTIVE)) { /* * Check to see if the controller is ready for us. */ if (MLY_IDBR_TRUE(sc, MLY_HM_CMDSENT)) { splx(s); return(EBUSY); } mc->mc_flags |= MLY_CMD_BUSY; /* * It's ready, send the command. */ MLY_SET_MBOX(sc, sc->mly_command_mailbox, &mc->mc_packetphys); MLY_SET_REG(sc, sc->mly_idbr, MLY_HM_CMDSENT); } else { /* use memory-mailbox mode */ pkt = &sc->mly_mmbox->mmm_command[sc->mly_mmbox_command_index]; /* check to see if the next index is free yet */ if (pkt->mmbox.flag != 0) { splx(s); return(EBUSY); } mc->mc_flags |= MLY_CMD_BUSY; /* copy in new command */ bcopy(mc->mc_packet->mmbox.data, pkt->mmbox.data, sizeof(pkt->mmbox.data)); /* barrier to ensure completion of previous write before we write the flag */ bus_space_barrier(sc->mly_btag, sc->mly_bhandle, 0, 0, BUS_SPACE_BARRIER_WRITE); /* copy flag last */ pkt->mmbox.flag = mc->mc_packet->mmbox.flag; /* barrier to ensure completion of previous write before we notify the controller */ bus_space_barrier(sc->mly_btag, sc->mly_bhandle, 0, 0, BUS_SPACE_BARRIER_WRITE); /* signal controller, update index */ MLY_SET_REG(sc, sc->mly_idbr, MLY_AM_CMDSENT); sc->mly_mmbox_command_index = (sc->mly_mmbox_command_index + 1) % MLY_MMBOX_COMMANDS; } mly_enqueue_busy(mc); splx(s); return(0); } /******************************************************************************** * Pick up command status from the controller, schedule a completion event */ static void mly_done(struct mly_softc *sc) { struct mly_command *mc; union mly_status_packet *sp; u_int16_t slot; int s, worked; s = splcam(); worked = 0; /* pick up hardware-mailbox commands */ if (MLY_ODBR_TRUE(sc, MLY_HM_STSREADY)) { slot = MLY_GET_REG2(sc, sc->mly_status_mailbox); if (slot < MLY_SLOT_MAX) { mc = &sc->mly_command[slot - MLY_SLOT_START]; mc->mc_status = MLY_GET_REG(sc, sc->mly_status_mailbox + 2); mc->mc_sense = MLY_GET_REG(sc, sc->mly_status_mailbox + 3); mc->mc_resid = MLY_GET_REG4(sc, sc->mly_status_mailbox + 4); mly_remove_busy(mc); mc->mc_flags &= ~MLY_CMD_BUSY; mly_enqueue_complete(mc); worked = 1; } else { /* slot 0xffff may mean "extremely bogus command" */ mly_printf(sc, "got HM completion for illegal slot %u\n", slot); } /* unconditionally acknowledge status */ MLY_SET_REG(sc, sc->mly_odbr, MLY_HM_STSREADY); MLY_SET_REG(sc, sc->mly_idbr, MLY_HM_STSACK); } /* pick up memory-mailbox commands */ if (MLY_ODBR_TRUE(sc, MLY_AM_STSREADY)) { for (;;) { sp = &sc->mly_mmbox->mmm_status[sc->mly_mmbox_status_index]; /* check for more status */ if (sp->mmbox.flag == 0) break; /* get slot number */ slot = sp->status.command_id; if (slot < MLY_SLOT_MAX) { mc = &sc->mly_command[slot - MLY_SLOT_START]; mc->mc_status = sp->status.status; mc->mc_sense = sp->status.sense_length; mc->mc_resid = sp->status.residue; mly_remove_busy(mc); mc->mc_flags &= ~MLY_CMD_BUSY; mly_enqueue_complete(mc); worked = 1; } else { /* slot 0xffff may mean "extremely bogus command" */ mly_printf(sc, "got AM completion for illegal slot %u at %d\n", slot, sc->mly_mmbox_status_index); } /* clear and move to next index */ sp->mmbox.flag = 0; sc->mly_mmbox_status_index = (sc->mly_mmbox_status_index + 1) % MLY_MMBOX_STATUS; } /* acknowledge that we have collected status value(s) */ MLY_SET_REG(sc, sc->mly_odbr, MLY_AM_STSREADY); } splx(s); if (worked) { if (sc->mly_state & MLY_STATE_INTERRUPTS_ON) taskqueue_enqueue(taskqueue_swi_giant, &sc->mly_task_complete); else mly_complete(sc, 0); } } /******************************************************************************** * Process completed commands */ static void mly_complete(void *context, int pending) { struct mly_softc *sc = (struct mly_softc *)context; struct mly_command *mc; void (* mc_complete)(struct mly_command *mc); debug_called(2); /* * Spin pulling commands off the completed queue and processing them. */ while ((mc = mly_dequeue_complete(sc)) != NULL) { /* * Free controller resources, mark command complete. * * Note that as soon as we mark the command complete, it may be freed * out from under us, so we need to save the mc_complete field in * order to later avoid dereferencing mc. (We would not expect to * have a polling/sleeping consumer with mc_complete != NULL). */ mly_unmap_command(mc); mc_complete = mc->mc_complete; mc->mc_flags |= MLY_CMD_COMPLETE; /* * Call completion handler or wake up sleeping consumer. */ if (mc_complete != NULL) { mc_complete(mc); } else { wakeup(mc); } } /* * XXX if we are deferring commands due to controller-busy status, we should * retry submitting them here. */ } /******************************************************************************** ******************************************************************************** Command Buffer Management ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Allocate a command. */ static int mly_alloc_command(struct mly_softc *sc, struct mly_command **mcp) { struct mly_command *mc; debug_called(3); if ((mc = mly_dequeue_free(sc)) == NULL) return(ENOMEM); *mcp = mc; return(0); } /******************************************************************************** * Release a command back to the freelist. */ static void mly_release_command(struct mly_command *mc) { debug_called(3); /* * Fill in parts of the command that may cause confusion if * a consumer doesn't when we are later allocated. */ mc->mc_data = NULL; mc->mc_flags = 0; mc->mc_complete = NULL; mc->mc_private = NULL; /* * By default, we set up to overwrite the command packet with * sense information. */ mc->mc_packet->generic.sense_buffer_address = mc->mc_packetphys; mc->mc_packet->generic.maximum_sense_size = sizeof(union mly_command_packet); mly_enqueue_free(mc); } /******************************************************************************** * Map helper for command allocation. */ static void mly_alloc_commands_map(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(1); sc->mly_packetphys = segs[0].ds_addr; } /******************************************************************************** * Allocate and initialise command and packet structures. * * If the controller supports fewer than MLY_MAX_COMMANDS commands, limit our * allocation to that number. If we don't yet know how many commands the * controller supports, allocate a very small set (suitable for initialisation * purposes only). */ static int mly_alloc_commands(struct mly_softc *sc) { struct mly_command *mc; int i, ncmd; if (sc->mly_controllerinfo == NULL) { ncmd = 4; } else { ncmd = min(MLY_MAX_COMMANDS, sc->mly_controllerinfo->maximum_parallel_commands); } /* * Allocate enough space for all the command packets in one chunk and * map them permanently into controller-visible space. */ if (bus_dmamem_alloc(sc->mly_packet_dmat, (void **)&sc->mly_packet, BUS_DMA_NOWAIT, &sc->mly_packetmap)) { return(ENOMEM); } if (bus_dmamap_load(sc->mly_packet_dmat, sc->mly_packetmap, sc->mly_packet, ncmd * sizeof(union mly_command_packet), mly_alloc_commands_map, sc, BUS_DMA_NOWAIT) != 0) return (ENOMEM); for (i = 0; i < ncmd; i++) { mc = &sc->mly_command[i]; bzero(mc, sizeof(*mc)); mc->mc_sc = sc; mc->mc_slot = MLY_SLOT_START + i; mc->mc_packet = sc->mly_packet + i; mc->mc_packetphys = sc->mly_packetphys + (i * sizeof(union mly_command_packet)); if (!bus_dmamap_create(sc->mly_buffer_dmat, 0, &mc->mc_datamap)) mly_release_command(mc); } return(0); } /******************************************************************************** * Free all the storage held by commands. * * Must be called with all commands on the free list. */ static void mly_release_commands(struct mly_softc *sc) { struct mly_command *mc; /* throw away command buffer DMA maps */ while (mly_alloc_command(sc, &mc) == 0) bus_dmamap_destroy(sc->mly_buffer_dmat, mc->mc_datamap); /* release the packet storage */ if (sc->mly_packet != NULL) { bus_dmamap_unload(sc->mly_packet_dmat, sc->mly_packetmap); bus_dmamem_free(sc->mly_packet_dmat, sc->mly_packet, sc->mly_packetmap); sc->mly_packet = NULL; } } /******************************************************************************** * Command-mapping helper function - populate this command's s/g table * with the s/g entries for its data. */ static void mly_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_command *mc = (struct mly_command *)arg; struct mly_softc *sc = mc->mc_sc; struct mly_command_generic *gen = &(mc->mc_packet->generic); struct mly_sg_entry *sg; int i, tabofs; debug_called(2); /* can we use the transfer structure directly? */ if (nseg <= 2) { sg = &gen->transfer.direct.sg[0]; gen->command_control.extended_sg_table = 0; } else { tabofs = ((mc->mc_slot - MLY_SLOT_START) * MLY_MAX_SGENTRIES); sg = sc->mly_sg_table + tabofs; gen->transfer.indirect.entries[0] = nseg; gen->transfer.indirect.table_physaddr[0] = sc->mly_sg_busaddr + (tabofs * sizeof(struct mly_sg_entry)); gen->command_control.extended_sg_table = 1; } /* copy the s/g table */ for (i = 0; i < nseg; i++) { sg[i].physaddr = segs[i].ds_addr; sg[i].length = segs[i].ds_len; } } #if 0 /******************************************************************************** * Command-mapping helper function - save the cdb's physical address. * * We don't support 'large' SCSI commands at this time, so this is unused. */ static void mly_map_command_cdb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_command *mc = (struct mly_command *)arg; debug_called(2); /* XXX can we safely assume that a CDB will never cross a page boundary? */ if ((segs[0].ds_addr % PAGE_SIZE) > ((segs[0].ds_addr + mc->mc_packet->scsi_large.cdb_length) % PAGE_SIZE)) panic("cdb crosses page boundary"); /* fix up fields in the command packet */ mc->mc_packet->scsi_large.cdb_physaddr = segs[0].ds_addr; } #endif /******************************************************************************** * Map a command into controller-visible space */ static void mly_map_command(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; debug_called(2); /* don't map more than once */ if (mc->mc_flags & MLY_CMD_MAPPED) return; /* does the command have a data buffer? */ if (mc->mc_data != NULL) { bus_dmamap_load(sc->mly_buffer_dmat, mc->mc_datamap, mc->mc_data, mc->mc_length, mly_map_command_sg, mc, 0); if (mc->mc_flags & MLY_CMD_DATAIN) bus_dmamap_sync(sc->mly_buffer_dmat, mc->mc_datamap, BUS_DMASYNC_PREREAD); if (mc->mc_flags & MLY_CMD_DATAOUT) bus_dmamap_sync(sc->mly_buffer_dmat, mc->mc_datamap, BUS_DMASYNC_PREWRITE); } mc->mc_flags |= MLY_CMD_MAPPED; } /******************************************************************************** * Unmap a command from controller-visible space */ static void mly_unmap_command(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; debug_called(2); if (!(mc->mc_flags & MLY_CMD_MAPPED)) return; /* does the command have a data buffer? */ if (mc->mc_data != NULL) { if (mc->mc_flags & MLY_CMD_DATAIN) bus_dmamap_sync(sc->mly_buffer_dmat, mc->mc_datamap, BUS_DMASYNC_POSTREAD); if (mc->mc_flags & MLY_CMD_DATAOUT) bus_dmamap_sync(sc->mly_buffer_dmat, mc->mc_datamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mly_buffer_dmat, mc->mc_datamap); } mc->mc_flags &= ~MLY_CMD_MAPPED; } /******************************************************************************** ******************************************************************************** CAM interface ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Attach the physical and virtual SCSI busses to CAM. * * Physical bus numbering starts from 0, virtual bus numbering from one greater * than the highest physical bus. Physical busses are only registered if * the kernel environment variable "hw.mly.register_physical_channels" is set. * * When we refer to a "bus", we are referring to the bus number registered with * the SIM, wheras a "channel" is a channel number given to the adapter. In order * to keep things simple, we map these 1:1, so "bus" and "channel" may be used * interchangeably. */ static int mly_cam_attach(struct mly_softc *sc) { struct cam_devq *devq; int chn, i; debug_called(1); /* * Allocate a devq for all our channels combined. */ if ((devq = cam_simq_alloc(sc->mly_controllerinfo->maximum_parallel_commands)) == NULL) { mly_printf(sc, "can't allocate CAM SIM queue\n"); return(ENOMEM); } /* * If physical channel registration has been requested, register these first. * Note that we enable tagged command queueing for physical channels. */ if (testenv("hw.mly.register_physical_channels")) { chn = 0; for (i = 0; i < sc->mly_controllerinfo->physical_channels_present; i++, chn++) { if ((sc->mly_cam_sim[chn] = cam_sim_alloc(mly_cam_action, mly_cam_poll, "mly", sc, device_get_unit(sc->mly_dev), &Giant, sc->mly_controllerinfo->maximum_parallel_commands, 1, devq)) == NULL) { return(ENOMEM); } if (xpt_bus_register(sc->mly_cam_sim[chn], sc->mly_dev, chn)) { mly_printf(sc, "CAM XPT phsyical channel registration failed\n"); return(ENXIO); } debug(1, "registered physical channel %d", chn); } } /* * Register our virtual channels, with bus numbers matching channel numbers. */ chn = sc->mly_controllerinfo->physical_channels_present; for (i = 0; i < sc->mly_controllerinfo->virtual_channels_present; i++, chn++) { if ((sc->mly_cam_sim[chn] = cam_sim_alloc(mly_cam_action, mly_cam_poll, "mly", sc, device_get_unit(sc->mly_dev), &Giant, sc->mly_controllerinfo->maximum_parallel_commands, 0, devq)) == NULL) { return(ENOMEM); } if (xpt_bus_register(sc->mly_cam_sim[chn], sc->mly_dev, chn)) { mly_printf(sc, "CAM XPT virtual channel registration failed\n"); return(ENXIO); } debug(1, "registered virtual channel %d", chn); } /* * This is the total number of channels that (might have been) registered with * CAM. Some may not have been; check the mly_cam_sim array to be certain. */ sc->mly_cam_channels = sc->mly_controllerinfo->physical_channels_present + sc->mly_controllerinfo->virtual_channels_present; return(0); } /******************************************************************************** * Detach from CAM */ static void mly_cam_detach(struct mly_softc *sc) { int i; debug_called(1); for (i = 0; i < sc->mly_cam_channels; i++) { if (sc->mly_cam_sim[i] != NULL) { xpt_bus_deregister(cam_sim_path(sc->mly_cam_sim[i])); cam_sim_free(sc->mly_cam_sim[i], 0); } } if (sc->mly_cam_devq != NULL) cam_simq_free(sc->mly_cam_devq); } /************************************************************************ * Rescan a device. */ static void mly_cam_rescan_btl(struct mly_softc *sc, int bus, int target) { union ccb *ccb; debug_called(1); if ((ccb = xpt_alloc_ccb()) == NULL) { mly_printf(sc, "rescan failed (can't allocate CCB)\n"); return; } if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(sc->mly_cam_sim[bus]), target, 0) != CAM_REQ_CMP) { mly_printf(sc, "rescan failed (can't create path)\n"); xpt_free_ccb(ccb); return; } debug(1, "rescan target %d:%d", bus, target); xpt_rescan(ccb); } /******************************************************************************** * Handle an action requested by CAM */ static void mly_cam_action(struct cam_sim *sim, union ccb *ccb) { struct mly_softc *sc = cam_sim_softc(sim); debug_called(2); switch (ccb->ccb_h.func_code) { /* perform SCSI I/O */ case XPT_SCSI_IO: if (!mly_cam_action_io(sim, (struct ccb_scsiio *)&ccb->csio)) return; break; /* perform geometry calculations */ case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg = &ccb->ccg; u_int32_t secs_per_cylinder; debug(2, "XPT_CALC_GEOMETRY %d:%d:%d", cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); if (sc->mly_controllerparam->bios_geometry == MLY_BIOSGEOM_8G) { ccg->heads = 255; ccg->secs_per_track = 63; } else { /* MLY_BIOSGEOM_2G */ ccg->heads = 128; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; break; } /* handle path attribute inquiry */ case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; debug(2, "XPT_PATH_INQ %d:%d:%d", cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); cpi->version_num = 1; cpi->hba_inquiry = PI_TAG_ABLE; /* XXX extra flags for physical channels? */ cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->max_target = MLY_MAX_TARGETS - 1; cpi->max_lun = MLY_MAX_LUNS - 1; cpi->initiator_id = sc->mly_controllerparam->initiator_id; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "FreeBSD", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 132 * 1024; /* XXX what to set this to? */ cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; int bus, target; struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags = 0; scsi->valid = 0; spi->flags = 0; spi->valid = 0; bus = cam_sim_bus(sim); target = cts->ccb_h.target_id; debug(2, "XPT_GET_TRAN_SETTINGS %d:%d", bus, target); /* logical device? */ if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_LOGICAL) { /* nothing special for these */ /* physical device? */ } else if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_PHYSICAL) { /* allow CAM to try tagged transactions */ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; /* convert speed (MHz) to usec */ if (sc->mly_btl[bus][target].mb_speed == 0) { spi->sync_period = 1000000 / 5; } else { spi->sync_period = 1000000 / sc->mly_btl[bus][target].mb_speed; } /* convert bus width to CAM internal encoding */ switch (sc->mly_btl[bus][target].mb_width) { case 32: spi->bus_width = MSG_EXT_WDTR_BUS_32_BIT; break; case 16: spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; case 8: default: spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } spi->valid |= CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_BUS_WIDTH; /* not a device, bail out */ } else { cts->ccb_h.status = CAM_REQ_CMP_ERR; break; } /* disconnect always OK */ spi->flags |= CTS_SPI_FLAGS_DISC_ENB; spi->valid |= CTS_SPI_VALID_DISC; cts->ccb_h.status = CAM_REQ_CMP; break; } default: /* we can't do this */ debug(2, "unspported func_code = 0x%x", ccb->ccb_h.func_code); ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } /******************************************************************************** * Handle an I/O operation requested by CAM */ static int mly_cam_action_io(struct cam_sim *sim, struct ccb_scsiio *csio) { struct mly_softc *sc = cam_sim_softc(sim); struct mly_command *mc; struct mly_command_scsi_small *ss; int bus, target; int error; int s; bus = cam_sim_bus(sim); target = csio->ccb_h.target_id; debug(2, "XPT_SCSI_IO %d:%d:%d", bus, target, csio->ccb_h.target_lun); /* validate bus number */ if (!MLY_BUS_IS_VALID(sc, bus)) { debug(0, " invalid bus %d", bus); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* check for I/O attempt to a protected device */ if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_PROTECTED) { debug(2, " device protected"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* check for I/O attempt to nonexistent device */ if (!(sc->mly_btl[bus][target].mb_flags & (MLY_BTL_LOGICAL | MLY_BTL_PHYSICAL))) { debug(2, " device %d:%d does not exist", bus, target); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* XXX increase if/when we support large SCSI commands */ if (csio->cdb_len > MLY_CMD_SCSI_SMALL_CDB) { debug(0, " command too large (%d > %d)", csio->cdb_len, MLY_CMD_SCSI_SMALL_CDB); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* check that the CDB pointer is not to a physical address */ if ((csio->ccb_h.flags & CAM_CDB_POINTER) && (csio->ccb_h.flags & CAM_CDB_PHYS)) { debug(0, " CDB pointer is to physical address"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } /* if there is data transfer, it must be to/from a virtual address */ if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if (csio->ccb_h.flags & CAM_DATA_PHYS) { /* we can't map it */ debug(0, " data pointer is to physical address"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } if (csio->ccb_h.flags & CAM_SCATTER_VALID) { /* we want to do the s/g setup */ debug(0, " data has premature s/g setup"); csio->ccb_h.status = CAM_REQ_CMP_ERR; } } /* abandon aborted ccbs or those that have failed validation */ if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { debug(2, "abandoning CCB due to abort/validation failure"); return(EINVAL); } /* * Get a command, or push the ccb back to CAM and freeze the queue. */ if ((error = mly_alloc_command(sc, &mc))) { s = splcam(); xpt_freeze_simq(sim, 1); csio->ccb_h.status |= CAM_REQUEUE_REQ; sc->mly_qfrzn_cnt++; splx(s); return(error); } /* build the command */ mc->mc_data = csio->data_ptr; mc->mc_length = csio->dxfer_len; mc->mc_complete = mly_cam_complete; mc->mc_private = csio; /* save the bus number in the ccb for later recovery XXX should be a better way */ csio->ccb_h.sim_priv.entries[0].field = bus; /* build the packet for the controller */ ss = &mc->mc_packet->scsi_small; ss->opcode = MDACMD_SCSI; if (csio->ccb_h.flags & CAM_DIS_DISCONNECT) ss->command_control.disable_disconnect = 1; if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) ss->command_control.data_direction = MLY_CCB_WRITE; ss->data_size = csio->dxfer_len; ss->addr.phys.lun = csio->ccb_h.target_lun; ss->addr.phys.target = csio->ccb_h.target_id; ss->addr.phys.channel = bus; if (csio->ccb_h.timeout < (60 * 1000)) { ss->timeout.value = csio->ccb_h.timeout / 1000; ss->timeout.scale = MLY_TIMEOUT_SECONDS; } else if (csio->ccb_h.timeout < (60 * 60 * 1000)) { ss->timeout.value = csio->ccb_h.timeout / (60 * 1000); ss->timeout.scale = MLY_TIMEOUT_MINUTES; } else { ss->timeout.value = csio->ccb_h.timeout / (60 * 60 * 1000); /* overflow? */ ss->timeout.scale = MLY_TIMEOUT_HOURS; } ss->maximum_sense_size = csio->sense_len; ss->cdb_length = csio->cdb_len; if (csio->ccb_h.flags & CAM_CDB_POINTER) { bcopy(csio->cdb_io.cdb_ptr, ss->cdb, csio->cdb_len); } else { bcopy(csio->cdb_io.cdb_bytes, ss->cdb, csio->cdb_len); } /* give the command to the controller */ if ((error = mly_start(mc))) { s = splcam(); xpt_freeze_simq(sim, 1); csio->ccb_h.status |= CAM_REQUEUE_REQ; sc->mly_qfrzn_cnt++; splx(s); return(error); } return(0); } /******************************************************************************** * Check for possibly-completed commands. */ static void mly_cam_poll(struct cam_sim *sim) { struct mly_softc *sc = cam_sim_softc(sim); debug_called(2); mly_done(sc); } /******************************************************************************** * Handle completion of a command - pass results back through the CCB */ static void mly_cam_complete(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; struct ccb_scsiio *csio = (struct ccb_scsiio *)mc->mc_private; struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr; struct mly_btl *btl; u_int8_t cmd; int bus, target; int s; debug_called(2); csio->scsi_status = mc->mc_status; switch(mc->mc_status) { case SCSI_STATUS_OK: /* * In order to report logical device type and status, we overwrite * the result of the INQUIRY command to logical devices. */ bus = csio->ccb_h.sim_priv.entries[0].field; target = csio->ccb_h.target_id; /* XXX validate bus/target? */ if (sc->mly_btl[bus][target].mb_flags & MLY_BTL_LOGICAL) { if (csio->ccb_h.flags & CAM_CDB_POINTER) { cmd = *csio->cdb_io.cdb_ptr; } else { cmd = csio->cdb_io.cdb_bytes[0]; } if (cmd == INQUIRY) { btl = &sc->mly_btl[bus][target]; padstr(inq->vendor, mly_describe_code(mly_table_device_type, btl->mb_type), 8); padstr(inq->product, mly_describe_code(mly_table_device_state, btl->mb_state), 16); padstr(inq->revision, "", 4); } } debug(2, "SCSI_STATUS_OK"); csio->ccb_h.status = CAM_REQ_CMP; break; case SCSI_STATUS_CHECK_COND: debug(1, "SCSI_STATUS_CHECK_COND sense %d resid %d", mc->mc_sense, mc->mc_resid); csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; bzero(&csio->sense_data, SSD_FULL_SIZE); bcopy(mc->mc_packet, &csio->sense_data, mc->mc_sense); csio->sense_len = mc->mc_sense; csio->ccb_h.status |= CAM_AUTOSNS_VALID; csio->resid = mc->mc_resid; /* XXX this is a signed value... */ break; case SCSI_STATUS_BUSY: debug(1, "SCSI_STATUS_BUSY"); csio->ccb_h.status = CAM_SCSI_BUSY; break; default: debug(1, "unknown status 0x%x", csio->scsi_status); csio->ccb_h.status = CAM_REQ_CMP_ERR; break; } s = splcam(); if (sc->mly_qfrzn_cnt) { csio->ccb_h.status |= CAM_RELEASE_SIMQ; sc->mly_qfrzn_cnt--; } splx(s); xpt_done((union ccb *)csio); mly_release_command(mc); } /******************************************************************************** * Find a peripheral attahed at (bus),(target) */ static struct cam_periph * mly_find_periph(struct mly_softc *sc, int bus, int target) { struct cam_periph *periph; struct cam_path *path; int status; status = xpt_create_path(&path, NULL, cam_sim_path(sc->mly_cam_sim[bus]), target, 0); if (status == CAM_REQ_CMP) { periph = cam_periph_find(path, NULL); xpt_free_path(path); } else { periph = NULL; } return(periph); } /******************************************************************************** * Name the device at (bus)(target) */ static int mly_name_device(struct mly_softc *sc, int bus, int target) { struct cam_periph *periph; if ((periph = mly_find_periph(sc, bus, target)) != NULL) { sprintf(sc->mly_btl[bus][target].mb_name, "%s%d", periph->periph_name, periph->unit_number); return(0); } sc->mly_btl[bus][target].mb_name[0] = 0; return(ENOENT); } /******************************************************************************** ******************************************************************************** Hardware Control ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Handshake with the firmware while the card is being initialised. */ static int mly_fwhandshake(struct mly_softc *sc) { u_int8_t error, param0, param1; int spinup = 0; debug_called(1); /* set HM_STSACK and let the firmware initialise */ MLY_SET_REG(sc, sc->mly_idbr, MLY_HM_STSACK); DELAY(1000); /* too short? */ /* if HM_STSACK is still true, the controller is initialising */ if (!MLY_IDBR_TRUE(sc, MLY_HM_STSACK)) return(0); mly_printf(sc, "controller initialisation started\n"); /* spin waiting for initialisation to finish, or for a message to be delivered */ while (MLY_IDBR_TRUE(sc, MLY_HM_STSACK)) { /* check for a message */ if (MLY_ERROR_VALID(sc)) { error = MLY_GET_REG(sc, sc->mly_error_status) & ~MLY_MSG_EMPTY; param0 = MLY_GET_REG(sc, sc->mly_command_mailbox); param1 = MLY_GET_REG(sc, sc->mly_command_mailbox + 1); switch(error) { case MLY_MSG_SPINUP: if (!spinup) { mly_printf(sc, "drive spinup in progress\n"); spinup = 1; /* only print this once (should print drive being spun?) */ } break; case MLY_MSG_RACE_RECOVERY_FAIL: mly_printf(sc, "mirror race recovery failed, one or more drives offline\n"); break; case MLY_MSG_RACE_IN_PROGRESS: mly_printf(sc, "mirror race recovery in progress\n"); break; case MLY_MSG_RACE_ON_CRITICAL: mly_printf(sc, "mirror race recovery on a critical drive\n"); break; case MLY_MSG_PARITY_ERROR: mly_printf(sc, "FATAL MEMORY PARITY ERROR\n"); return(ENXIO); default: mly_printf(sc, "unknown initialisation code 0x%x\n", error); } } } return(0); } /******************************************************************************** ******************************************************************************** Debugging and Diagnostics ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Print some information about the controller. */ static void mly_describe_controller(struct mly_softc *sc) { struct mly_ioctl_getcontrollerinfo *mi = sc->mly_controllerinfo; mly_printf(sc, "%16s, %d channel%s, firmware %d.%02d-%d-%02d (%02d%02d%02d%02d), %dMB RAM\n", mi->controller_name, mi->physical_channels_present, (mi->physical_channels_present) > 1 ? "s" : "", mi->fw_major, mi->fw_minor, mi->fw_turn, mi->fw_build, /* XXX turn encoding? */ mi->fw_century, mi->fw_year, mi->fw_month, mi->fw_day, mi->memory_size); if (bootverbose) { mly_printf(sc, "%s %s (%x), %dMHz %d-bit %.16s\n", mly_describe_code(mly_table_oemname, mi->oem_information), mly_describe_code(mly_table_controllertype, mi->controller_type), mi->controller_type, mi->interface_speed, mi->interface_width, mi->interface_name); mly_printf(sc, "%dMB %dMHz %d-bit %s%s%s, cache %dMB\n", mi->memory_size, mi->memory_speed, mi->memory_width, mly_describe_code(mly_table_memorytype, mi->memory_type), mi->memory_parity ? "+parity": "",mi->memory_ecc ? "+ECC": "", mi->cache_size); mly_printf(sc, "CPU: %s @ %dMHz\n", mly_describe_code(mly_table_cputype, mi->cpu[0].type), mi->cpu[0].speed); if (mi->l2cache_size != 0) mly_printf(sc, "%dKB L2 cache\n", mi->l2cache_size); if (mi->exmemory_size != 0) mly_printf(sc, "%dMB %dMHz %d-bit private %s%s%s\n", mi->exmemory_size, mi->exmemory_speed, mi->exmemory_width, mly_describe_code(mly_table_memorytype, mi->exmemory_type), mi->exmemory_parity ? "+parity": "",mi->exmemory_ecc ? "+ECC": ""); mly_printf(sc, "battery backup %s\n", mi->bbu_present ? "present" : "not installed"); mly_printf(sc, "maximum data transfer %d blocks, maximum sg entries/command %d\n", mi->maximum_block_count, mi->maximum_sg_entries); mly_printf(sc, "logical devices present/critical/offline %d/%d/%d\n", mi->logical_devices_present, mi->logical_devices_critical, mi->logical_devices_offline); mly_printf(sc, "physical devices present %d\n", mi->physical_devices_present); mly_printf(sc, "physical disks present/offline %d/%d\n", mi->physical_disks_present, mi->physical_disks_offline); mly_printf(sc, "%d physical channel%s, %d virtual channel%s of %d possible\n", mi->physical_channels_present, mi->physical_channels_present == 1 ? "" : "s", mi->virtual_channels_present, mi->virtual_channels_present == 1 ? "" : "s", mi->virtual_channels_possible); mly_printf(sc, "%d parallel commands supported\n", mi->maximum_parallel_commands); mly_printf(sc, "%dMB flash ROM, %d of %d maximum cycles\n", mi->flash_size, mi->flash_age, mi->flash_maximum_age); } } #ifdef MLY_DEBUG /******************************************************************************** * Print some controller state */ static void mly_printstate(struct mly_softc *sc) { mly_printf(sc, "IDBR %02x ODBR %02x ERROR %02x (%x %x %x)\n", MLY_GET_REG(sc, sc->mly_idbr), MLY_GET_REG(sc, sc->mly_odbr), MLY_GET_REG(sc, sc->mly_error_status), sc->mly_idbr, sc->mly_odbr, sc->mly_error_status); mly_printf(sc, "IMASK %02x ISTATUS %02x\n", MLY_GET_REG(sc, sc->mly_interrupt_mask), MLY_GET_REG(sc, sc->mly_interrupt_status)); mly_printf(sc, "COMMAND %02x %02x %02x %02x %02x %02x %02x %02x\n", MLY_GET_REG(sc, sc->mly_command_mailbox), MLY_GET_REG(sc, sc->mly_command_mailbox + 1), MLY_GET_REG(sc, sc->mly_command_mailbox + 2), MLY_GET_REG(sc, sc->mly_command_mailbox + 3), MLY_GET_REG(sc, sc->mly_command_mailbox + 4), MLY_GET_REG(sc, sc->mly_command_mailbox + 5), MLY_GET_REG(sc, sc->mly_command_mailbox + 6), MLY_GET_REG(sc, sc->mly_command_mailbox + 7)); mly_printf(sc, "STATUS %02x %02x %02x %02x %02x %02x %02x %02x\n", MLY_GET_REG(sc, sc->mly_status_mailbox), MLY_GET_REG(sc, sc->mly_status_mailbox + 1), MLY_GET_REG(sc, sc->mly_status_mailbox + 2), MLY_GET_REG(sc, sc->mly_status_mailbox + 3), MLY_GET_REG(sc, sc->mly_status_mailbox + 4), MLY_GET_REG(sc, sc->mly_status_mailbox + 5), MLY_GET_REG(sc, sc->mly_status_mailbox + 6), MLY_GET_REG(sc, sc->mly_status_mailbox + 7)); mly_printf(sc, " %04x %08x\n", MLY_GET_REG2(sc, sc->mly_status_mailbox), MLY_GET_REG4(sc, sc->mly_status_mailbox + 4)); } struct mly_softc *mly_softc0 = NULL; void mly_printstate0(void) { if (mly_softc0 != NULL) mly_printstate(mly_softc0); } /******************************************************************************** * Print a command */ static void mly_print_command(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; mly_printf(sc, "COMMAND @ %p\n", mc); mly_printf(sc, " slot %d\n", mc->mc_slot); mly_printf(sc, " status 0x%x\n", mc->mc_status); mly_printf(sc, " sense len %d\n", mc->mc_sense); mly_printf(sc, " resid %d\n", mc->mc_resid); mly_printf(sc, " packet %p/0x%llx\n", mc->mc_packet, mc->mc_packetphys); if (mc->mc_packet != NULL) mly_print_packet(mc); mly_printf(sc, " data %p/%d\n", mc->mc_data, mc->mc_length); mly_printf(sc, " flags %b\n", mc->mc_flags, "\20\1busy\2complete\3slotted\4mapped\5datain\6dataout\n"); mly_printf(sc, " complete %p\n", mc->mc_complete); mly_printf(sc, " private %p\n", mc->mc_private); } /******************************************************************************** * Print a command packet */ static void mly_print_packet(struct mly_command *mc) { struct mly_softc *sc = mc->mc_sc; struct mly_command_generic *ge = (struct mly_command_generic *)mc->mc_packet; struct mly_command_scsi_small *ss = (struct mly_command_scsi_small *)mc->mc_packet; struct mly_command_scsi_large *sl = (struct mly_command_scsi_large *)mc->mc_packet; struct mly_command_ioctl *io = (struct mly_command_ioctl *)mc->mc_packet; int transfer; mly_printf(sc, " command_id %d\n", ge->command_id); mly_printf(sc, " opcode %d\n", ge->opcode); mly_printf(sc, " command_control fua %d dpo %d est %d dd %s nas %d ddis %d\n", ge->command_control.force_unit_access, ge->command_control.disable_page_out, ge->command_control.extended_sg_table, (ge->command_control.data_direction == MLY_CCB_WRITE) ? "WRITE" : "READ", ge->command_control.no_auto_sense, ge->command_control.disable_disconnect); mly_printf(sc, " data_size %d\n", ge->data_size); mly_printf(sc, " sense_buffer_address 0x%llx\n", ge->sense_buffer_address); mly_printf(sc, " lun %d\n", ge->addr.phys.lun); mly_printf(sc, " target %d\n", ge->addr.phys.target); mly_printf(sc, " channel %d\n", ge->addr.phys.channel); mly_printf(sc, " logical device %d\n", ge->addr.log.logdev); mly_printf(sc, " controller %d\n", ge->addr.phys.controller); mly_printf(sc, " timeout %d %s\n", ge->timeout.value, (ge->timeout.scale == MLY_TIMEOUT_SECONDS) ? "seconds" : ((ge->timeout.scale == MLY_TIMEOUT_MINUTES) ? "minutes" : "hours")); mly_printf(sc, " maximum_sense_size %d\n", ge->maximum_sense_size); switch(ge->opcode) { case MDACMD_SCSIPT: case MDACMD_SCSI: mly_printf(sc, " cdb length %d\n", ss->cdb_length); mly_printf(sc, " cdb %*D\n", ss->cdb_length, ss->cdb, " "); transfer = 1; break; case MDACMD_SCSILC: case MDACMD_SCSILCPT: mly_printf(sc, " cdb length %d\n", sl->cdb_length); mly_printf(sc, " cdb 0x%llx\n", sl->cdb_physaddr); transfer = 1; break; case MDACMD_IOCTL: mly_printf(sc, " sub_ioctl 0x%x\n", io->sub_ioctl); switch(io->sub_ioctl) { case MDACIOCTL_SETMEMORYMAILBOX: mly_printf(sc, " health_buffer_size %d\n", io->param.setmemorymailbox.health_buffer_size); mly_printf(sc, " health_buffer_phys 0x%llx\n", io->param.setmemorymailbox.health_buffer_physaddr); mly_printf(sc, " command_mailbox 0x%llx\n", io->param.setmemorymailbox.command_mailbox_physaddr); mly_printf(sc, " status_mailbox 0x%llx\n", io->param.setmemorymailbox.status_mailbox_physaddr); transfer = 0; break; case MDACIOCTL_SETREALTIMECLOCK: case MDACIOCTL_GETHEALTHSTATUS: case MDACIOCTL_GETCONTROLLERINFO: case MDACIOCTL_GETLOGDEVINFOVALID: case MDACIOCTL_GETPHYSDEVINFOVALID: case MDACIOCTL_GETPHYSDEVSTATISTICS: case MDACIOCTL_GETLOGDEVSTATISTICS: case MDACIOCTL_GETCONTROLLERSTATISTICS: case MDACIOCTL_GETBDT_FOR_SYSDRIVE: case MDACIOCTL_CREATENEWCONF: case MDACIOCTL_ADDNEWCONF: case MDACIOCTL_GETDEVCONFINFO: case MDACIOCTL_GETFREESPACELIST: case MDACIOCTL_MORE: case MDACIOCTL_SETPHYSDEVPARAMETER: case MDACIOCTL_GETPHYSDEVPARAMETER: case MDACIOCTL_GETLOGDEVPARAMETER: case MDACIOCTL_SETLOGDEVPARAMETER: mly_printf(sc, " param %10D\n", io->param.data.param, " "); transfer = 1; break; case MDACIOCTL_GETEVENT: mly_printf(sc, " event %d\n", io->param.getevent.sequence_number_low + ((u_int32_t)io->addr.log.logdev << 16)); transfer = 1; break; case MDACIOCTL_SETRAIDDEVSTATE: mly_printf(sc, " state %d\n", io->param.setraiddevstate.state); transfer = 0; break; case MDACIOCTL_XLATEPHYSDEVTORAIDDEV: mly_printf(sc, " raid_device %d\n", io->param.xlatephysdevtoraiddev.raid_device); mly_printf(sc, " controller %d\n", io->param.xlatephysdevtoraiddev.controller); mly_printf(sc, " channel %d\n", io->param.xlatephysdevtoraiddev.channel); mly_printf(sc, " target %d\n", io->param.xlatephysdevtoraiddev.target); mly_printf(sc, " lun %d\n", io->param.xlatephysdevtoraiddev.lun); transfer = 0; break; case MDACIOCTL_GETGROUPCONFINFO: mly_printf(sc, " group %d\n", io->param.getgroupconfinfo.group); transfer = 1; break; case MDACIOCTL_GET_SUBSYSTEM_DATA: case MDACIOCTL_SET_SUBSYSTEM_DATA: case MDACIOCTL_STARTDISOCVERY: case MDACIOCTL_INITPHYSDEVSTART: case MDACIOCTL_INITPHYSDEVSTOP: case MDACIOCTL_INITRAIDDEVSTART: case MDACIOCTL_INITRAIDDEVSTOP: case MDACIOCTL_REBUILDRAIDDEVSTART: case MDACIOCTL_REBUILDRAIDDEVSTOP: case MDACIOCTL_MAKECONSISTENTDATASTART: case MDACIOCTL_MAKECONSISTENTDATASTOP: case MDACIOCTL_CONSISTENCYCHECKSTART: case MDACIOCTL_CONSISTENCYCHECKSTOP: case MDACIOCTL_RESETDEVICE: case MDACIOCTL_FLUSHDEVICEDATA: case MDACIOCTL_PAUSEDEVICE: case MDACIOCTL_UNPAUSEDEVICE: case MDACIOCTL_LOCATEDEVICE: case MDACIOCTL_SETMASTERSLAVEMODE: case MDACIOCTL_DELETERAIDDEV: case MDACIOCTL_REPLACEINTERNALDEV: case MDACIOCTL_CLEARCONF: case MDACIOCTL_GETCONTROLLERPARAMETER: case MDACIOCTL_SETCONTRLLERPARAMETER: case MDACIOCTL_CLEARCONFSUSPMODE: case MDACIOCTL_STOREIMAGE: case MDACIOCTL_READIMAGE: case MDACIOCTL_FLASHIMAGES: case MDACIOCTL_RENAMERAIDDEV: default: /* no idea what to print */ transfer = 0; break; } break; case MDACMD_IOCTLCHECK: case MDACMD_MEMCOPY: default: transfer = 0; break; /* print nothing */ } if (transfer) { if (ge->command_control.extended_sg_table) { mly_printf(sc, " sg table 0x%llx/%d\n", ge->transfer.indirect.table_physaddr[0], ge->transfer.indirect.entries[0]); } else { mly_printf(sc, " 0000 0x%llx/%lld\n", ge->transfer.direct.sg[0].physaddr, ge->transfer.direct.sg[0].length); mly_printf(sc, " 0001 0x%llx/%lld\n", ge->transfer.direct.sg[1].physaddr, ge->transfer.direct.sg[1].length); } } } /******************************************************************************** * Panic in a slightly informative fashion */ static void mly_panic(struct mly_softc *sc, char *reason) { mly_printstate(sc); panic(reason); } /******************************************************************************** * Print queue statistics, callable from DDB. */ void mly_print_controller(int controller) { struct mly_softc *sc; if ((sc = devclass_get_softc(devclass_find("mly"), controller)) == NULL) { printf("mly: controller %d invalid\n", controller); } else { device_printf(sc->mly_dev, "queue curr max\n"); device_printf(sc->mly_dev, "free %04d/%04d\n", sc->mly_qstat[MLYQ_FREE].q_length, sc->mly_qstat[MLYQ_FREE].q_max); device_printf(sc->mly_dev, "busy %04d/%04d\n", sc->mly_qstat[MLYQ_BUSY].q_length, sc->mly_qstat[MLYQ_BUSY].q_max); device_printf(sc->mly_dev, "complete %04d/%04d\n", sc->mly_qstat[MLYQ_COMPLETE].q_length, sc->mly_qstat[MLYQ_COMPLETE].q_max); } } #endif /******************************************************************************** ******************************************************************************** Control device interface ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Accept an open operation on the control device. */ static int mly_user_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mly_softc *sc = dev->si_drv1; sc->mly_state |= MLY_STATE_OPEN; return(0); } /******************************************************************************** * Accept the last close on the control device. */ static int mly_user_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mly_softc *sc = dev->si_drv1; sc->mly_state &= ~MLY_STATE_OPEN; return (0); } /******************************************************************************** * Handle controller-specific control operations. */ static int mly_user_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) { struct mly_softc *sc = (struct mly_softc *)dev->si_drv1; struct mly_user_command *uc = (struct mly_user_command *)addr; struct mly_user_health *uh = (struct mly_user_health *)addr; switch(cmd) { case MLYIO_COMMAND: return(mly_user_command(sc, uc)); case MLYIO_HEALTH: return(mly_user_health(sc, uh)); default: return(ENOIOCTL); } } /******************************************************************************** * Execute a command passed in from userspace. * * The control structure contains the actual command for the controller, as well * as the user-space data pointer and data size, and an optional sense buffer * size/pointer. On completion, the data size is adjusted to the command * residual, and the sense buffer size to the size of the returned sense data. * */ static int mly_user_command(struct mly_softc *sc, struct mly_user_command *uc) { struct mly_command *mc; int error, s; /* allocate a command */ if (mly_alloc_command(sc, &mc)) { error = ENOMEM; goto out; /* XXX Linux version will wait for a command */ } /* handle data size/direction */ mc->mc_length = (uc->DataTransferLength >= 0) ? uc->DataTransferLength : -uc->DataTransferLength; if (mc->mc_length > 0) { if ((mc->mc_data = malloc(mc->mc_length, M_DEVBUF, M_NOWAIT)) == NULL) { error = ENOMEM; goto out; } } if (uc->DataTransferLength > 0) { mc->mc_flags |= MLY_CMD_DATAIN; bzero(mc->mc_data, mc->mc_length); } if (uc->DataTransferLength < 0) { mc->mc_flags |= MLY_CMD_DATAOUT; if ((error = copyin(uc->DataTransferBuffer, mc->mc_data, mc->mc_length)) != 0) goto out; } /* copy the controller command */ bcopy(&uc->CommandMailbox, mc->mc_packet, sizeof(uc->CommandMailbox)); /* clear command completion handler so that we get woken up */ mc->mc_complete = NULL; /* execute the command */ if ((error = mly_start(mc)) != 0) goto out; s = splcam(); while (!(mc->mc_flags & MLY_CMD_COMPLETE)) tsleep(mc, PRIBIO, "mlyioctl", 0); splx(s); /* return the data to userspace */ if (uc->DataTransferLength > 0) if ((error = copyout(mc->mc_data, uc->DataTransferBuffer, mc->mc_length)) != 0) goto out; /* return the sense buffer to userspace */ if ((uc->RequestSenseLength > 0) && (mc->mc_sense > 0)) { if ((error = copyout(mc->mc_packet, uc->RequestSenseBuffer, min(uc->RequestSenseLength, mc->mc_sense))) != 0) goto out; } /* return command results to userspace (caller will copy out) */ uc->DataTransferLength = mc->mc_resid; uc->RequestSenseLength = min(uc->RequestSenseLength, mc->mc_sense); uc->CommandStatus = mc->mc_status; error = 0; out: if (mc->mc_data != NULL) free(mc->mc_data, M_DEVBUF); if (mc != NULL) mly_release_command(mc); return(error); } /******************************************************************************** * Return health status to userspace. If the health change index in the user * structure does not match that currently exported by the controller, we * return the current status immediately. Otherwise, we block until either * interrupted or new status is delivered. */ static int mly_user_health(struct mly_softc *sc, struct mly_user_health *uh) { struct mly_health_status mh; int error, s; /* fetch the current health status from userspace */ if ((error = copyin(uh->HealthStatusBuffer, &mh, sizeof(mh))) != 0) return(error); /* spin waiting for a status update */ s = splcam(); error = EWOULDBLOCK; while ((error != 0) && (sc->mly_event_change == mh.change_counter)) error = tsleep(&sc->mly_event_change, PRIBIO | PCATCH, "mlyhealth", 0); splx(s); /* copy the controller's health status buffer out (there is a race here if it changes again) */ error = copyout(&sc->mly_mmbox->mmm_health.status, uh->HealthStatusBuffer, sizeof(uh->HealthStatusBuffer)); return(error); } #ifdef MLY_DEBUG static int mly_timeout(struct mly_softc *sc) { struct mly_command *mc; int deadline; deadline = time_second - MLY_CMD_TIMEOUT; TAILQ_FOREACH(mc, &sc->mly_busy, mc_link) { if ((mc->mc_timestamp < deadline)) { device_printf(sc->mly_dev, "COMMAND %p TIMEOUT AFTER %d SECONDS\n", mc, (int)(time_second - mc->mc_timestamp)); } } timeout((timeout_t *)mly_timeout, sc, MLY_CMD_TIMEOUT * hz); return (0); } #endif Index: head/sys/dev/twe/twe_freebsd.c =================================================================== --- head/sys/dev/twe/twe_freebsd.c (revision 232853) +++ head/sys/dev/twe/twe_freebsd.c (revision 232854) @@ -1,1164 +1,1164 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2003 Paul Saab * Copyright (c) 2003 Vinod Kashyap * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * FreeBSD-specific code. */ #include #include #include #include #include #include static devclass_t twe_devclass; #ifdef TWE_DEBUG static u_int32_t twed_bio_in; #define TWED_BIO_IN twed_bio_in++ static u_int32_t twed_bio_out; #define TWED_BIO_OUT twed_bio_out++ #else #define TWED_BIO_IN #define TWED_BIO_OUT #endif static void twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); static void twe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); /******************************************************************************** ******************************************************************************** Control device interface ******************************************************************************** ********************************************************************************/ static d_open_t twe_open; static d_close_t twe_close; static d_ioctl_t twe_ioctl_wrapper; static struct cdevsw twe_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = twe_open, .d_close = twe_close, .d_ioctl = twe_ioctl_wrapper, .d_name = "twe", }; /******************************************************************************** * Accept an open operation on the control device. */ static int twe_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct twe_softc *sc = (struct twe_softc *)dev->si_drv1; sc->twe_state |= TWE_STATE_OPEN; return(0); } /******************************************************************************** * Accept the last close on the control device. */ static int twe_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct twe_softc *sc = (struct twe_softc *)dev->si_drv1; sc->twe_state &= ~TWE_STATE_OPEN; return (0); } /******************************************************************************** * Handle controller-specific control operations. */ static int twe_ioctl_wrapper(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) { struct twe_softc *sc = (struct twe_softc *)dev->si_drv1; return(twe_ioctl(sc, cmd, addr)); } /******************************************************************************** ******************************************************************************** PCI device interface ******************************************************************************** ********************************************************************************/ static int twe_probe(device_t dev); static int twe_attach(device_t dev); static void twe_free(struct twe_softc *sc); static int twe_detach(device_t dev); static int twe_shutdown(device_t dev); static int twe_suspend(device_t dev); static int twe_resume(device_t dev); static void twe_pci_intr(void *arg); static void twe_intrhook(void *arg); static device_method_t twe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, twe_probe), DEVMETHOD(device_attach, twe_attach), DEVMETHOD(device_detach, twe_detach), DEVMETHOD(device_shutdown, twe_shutdown), DEVMETHOD(device_suspend, twe_suspend), DEVMETHOD(device_resume, twe_resume), DEVMETHOD_END }; static driver_t twe_pci_driver = { "twe", twe_methods, sizeof(struct twe_softc) }; DRIVER_MODULE(twe, pci, twe_pci_driver, twe_devclass, 0, 0); /******************************************************************************** * Match a 3ware Escalade ATA RAID controller. */ static int twe_probe(device_t dev) { debug_called(4); if ((pci_get_vendor(dev) == TWE_VENDOR_ID) && ((pci_get_device(dev) == TWE_DEVICE_ID) || (pci_get_device(dev) == TWE_DEVICE_ID_ASIC))) { device_set_desc_copy(dev, TWE_DEVICE_NAME ". Driver version " TWE_DRIVER_VERSION_STRING); return(BUS_PROBE_DEFAULT); } return(ENXIO); } /******************************************************************************** * Allocate resources, initialise the controller. */ static int twe_attach(device_t dev) { struct twe_softc *sc; int rid, error; u_int32_t command; debug_called(4); /* * Initialise the softc structure. */ sc = device_get_softc(dev); sc->twe_dev = dev; sysctl_ctx_init(&sc->sysctl_ctx); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0, ""); if (sc->sysctl_tree == NULL) { twe_printf(sc, "cannot add sysctl tree node\n"); return (ENXIO); } SYSCTL_ADD_STRING(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "driver_version", CTLFLAG_RD, TWE_DRIVER_VERSION_STRING, 0, "TWE driver version"); /* * Make sure we are going to be able to talk to this board. */ command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_PORTEN) == 0) { twe_printf(sc, "register window not available\n"); return(ENXIO); } /* * Force the busmaster enable bit on, in case the BIOS forgot. */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); /* * Allocate the PCI register window. */ rid = TWE_IO_CONFIG_REG; if ((sc->twe_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE)) == NULL) { twe_printf(sc, "can't allocate register window\n"); twe_free(sc); return(ENXIO); } sc->twe_btag = rman_get_bustag(sc->twe_io); sc->twe_bhandle = rman_get_bushandle(sc->twe_io); /* * Allocate the parent bus DMA tag appropriate for PCI. */ - if (bus_dma_tag_create(NULL, /* parent */ + if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, TWE_MAX_SGL_LENGTH, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->twe_parent_dmat)) { twe_printf(sc, "can't allocate parent DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Allocate and connect our interrupt. */ rid = 0; if ((sc->twe_irq = bus_alloc_resource_any(sc->twe_dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { twe_printf(sc, "can't allocate interrupt\n"); twe_free(sc); return(ENXIO); } if (bus_setup_intr(sc->twe_dev, sc->twe_irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, twe_pci_intr, sc, &sc->twe_intr)) { twe_printf(sc, "can't set up interrupt\n"); twe_free(sc); return(ENXIO); } /* * Create DMA tag for mapping command's into controller-addressable space. */ if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(TWE_Command) * TWE_Q_LENGTH, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->twe_cmd_dmat)) { twe_printf(sc, "can't allocate data buffer DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Allocate memory and make it available for DMA. */ if (bus_dmamem_alloc(sc->twe_cmd_dmat, (void **)&sc->twe_cmd, BUS_DMA_NOWAIT, &sc->twe_cmdmap)) { twe_printf(sc, "can't allocate command memory\n"); return(ENOMEM); } bus_dmamap_load(sc->twe_cmd_dmat, sc->twe_cmdmap, sc->twe_cmd, sizeof(TWE_Command) * TWE_Q_LENGTH, twe_setup_request_dmamap, sc, 0); bzero(sc->twe_cmd, sizeof(TWE_Command) * TWE_Q_LENGTH); /* * Create DMA tag for mapping objects into controller-addressable space. */ if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, TWE_MAX_SGL_LENGTH,/* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &Giant, /* lockarg */ &sc->twe_buffer_dmat)) { twe_printf(sc, "can't allocate data buffer DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Create DMA tag for mapping objects into controller-addressable space. */ if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, 1, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->twe_immediate_dmat)) { twe_printf(sc, "can't allocate data buffer DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Allocate memory for requests which cannot sleep or support continuation. */ if (bus_dmamem_alloc(sc->twe_immediate_dmat, (void **)&sc->twe_immediate, BUS_DMA_NOWAIT, &sc->twe_immediate_map)) { twe_printf(sc, "can't allocate memory for immediate requests\n"); return(ENOMEM); } /* * Initialise the controller and driver core. */ if ((error = twe_setup(sc))) { twe_free(sc); return(error); } /* * Print some information about the controller and configuration. */ twe_describe_controller(sc); /* * Create the control device. */ sc->twe_dev_t = make_dev(&twe_cdevsw, device_get_unit(sc->twe_dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "twe%d", device_get_unit(sc->twe_dev)); sc->twe_dev_t->si_drv1 = sc; /* * Schedule ourselves to bring the controller up once interrupts are available. * This isn't strictly necessary, since we disable interrupts while probing the * controller, but it is more in keeping with common practice for other disk * devices. */ sc->twe_ich.ich_func = twe_intrhook; sc->twe_ich.ich_arg = sc; if (config_intrhook_establish(&sc->twe_ich) != 0) { twe_printf(sc, "can't establish configuration hook\n"); twe_free(sc); return(ENXIO); } return(0); } /******************************************************************************** * Free all of the resources associated with (sc). * * Should not be called if the controller is active. */ static void twe_free(struct twe_softc *sc) { struct twe_request *tr; debug_called(4); /* throw away any command buffers */ while ((tr = twe_dequeue_free(sc)) != NULL) twe_free_request(tr); if (sc->twe_cmd != NULL) { bus_dmamap_unload(sc->twe_cmd_dmat, sc->twe_cmdmap); bus_dmamem_free(sc->twe_cmd_dmat, sc->twe_cmd, sc->twe_cmdmap); } if (sc->twe_immediate != NULL) { bus_dmamap_unload(sc->twe_immediate_dmat, sc->twe_immediate_map); bus_dmamem_free(sc->twe_immediate_dmat, sc->twe_immediate, sc->twe_immediate_map); } if (sc->twe_immediate_dmat) bus_dma_tag_destroy(sc->twe_immediate_dmat); /* destroy the data-transfer DMA tag */ if (sc->twe_buffer_dmat) bus_dma_tag_destroy(sc->twe_buffer_dmat); /* disconnect the interrupt handler */ if (sc->twe_intr) bus_teardown_intr(sc->twe_dev, sc->twe_irq, sc->twe_intr); if (sc->twe_irq != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IRQ, 0, sc->twe_irq); /* destroy the parent DMA tag */ if (sc->twe_parent_dmat) bus_dma_tag_destroy(sc->twe_parent_dmat); /* release the register window mapping */ if (sc->twe_io != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IOPORT, TWE_IO_CONFIG_REG, sc->twe_io); /* destroy control device */ if (sc->twe_dev_t != (struct cdev *)NULL) destroy_dev(sc->twe_dev_t); sysctl_ctx_free(&sc->sysctl_ctx); } /******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ static int twe_detach(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int s, error; debug_called(4); error = EBUSY; s = splbio(); if (sc->twe_state & TWE_STATE_OPEN) goto out; /* * Shut the controller down. */ if (twe_shutdown(dev)) goto out; twe_free(sc); error = 0; out: splx(s); return(error); } /******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int twe_shutdown(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int i, s, error = 0; debug_called(4); s = splbio(); /* * Delete all our child devices. */ for (i = 0; i < TWE_MAX_UNITS; i++) { if (sc->twe_drive[i].td_disk != 0) { if ((error = twe_detach_drive(sc, i)) != 0) goto out; } } /* * Bring the controller down. */ twe_deinit(sc); out: splx(s); return(error); } /******************************************************************************** * Bring the controller to a quiescent state, ready for system suspend. */ static int twe_suspend(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int s; debug_called(4); s = splbio(); sc->twe_state |= TWE_STATE_SUSPEND; twe_disable_interrupts(sc); splx(s); return(0); } /******************************************************************************** * Bring the controller back to a state ready for operation. */ static int twe_resume(device_t dev) { struct twe_softc *sc = device_get_softc(dev); debug_called(4); sc->twe_state &= ~TWE_STATE_SUSPEND; twe_enable_interrupts(sc); return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ static void twe_pci_intr(void *arg) { twe_intr((struct twe_softc *)arg); } /******************************************************************************** * Delayed-startup hook */ static void twe_intrhook(void *arg) { struct twe_softc *sc = (struct twe_softc *)arg; /* pull ourselves off the intrhook chain */ config_intrhook_disestablish(&sc->twe_ich); /* call core startup routine */ twe_init(sc); } /******************************************************************************** * Given a detected drive, attach it to the bio interface. * * This is called from twe_add_unit. */ int twe_attach_drive(struct twe_softc *sc, struct twe_drive *dr) { char buf[80]; int error; dr->td_disk = device_add_child(sc->twe_dev, NULL, -1); if (dr->td_disk == NULL) { twe_printf(sc, "Cannot add unit\n"); return (EIO); } device_set_ivars(dr->td_disk, dr); /* * XXX It would make sense to test the online/initialising bits, but they seem to be * always set... */ sprintf(buf, "Unit %d, %s, %s", dr->td_twe_unit, twe_describe_code(twe_table_unittype, dr->td_type), twe_describe_code(twe_table_unitstate, dr->td_state & TWE_PARAM_UNITSTATUS_MASK)); device_set_desc_copy(dr->td_disk, buf); if ((error = bus_generic_attach(sc->twe_dev)) != 0) { twe_printf(sc, "Cannot attach unit to controller. error = %d\n", error); return (EIO); } return (0); } /******************************************************************************** * Detach the specified unit if it exsists * * This is called from twe_del_unit. */ int twe_detach_drive(struct twe_softc *sc, int unit) { int error = 0; if ((error = device_delete_child(sc->twe_dev, sc->twe_drive[unit].td_disk)) != 0) { twe_printf(sc, "failed to delete unit %d\n", unit); return(error); } bzero(&sc->twe_drive[unit], sizeof(sc->twe_drive[unit])); return(error); } /******************************************************************************** * Clear a PCI parity error. */ void twe_clear_pci_parity_error(struct twe_softc *sc) { TWE_CONTROL(sc, TWE_CONTROL_CLEAR_PARITY_ERROR); pci_write_config(sc->twe_dev, PCIR_STATUS, TWE_PCI_CLEAR_PARITY_ERROR, 2); } /******************************************************************************** * Clear a PCI abort. */ void twe_clear_pci_abort(struct twe_softc *sc) { TWE_CONTROL(sc, TWE_CONTROL_CLEAR_PCI_ABORT); pci_write_config(sc->twe_dev, PCIR_STATUS, TWE_PCI_CLEAR_PCI_ABORT, 2); } /******************************************************************************** ******************************************************************************** Disk device ******************************************************************************** ********************************************************************************/ /* * Disk device softc */ struct twed_softc { device_t twed_dev; struct twe_softc *twed_controller; /* parent device softc */ struct twe_drive *twed_drive; /* drive data in parent softc */ struct disk *twed_disk; /* generic disk handle */ }; /* * Disk device bus interface */ static int twed_probe(device_t dev); static int twed_attach(device_t dev); static int twed_detach(device_t dev); static device_method_t twed_methods[] = { DEVMETHOD(device_probe, twed_probe), DEVMETHOD(device_attach, twed_attach), DEVMETHOD(device_detach, twed_detach), { 0, 0 } }; static driver_t twed_driver = { "twed", twed_methods, sizeof(struct twed_softc) }; static devclass_t twed_devclass; DRIVER_MODULE(twed, twe, twed_driver, twed_devclass, 0, 0); /* * Disk device control interface. */ #ifdef FREEBSD_4 static int disks_registered = 0; #endif /******************************************************************************** * Handle open from generic layer. * * Note that this is typically only called by the diskslice code, and not * for opens on subdevices (eg. slices, partitions). */ static int twed_open(struct disk *dp) { struct twed_softc *sc = (struct twed_softc *)dp->d_drv1; debug_called(4); if (sc == NULL) return (ENXIO); /* check that the controller is up and running */ if (sc->twed_controller->twe_state & TWE_STATE_SHUTDOWN) return(ENXIO); return (0); } /******************************************************************************** * Handle an I/O request. */ static void twed_strategy(twe_bio *bp) { struct twed_softc *sc = (struct twed_softc *)TWE_BIO_SOFTC(bp); debug_called(4); bp->bio_driver1 = &sc->twed_drive->td_twe_unit; TWED_BIO_IN; /* bogus disk? */ if (sc == NULL || sc->twed_drive->td_disk == NULL) { TWE_BIO_SET_ERROR(bp, EINVAL); printf("twe: bio for invalid disk!\n"); TWE_BIO_DONE(bp); TWED_BIO_OUT; return; } /* perform accounting */ TWE_BIO_STATS_START(bp); /* queue the bio on the controller */ twe_enqueue_bio(sc->twed_controller, bp); /* poke the controller to start I/O */ twe_startio(sc->twed_controller); return; } /******************************************************************************** * System crashdump support */ static int twed_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct twed_softc *twed_sc; struct twe_softc *twe_sc; int error; struct disk *dp; dp = arg; twed_sc = (struct twed_softc *)dp->d_drv1; if (twed_sc == NULL) return(ENXIO); twe_sc = (struct twe_softc *)twed_sc->twed_controller; if (length > 0) { if ((error = twe_dump_blocks(twe_sc, twed_sc->twed_drive->td_twe_unit, offset / TWE_BLOCK_SIZE, virtual, length / TWE_BLOCK_SIZE)) != 0) return(error); } return(0); } /******************************************************************************** * Handle completion of an I/O request. */ void twed_intr(twe_bio *bp) { debug_called(4); /* if no error, transfer completed */ if (!TWE_BIO_HAS_ERROR(bp)) TWE_BIO_RESID(bp) = 0; TWE_BIO_STATS_END(bp); TWE_BIO_DONE(bp); TWED_BIO_OUT; } /******************************************************************************** * Default probe stub. */ static int twed_probe(device_t dev) { return (0); } /******************************************************************************** * Attach a unit to the controller. */ static int twed_attach(device_t dev) { struct twed_softc *sc; device_t parent; debug_called(4); /* initialise our softc */ sc = device_get_softc(dev); parent = device_get_parent(dev); sc->twed_controller = (struct twe_softc *)device_get_softc(parent); sc->twed_drive = device_get_ivars(dev); sc->twed_dev = dev; /* report the drive */ twed_printf(sc, "%uMB (%u sectors)\n", sc->twed_drive->td_size / ((1024 * 1024) / TWE_BLOCK_SIZE), sc->twed_drive->td_size); /* attach a generic disk device to ourselves */ sc->twed_drive->td_sys_unit = device_get_unit(dev); sc->twed_disk = disk_alloc(); sc->twed_disk->d_open = twed_open; sc->twed_disk->d_strategy = twed_strategy; sc->twed_disk->d_dump = (dumper_t *)twed_dump; sc->twed_disk->d_name = "twed"; sc->twed_disk->d_drv1 = sc; sc->twed_disk->d_maxsize = (TWE_MAX_SGL_LENGTH - 1) * PAGE_SIZE; sc->twed_disk->d_sectorsize = TWE_BLOCK_SIZE; sc->twed_disk->d_mediasize = TWE_BLOCK_SIZE * (off_t)sc->twed_drive->td_size; if (sc->twed_drive->td_type == TWE_UD_CONFIG_RAID0 || sc->twed_drive->td_type == TWE_UD_CONFIG_RAID5 || sc->twed_drive->td_type == TWE_UD_CONFIG_RAID10) { sc->twed_disk->d_stripesize = TWE_BLOCK_SIZE << sc->twed_drive->td_stripe; sc->twed_disk->d_stripeoffset = 0; } sc->twed_disk->d_fwsectors = sc->twed_drive->td_sectors; sc->twed_disk->d_fwheads = sc->twed_drive->td_heads; sc->twed_disk->d_unit = sc->twed_drive->td_sys_unit; sc->twed_disk->d_flags = DISKFLAG_NEEDSGIANT; disk_create(sc->twed_disk, DISK_VERSION); #ifdef FREEBSD_4 disks_registered++; #endif /* set the maximum I/O size to the theoretical maximum allowed by the S/G list size */ return (0); } /******************************************************************************** * Disconnect ourselves from the system. */ static int twed_detach(device_t dev) { struct twed_softc *sc = (struct twed_softc *)device_get_softc(dev); debug_called(4); if (sc->twed_disk->d_flags & DISKFLAG_OPEN) return(EBUSY); disk_destroy(sc->twed_disk); #ifdef FREEBSD_4 if (--disks_registered == 0) cdevsw_remove(&tweddisk_cdevsw); #endif return(0); } /******************************************************************************** ******************************************************************************** Misc ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Allocate a command buffer */ static MALLOC_DEFINE(TWE_MALLOC_CLASS, "twe_commands", "twe commands"); struct twe_request * twe_allocate_request(struct twe_softc *sc, int tag) { struct twe_request *tr; if ((tr = malloc(sizeof(struct twe_request), TWE_MALLOC_CLASS, M_WAITOK)) == NULL) { twe_printf(sc, "unable to allocate memory for tag %d\n", tag); return(NULL); } bzero(tr, sizeof(*tr)); tr->tr_sc = sc; tr->tr_tag = tag; if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_dmamap)) { twe_free_request(tr); twe_printf(sc, "unable to allocate dmamap for tag %d\n", tag); return(NULL); } return(tr); } /******************************************************************************** * Permanently discard a command buffer. */ void twe_free_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; debug_called(4); bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_dmamap); free(tr, TWE_MALLOC_CLASS); } /******************************************************************************** * Map/unmap (tr)'s command and data in the controller's addressable space. * * These routines ensure that the data which the controller is going to try to * access is actually visible to the controller, in a machine-independant * fashion. Due to a hardware limitation, I/O buffers must be 512-byte aligned * and we take care of that here as well. */ static void twe_fillin_sgl(TWE_SG_Entry *sgl, bus_dma_segment_t *segs, int nsegments, int max_sgl) { int i; for (i = 0; i < nsegments; i++) { sgl[i].address = segs[i].ds_addr; sgl[i].length = segs[i].ds_len; } for (; i < max_sgl; i++) { /* XXX necessary? */ sgl[i].address = 0; sgl[i].length = 0; } } static void twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct twe_request *tr = (struct twe_request *)arg; struct twe_softc *sc = tr->tr_sc; TWE_Command *cmd = TWE_FIND_COMMAND(tr); debug_called(4); if (tr->tr_flags & TWE_CMD_MAPPED) panic("already mapped command"); tr->tr_flags |= TWE_CMD_MAPPED; if (tr->tr_flags & TWE_CMD_IN_PROGRESS) sc->twe_state &= ~TWE_STATE_FRZN; /* save base of first segment in command (applicable if there only one segment) */ tr->tr_dataphys = segs[0].ds_addr; /* correct command size for s/g list size */ cmd->generic.size += 2 * nsegments; /* * Due to the fact that parameter and I/O commands have the scatter/gather list in * different places, we need to determine which sort of command this actually is * before we can populate it correctly. */ switch(cmd->generic.opcode) { case TWE_OP_GET_PARAM: case TWE_OP_SET_PARAM: cmd->generic.sgl_offset = 2; twe_fillin_sgl(&cmd->param.sgl[0], segs, nsegments, TWE_MAX_SGL_LENGTH); break; case TWE_OP_READ: case TWE_OP_WRITE: cmd->generic.sgl_offset = 3; twe_fillin_sgl(&cmd->io.sgl[0], segs, nsegments, TWE_MAX_SGL_LENGTH); break; case TWE_OP_ATA_PASSTHROUGH: cmd->generic.sgl_offset = 5; twe_fillin_sgl(&cmd->ata.sgl[0], segs, nsegments, TWE_MAX_ATA_SGL_LENGTH); break; default: /* * Fall back to what the linux driver does. * Do this because the API may send an opcode * the driver knows nothing about and this will * at least stop PCIABRT's from hosing us. */ switch (cmd->generic.sgl_offset) { case 2: twe_fillin_sgl(&cmd->param.sgl[0], segs, nsegments, TWE_MAX_SGL_LENGTH); break; case 3: twe_fillin_sgl(&cmd->io.sgl[0], segs, nsegments, TWE_MAX_SGL_LENGTH); break; case 5: twe_fillin_sgl(&cmd->ata.sgl[0], segs, nsegments, TWE_MAX_ATA_SGL_LENGTH); break; } } if (tr->tr_flags & TWE_CMD_DATAIN) { if (tr->tr_flags & TWE_CMD_IMMEDIATE) { bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, BUS_DMASYNC_PREREAD); } else { bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREREAD); } } if (tr->tr_flags & TWE_CMD_DATAOUT) { /* * if we're using an alignment buffer, and we're writing data * copy the real data out */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) bcopy(tr->tr_realdata, tr->tr_data, tr->tr_length); if (tr->tr_flags & TWE_CMD_IMMEDIATE) { bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, BUS_DMASYNC_PREWRITE); } else { bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREWRITE); } } if (twe_start(tr) == EBUSY) { tr->tr_sc->twe_state |= TWE_STATE_CTLR_BUSY; twe_requeue_ready(tr); } } static void twe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct twe_softc *sc = (struct twe_softc *)arg; debug_called(4); /* command can't cross a page boundary */ sc->twe_cmdphys = segs[0].ds_addr; } int twe_map_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; int error = 0; debug_called(4); if (sc->twe_state & (TWE_STATE_CTLR_BUSY | TWE_STATE_FRZN)) { twe_requeue_ready(tr); return (EBUSY); } bus_dmamap_sync(sc->twe_cmd_dmat, sc->twe_cmdmap, BUS_DMASYNC_PREWRITE); /* * If the command involves data, map that too. */ if (tr->tr_data != NULL && ((tr->tr_flags & TWE_CMD_MAPPED) == 0)) { /* * Data must be 64-byte aligned; allocate a fixup buffer if it's not. */ if (((vm_offset_t)tr->tr_data % TWE_ALIGNMENT) != 0) { tr->tr_realdata = tr->tr_data; /* save pointer to 'real' data */ tr->tr_flags |= TWE_CMD_ALIGNBUF; tr->tr_data = malloc(tr->tr_length, TWE_MALLOC_CLASS, M_NOWAIT); if (tr->tr_data == NULL) { twe_printf(sc, "%s: malloc failed\n", __func__); tr->tr_data = tr->tr_realdata; /* restore original data pointer */ return(ENOMEM); } } /* * Map the data buffer into bus space and build the s/g list. */ if (tr->tr_flags & TWE_CMD_IMMEDIATE) { error = bus_dmamap_load(sc->twe_immediate_dmat, sc->twe_immediate_map, sc->twe_immediate, tr->tr_length, twe_setup_data_dmamap, tr, BUS_DMA_NOWAIT); } else { error = bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_dmamap, tr->tr_data, tr->tr_length, twe_setup_data_dmamap, tr, 0); } if (error == EINPROGRESS) { tr->tr_flags |= TWE_CMD_IN_PROGRESS; sc->twe_state |= TWE_STATE_FRZN; error = 0; } } else if ((error = twe_start(tr)) == EBUSY) { sc->twe_state |= TWE_STATE_CTLR_BUSY; twe_requeue_ready(tr); } return(error); } void twe_unmap_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; debug_called(4); bus_dmamap_sync(sc->twe_cmd_dmat, sc->twe_cmdmap, BUS_DMASYNC_POSTWRITE); /* * If the command involved data, unmap that too. */ if (tr->tr_data != NULL) { if (tr->tr_flags & TWE_CMD_DATAIN) { if (tr->tr_flags & TWE_CMD_IMMEDIATE) { bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, BUS_DMASYNC_POSTREAD); } else { bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTREAD); } /* if we're using an alignment buffer, and we're reading data, copy the real data in */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) bcopy(tr->tr_data, tr->tr_realdata, tr->tr_length); } if (tr->tr_flags & TWE_CMD_DATAOUT) { if (tr->tr_flags & TWE_CMD_IMMEDIATE) { bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, BUS_DMASYNC_POSTWRITE); } else { bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTWRITE); } } if (tr->tr_flags & TWE_CMD_IMMEDIATE) { bus_dmamap_unload(sc->twe_immediate_dmat, sc->twe_immediate_map); } else { bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_dmamap); } } /* free alignment buffer if it was used */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) { free(tr->tr_data, TWE_MALLOC_CLASS); tr->tr_data = tr->tr_realdata; /* restore 'real' data pointer */ } } #ifdef TWE_DEBUG void twe_report(void); /******************************************************************************** * Print current controller status, call from DDB. */ void twe_report(void) { struct twe_softc *sc; int i, s; s = splbio(); for (i = 0; (sc = devclass_get_softc(twe_devclass, i)) != NULL; i++) twe_print_controller(sc); printf("twed: total bio count in %u out %u\n", twed_bio_in, twed_bio_out); splx(s); } #endif Index: head/sys/dev/tws/tws.c =================================================================== --- head/sys/dev/tws/tws.c (revision 232853) +++ head/sys/dev/tws/tws.c (revision 232854) @@ -1,904 +1,904 @@ /* * Copyright (c) 2010, LSI Corp. * All rights reserved. * Author : Manjunath Ranganathaiah * Support: freebsdraid@lsi.com * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver"); int tws_queue_depth = TWS_MAX_REQS; int tws_enable_msi = 0; int tws_enable_msix = 0; /* externs */ extern int tws_cam_attach(struct tws_softc *sc); extern void tws_cam_detach(struct tws_softc *sc); extern int tws_init_ctlr(struct tws_softc *sc); extern boolean tws_ctlr_ready(struct tws_softc *sc); extern void tws_turn_off_interrupts(struct tws_softc *sc); extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern struct tws_request *tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern struct tws_request *tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ); extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id); extern boolean tws_ctlr_reset(struct tws_softc *sc); extern void tws_intr(void *arg); extern int tws_use_32bit_sgls; struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type); int tws_init_connect(struct tws_softc *sc, u_int16_t mc); void tws_send_event(struct tws_softc *sc, u_int8_t event); uint8_t tws_get_state(struct tws_softc *sc); void tws_release_request(struct tws_request *req); /* Function prototypes */ static d_open_t tws_open; static d_close_t tws_close; static d_read_t tws_read; static d_write_t tws_write; extern d_ioctl_t tws_ioctl; static int tws_init(struct tws_softc *sc); static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size); static int tws_init_aen_q(struct tws_softc *sc); static int tws_init_trace_q(struct tws_softc *sc); static int tws_setup_irq(struct tws_softc *sc); int tws_setup_intr(struct tws_softc *sc, int irqs); int tws_teardown_intr(struct tws_softc *sc); /* Character device entry points */ static struct cdevsw tws_cdevsw = { .d_version = D_VERSION, .d_open = tws_open, .d_close = tws_close, .d_read = tws_read, .d_write = tws_write, .d_ioctl = tws_ioctl, .d_name = "tws", }; /* * In the cdevsw routines, we find our softc by using the si_drv1 member * of struct cdev. We set this variable to point to our softc in our * attach routine when we create the /dev entry. */ int tws_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, oflags); return (0); } int tws_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, fflag); return (0); } int tws_read(struct cdev *dev, struct uio *uio, int ioflag) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); return (0); } int tws_write(struct cdev *dev, struct uio *uio, int ioflag) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); return (0); } /* PCI Support Functions */ /* * Compare the device ID of this device against the IDs that this driver * supports. If there is a match, set the description and return success. */ static int tws_probe(device_t dev) { static u_int8_t first_ctlr = 1; if ((pci_get_vendor(dev) == TWS_VENDOR_ID) && (pci_get_device(dev) == TWS_DEVICE_ID)) { device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller"); if (first_ctlr) { printf("LSI 3ware device driver for SAS/SATA storage " "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING); first_ctlr = 0; } return(BUS_PROBE_DEFAULT); } return (ENXIO); } /* Attach function is only called if the probe is successful. */ static int tws_attach(device_t dev) { struct tws_softc *sc = device_get_softc(dev); u_int32_t cmd, bar; int error=0,i; /* no tracing yet */ /* Look up our softc and initialize its fields. */ sc->tws_dev = dev; sc->device_id = pci_get_device(dev); sc->subvendor_id = pci_get_subvendor(dev); sc->subdevice_id = pci_get_subdevice(dev); /* Intialize mutexes */ mtx_init( &sc->q_lock, "tws_q_lock", NULL, MTX_DEF); mtx_init( &sc->sim_lock, "tws_sim_lock", NULL, MTX_DEF); mtx_init( &sc->gen_lock, "tws_gen_lock", NULL, MTX_DEF); mtx_init( &sc->io_lock, "tws_io_lock", NULL, MTX_DEF); if ( tws_init_trace_q(sc) == FAILURE ) printf("trace init failure\n"); /* send init event */ mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_INIT_START); mtx_unlock(&sc->gen_lock); #if _BYTE_ORDER == _BIG_ENDIAN TWS_TRACE(sc, "BIG endian", 0, 0); #endif /* sysctl context setup */ sysctl_ctx_init(&sc->tws_clist); sc->tws_oidp = SYSCTL_ADD_NODE(&sc->tws_clist, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0, ""); if ( sc->tws_oidp == NULL ) { tws_log(sc, SYSCTL_TREE_NODE_ADD); goto attach_fail_1; } SYSCTL_ADD_STRING(&sc->tws_clist, SYSCTL_CHILDREN(sc->tws_oidp), OID_AUTO, "driver_version", CTLFLAG_RD, TWS_DRIVER_VERSION_STRING, 0, "TWS driver version"); cmd = pci_read_config(dev, PCIR_COMMAND, 2); if ( (cmd & PCIM_CMD_PORTEN) == 0) { tws_log(sc, PCI_COMMAND_READ); goto attach_fail_1; } /* Force the busmaster enable bit on. */ cmd |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, cmd, 2); bar = pci_read_config(dev, TWS_PCI_BAR0, 4); TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0); bar = pci_read_config(dev, TWS_PCI_BAR1, 4); bar = bar & ~TWS_BIT2; TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0); /* MFA base address is BAR2 register used for * push mode. Firmware will evatualy move to * pull mode during witch this needs to change */ #ifndef TWS_PULL_MODE_ENABLE sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4); sc->mfa_base = sc->mfa_base & ~TWS_BIT2; TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0); #endif /* allocate MMIO register space */ sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */ if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE)) == NULL) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_1; } sc->bus_tag = rman_get_bustag(sc->reg_res); sc->bus_handle = rman_get_bushandle(sc->reg_res); #ifndef TWS_PULL_MODE_ENABLE /* Allocate bus space for inbound mfa */ sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */ if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE)) == NULL) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_2; } sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res); sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res); #endif /* Allocate and register our interrupt. */ sc->intr_type = TWS_INTx; /* default */ if ( tws_enable_msi ) sc->intr_type = TWS_MSI; if ( tws_setup_irq(sc) == FAILURE ) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_3; } /* * Create a /dev entry for this device. The kernel will assign us * a major number automatically. We use the unit number of this * device as the minor number and name the character device * "tws". */ sc->tws_cdev = make_dev(&tws_cdevsw, device_get_unit(dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u", device_get_unit(dev)); sc->tws_cdev->si_drv1 = sc; if ( tws_init(sc) == FAILURE ) { tws_log(sc, TWS_INIT_FAILURE); goto attach_fail_4; } if ( tws_init_ctlr(sc) == FAILURE ) { tws_log(sc, TWS_CTLR_INIT_FAILURE); goto attach_fail_4; } if ((error = tws_cam_attach(sc))) { tws_log(sc, TWS_CAM_ATTACH); goto attach_fail_4; } /* send init complete event */ mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_INIT_COMPLETE); mtx_unlock(&sc->gen_lock); TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id); return(0); attach_fail_4: tws_teardown_intr(sc); destroy_dev(sc->tws_cdev); attach_fail_3: for(i=0;iirqs;i++) { if ( sc->irq_res[i] ){ if (bus_release_resource(sc->tws_dev, SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) TWS_TRACE(sc, "bus irq res", 0, 0); } } #ifndef TWS_PULL_MODE_ENABLE attach_fail_2: #endif if ( sc->mfa_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id); } if ( sc->reg_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id); } attach_fail_1: mtx_destroy(&sc->q_lock); mtx_destroy(&sc->sim_lock); mtx_destroy(&sc->gen_lock); mtx_destroy(&sc->io_lock); sysctl_ctx_free(&sc->tws_clist); return (ENXIO); } /* Detach device. */ static int tws_detach(device_t dev) { struct tws_softc *sc = device_get_softc(dev); int i; u_int32_t reg; TWS_TRACE_DEBUG(sc, "entry", 0, 0); mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_UNINIT_START); mtx_unlock(&sc->gen_lock); /* needs to disable interrupt before detaching from cam */ tws_turn_off_interrupts(sc); /* clear door bell */ tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0); sc->obfl_q_overrun = false; tws_init_connect(sc, 1); /* Teardown the state in our softc created in our attach routine. */ /* Disconnect the interrupt handler. */ tws_teardown_intr(sc); /* Release irq resource */ for(i=0;iirqs;i++) { if ( sc->irq_res[i] ){ if (bus_release_resource(sc->tws_dev, SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) TWS_TRACE(sc, "bus release irq resource", i, sc->irq_res_id[i]); } } if ( sc->intr_type == TWS_MSI ) { pci_release_msi(sc->tws_dev); } tws_cam_detach(sc); /* Release memory resource */ if ( sc->mfa_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id); } if ( sc->reg_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id); } free(sc->reqs, M_TWS); free(sc->sense_bufs, M_TWS); free(sc->scan_ccb, M_TWS); free(sc->aen_q.q, M_TWS); free(sc->trace_q.q, M_TWS); mtx_destroy(&sc->q_lock); mtx_destroy(&sc->sim_lock); mtx_destroy(&sc->gen_lock); mtx_destroy(&sc->io_lock); destroy_dev(sc->tws_cdev); sysctl_ctx_free(&sc->tws_clist); return (0); } int tws_setup_intr(struct tws_softc *sc, int irqs) { int i, error; for(i=0;iintr_handle[i])) { if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i], INTR_TYPE_CAM | INTR_MPSAFE, #if (__FreeBSD_version >= 700000) NULL, #endif tws_intr, sc, &sc->intr_handle[i]))) { tws_log(sc, SETUP_INTR_RES); return(FAILURE); } } } return(SUCCESS); } int tws_teardown_intr(struct tws_softc *sc) { int i, error; for(i=0;iirqs;i++) { if (sc->intr_handle[i]) { error = bus_teardown_intr(sc->tws_dev, sc->irq_res[i], sc->intr_handle[i]); sc->intr_handle[i] = NULL; } } return(SUCCESS); } static int tws_setup_irq(struct tws_softc *sc) { int messages; u_int16_t cmd; cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2); switch(sc->intr_type) { case TWS_INTx : cmd = cmd & ~0x0400; pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); sc->irqs = 1; sc->irq_res_id[0] = 0; sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); if ( ! sc->irq_res[0] ) return(FAILURE); if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) return(FAILURE); device_printf(sc->tws_dev, "Using legacy INTx\n"); break; case TWS_MSI : cmd = cmd | 0x0400; pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); sc->irqs = 1; sc->irq_res_id[0] = 1; messages = 1; if (pci_alloc_msi(sc->tws_dev, &messages) != 0 ) { TWS_TRACE(sc, "pci alloc msi fail", 0, messages); return(FAILURE); } sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); if ( !sc->irq_res[0] ) return(FAILURE); if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) return(FAILURE); device_printf(sc->tws_dev, "Using MSI\n"); break; } return(SUCCESS); } static int tws_init(struct tws_softc *sc) { u_int32_t max_sg_elements; u_int32_t dma_mem_size; int error; u_int32_t reg; sc->seq_id = 0; if ( tws_queue_depth > TWS_MAX_REQS ) tws_queue_depth = TWS_MAX_REQS; if (tws_queue_depth < TWS_RESERVED_REQS+1) tws_queue_depth = TWS_RESERVED_REQS+1; sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false; max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ? TWS_MAX_64BIT_SG_ELEMENTS : TWS_MAX_32BIT_SG_ELEMENTS; dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) + (TWS_SECTOR_SIZE) ; - if ( bus_dma_tag_create(NULL, /* parent */ + if ( bus_dma_tag_create(bus_get_dma_tag(sc->tws_dev), /* PCI parent */ TWS_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE, /* maxsize */ max_sg_elements, /* numsegs */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->parent_tag /* tag */ )) { TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } /* In bound message frame requires 16byte alignment. * Outbound MF's can live with 4byte alignment - for now just * use 16 for both. */ if ( bus_dma_tag_create(sc->parent_tag, /* parent */ TWS_IN_MF_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ dma_mem_size, /* maxsize */ 1, /* numsegs */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->cmd_tag /* tag */ )) { TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, BUS_DMA_NOWAIT, &sc->cmd_map)) { TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit); return(ENOMEM); } /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */ sc->dma_mem_phys=0; error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem, dma_mem_size, tws_dmamap_cmds_load_cbfn, &sc->dma_mem_phys, 0); /* * Create a dma tag for data buffers; size will be the maximum * possible I/O size (128kB). */ if (bus_dma_tag_create(sc->parent_tag, /* parent */ TWS_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TWS_MAX_IO_SIZE, /* maxsize */ max_sg_elements, /* nsegments */ TWS_MAX_IO_SIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->io_lock, /* lockfuncarg */ &sc->data_tag /* tag */)) { TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } sc->reqs = malloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS, M_WAITOK | M_ZERO); if ( sc->reqs == NULL ) { TWS_TRACE_DEBUG(sc, "malloc failed", 0, sc->is64bit); return(ENOMEM); } sc->sense_bufs = malloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS, M_WAITOK | M_ZERO); if ( sc->sense_bufs == NULL ) { TWS_TRACE_DEBUG(sc, "sense malloc failed", 0, sc->is64bit); return(ENOMEM); } sc->scan_ccb = malloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO); if ( sc->scan_ccb == NULL ) { TWS_TRACE_DEBUG(sc, "ccb malloc failed", 0, sc->is64bit); return(ENOMEM); } if ( !tws_ctlr_ready(sc) ) if( !tws_ctlr_reset(sc) ) return(FAILURE); bzero(&sc->stats, sizeof(struct tws_stats)); tws_init_qs(sc); tws_turn_off_interrupts(sc); /* * enable pull mode by setting bit1 . * setting bit0 to 1 will enable interrupt coalesing * will revisit. */ #ifdef TWS_PULL_MODE_ENABLE reg = tws_read_reg(sc, TWS_I2O0_CTL, 4); TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL); tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4); #endif TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL); if ( tws_init_reqs(sc, dma_mem_size) == FAILURE ) return(FAILURE); if ( tws_init_aen_q(sc) == FAILURE ) return(FAILURE); return(SUCCESS); } static int tws_init_aen_q(struct tws_softc *sc) { sc->aen_q.head=0; sc->aen_q.tail=0; sc->aen_q.depth=256; sc->aen_q.overflow=0; sc->aen_q.q = malloc(sizeof(struct tws_event_packet)*sc->aen_q.depth, M_TWS, M_WAITOK | M_ZERO); if ( ! sc->aen_q.q ) return(FAILURE); return(SUCCESS); } static int tws_init_trace_q(struct tws_softc *sc) { sc->trace_q.head=0; sc->trace_q.tail=0; sc->trace_q.depth=256; sc->trace_q.overflow=0; sc->trace_q.q = malloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth, M_TWS, M_WAITOK | M_ZERO); if ( ! sc->trace_q.q ) return(FAILURE); return(SUCCESS); } static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size) { struct tws_command_packet *cmd_buf; cmd_buf = (struct tws_command_packet *)sc->dma_mem; int i; bzero(cmd_buf, dma_mem_size); TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0); mtx_lock(&sc->q_lock); for ( i=0; i< tws_queue_depth; i++) { if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) { /* log a ENOMEM failure msg here */ mtx_unlock(&sc->q_lock); return(FAILURE); } sc->reqs[i].cmd_pkt = &cmd_buf[i]; sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ; sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys + (i * sizeof(struct tws_command_packet)); sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys + sizeof(struct tws_command_header) + (i * sizeof(struct tws_command_packet)); sc->reqs[i].request_id = i; sc->reqs[i].sc = sc; sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128; sc->reqs[i].state = TWS_REQ_STATE_FREE; if ( i >= TWS_RESERVED_REQS ) tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q); } mtx_unlock(&sc->q_lock); return(SUCCESS); } static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error) { /* printf("command load done \n"); */ *((bus_addr_t *)arg) = segs[0].ds_addr; } void tws_send_event(struct tws_softc *sc, u_int8_t event) { mtx_assert(&sc->gen_lock, MA_OWNED); TWS_TRACE_DEBUG(sc, "received event ", 0, event); switch (event) { case TWS_INIT_START: sc->tws_state = TWS_INIT; break; case TWS_INIT_COMPLETE: if (sc->tws_state != TWS_INIT) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_ONLINE\n", sc->tws_state); } else { sc->tws_state = TWS_ONLINE; } break; case TWS_RESET_START: /* We can transition to reset state from any state except reset*/ if (sc->tws_state != TWS_RESET) { sc->tws_prev_state = sc->tws_state; sc->tws_state = TWS_RESET; } break; case TWS_RESET_COMPLETE: if (sc->tws_state != TWS_RESET) { device_printf(sc->tws_dev, "invalid state transition %d => %d (previous state)\n", sc->tws_state, sc->tws_prev_state); } else { sc->tws_state = sc->tws_prev_state; } break; case TWS_SCAN_FAILURE: if (sc->tws_state != TWS_ONLINE) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_OFFLINE\n", sc->tws_state); } else { sc->tws_state = TWS_OFFLINE; } break; case TWS_UNINIT_START: if ((sc->tws_state != TWS_ONLINE) && (sc->tws_state != TWS_OFFLINE)) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_UNINIT\n", sc->tws_state); } else { sc->tws_state = TWS_UNINIT; } break; } } uint8_t tws_get_state(struct tws_softc *sc) { return((u_int8_t)sc->tws_state); } /* Called during system shutdown after sync. */ static int tws_shutdown(device_t dev) { struct tws_softc *sc = device_get_softc(dev); TWS_TRACE_DEBUG(sc, "entry", 0, 0); tws_turn_off_interrupts(sc); tws_init_connect(sc, 1); return (0); } /* * Device suspend routine. */ static int tws_suspend(device_t dev) { struct tws_softc *sc = device_get_softc(dev); if ( sc ) TWS_TRACE_DEBUG(sc, "entry", 0, 0); return (0); } /* * Device resume routine. */ static int tws_resume(device_t dev) { struct tws_softc *sc = device_get_softc(dev); if ( sc ) TWS_TRACE_DEBUG(sc, "entry", 0, 0); return (0); } struct tws_request * tws_get_request(struct tws_softc *sc, u_int16_t type) { struct mtx *my_mutex = ((type == TWS_REQ_TYPE_SCSI_IO) ? &sc->q_lock : &sc->gen_lock); struct tws_request *r = NULL; mtx_lock(my_mutex); if (type == TWS_REQ_TYPE_SCSI_IO) { r = tws_q_remove_head(sc, TWS_FREE_Q); } else { if ( sc->reqs[type].state == TWS_REQ_STATE_FREE ) { r = &sc->reqs[type]; } } if ( r ) { bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache)); r->data = NULL; r->length = 0; r->type = type; r->flags = TWS_DIR_UNKNOWN; r->error_code = TWS_REQ_RET_INVALID; r->cb = NULL; r->ccb_ptr = NULL; r->thandle.callout = NULL; r->next = r->prev = NULL; r->state = ((type == TWS_REQ_TYPE_SCSI_IO) ? TWS_REQ_STATE_TRAN : TWS_REQ_STATE_BUSY); } mtx_unlock(my_mutex); return(r); } void tws_release_request(struct tws_request *req) { struct tws_softc *sc = req->sc; TWS_TRACE_DEBUG(sc, "entry", sc, 0); mtx_lock(&sc->q_lock); tws_q_insert_tail(sc, req, TWS_FREE_Q); mtx_unlock(&sc->q_lock); } static device_method_t tws_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tws_probe), DEVMETHOD(device_attach, tws_attach), DEVMETHOD(device_detach, tws_detach), DEVMETHOD(device_shutdown, tws_shutdown), DEVMETHOD(device_suspend, tws_suspend), DEVMETHOD(device_resume, tws_resume), DEVMETHOD_END }; static driver_t tws_driver = { "tws", tws_methods, sizeof(struct tws_softc) }; static devclass_t tws_devclass; /* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */ DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, 0, 0); MODULE_DEPEND(tws, cam, 1, 1, 1); MODULE_DEPEND(tws, pci, 1, 1, 1); TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth); TUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi);