Index: head/sys/dev/ata/chipsets/ata-promise.c =================================================================== --- head/sys/dev/ata/chipsets/ata-promise.c (revision 295663) +++ head/sys/dev/ata/chipsets/ata-promise.c (revision 295664) @@ -1,1282 +1,1282 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * 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. * * 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. */ #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 /* local prototypes */ static int ata_promise_chipinit(device_t dev); static int ata_promise_ch_attach(device_t dev); static int ata_promise_status(device_t dev); static int ata_promise_dmastart(struct ata_request *request); static int ata_promise_dmastop(struct ata_request *request); static void ata_promise_dmareset(device_t dev); static int ata_promise_setmode(device_t dev, int target, int mode); static int ata_promise_tx2_ch_attach(device_t dev); static int ata_promise_tx2_status(device_t dev); static int ata_promise_mio_ch_attach(device_t dev); static int ata_promise_mio_ch_detach(device_t dev); static void ata_promise_mio_intr(void *data); static int ata_promise_mio_status(device_t dev); static int ata_promise_mio_command(struct ata_request *request); static void ata_promise_mio_reset(device_t dev); static int ata_promise_mio_pm_read(device_t dev, int port, int reg, u_int32_t *result); static int ata_promise_mio_pm_write(device_t dev, int port, int reg, u_int32_t result); static u_int32_t ata_promise_mio_softreset(device_t dev, int port); static void ata_promise_mio_dmainit(device_t dev); static void ata_promise_mio_setprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static int ata_promise_mio_setmode(device_t dev, int target, int mode); static int ata_promise_mio_getrev(device_t dev, int target); static void ata_promise_sx4_intr(void *data); static int ata_promise_sx4_command(struct ata_request *request); static int ata_promise_apkt(u_int8_t *bytep, struct ata_request *request); static void ata_promise_queue_hpkt(struct ata_pci_controller *ctlr, u_int32_t hpkt); static void ata_promise_next_hpkt(struct ata_pci_controller *ctlr); /* misc defines */ #define PR_OLD 0 #define PR_NEW 1 #define PR_TX 2 #define PR_MIO 3 #define PR_TX4 0x01 #define PR_SX4X 0x02 #define PR_SX6K 0x04 #define PR_PATA 0x08 #define PR_CMBO 0x10 #define PR_CMBO2 0x20 #define PR_SATA 0x40 #define PR_SATA2 0x80 /* * Promise chipset support functions */ #define ATA_PDC_APKT_OFFSET 0x00000010 #define ATA_PDC_HPKT_OFFSET 0x00000040 #define ATA_PDC_ASG_OFFSET 0x00000080 #define ATA_PDC_LSG_OFFSET 0x000000c0 #define ATA_PDC_HSG_OFFSET 0x00000100 #define ATA_PDC_CHN_OFFSET 0x00000400 #define ATA_PDC_BUF_BASE 0x00400000 #define ATA_PDC_BUF_OFFSET 0x00100000 #define ATA_PDC_MAX_HPKT 8 #define ATA_PDC_WRITE_REG 0x00 #define ATA_PDC_WRITE_CTL 0x0e #define ATA_PDC_WRITE_END 0x08 #define ATA_PDC_WAIT_NBUSY 0x10 #define ATA_PDC_WAIT_READY 0x18 #define ATA_PDC_1B 0x20 #define ATA_PDC_2B 0x40 struct host_packet { u_int32_t addr; TAILQ_ENTRY(host_packet) chain; }; struct ata_promise_sx4 { struct mtx mtx; TAILQ_HEAD(, host_packet) queue; int busy; }; static int ata_promise_probe(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(dev); const struct ata_chip_id *idx; static const struct ata_chip_id ids[] = {{ ATA_PDC20246, 0, PR_OLD, 0x00, ATA_UDMA2, "PDC20246" }, { ATA_PDC20262, 0, PR_NEW, 0x00, ATA_UDMA4, "PDC20262" }, { ATA_PDC20263, 0, PR_NEW, 0x00, ATA_UDMA4, "PDC20263" }, { ATA_PDC20265, 0, PR_NEW, 0x00, ATA_UDMA5, "PDC20265" }, { ATA_PDC20267, 0, PR_NEW, 0x00, ATA_UDMA5, "PDC20267" }, { ATA_PDC20268, 0, PR_TX, PR_TX4, ATA_UDMA5, "PDC20268" }, { ATA_PDC20269, 0, PR_TX, 0x00, ATA_UDMA6, "PDC20269" }, { ATA_PDC20270, 0, PR_TX, PR_TX4, ATA_UDMA5, "PDC20270" }, { ATA_PDC20271, 0, PR_TX, 0x00, ATA_UDMA6, "PDC20271" }, { ATA_PDC20275, 0, PR_TX, 0x00, ATA_UDMA6, "PDC20275" }, { ATA_PDC20276, 0, PR_TX, PR_SX6K, ATA_UDMA6, "PDC20276" }, { ATA_PDC20277, 0, PR_TX, 0x00, ATA_UDMA6, "PDC20277" }, { ATA_PDC20318, 0, PR_MIO, PR_SATA, ATA_SA150, "PDC20318" }, { ATA_PDC20319, 0, PR_MIO, PR_SATA, ATA_SA150, "PDC20319" }, { ATA_PDC20371, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20371" }, { ATA_PDC20375, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20375" }, { ATA_PDC20376, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20376" }, { ATA_PDC20377, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20377" }, { ATA_PDC20378, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20378" }, { ATA_PDC20379, 0, PR_MIO, PR_CMBO, ATA_SA150, "PDC20379" }, { ATA_PDC20571, 0, PR_MIO, PR_CMBO2, ATA_SA150, "PDC20571" }, { ATA_PDC20575, 0, PR_MIO, PR_CMBO2, ATA_SA150, "PDC20575" }, { ATA_PDC20579, 0, PR_MIO, PR_CMBO2, ATA_SA150, "PDC20579" }, { ATA_PDC20771, 0, PR_MIO, PR_CMBO2, ATA_SA300, "PDC20771" }, { ATA_PDC40775, 0, PR_MIO, PR_CMBO2, ATA_SA300, "PDC40775" }, { ATA_PDC20617, 0, PR_MIO, PR_PATA, ATA_UDMA6, "PDC20617" }, { ATA_PDC20618, 0, PR_MIO, PR_PATA, ATA_UDMA6, "PDC20618" }, { ATA_PDC20619, 0, PR_MIO, PR_PATA, ATA_UDMA6, "PDC20619" }, { ATA_PDC20620, 0, PR_MIO, PR_PATA, ATA_UDMA6, "PDC20620" }, { ATA_PDC20621, 0, PR_MIO, PR_SX4X, ATA_UDMA5, "PDC20621" }, { ATA_PDC20622, 0, PR_MIO, PR_SX4X, ATA_SA150, "PDC20622" }, { ATA_PDC40518, 0, PR_MIO, PR_SATA2, ATA_SA150, "PDC40518" }, { ATA_PDC40519, 0, PR_MIO, PR_SATA2, ATA_SA150, "PDC40519" }, { ATA_PDC40718, 0, PR_MIO, PR_SATA2, ATA_SA300, "PDC40718" }, { ATA_PDC40719, 0, PR_MIO, PR_SATA2, ATA_SA300, "PDC40719" }, { ATA_PDC40779, 0, PR_MIO, PR_SATA2, ATA_SA300, "PDC40779" }, { 0, 0, 0, 0, 0, 0}}; char buffer[64]; uintptr_t devid = 0; if (pci_get_vendor(dev) != ATA_PROMISE_ID) return ENXIO; if (!(idx = ata_match_chip(dev, ids))) return ENXIO; /* if we are on a SuperTrak SX6000 dont attach */ if ((idx->cfg2 & PR_SX6K) && pci_get_class(GRANDPARENT(dev))==PCIC_BRIDGE && !BUS_READ_IVAR(device_get_parent(GRANDPARENT(dev)), GRANDPARENT(dev), PCI_IVAR_DEVID, &devid) && devid == ATA_I960RM) return ENXIO; strcpy(buffer, "Promise "); strcat(buffer, idx->text); /* if we are on a FastTrak TX4, adjust the interrupt resource */ if ((idx->cfg2 & PR_TX4) && pci_get_class(GRANDPARENT(dev))==PCIC_BRIDGE && !BUS_READ_IVAR(device_get_parent(GRANDPARENT(dev)), GRANDPARENT(dev), PCI_IVAR_DEVID, &devid) && ((devid == ATA_DEC_21150) || (devid == ATA_DEC_21150_1))) { - static long start = 0, end = 0; + static rman_res_t start = 0, end = 0; if (pci_get_slot(dev) == 1) { bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &end); strcat(buffer, " (channel 0+1)"); } else if (pci_get_slot(dev) == 2 && start && end) { bus_set_resource(dev, SYS_RES_IRQ, 0, start, end); strcat(buffer, " (channel 2+3)"); } else { start = end = 0; } } sprintf(buffer, "%s %s controller", buffer, ata_mode2str(idx->max_dma)); device_set_desc_copy(dev, buffer); ctlr->chip = idx; ctlr->chipinit = ata_promise_chipinit; return (BUS_PROBE_LOW_PRIORITY); } static int ata_promise_chipinit(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(dev); int stat_reg; if (ata_setup_interrupt(dev, ata_generic_intr)) return ENXIO; switch (ctlr->chip->cfg1) { case PR_NEW: /* setup clocks */ ATA_OUTB(ctlr->r_res1, 0x11, ATA_INB(ctlr->r_res1, 0x11) | 0x0a); /* FALLTHROUGH */ case PR_OLD: /* enable burst mode */ ATA_OUTB(ctlr->r_res1, 0x1f, ATA_INB(ctlr->r_res1, 0x1f) | 0x01); ctlr->ch_attach = ata_promise_ch_attach; ctlr->ch_detach = ata_pci_ch_detach; ctlr->setmode = ata_promise_setmode; return 0; case PR_TX: ctlr->ch_attach = ata_promise_tx2_ch_attach; ctlr->ch_detach = ata_pci_ch_detach; ctlr->setmode = ata_promise_setmode; return 0; case PR_MIO: ctlr->r_type1 = SYS_RES_MEMORY; ctlr->r_rid1 = PCIR_BAR(4); if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1, &ctlr->r_rid1, RF_ACTIVE))) goto failnfree; #ifdef __sparc64__ if (ctlr->chip->cfg2 == PR_SX4X && !bus_space_map(rman_get_bustag(ctlr->r_res1), rman_get_bushandle(ctlr->r_res1), rman_get_size(ctlr->r_res1), BUS_SPACE_MAP_LINEAR, NULL)) goto failnfree; #endif ctlr->r_type2 = SYS_RES_MEMORY; ctlr->r_rid2 = PCIR_BAR(3); if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, RF_ACTIVE))) goto failnfree; if (ctlr->chip->cfg2 == PR_SX4X) { struct ata_promise_sx4 *hpkt; u_int32_t dimm = ATA_INL(ctlr->r_res2, 0x000c0080); if (bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle) || bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS, NULL, ata_promise_sx4_intr, ctlr, &ctlr->handle)) { device_printf(dev, "unable to setup interrupt\n"); goto failnfree; } /* print info about cache memory */ device_printf(dev, "DIMM size %dMB @ 0x%08x%s\n", (((dimm >> 16) & 0xff)-((dimm >> 24) & 0xff)+1) << 4, ((dimm >> 24) & 0xff), ATA_INL(ctlr->r_res2, 0x000c0088) & (1<<16) ? " ECC enabled" : "" ); /* adjust cache memory parameters */ ATA_OUTL(ctlr->r_res2, 0x000c000c, (ATA_INL(ctlr->r_res2, 0x000c000c) & 0xffff0000)); /* setup host packet controls */ hpkt = malloc(sizeof(struct ata_promise_sx4), M_ATAPCI, M_NOWAIT | M_ZERO); if (hpkt == NULL) { device_printf(dev, "Cannot allocate HPKT\n"); goto failnfree; } mtx_init(&hpkt->mtx, "ATA promise HPKT lock", NULL, MTX_DEF); TAILQ_INIT(&hpkt->queue); hpkt->busy = 0; ctlr->chipset_data = hpkt; ctlr->ch_attach = ata_promise_mio_ch_attach; ctlr->ch_detach = ata_promise_mio_ch_detach; ctlr->reset = ata_promise_mio_reset; ctlr->setmode = ata_promise_setmode; ctlr->channels = 4; return 0; } /* mio type controllers need an interrupt intercept */ if (bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle) || bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS, NULL, ata_promise_mio_intr, ctlr, &ctlr->handle)) { device_printf(dev, "unable to setup interrupt\n"); goto failnfree; } switch (ctlr->chip->cfg2) { case PR_PATA: ctlr->channels = ((ATA_INL(ctlr->r_res2, 0x48) & 0x01) > 0) + ((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 2; goto sata150; case PR_CMBO: ctlr->channels = 3; goto sata150; case PR_SATA: ctlr->channels = 4; sata150: stat_reg = 0x6c; break; case PR_CMBO2: ctlr->channels = 3; goto sataii; case PR_SATA2: default: ctlr->channels = 4; sataii: stat_reg = 0x60; break; } /* prime fake interrupt register */ ctlr->chipset_data = (void *)(uintptr_t)0xffffffff; /* clear SATA status and unmask interrupts */ ATA_OUTL(ctlr->r_res2, stat_reg, 0x000000ff); /* enable "long burst length" on gen2 chips */ if ((ctlr->chip->cfg2 == PR_SATA2) || (ctlr->chip->cfg2 == PR_CMBO2)) ATA_OUTL(ctlr->r_res2, 0x44, ATA_INL(ctlr->r_res2, 0x44) | 0x2000); ctlr->ch_attach = ata_promise_mio_ch_attach; ctlr->ch_detach = ata_promise_mio_ch_detach; ctlr->reset = ata_promise_mio_reset; ctlr->setmode = ata_promise_mio_setmode; ctlr->getrev = ata_promise_mio_getrev; return 0; } failnfree: if (ctlr->r_res2) bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2); if (ctlr->r_res1) bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1, ctlr->r_res1); return ENXIO; } static int ata_promise_ch_attach(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); if (ata_pci_ch_attach(dev)) return ENXIO; if (ctlr->chip->cfg1 == PR_NEW) { ch->dma.start = ata_promise_dmastart; ch->dma.stop = ata_promise_dmastop; ch->dma.reset = ata_promise_dmareset; } ch->hw.status = ata_promise_status; ch->flags |= ATA_NO_ATAPI_DMA; ch->flags |= ATA_CHECKS_CABLE; return 0; } static int ata_promise_status(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); if (ATA_INL(ctlr->r_res1, 0x1c) & (ch->unit ? 0x00004000 : 0x00000400)) { return ata_pci_status(dev); } return 0; } static int ata_promise_dmastart(struct ata_request *request) { struct ata_pci_controller *ctlr=device_get_softc(device_get_parent(request->parent)); struct ata_channel *ch = device_get_softc(request->parent); if (request->flags & ATA_R_48BIT) { ATA_OUTB(ctlr->r_res1, 0x11, ATA_INB(ctlr->r_res1, 0x11) | (ch->unit ? 0x08 : 0x02)); ATA_OUTL(ctlr->r_res1, ch->unit ? 0x24 : 0x20, ((request->flags & ATA_R_READ) ? 0x05000000 : 0x06000000) | (request->bytecount >> 1)); } ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, (ATA_IDX_INB(ch, ATA_BMSTAT_PORT) | (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR))); ATA_IDX_OUTL(ch, ATA_BMDTP_PORT, request->dma->sg_bus); ATA_IDX_OUTB(ch, ATA_BMCMD_PORT, ((request->flags & ATA_R_READ) ? ATA_BMCMD_WRITE_READ : 0) | ATA_BMCMD_START_STOP); ch->dma.flags |= ATA_DMA_ACTIVE; return 0; } static int ata_promise_dmastop(struct ata_request *request) { struct ata_pci_controller *ctlr=device_get_softc(device_get_parent(request->parent)); struct ata_channel *ch = device_get_softc(request->parent); int error; if (request->flags & ATA_R_48BIT) { ATA_OUTB(ctlr->r_res1, 0x11, ATA_INB(ctlr->r_res1, 0x11) & ~(ch->unit ? 0x08 : 0x02)); ATA_OUTL(ctlr->r_res1, ch->unit ? 0x24 : 0x20, 0); } error = ATA_IDX_INB(ch, ATA_BMSTAT_PORT); ATA_IDX_OUTB(ch, ATA_BMCMD_PORT, ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP); ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR); ch->dma.flags &= ~ATA_DMA_ACTIVE; return error; } static void ata_promise_dmareset(device_t dev) { struct ata_channel *ch = device_get_softc(dev); ATA_IDX_OUTB(ch, ATA_BMCMD_PORT, ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP); ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR); ch->flags &= ~ATA_DMA_ACTIVE; } static int ata_promise_setmode(device_t dev, int target, int mode) { device_t parent = device_get_parent(dev); struct ata_pci_controller *ctlr = device_get_softc(parent); struct ata_channel *ch = device_get_softc(dev); int devno = (ch->unit << 1) + target; static const uint32_t timings[][2] = { /* PR_OLD PR_NEW mode */ { 0x004ff329, 0x004fff2f }, /* PIO 0 */ { 0x004fec25, 0x004ff82a }, /* PIO 1 */ { 0x004fe823, 0x004ff026 }, /* PIO 2 */ { 0x004fe622, 0x004fec24 }, /* PIO 3 */ { 0x004fe421, 0x004fe822 }, /* PIO 4 */ { 0x004567f3, 0x004acef6 }, /* MWDMA 0 */ { 0x004467f3, 0x0048cef6 }, /* MWDMA 1 */ { 0x004367f3, 0x0046cef6 }, /* MWDMA 2 */ { 0x004367f3, 0x0046cef6 }, /* UDMA 0 */ { 0x004247f3, 0x00448ef6 }, /* UDMA 1 */ { 0x004127f3, 0x00436ef6 }, /* UDMA 2 */ { 0, 0x00424ef6 }, /* UDMA 3 */ { 0, 0x004127f3 }, /* UDMA 4 */ { 0, 0x004127f3 } /* UDMA 5 */ }; mode = min(mode, ctlr->chip->max_dma); switch (ctlr->chip->cfg1) { case PR_OLD: case PR_NEW: if (ata_dma_check_80pin && mode > ATA_UDMA2 && (pci_read_config(parent, 0x50, 2) & (ch->unit ? 1 << 11 : 1 << 10))) { ata_print_cable(dev, "controller"); mode = ATA_UDMA2; } break; case PR_TX: ATA_IDX_OUTB(ch, ATA_BMDEVSPEC_0, 0x0b); if (ata_dma_check_80pin && mode > ATA_UDMA2 && ATA_IDX_INB(ch, ATA_BMDEVSPEC_1) & 0x04) { ata_print_cable(dev, "controller"); mode = ATA_UDMA2; } break; case PR_MIO: if (ata_dma_check_80pin && mode > ATA_UDMA2 && (ATA_INL(ctlr->r_res2, (ctlr->chip->cfg2 & PR_SX4X ? 0x000c0260 : 0x0260) + (ch->unit << 7)) & 0x01000000)) { ata_print_cable(dev, "controller"); mode = ATA_UDMA2; } break; } if (ctlr->chip->cfg1 < PR_TX) pci_write_config(parent, 0x60 + (devno << 2), timings[ata_mode2idx(mode)][ctlr->chip->cfg1], 4); return (mode); } static int ata_promise_tx2_ch_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); if (ata_pci_ch_attach(dev)) return ENXIO; ch->hw.status = ata_promise_tx2_status; ch->flags |= ATA_CHECKS_CABLE; return 0; } static int ata_promise_tx2_status(device_t dev) { struct ata_channel *ch = device_get_softc(dev); ATA_IDX_OUTB(ch, ATA_BMDEVSPEC_0, 0x0b); if (ATA_IDX_INB(ch, ATA_BMDEVSPEC_1) & 0x20) { return ata_pci_status(dev); } return 0; } static int ata_promise_mio_ch_attach(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int offset = (ctlr->chip->cfg2 & PR_SX4X) ? 0x000c0000 : 0; int i; ata_promise_mio_dmainit(dev); for (i = ATA_DATA; i <= ATA_COMMAND; i++) { ch->r_io[i].res = ctlr->r_res2; ch->r_io[i].offset = offset + 0x0200 + (i << 2) + (ch->unit << 7); } ch->r_io[ATA_CONTROL].res = ctlr->r_res2; ch->r_io[ATA_CONTROL].offset = offset + 0x0238 + (ch->unit << 7); ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2; ata_default_registers(dev); if ((ctlr->chip->cfg2 & (PR_SATA | PR_SATA2)) || ((ctlr->chip->cfg2 & (PR_CMBO | PR_CMBO2)) && ch->unit < 2)) { ch->r_io[ATA_SSTATUS].res = ctlr->r_res2; ch->r_io[ATA_SSTATUS].offset = 0x400 + (ch->unit << 8); ch->r_io[ATA_SERROR].res = ctlr->r_res2; ch->r_io[ATA_SERROR].offset = 0x404 + (ch->unit << 8); ch->r_io[ATA_SCONTROL].res = ctlr->r_res2; ch->r_io[ATA_SCONTROL].offset = 0x408 + (ch->unit << 8); ch->flags |= ATA_NO_SLAVE; ch->flags |= ATA_SATA; } ch->flags |= ATA_USE_16BIT; ch->flags |= ATA_CHECKS_CABLE; ata_generic_hw(dev); if (ctlr->chip->cfg2 & PR_SX4X) { ch->hw.command = ata_promise_sx4_command; } else { ch->hw.command = ata_promise_mio_command; ch->hw.status = ata_promise_mio_status; ch->hw.softreset = ata_promise_mio_softreset; ch->hw.pm_read = ata_promise_mio_pm_read; ch->hw.pm_write = ata_promise_mio_pm_write; } return 0; } static int ata_promise_mio_ch_detach(device_t dev) { ata_dmafini(dev); return (0); } static void ata_promise_mio_intr(void *data) { struct ata_pci_controller *ctlr = data; struct ata_channel *ch; u_int32_t vector; int unit; /* * since reading interrupt status register on early "mio" chips * clears the status bits we cannot read it for each channel later on * in the generic interrupt routine. */ vector = ATA_INL(ctlr->r_res2, 0x040); ATA_OUTL(ctlr->r_res2, 0x040, vector); ctlr->chipset_data = (void *)(uintptr_t)vector; for (unit = 0; unit < ctlr->channels; unit++) { if ((ch = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(ch); } ctlr->chipset_data = (void *)(uintptr_t)0xffffffff; } static int ata_promise_mio_status(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); u_int32_t stat_reg, vector, status; switch (ctlr->chip->cfg2) { case PR_PATA: case PR_CMBO: case PR_SATA: stat_reg = 0x6c; break; case PR_CMBO2: case PR_SATA2: default: stat_reg = 0x60; break; } /* read and acknowledge interrupt */ vector = (uint32_t)(uintptr_t)ctlr->chipset_data; /* read and clear interface status */ status = ATA_INL(ctlr->r_res2, stat_reg); ATA_OUTL(ctlr->r_res2, stat_reg, status & (0x00000011 << ch->unit)); /* check for and handle disconnect events */ if (status & (0x00000001 << ch->unit)) { if (bootverbose) device_printf(dev, "DISCONNECT requested\n"); taskqueue_enqueue(taskqueue_thread, &ch->conntask); } /* check for and handle connect events */ if (status & (0x00000010 << ch->unit)) { if (bootverbose) device_printf(dev, "CONNECT requested\n"); taskqueue_enqueue(taskqueue_thread, &ch->conntask); } /* do we have any device action ? */ return (vector & (1 << (ch->unit + 1))); } static int ata_promise_mio_command(struct ata_request *request) { struct ata_pci_controller *ctlr=device_get_softc(device_get_parent(request->parent)); struct ata_channel *ch = device_get_softc(request->parent); u_int32_t *wordp = (u_int32_t *)ch->dma.work; ATA_OUTL(ctlr->r_res2, (ch->unit + 1) << 2, 0x00000001); if ((ctlr->chip->cfg2 == PR_SATA2) || ((ctlr->chip->cfg2 == PR_CMBO2) && (ch->unit < 2))) { /* set portmultiplier port */ ATA_OUTB(ctlr->r_res2, 0x4e8 + (ch->unit << 8), request->unit & 0x0f); } /* XXX SOS add ATAPI commands support later */ switch (request->u.ata.command) { default: return ata_generic_command(request); case ATA_READ_DMA: case ATA_READ_DMA48: wordp[0] = htole32(0x04 | ((ch->unit + 1) << 16) | (0x00 << 24)); break; case ATA_WRITE_DMA: case ATA_WRITE_DMA48: wordp[0] = htole32(0x00 | ((ch->unit + 1) << 16) | (0x00 << 24)); break; } wordp[1] = htole32(request->dma->sg_bus); wordp[2] = 0; ata_promise_apkt((u_int8_t*)wordp, request); ATA_OUTL(ctlr->r_res2, 0x0240 + (ch->unit << 7), ch->dma.work_bus); return 0; } static void ata_promise_mio_reset(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); struct ata_promise_sx4 *hpktp; switch (ctlr->chip->cfg2) { case PR_SX4X: /* softreset channel ATA module */ hpktp = ctlr->chipset_data; ATA_OUTL(ctlr->r_res2, 0xc0260 + (ch->unit << 7), ch->unit + 1); ata_udelay(1000); ATA_OUTL(ctlr->r_res2, 0xc0260 + (ch->unit << 7), (ATA_INL(ctlr->r_res2, 0xc0260 + (ch->unit << 7)) & ~0x00003f9f) | (ch->unit + 1)); /* softreset HOST module */ /* XXX SOS what about other outstandings */ mtx_lock(&hpktp->mtx); ATA_OUTL(ctlr->r_res2, 0xc012c, (ATA_INL(ctlr->r_res2, 0xc012c) & ~0x00000f9f) | (1 << 11)); DELAY(10); ATA_OUTL(ctlr->r_res2, 0xc012c, (ATA_INL(ctlr->r_res2, 0xc012c) & ~0x00000f9f)); hpktp->busy = 0; mtx_unlock(&hpktp->mtx); ata_generic_reset(dev); break; case PR_PATA: case PR_CMBO: case PR_SATA: if ((ctlr->chip->cfg2 == PR_SATA) || ((ctlr->chip->cfg2 == PR_CMBO) && (ch->unit < 2))) { /* mask plug/unplug intr */ ATA_OUTL(ctlr->r_res2, 0x06c, (0x00110000 << ch->unit)); } /* softreset channels ATA module */ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (1 << 11)); ata_udelay(10000); ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (ATA_INL(ctlr->r_res2, 0x0260 + (ch->unit << 7)) & ~0x00003f9f) | (ch->unit + 1)); if ((ctlr->chip->cfg2 == PR_SATA) || ((ctlr->chip->cfg2 == PR_CMBO) && (ch->unit < 2))) { if (ata_sata_phy_reset(dev, -1, 1)) ata_generic_reset(dev); else ch->devices = 0; /* reset and enable plug/unplug intr */ ATA_OUTL(ctlr->r_res2, 0x06c, (0x00000011 << ch->unit)); } else ata_generic_reset(dev); break; case PR_CMBO2: case PR_SATA2: if ((ctlr->chip->cfg2 == PR_SATA2) || ((ctlr->chip->cfg2 == PR_CMBO2) && (ch->unit < 2))) { /* set portmultiplier port */ //ATA_OUTL(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x0f); /* mask plug/unplug intr */ ATA_OUTL(ctlr->r_res2, 0x060, (0x00110000 << ch->unit)); } /* softreset channels ATA module */ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (1 << 11)); ata_udelay(10000); ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (ATA_INL(ctlr->r_res2, 0x0260 + (ch->unit << 7)) & ~0x00003f9f) | (ch->unit + 1)); if ((ctlr->chip->cfg2 == PR_SATA2) || ((ctlr->chip->cfg2 == PR_CMBO2) && (ch->unit < 2))) { /* set PHY mode to "improved" */ ATA_OUTL(ctlr->r_res2, 0x414 + (ch->unit << 8), (ATA_INL(ctlr->r_res2, 0x414 + (ch->unit << 8)) & ~0x00000003) | 0x00000001); if (ata_sata_phy_reset(dev, -1, 1)) { u_int32_t signature = ch->hw.softreset(dev, ATA_PM); if (1 | bootverbose) device_printf(dev, "SIGNATURE: %08x\n", signature); switch (signature >> 16) { case 0x0000: ch->devices = ATA_ATA_MASTER; break; case 0x9669: ch->devices = ATA_PORTMULTIPLIER; ata_pm_identify(dev); break; case 0xeb14: ch->devices = ATA_ATAPI_MASTER; break; default: /* SOS XXX */ if (bootverbose) device_printf(dev, "No signature, assuming disk device\n"); ch->devices = ATA_ATA_MASTER; } if (bootverbose) device_printf(dev, "promise_mio_reset devices=%08x\n", ch->devices); } else ch->devices = 0; /* reset and enable plug/unplug intr */ ATA_OUTL(ctlr->r_res2, 0x060, (0x00000011 << ch->unit)); ///* set portmultiplier port */ ATA_OUTL(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x00); } else ata_generic_reset(dev); break; } } static int ata_promise_mio_pm_read(device_t dev, int port, int reg, u_int32_t *result) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int timeout = 0; if (port < 0) { *result = ATA_IDX_INL(ch, reg); return (0); } if (port < ATA_PM) { switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SERROR: reg = 1; break; case ATA_SCONTROL: reg = 2; break; default: return (EINVAL); } } /* set portmultiplier port */ ATA_OUTB(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x0f); ATA_IDX_OUTB(ch, ATA_FEATURE, reg); ATA_IDX_OUTB(ch, ATA_DRIVE, port); ATA_IDX_OUTB(ch, ATA_COMMAND, ATA_READ_PM); while (timeout < 1000000) { u_int8_t status = ATA_IDX_INB(ch, ATA_STATUS); if (!(status & ATA_S_BUSY)) break; timeout += 1000; DELAY(1000); } if (timeout >= 1000000) return ATA_E_ABORT; *result = ATA_IDX_INB(ch, ATA_COUNT) | (ATA_IDX_INB(ch, ATA_SECTOR) << 8) | (ATA_IDX_INB(ch, ATA_CYL_LSB) << 16) | (ATA_IDX_INB(ch, ATA_CYL_MSB) << 24); return 0; } static int ata_promise_mio_pm_write(device_t dev, int port, int reg, u_int32_t value) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int timeout = 0; if (port < 0) { ATA_IDX_OUTL(ch, reg, value); return (0); } if (port < ATA_PM) { switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SERROR: reg = 1; break; case ATA_SCONTROL: reg = 2; break; default: return (EINVAL); } } /* set portmultiplier port */ ATA_OUTB(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x0f); ATA_IDX_OUTB(ch, ATA_FEATURE, reg); ATA_IDX_OUTB(ch, ATA_DRIVE, port); ATA_IDX_OUTB(ch, ATA_COUNT, value & 0xff); ATA_IDX_OUTB(ch, ATA_SECTOR, (value >> 8) & 0xff); ATA_IDX_OUTB(ch, ATA_CYL_LSB, (value >> 16) & 0xff); ATA_IDX_OUTB(ch, ATA_CYL_MSB, (value >> 24) & 0xff); ATA_IDX_OUTB(ch, ATA_COMMAND, ATA_WRITE_PM); while (timeout < 1000000) { u_int8_t status = ATA_IDX_INB(ch, ATA_STATUS); if (!(status & ATA_S_BUSY)) break; timeout += 1000; DELAY(1000); } if (timeout >= 1000000) return ATA_E_ABORT; return ATA_IDX_INB(ch, ATA_ERROR); } /* must be called with ATA channel locked and state_mtx held */ static u_int32_t ata_promise_mio_softreset(device_t dev, int port) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int timeout; /* set portmultiplier port */ ATA_OUTB(ctlr->r_res2, 0x4e8 + (ch->unit << 8), port & 0x0f); /* softreset device on this channel */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_MASTER)); DELAY(10); ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS | ATA_A_RESET); ata_udelay(10000); ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS); ata_udelay(150000); ATA_IDX_INB(ch, ATA_ERROR); /* wait for BUSY to go inactive */ for (timeout = 0; timeout < 100; timeout++) { u_int8_t /* err, */ stat; /* err = */ ATA_IDX_INB(ch, ATA_ERROR); stat = ATA_IDX_INB(ch, ATA_STATUS); //if (stat == err && timeout > (stat & ATA_S_BUSY ? 100 : 10)) //break; if (!(stat & ATA_S_BUSY)) { //if ((err & 0x7f) == ATA_E_ILI) { return ATA_IDX_INB(ch, ATA_COUNT) | (ATA_IDX_INB(ch, ATA_SECTOR) << 8) | (ATA_IDX_INB(ch, ATA_CYL_LSB) << 16) | (ATA_IDX_INB(ch, ATA_CYL_MSB) << 24); //} //else if (stat & 0x0f) { //stat |= ATA_S_BUSY; //} } if (!(stat & ATA_S_BUSY) || (stat == 0xff && timeout > 10)) break; ata_udelay(100000); } return -1; } static void ata_promise_mio_dmainit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* note start and stop are not used here */ ch->dma.setprd = ata_promise_mio_setprd; ch->dma.max_iosize = 65536; ata_dmainit(dev); } #define MAXLASTSGSIZE (32 * sizeof(u_int32_t)) static void ata_promise_mio_setprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct ata_dmasetprd_args *args = xsc; struct ata_dma_prdentry *prd = args->dmatab; int i; if ((args->error = error)) return; for (i = 0; i < nsegs; i++) { prd[i].addr = htole32(segs[i].ds_addr); prd[i].count = htole32(segs[i].ds_len); } if (segs[i - 1].ds_len > MAXLASTSGSIZE) { //printf("split last SG element of %u\n", segs[i - 1].ds_len); prd[i - 1].count = htole32(segs[i - 1].ds_len - MAXLASTSGSIZE); prd[i].count = htole32(MAXLASTSGSIZE); prd[i].addr = htole32(segs[i - 1].ds_addr + (segs[i - 1].ds_len - MAXLASTSGSIZE)); nsegs++; i++; } prd[i - 1].count |= htole32(ATA_DMA_EOT); KASSERT(nsegs <= ATA_DMA_ENTRIES, ("too many DMA segment entries\n")); args->nsegs = nsegs; } static int ata_promise_mio_setmode(device_t dev, int target, int mode) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); if ( (ctlr->chip->cfg2 == PR_SATA) || ((ctlr->chip->cfg2 == PR_CMBO) && (ch->unit < 2)) || (ctlr->chip->cfg2 == PR_SATA2) || ((ctlr->chip->cfg2 == PR_CMBO2) && (ch->unit < 2))) mode = ata_sata_setmode(dev, target, mode); else mode = ata_promise_setmode(dev, target, mode); return (mode); } static int ata_promise_mio_getrev(device_t dev, int target) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); if ( (ctlr->chip->cfg2 == PR_SATA) || ((ctlr->chip->cfg2 == PR_CMBO) && (ch->unit < 2)) || (ctlr->chip->cfg2 == PR_SATA2) || ((ctlr->chip->cfg2 == PR_CMBO2) && (ch->unit < 2))) return (ata_sata_getrev(dev, target)); else return (0); } static void ata_promise_sx4_intr(void *data) { struct ata_pci_controller *ctlr = data; struct ata_channel *ch; u_int32_t vector = ATA_INL(ctlr->r_res2, 0x000c0480); int unit; for (unit = 0; unit < ctlr->channels; unit++) { if (vector & (1 << (unit + 1))) if ((ch = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(ch); if (vector & (1 << (unit + 5))) if ((ch = ctlr->interrupt[unit].argument)) ata_promise_queue_hpkt(ctlr, htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HPKT_OFFSET)); if (vector & (1 << (unit + 9))) { ata_promise_next_hpkt(ctlr); if ((ch = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(ch); } if (vector & (1 << (unit + 13))) { ata_promise_next_hpkt(ctlr); if ((ch = ctlr->interrupt[unit].argument)) ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7), htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET)); } } } static int ata_promise_sx4_command(struct ata_request *request) { device_t gparent = device_get_parent(request->parent); struct ata_pci_controller *ctlr = device_get_softc(gparent); struct ata_channel *ch = device_get_softc(request->parent); struct ata_dma_prdentry *prd; caddr_t window = rman_get_virtual(ctlr->r_res1); u_int32_t *wordp; int i, idx, length = 0; /* XXX SOS add ATAPI commands support later */ switch (request->u.ata.command) { default: return -1; case ATA_ATA_IDENTIFY: case ATA_READ: case ATA_READ48: case ATA_READ_MUL: case ATA_READ_MUL48: case ATA_WRITE: case ATA_WRITE48: case ATA_WRITE_MUL: case ATA_WRITE_MUL48: ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit + 1) << 2), 0x00000001); return ata_generic_command(request); case ATA_SETFEATURES: case ATA_FLUSHCACHE: case ATA_FLUSHCACHE48: case ATA_SLEEP: case ATA_SET_MULTI: wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET); wordp[0] = htole32(0x08 | ((ch->unit + 1)<<16) | (0x00 << 24)); wordp[1] = 0; wordp[2] = 0; ata_promise_apkt((u_int8_t *)wordp, request); ATA_OUTL(ctlr->r_res2, 0x000c0484, 0x00000001); ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit + 1) << 2), 0x00000001); ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7), htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_APKT_OFFSET)); return 0; case ATA_READ_DMA: case ATA_READ_DMA48: case ATA_WRITE_DMA: case ATA_WRITE_DMA48: prd = request->dma->sg; wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HSG_OFFSET); i = idx = 0; do { wordp[idx++] = prd[i].addr; wordp[idx++] = prd[i].count; length += (prd[i].count & ~ATA_DMA_EOT); } while (!(prd[i++].count & ATA_DMA_EOT)); wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_LSG_OFFSET); wordp[0] = htole32((ch->unit * ATA_PDC_BUF_OFFSET) + ATA_PDC_BUF_BASE); wordp[1] = htole32(request->bytecount | ATA_DMA_EOT); wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_ASG_OFFSET); wordp[0] = htole32((ch->unit * ATA_PDC_BUF_OFFSET) + ATA_PDC_BUF_BASE); wordp[1] = htole32(request->bytecount | ATA_DMA_EOT); wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HPKT_OFFSET); if (request->flags & ATA_R_READ) wordp[0] = htole32(0x14 | ((ch->unit+9)<<16) | ((ch->unit+5)<<24)); if (request->flags & ATA_R_WRITE) wordp[0] = htole32(0x00 | ((ch->unit+13)<<16) | (0x00<<24)); wordp[1] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_HSG_OFFSET); wordp[2] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_LSG_OFFSET); wordp[3] = 0; wordp = (u_int32_t *) (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET); if (request->flags & ATA_R_READ) wordp[0] = htole32(0x04 | ((ch->unit+5)<<16) | (0x00<<24)); if (request->flags & ATA_R_WRITE) wordp[0] = htole32(0x10 | ((ch->unit+1)<<16) | ((ch->unit+13)<<24)); wordp[1] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_ASG_OFFSET); wordp[2] = 0; ata_promise_apkt((u_int8_t *)wordp, request); ATA_OUTL(ctlr->r_res2, 0x000c0484, 0x00000001); if (request->flags & ATA_R_READ) { ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+5)<<2), 0x00000001); ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+9)<<2), 0x00000001); ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7), htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET)); } if (request->flags & ATA_R_WRITE) { ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+1)<<2), 0x00000001); ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+13)<<2), 0x00000001); ata_promise_queue_hpkt(ctlr, htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HPKT_OFFSET)); } return 0; } } static int ata_promise_apkt(u_int8_t *bytep, struct ata_request *request) { int i = 12; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_PDC_WAIT_NBUSY|ATA_DRIVE; bytep[i++] = ATA_D_IBM | ATA_D_LBA | ATA_DEV(request->unit); bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_CTL; bytep[i++] = ATA_A_4BIT; if (request->flags & ATA_R_48BIT) { bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_FEATURE; bytep[i++] = request->u.ata.feature >> 8; bytep[i++] = request->u.ata.feature; bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_COUNT; bytep[i++] = request->u.ata.count >> 8; bytep[i++] = request->u.ata.count; bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_SECTOR; bytep[i++] = request->u.ata.lba >> 24; bytep[i++] = request->u.ata.lba; bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_CYL_LSB; bytep[i++] = request->u.ata.lba >> 32; bytep[i++] = request->u.ata.lba >> 8; bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_CYL_MSB; bytep[i++] = request->u.ata.lba >> 40; bytep[i++] = request->u.ata.lba >> 16; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_DRIVE; bytep[i++] = ATA_D_LBA | ATA_DEV(request->unit); } else { bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_FEATURE; bytep[i++] = request->u.ata.feature; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_COUNT; bytep[i++] = request->u.ata.count; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_SECTOR; bytep[i++] = request->u.ata.lba; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_CYL_LSB; bytep[i++] = request->u.ata.lba >> 8; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_CYL_MSB; bytep[i++] = request->u.ata.lba >> 16; bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_DRIVE; bytep[i++] = ATA_D_LBA | ATA_D_IBM | ATA_DEV(request->unit) | ((request->u.ata.lba >> 24)&0xf); } bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_END | ATA_COMMAND; bytep[i++] = request->u.ata.command; return i; } static void ata_promise_queue_hpkt(struct ata_pci_controller *ctlr, u_int32_t hpkt) { struct ata_promise_sx4 *hpktp = ctlr->chipset_data; mtx_lock(&hpktp->mtx); if (hpktp->busy) { struct host_packet *hp = malloc(sizeof(struct host_packet), M_TEMP, M_NOWAIT | M_ZERO); hp->addr = hpkt; TAILQ_INSERT_TAIL(&hpktp->queue, hp, chain); } else { hpktp->busy = 1; ATA_OUTL(ctlr->r_res2, 0x000c0100, hpkt); } mtx_unlock(&hpktp->mtx); } static void ata_promise_next_hpkt(struct ata_pci_controller *ctlr) { struct ata_promise_sx4 *hpktp = ctlr->chipset_data; struct host_packet *hp; mtx_lock(&hpktp->mtx); if ((hp = TAILQ_FIRST(&hpktp->queue))) { TAILQ_REMOVE(&hpktp->queue, hp, chain); ATA_OUTL(ctlr->r_res2, 0x000c0100, hp->addr); free(hp, M_TEMP); } else hpktp->busy = 0; mtx_unlock(&hpktp->mtx); } ATA_DECLARE_DRIVER(ata_promise); Index: head/sys/dev/pci/pci_pci.c =================================================================== --- head/sys/dev/pci/pci_pci.c (revision 295663) +++ head/sys/dev/pci/pci_pci.c (revision 295664) @@ -1,2119 +1,2119 @@ /*- * Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier * Copyright (c) 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. * 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$"); /* * PCI:PCI bridge support. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" static int pcib_probe(device_t dev); static int pcib_suspend(device_t dev); static int pcib_resume(device_t dev); static int pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate); static uint16_t pcib_ari_get_rid(device_t pcib, device_t dev); static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width); static void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width); static int pcib_ari_maxslots(device_t dev); static int pcib_ari_maxfuncs(device_t dev); static int pcib_try_enable_ari(device_t pcib, device_t dev); static int pcib_ari_enabled(device_t pcib); static void pcib_ari_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, int *func); static device_method_t pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcib_probe), DEVMETHOD(device_attach, pcib_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, pcib_suspend), DEVMETHOD(device_resume, pcib_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), #ifdef NEW_PCIB DEVMETHOD(bus_adjust_resource, pcib_adjust_resource), DEVMETHOD(bus_release_resource, pcib_release_resource), #else DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), #endif DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ DEVMETHOD(pcib_maxslots, pcib_ari_maxslots), DEVMETHOD(pcib_maxfuncs, pcib_ari_maxfuncs), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi), DEVMETHOD(pcib_release_msi, pcib_release_msi), DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix), DEVMETHOD(pcib_release_msix, pcib_release_msix), DEVMETHOD(pcib_map_msi, pcib_map_msi), DEVMETHOD(pcib_power_for_sleep, pcib_power_for_sleep), DEVMETHOD(pcib_get_rid, pcib_ari_get_rid), DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari), DEVMETHOD(pcib_ari_enabled, pcib_ari_enabled), DEVMETHOD(pcib_decode_rid, pcib_ari_decode_rid), DEVMETHOD_END }; static devclass_t pcib_devclass; DEFINE_CLASS_0(pcib, pcib_driver, pcib_methods, sizeof(struct pcib_softc)); DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, NULL, NULL); #ifdef NEW_PCIB SYSCTL_DECL(_hw_pci); static int pci_clear_pcib; SYSCTL_INT(_hw_pci, OID_AUTO, clear_pcib, CTLFLAG_RDTUN, &pci_clear_pcib, 0, "Clear firmware-assigned resources for PCI-PCI bridge I/O windows."); /* * Is a resource from a child device sub-allocated from one of our * resource managers? */ static int pcib_is_resource_managed(struct pcib_softc *sc, int type, struct resource *r) { switch (type) { #ifdef PCI_RES_BUS case PCI_RES_BUS: return (rman_is_region_manager(r, &sc->bus.rman)); #endif case SYS_RES_IOPORT: return (rman_is_region_manager(r, &sc->io.rman)); case SYS_RES_MEMORY: /* Prefetchable resources may live in either memory rman. */ if (rman_get_flags(r) & RF_PREFETCHABLE && rman_is_region_manager(r, &sc->pmem.rman)) return (1); return (rman_is_region_manager(r, &sc->mem.rman)); } return (0); } static int pcib_is_window_open(struct pcib_window *pw) { return (pw->valid && pw->base < pw->limit); } /* * XXX: If RF_ACTIVE did not also imply allocating a bus space tag and * handle for the resource, we could pass RF_ACTIVE up to the PCI bus * when allocating the resource windows and rely on the PCI bus driver * to do this for us. */ static void pcib_activate_window(struct pcib_softc *sc, int type) { PCI_ENABLE_IO(device_get_parent(sc->dev), sc->dev, type); } static void pcib_write_windows(struct pcib_softc *sc, int mask) { device_t dev; uint32_t val; dev = sc->dev; if (sc->io.valid && mask & WIN_IO) { val = pci_read_config(dev, PCIR_IOBASEL_1, 1); if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) { pci_write_config(dev, PCIR_IOBASEH_1, sc->io.base >> 16, 2); pci_write_config(dev, PCIR_IOLIMITH_1, sc->io.limit >> 16, 2); } pci_write_config(dev, PCIR_IOBASEL_1, sc->io.base >> 8, 1); pci_write_config(dev, PCIR_IOLIMITL_1, sc->io.limit >> 8, 1); } if (mask & WIN_MEM) { pci_write_config(dev, PCIR_MEMBASE_1, sc->mem.base >> 16, 2); pci_write_config(dev, PCIR_MEMLIMIT_1, sc->mem.limit >> 16, 2); } if (sc->pmem.valid && mask & WIN_PMEM) { val = pci_read_config(dev, PCIR_PMBASEL_1, 2); if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) { pci_write_config(dev, PCIR_PMBASEH_1, sc->pmem.base >> 32, 4); pci_write_config(dev, PCIR_PMLIMITH_1, sc->pmem.limit >> 32, 4); } pci_write_config(dev, PCIR_PMBASEL_1, sc->pmem.base >> 16, 2); pci_write_config(dev, PCIR_PMLIMITL_1, sc->pmem.limit >> 16, 2); } } /* * This is used to reject I/O port allocations that conflict with an * ISA alias range. */ static int pcib_is_isa_range(struct pcib_softc *sc, rman_res_t start, rman_res_t end, rman_res_t count) { rman_res_t next_alias; if (!(sc->bridgectl & PCIB_BCR_ISA_ENABLE)) return (0); /* Only check fixed ranges for overlap. */ if (start + count - 1 != end) return (0); /* ISA aliases are only in the lower 64KB of I/O space. */ if (start >= 65536) return (0); /* Check for overlap with 0x000 - 0x0ff as a special case. */ if (start < 0x100) goto alias; /* * If the start address is an alias, the range is an alias. * Otherwise, compute the start of the next alias range and * check if it is before the end of the candidate range. */ if ((start & 0x300) != 0) goto alias; next_alias = (start & ~0x3fful) | 0x100; if (next_alias <= end) goto alias; return (0); alias: if (bootverbose) device_printf(sc->dev, "I/O range %#lx-%#lx overlaps with an ISA alias\n", start, end); return (1); } static void pcib_add_window_resources(struct pcib_window *w, struct resource **res, int count) { struct resource **newarray; int error, i; newarray = malloc(sizeof(struct resource *) * (w->count + count), M_DEVBUF, M_WAITOK); if (w->res != NULL) bcopy(w->res, newarray, sizeof(struct resource *) * w->count); bcopy(res, newarray + w->count, sizeof(struct resource *) * count); free(w->res, M_DEVBUF); w->res = newarray; w->count += count; for (i = 0; i < count; i++) { error = rman_manage_region(&w->rman, rman_get_start(res[i]), rman_get_end(res[i])); if (error) panic("Failed to add resource to rman"); } } typedef void (nonisa_callback)(rman_res_t start, rman_res_t end, void *arg); static void pcib_walk_nonisa_ranges(rman_res_t start, rman_res_t end, nonisa_callback *cb, void *arg) { rman_res_t next_end; /* * If start is within an ISA alias range, move up to the start * of the next non-alias range. As a special case, addresses * in the range 0x000 - 0x0ff should also be skipped since * those are used for various system I/O devices in ISA * systems. */ if (start <= 65535) { if (start < 0x100 || (start & 0x300) != 0) { start &= ~0x3ff; start += 0x400; } } /* ISA aliases are only in the lower 64KB of I/O space. */ while (start <= MIN(end, 65535)) { next_end = MIN(start | 0xff, end); cb(start, next_end, arg); start += 0x400; } if (start <= end) cb(start, end, arg); } static void count_ranges(rman_res_t start, rman_res_t end, void *arg) { int *countp; countp = arg; (*countp)++; } struct alloc_state { struct resource **res; struct pcib_softc *sc; int count, error; }; static void alloc_ranges(rman_res_t start, rman_res_t end, void *arg) { struct alloc_state *as; struct pcib_window *w; int rid; as = arg; if (as->error != 0) return; w = &as->sc->io; rid = w->reg; if (bootverbose) device_printf(as->sc->dev, "allocating non-ISA range %#lx-%#lx\n", start, end); as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT, &rid, start, end, end - start + 1, 0); if (as->res[as->count] == NULL) as->error = ENXIO; else as->count++; } static int pcib_alloc_nonisa_ranges(struct pcib_softc *sc, rman_res_t start, rman_res_t end) { struct alloc_state as; int i, new_count; /* First, see how many ranges we need. */ new_count = 0; pcib_walk_nonisa_ranges(start, end, count_ranges, &new_count); /* Second, allocate the ranges. */ as.res = malloc(sizeof(struct resource *) * new_count, M_DEVBUF, M_WAITOK); as.sc = sc; as.count = 0; as.error = 0; pcib_walk_nonisa_ranges(start, end, alloc_ranges, &as); if (as.error != 0) { for (i = 0; i < as.count; i++) bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->io.reg, as.res[i]); free(as.res, M_DEVBUF); return (as.error); } KASSERT(as.count == new_count, ("%s: count mismatch", __func__)); /* Third, add the ranges to the window. */ pcib_add_window_resources(&sc->io, as.res, as.count); free(as.res, M_DEVBUF); return (0); } static void pcib_alloc_window(struct pcib_softc *sc, struct pcib_window *w, int type, int flags, pci_addr_t max_address) { struct resource *res; char buf[64]; int error, rid; - if (max_address != (u_long)max_address) + if (max_address != (rman_res_t)max_address) max_address = ~0ul; w->rman.rm_start = 0; w->rman.rm_end = max_address; w->rman.rm_type = RMAN_ARRAY; snprintf(buf, sizeof(buf), "%s %s window", device_get_nameunit(sc->dev), w->name); w->rman.rm_descr = strdup(buf, M_DEVBUF); error = rman_init(&w->rman); if (error) panic("Failed to initialize %s %s rman", device_get_nameunit(sc->dev), w->name); if (!pcib_is_window_open(w)) return; if (w->base > max_address || w->limit > max_address) { device_printf(sc->dev, "initial %s window has too many bits, ignoring\n", w->name); return; } if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE) (void)pcib_alloc_nonisa_ranges(sc, w->base, w->limit); else { rid = w->reg; res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit, w->limit - w->base + 1, flags); if (res != NULL) pcib_add_window_resources(w, &res, 1); } if (w->res == NULL) { device_printf(sc->dev, "failed to allocate initial %s window: %#jx-%#jx\n", w->name, (uintmax_t)w->base, (uintmax_t)w->limit); w->base = max_address; w->limit = 0; pcib_write_windows(sc, w->mask); return; } pcib_activate_window(sc, type); } /* * Initialize I/O windows. */ static void pcib_probe_windows(struct pcib_softc *sc) { pci_addr_t max; device_t dev; uint32_t val; dev = sc->dev; if (pci_clear_pcib) { pcib_bridge_init(dev); } /* Determine if the I/O port window is implemented. */ val = pci_read_config(dev, PCIR_IOBASEL_1, 1); if (val == 0) { /* * If 'val' is zero, then only 16-bits of I/O space * are supported. */ pci_write_config(dev, PCIR_IOBASEL_1, 0xff, 1); if (pci_read_config(dev, PCIR_IOBASEL_1, 1) != 0) { sc->io.valid = 1; pci_write_config(dev, PCIR_IOBASEL_1, 0, 1); } } else sc->io.valid = 1; /* Read the existing I/O port window. */ if (sc->io.valid) { sc->io.reg = PCIR_IOBASEL_1; sc->io.step = 12; sc->io.mask = WIN_IO; sc->io.name = "I/O port"; if ((val & PCIM_BRIO_MASK) == PCIM_BRIO_32) { sc->io.base = PCI_PPBIOBASE( pci_read_config(dev, PCIR_IOBASEH_1, 2), val); sc->io.limit = PCI_PPBIOLIMIT( pci_read_config(dev, PCIR_IOLIMITH_1, 2), pci_read_config(dev, PCIR_IOLIMITL_1, 1)); max = 0xffffffff; } else { sc->io.base = PCI_PPBIOBASE(0, val); sc->io.limit = PCI_PPBIOLIMIT(0, pci_read_config(dev, PCIR_IOLIMITL_1, 1)); max = 0xffff; } pcib_alloc_window(sc, &sc->io, SYS_RES_IOPORT, 0, max); } /* Read the existing memory window. */ sc->mem.valid = 1; sc->mem.reg = PCIR_MEMBASE_1; sc->mem.step = 20; sc->mem.mask = WIN_MEM; sc->mem.name = "memory"; sc->mem.base = PCI_PPBMEMBASE(0, pci_read_config(dev, PCIR_MEMBASE_1, 2)); sc->mem.limit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_MEMLIMIT_1, 2)); pcib_alloc_window(sc, &sc->mem, SYS_RES_MEMORY, 0, 0xffffffff); /* Determine if the prefetchable memory window is implemented. */ val = pci_read_config(dev, PCIR_PMBASEL_1, 2); if (val == 0) { /* * If 'val' is zero, then only 32-bits of memory space * are supported. */ pci_write_config(dev, PCIR_PMBASEL_1, 0xffff, 2); if (pci_read_config(dev, PCIR_PMBASEL_1, 2) != 0) { sc->pmem.valid = 1; pci_write_config(dev, PCIR_PMBASEL_1, 0, 2); } } else sc->pmem.valid = 1; /* Read the existing prefetchable memory window. */ if (sc->pmem.valid) { sc->pmem.reg = PCIR_PMBASEL_1; sc->pmem.step = 20; sc->pmem.mask = WIN_PMEM; sc->pmem.name = "prefetch"; if ((val & PCIM_BRPM_MASK) == PCIM_BRPM_64) { sc->pmem.base = PCI_PPBMEMBASE( pci_read_config(dev, PCIR_PMBASEH_1, 4), val); sc->pmem.limit = PCI_PPBMEMLIMIT( pci_read_config(dev, PCIR_PMLIMITH_1, 4), pci_read_config(dev, PCIR_PMLIMITL_1, 2)); max = 0xffffffffffffffff; } else { sc->pmem.base = PCI_PPBMEMBASE(0, val); sc->pmem.limit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_PMLIMITL_1, 2)); max = 0xffffffff; } pcib_alloc_window(sc, &sc->pmem, SYS_RES_MEMORY, RF_PREFETCHABLE, max); } } #ifdef PCI_RES_BUS /* * Allocate a suitable secondary bus for this bridge if needed and * initialize the resource manager for the secondary bus range. Note * that the minimum count is a desired value and this may allocate a * smaller range. */ void pcib_setup_secbus(device_t dev, struct pcib_secbus *bus, int min_count) { char buf[64]; int error, rid, sec_reg; switch (pci_read_config(dev, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) { case PCIM_HDRTYPE_BRIDGE: sec_reg = PCIR_SECBUS_1; bus->sub_reg = PCIR_SUBBUS_1; break; case PCIM_HDRTYPE_CARDBUS: sec_reg = PCIR_SECBUS_2; bus->sub_reg = PCIR_SUBBUS_2; break; default: panic("not a PCI bridge"); } bus->sec = pci_read_config(dev, sec_reg, 1); bus->sub = pci_read_config(dev, bus->sub_reg, 1); bus->dev = dev; bus->rman.rm_start = 0; bus->rman.rm_end = PCI_BUSMAX; bus->rman.rm_type = RMAN_ARRAY; snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev)); bus->rman.rm_descr = strdup(buf, M_DEVBUF); error = rman_init(&bus->rman); if (error) panic("Failed to initialize %s bus number rman", device_get_nameunit(dev)); /* * Allocate a bus range. This will return an existing bus range * if one exists, or a new bus range if one does not. */ rid = 0; bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul, min_count, 0); if (bus->res == NULL) { /* * Fall back to just allocating a range of a single bus * number. */ bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul, 1, 0); } else if (rman_get_size(bus->res) < min_count) /* * Attempt to grow the existing range to satisfy the * minimum desired count. */ (void)bus_adjust_resource(dev, PCI_RES_BUS, bus->res, rman_get_start(bus->res), rman_get_start(bus->res) + min_count - 1); /* * Add the initial resource to the rman. */ if (bus->res != NULL) { error = rman_manage_region(&bus->rman, rman_get_start(bus->res), rman_get_end(bus->res)); if (error) panic("Failed to add resource to rman"); bus->sec = rman_get_start(bus->res); bus->sub = rman_get_end(bus->res); } } static struct resource * pcib_suballoc_bus(struct pcib_secbus *bus, device_t child, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res; res = rman_reserve_resource(&bus->rman, start, end, count, flags, child); if (res == NULL) return (NULL); if (bootverbose) device_printf(bus->dev, "allocated bus range (%lu-%lu) for rid %d of %s\n", rman_get_start(res), rman_get_end(res), *rid, pcib_child_name(child)); rman_set_rid(res, *rid); return (res); } /* * Attempt to grow the secondary bus range. This is much simpler than * for I/O windows as the range can only be grown by increasing * subbus. */ static int pcib_grow_subbus(struct pcib_secbus *bus, rman_res_t new_end) { rman_res_t old_end; int error; old_end = rman_get_end(bus->res); KASSERT(new_end > old_end, ("attempt to shrink subbus")); error = bus_adjust_resource(bus->dev, PCI_RES_BUS, bus->res, rman_get_start(bus->res), new_end); if (error) return (error); if (bootverbose) device_printf(bus->dev, "grew bus range to %lu-%lu\n", rman_get_start(bus->res), rman_get_end(bus->res)); error = rman_manage_region(&bus->rman, old_end + 1, rman_get_end(bus->res)); if (error) panic("Failed to add resource to rman"); bus->sub = rman_get_end(bus->res); pci_write_config(bus->dev, bus->sub_reg, bus->sub, 1); return (0); } struct resource * pcib_alloc_subbus(struct pcib_secbus *bus, device_t child, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res; rman_res_t start_free, end_free, new_end; /* * First, see if the request can be satisified by the existing * bus range. */ res = pcib_suballoc_bus(bus, child, rid, start, end, count, flags); if (res != NULL) return (res); /* * Figure out a range to grow the bus range. First, find the * first bus number after the last allocated bus in the rman and * enforce that as a minimum starting point for the range. */ if (rman_last_free_region(&bus->rman, &start_free, &end_free) != 0 || end_free != bus->sub) start_free = bus->sub + 1; if (start_free < start) start_free = start; new_end = start_free + count - 1; /* * See if this new range would satisfy the request if it * succeeds. */ if (new_end > end) return (NULL); /* Finally, attempt to grow the existing resource. */ if (bootverbose) { device_printf(bus->dev, "attempting to grow bus range for %lu buses\n", count); printf("\tback candidate range: %lu-%lu\n", start_free, new_end); } if (pcib_grow_subbus(bus, new_end) == 0) return (pcib_suballoc_bus(bus, child, rid, start, end, count, flags)); return (NULL); } #endif #else /* * Is the prefetch window open (eg, can we allocate memory in it?) */ static int pcib_is_prefetch_open(struct pcib_softc *sc) { return (sc->pmembase > 0 && sc->pmembase < sc->pmemlimit); } /* * Is the nonprefetch window open (eg, can we allocate memory in it?) */ static int pcib_is_nonprefetch_open(struct pcib_softc *sc) { return (sc->membase > 0 && sc->membase < sc->memlimit); } /* * Is the io window open (eg, can we allocate ports in it?) */ static int pcib_is_io_open(struct pcib_softc *sc) { return (sc->iobase > 0 && sc->iobase < sc->iolimit); } /* * Get current I/O decode. */ static void pcib_get_io_decode(struct pcib_softc *sc) { device_t dev; uint32_t iolow; dev = sc->dev; iolow = pci_read_config(dev, PCIR_IOBASEL_1, 1); if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) sc->iobase = PCI_PPBIOBASE( pci_read_config(dev, PCIR_IOBASEH_1, 2), iolow); else sc->iobase = PCI_PPBIOBASE(0, iolow); iolow = pci_read_config(dev, PCIR_IOLIMITL_1, 1); if ((iolow & PCIM_BRIO_MASK) == PCIM_BRIO_32) sc->iolimit = PCI_PPBIOLIMIT( pci_read_config(dev, PCIR_IOLIMITH_1, 2), iolow); else sc->iolimit = PCI_PPBIOLIMIT(0, iolow); } /* * Get current memory decode. */ static void pcib_get_mem_decode(struct pcib_softc *sc) { device_t dev; pci_addr_t pmemlow; dev = sc->dev; sc->membase = PCI_PPBMEMBASE(0, pci_read_config(dev, PCIR_MEMBASE_1, 2)); sc->memlimit = PCI_PPBMEMLIMIT(0, pci_read_config(dev, PCIR_MEMLIMIT_1, 2)); pmemlow = pci_read_config(dev, PCIR_PMBASEL_1, 2); if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) sc->pmembase = PCI_PPBMEMBASE( pci_read_config(dev, PCIR_PMBASEH_1, 4), pmemlow); else sc->pmembase = PCI_PPBMEMBASE(0, pmemlow); pmemlow = pci_read_config(dev, PCIR_PMLIMITL_1, 2); if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) sc->pmemlimit = PCI_PPBMEMLIMIT( pci_read_config(dev, PCIR_PMLIMITH_1, 4), pmemlow); else sc->pmemlimit = PCI_PPBMEMLIMIT(0, pmemlow); } /* * Restore previous I/O decode. */ static void pcib_set_io_decode(struct pcib_softc *sc) { device_t dev; uint32_t iohi; dev = sc->dev; iohi = sc->iobase >> 16; if (iohi > 0) pci_write_config(dev, PCIR_IOBASEH_1, iohi, 2); pci_write_config(dev, PCIR_IOBASEL_1, sc->iobase >> 8, 1); iohi = sc->iolimit >> 16; if (iohi > 0) pci_write_config(dev, PCIR_IOLIMITH_1, iohi, 2); pci_write_config(dev, PCIR_IOLIMITL_1, sc->iolimit >> 8, 1); } /* * Restore previous memory decode. */ static void pcib_set_mem_decode(struct pcib_softc *sc) { device_t dev; pci_addr_t pmemhi; dev = sc->dev; pci_write_config(dev, PCIR_MEMBASE_1, sc->membase >> 16, 2); pci_write_config(dev, PCIR_MEMLIMIT_1, sc->memlimit >> 16, 2); pmemhi = sc->pmembase >> 32; if (pmemhi > 0) pci_write_config(dev, PCIR_PMBASEH_1, pmemhi, 4); pci_write_config(dev, PCIR_PMBASEL_1, sc->pmembase >> 16, 2); pmemhi = sc->pmemlimit >> 32; if (pmemhi > 0) pci_write_config(dev, PCIR_PMLIMITH_1, pmemhi, 4); pci_write_config(dev, PCIR_PMLIMITL_1, sc->pmemlimit >> 16, 2); } #endif /* * Get current bridge configuration. */ static void pcib_cfg_save(struct pcib_softc *sc) { #ifndef NEW_PCIB device_t dev; uint16_t command; dev = sc->dev; command = pci_read_config(dev, PCIR_COMMAND, 2); if (command & PCIM_CMD_PORTEN) pcib_get_io_decode(sc); if (command & PCIM_CMD_MEMEN) pcib_get_mem_decode(sc); #endif } /* * Restore previous bridge configuration. */ static void pcib_cfg_restore(struct pcib_softc *sc) { device_t dev; #ifndef NEW_PCIB uint16_t command; #endif dev = sc->dev; #ifdef NEW_PCIB pcib_write_windows(sc, WIN_IO | WIN_MEM | WIN_PMEM); #else command = pci_read_config(dev, PCIR_COMMAND, 2); if (command & PCIM_CMD_PORTEN) pcib_set_io_decode(sc); if (command & PCIM_CMD_MEMEN) pcib_set_mem_decode(sc); #endif } /* * Generic device interface */ static int pcib_probe(device_t dev) { if ((pci_get_class(dev) == PCIC_BRIDGE) && (pci_get_subclass(dev) == PCIS_BRIDGE_PCI)) { device_set_desc(dev, "PCI-PCI bridge"); return(-10000); } return(ENXIO); } void pcib_attach_common(device_t dev) { struct pcib_softc *sc; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; int comma; sc = device_get_softc(dev); sc->dev = dev; /* * Get current bridge configuration. */ sc->domain = pci_get_domain(dev); #if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) sc->bus.sec = pci_read_config(dev, PCIR_SECBUS_1, 1); sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1); #endif sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2); pcib_cfg_save(sc); /* * The primary bus register should always be the bus of the * parent. */ sc->pribus = pci_get_bus(dev); pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1); /* * Setup sysctl reporting nodes */ sctx = device_get_sysctl_ctx(dev); soid = device_get_sysctl_tree(dev); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "domain", CTLFLAG_RD, &sc->domain, 0, "Domain number"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus", CTLFLAG_RD, &sc->pribus, 0, "Primary bus number"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus", CTLFLAG_RD, &sc->bus.sec, 0, "Secondary bus number"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus", CTLFLAG_RD, &sc->bus.sub, 0, "Subordinate bus number"); /* * Quirk handling. */ switch (pci_get_devid(dev)) { #if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) case 0x12258086: /* Intel 82454KX/GX (Orion) */ { uint8_t supbus; supbus = pci_read_config(dev, 0x41, 1); if (supbus != 0xff) { sc->bus.sec = supbus + 1; sc->bus.sub = supbus + 1; } break; } #endif /* * The i82380FB mobile docking controller is a PCI-PCI bridge, * and it is a subtractive bridge. However, the ProgIf is wrong * so the normal setting of PCIB_SUBTRACTIVE bit doesn't * happen. There are also Toshiba and Cavium ThunderX bridges * that behave this way. */ case 0xa002177d: /* Cavium ThunderX */ case 0x124b8086: /* Intel 82380FB Mobile */ case 0x060513d7: /* Toshiba ???? */ sc->flags |= PCIB_SUBTRACTIVE; break; #if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) /* Compaq R3000 BIOS sets wrong subordinate bus number. */ case 0x00dd10de: { char *cp; if ((cp = kern_getenv("smbios.planar.maker")) == NULL) break; if (strncmp(cp, "Compal", 6) != 0) { freeenv(cp); break; } freeenv(cp); if ((cp = kern_getenv("smbios.planar.product")) == NULL) break; if (strncmp(cp, "08A0", 4) != 0) { freeenv(cp); break; } freeenv(cp); if (sc->bus.sub < 0xa) { pci_write_config(dev, PCIR_SUBBUS_1, 0xa, 1); sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1); } break; } #endif } if (pci_msi_device_blacklisted(dev)) sc->flags |= PCIB_DISABLE_MSI; if (pci_msix_device_blacklisted(dev)) sc->flags |= PCIB_DISABLE_MSIX; /* * Intel 815, 845 and other chipsets say they are PCI-PCI bridges, * but have a ProgIF of 0x80. The 82801 family (AA, AB, BAM/CAM, * BA/CA/DB and E) PCI bridges are HUB-PCI bridges, in Intelese. * This means they act as if they were subtractively decoding * bridges and pass all transactions. Mark them and real ProgIf 1 * parts as subtractive. */ if ((pci_get_devid(dev) & 0xff00ffff) == 0x24008086 || pci_read_config(dev, PCIR_PROGIF, 1) == PCIP_BRIDGE_PCI_SUBTRACTIVE) sc->flags |= PCIB_SUBTRACTIVE; #ifdef NEW_PCIB #ifdef PCI_RES_BUS pcib_setup_secbus(dev, &sc->bus, 1); #endif pcib_probe_windows(sc); #endif if (bootverbose) { device_printf(dev, " domain %d\n", sc->domain); device_printf(dev, " secondary bus %d\n", sc->bus.sec); device_printf(dev, " subordinate bus %d\n", sc->bus.sub); #ifdef NEW_PCIB if (pcib_is_window_open(&sc->io)) device_printf(dev, " I/O decode 0x%jx-0x%jx\n", (uintmax_t)sc->io.base, (uintmax_t)sc->io.limit); if (pcib_is_window_open(&sc->mem)) device_printf(dev, " memory decode 0x%jx-0x%jx\n", (uintmax_t)sc->mem.base, (uintmax_t)sc->mem.limit); if (pcib_is_window_open(&sc->pmem)) device_printf(dev, " prefetched decode 0x%jx-0x%jx\n", (uintmax_t)sc->pmem.base, (uintmax_t)sc->pmem.limit); #else if (pcib_is_io_open(sc)) device_printf(dev, " I/O decode 0x%x-0x%x\n", sc->iobase, sc->iolimit); if (pcib_is_nonprefetch_open(sc)) device_printf(dev, " memory decode 0x%jx-0x%jx\n", (uintmax_t)sc->membase, (uintmax_t)sc->memlimit); if (pcib_is_prefetch_open(sc)) device_printf(dev, " prefetched decode 0x%jx-0x%jx\n", (uintmax_t)sc->pmembase, (uintmax_t)sc->pmemlimit); #endif if (sc->bridgectl & (PCIB_BCR_ISA_ENABLE | PCIB_BCR_VGA_ENABLE) || sc->flags & PCIB_SUBTRACTIVE) { device_printf(dev, " special decode "); comma = 0; if (sc->bridgectl & PCIB_BCR_ISA_ENABLE) { printf("ISA"); comma = 1; } if (sc->bridgectl & PCIB_BCR_VGA_ENABLE) { printf("%sVGA", comma ? ", " : ""); comma = 1; } if (sc->flags & PCIB_SUBTRACTIVE) printf("%ssubtractive", comma ? ", " : ""); printf("\n"); } } /* * Always enable busmastering on bridges so that transactions * initiated on the secondary bus are passed through to the * primary bus. */ pci_enable_busmaster(dev); } int pcib_attach(device_t dev) { struct pcib_softc *sc; device_t child; pcib_attach_common(dev); sc = device_get_softc(dev); if (sc->bus.sec != 0) { child = device_add_child(dev, "pci", -1); if (child != NULL) return(bus_generic_attach(dev)); } /* no secondary bus; we should have fixed this */ return(0); } int pcib_suspend(device_t dev) { pcib_cfg_save(device_get_softc(dev)); return (bus_generic_suspend(dev)); } int pcib_resume(device_t dev) { pcib_cfg_restore(device_get_softc(dev)); return (bus_generic_resume(dev)); } void pcib_bridge_init(device_t dev) { pci_write_config(dev, PCIR_IOBASEL_1, 0xff, 1); pci_write_config(dev, PCIR_IOBASEH_1, 0xffff, 2); pci_write_config(dev, PCIR_IOLIMITL_1, 0, 1); pci_write_config(dev, PCIR_IOLIMITH_1, 0, 2); pci_write_config(dev, PCIR_MEMBASE_1, 0xffff, 2); pci_write_config(dev, PCIR_MEMLIMIT_1, 0, 2); pci_write_config(dev, PCIR_PMBASEL_1, 0xffff, 2); pci_write_config(dev, PCIR_PMBASEH_1, 0xffffffff, 4); pci_write_config(dev, PCIR_PMLIMITL_1, 0, 2); pci_write_config(dev, PCIR_PMLIMITH_1, 0, 4); } int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct pcib_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: *result = sc->domain; return(0); case PCIB_IVAR_BUS: *result = sc->bus.sec; return(0); } return(ENOENT); } int pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { switch (which) { case PCIB_IVAR_DOMAIN: return(EINVAL); case PCIB_IVAR_BUS: return(EINVAL); } return(ENOENT); } #ifdef NEW_PCIB /* * Attempt to allocate a resource from the existing resources assigned * to a window. */ static struct resource * pcib_suballoc_resource(struct pcib_softc *sc, struct pcib_window *w, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res; if (!pcib_is_window_open(w)) return (NULL); res = rman_reserve_resource(&w->rman, start, end, count, flags & ~RF_ACTIVE, child); if (res == NULL) return (NULL); if (bootverbose) device_printf(sc->dev, "allocated %s range (%#lx-%#lx) for rid %x of %s\n", w->name, rman_get_start(res), rman_get_end(res), *rid, pcib_child_name(child)); rman_set_rid(res, *rid); /* * If the resource should be active, pass that request up the * tree. This assumes the parent drivers can handle * activating sub-allocated resources. */ if (flags & RF_ACTIVE) { if (bus_activate_resource(child, type, *rid, res) != 0) { rman_release_resource(res); return (NULL); } } return (res); } /* Allocate a fresh resource range for an unconfigured window. */ static int pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res; rman_res_t base, limit, wmask; int rid; /* * If this is an I/O window on a bridge with ISA enable set * and the start address is below 64k, then try to allocate an * initial window of 0x1000 bytes long starting at address * 0xf000 and walking down. Note that if the original request * was larger than the non-aliased range size of 0x100 our * caller would have raised the start address up to 64k * already. */ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE && start < 65536) { for (base = 0xf000; (long)base >= 0; base -= 0x1000) { limit = base + 0xfff; /* * Skip ranges that wouldn't work for the * original request. Note that the actual * window that overlaps are the non-alias * ranges within [base, limit], so this isn't * quite a simple comparison. */ if (start + count > limit - 0x400) continue; if (base == 0) { /* * The first open region for the window at * 0 is 0x400-0x4ff. */ if (end - count + 1 < 0x400) continue; } else { if (end - count + 1 < base) continue; } if (pcib_alloc_nonisa_ranges(sc, base, limit) == 0) { w->base = base; w->limit = limit; return (0); } } return (ENOSPC); } - wmask = (1ul << w->step) - 1; + wmask = ((rman_res_t)1 << w->step) - 1; if (RF_ALIGNMENT(flags) < w->step) { flags &= ~RF_ALIGNMENT_MASK; flags |= RF_ALIGNMENT_LOG2(w->step); } start &= ~wmask; end |= wmask; - count = roundup2(count, 1ul << w->step); + count = roundup2(count, (rman_res_t)1 << w->step); rid = w->reg; res = bus_alloc_resource(sc->dev, type, &rid, start, end, count, flags & ~RF_ACTIVE); if (res == NULL) return (ENOSPC); pcib_add_window_resources(w, &res, 1); pcib_activate_window(sc, type); w->base = rman_get_start(res); w->limit = rman_get_end(res); return (0); } /* Try to expand an existing window to the requested base and limit. */ static int pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type, rman_res_t base, rman_res_t limit) { struct resource *res; int error, i, force_64k_base; KASSERT(base <= w->base && limit >= w->limit, ("attempting to shrink window")); /* * XXX: pcib_grow_window() doesn't try to do this anyway and * the error handling for all the edge cases would be tedious. */ KASSERT(limit == w->limit || base == w->base, ("attempting to grow both ends of a window")); /* * Yet more special handling for requests to expand an I/O * window behind an ISA-enabled bridge. Since I/O windows * have to grow in 0x1000 increments and the end of the 0xffff * range is an alias, growing a window below 64k will always * result in allocating new resources and never adjusting an * existing resource. */ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE && (limit <= 65535 || (base <= 65535 && base != w->base))) { KASSERT(limit == w->limit || limit <= 65535, ("attempting to grow both ends across 64k ISA alias")); if (base != w->base) error = pcib_alloc_nonisa_ranges(sc, base, w->base - 1); else error = pcib_alloc_nonisa_ranges(sc, w->limit + 1, limit); if (error == 0) { w->base = base; w->limit = limit; } return (error); } /* * Find the existing resource to adjust. Usually there is only one, * but for an ISA-enabled bridge we might be growing the I/O window * above 64k and need to find the existing resource that maps all * of the area above 64k. */ for (i = 0; i < w->count; i++) { if (rman_get_end(w->res[i]) == w->limit) break; } KASSERT(i != w->count, ("did not find existing resource")); res = w->res[i]; /* * Usually the resource we found should match the window's * existing range. The one exception is the ISA-enabled case * mentioned above in which case the resource should start at * 64k. */ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE && w->base <= 65535) { KASSERT(rman_get_start(res) == 65536, ("existing resource mismatch")); force_64k_base = 1; } else { KASSERT(w->base == rman_get_start(res), ("existing resource mismatch")); force_64k_base = 0; } error = bus_adjust_resource(sc->dev, type, res, force_64k_base ? rman_get_start(res) : base, limit); if (error) return (error); /* Add the newly allocated region to the resource manager. */ if (w->base != base) { error = rman_manage_region(&w->rman, base, w->base - 1); w->base = base; } else { error = rman_manage_region(&w->rman, w->limit + 1, limit); w->limit = limit; } if (error) { if (bootverbose) device_printf(sc->dev, "failed to expand %s resource manager\n", w->name); (void)bus_adjust_resource(sc->dev, type, res, force_64k_base ? rman_get_start(res) : w->base, w->limit); } return (error); } /* * Attempt to grow a window to make room for a given resource request. */ static int pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { rman_res_t align, start_free, end_free, front, back, wmask; int error; /* * Clamp the desired resource range to the maximum address * this window supports. Reject impossible requests. * * For I/O port requests behind a bridge with the ISA enable * bit set, force large allocations to start above 64k. */ if (!w->valid) return (EINVAL); if (sc->bridgectl & PCIB_BCR_ISA_ENABLE && count > 0x100 && start < 65536) start = 65536; if (end > w->rman.rm_end) end = w->rman.rm_end; if (start + count - 1 > end || start + count < start) return (EINVAL); - wmask = (1ul << w->step) - 1; + wmask = ((rman_res_t)1 << w->step) - 1; /* * If there is no resource at all, just try to allocate enough * aligned space for this resource. */ if (w->res == NULL) { error = pcib_alloc_new_window(sc, w, type, start, end, count, flags); if (error) { if (bootverbose) device_printf(sc->dev, "failed to allocate initial %s window (%#lx-%#lx,%#lx)\n", w->name, start, end, count); return (error); } if (bootverbose) device_printf(sc->dev, "allocated initial %s window of %#jx-%#jx\n", w->name, (uintmax_t)w->base, (uintmax_t)w->limit); goto updatewin; } /* * See if growing the window would help. Compute the minimum * amount of address space needed on both the front and back * ends of the existing window to satisfy the allocation. * * For each end, build a candidate region adjusting for the * required alignment, etc. If there is a free region at the * edge of the window, grow from the inner edge of the free * region. Otherwise grow from the window boundary. * * Growing an I/O window below 64k for a bridge with the ISA * enable bit doesn't require any special magic as the step * size of an I/O window (1k) always includes multiple * non-alias ranges when it is grown in either direction. * * XXX: Special case: if w->res is completely empty and the * request size is larger than w->res, we should find the * optimal aligned buffer containing w->res and allocate that. */ if (bootverbose) device_printf(sc->dev, "attempting to grow %s window for (%#lx-%#lx,%#lx)\n", w->name, start, end, count); - align = 1ul << RF_ALIGNMENT(flags); + align = (rman_res_t)1 << RF_ALIGNMENT(flags); if (start < w->base) { if (rman_first_free_region(&w->rman, &start_free, &end_free) != 0 || start_free != w->base) end_free = w->base; if (end_free > end) end_free = end + 1; /* Move end_free down until it is properly aligned. */ end_free &= ~(align - 1); end_free--; front = end_free - (count - 1); /* * The resource would now be allocated at (front, * end_free). Ensure that fits in the (start, end) * bounds. end_free is checked above. If 'front' is * ok, ensure it is properly aligned for this window. * Also check for underflow. */ if (front >= start && front <= end_free) { if (bootverbose) printf("\tfront candidate range: %#lx-%#lx\n", front, end_free); front &= ~wmask; front = w->base - front; } else front = 0; } else front = 0; if (end > w->limit) { if (rman_last_free_region(&w->rman, &start_free, &end_free) != 0 || end_free != w->limit) start_free = w->limit + 1; if (start_free < start) start_free = start; /* Move start_free up until it is properly aligned. */ start_free = roundup2(start_free, align); back = start_free + count - 1; /* * The resource would now be allocated at (start_free, * back). Ensure that fits in the (start, end) * bounds. start_free is checked above. If 'back' is * ok, ensure it is properly aligned for this window. * Also check for overflow. */ if (back <= end && start_free <= back) { if (bootverbose) printf("\tback candidate range: %#lx-%#lx\n", start_free, back); back |= wmask; back -= w->limit; } else back = 0; } else back = 0; /* * Try to allocate the smallest needed region first. * If that fails, fall back to the other region. */ error = ENOSPC; while (front != 0 || back != 0) { if (front != 0 && (front <= back || back == 0)) { error = pcib_expand_window(sc, w, type, w->base - front, w->limit); if (error == 0) break; front = 0; } else { error = pcib_expand_window(sc, w, type, w->base, w->limit + back); if (error == 0) break; back = 0; } } if (error) return (error); if (bootverbose) device_printf(sc->dev, "grew %s window to %#jx-%#jx\n", w->name, (uintmax_t)w->base, (uintmax_t)w->limit); updatewin: /* Write the new window. */ KASSERT((w->base & wmask) == 0, ("start address is not aligned")); KASSERT((w->limit & wmask) == wmask, ("end address is not aligned")); pcib_write_windows(sc, w->mask); return (0); } /* * We have to trap resource allocation requests and ensure that the bridge * is set up to, or capable of handling them. */ struct resource * pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pcib_softc *sc; struct resource *r; sc = device_get_softc(dev); /* * VGA resources are decoded iff the VGA enable bit is set in * the bridge control register. VGA resources do not fall into * the resource windows and are passed up to the parent. */ if ((type == SYS_RES_IOPORT && pci_is_vga_ioport_range(start, end)) || (type == SYS_RES_MEMORY && pci_is_vga_memory_range(start, end))) { if (sc->bridgectl & PCIB_BCR_VGA_ENABLE) return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); else return (NULL); } switch (type) { #ifdef PCI_RES_BUS case PCI_RES_BUS: return (pcib_alloc_subbus(&sc->bus, child, rid, start, end, count, flags)); #endif case SYS_RES_IOPORT: if (pcib_is_isa_range(sc, start, end, count)) return (NULL); r = pcib_suballoc_resource(sc, &sc->io, child, type, rid, start, end, count, flags); if (r != NULL || (sc->flags & PCIB_SUBTRACTIVE) != 0) break; if (pcib_grow_window(sc, &sc->io, type, start, end, count, flags) == 0) r = pcib_suballoc_resource(sc, &sc->io, child, type, rid, start, end, count, flags); break; case SYS_RES_MEMORY: /* * For prefetchable resources, prefer the prefetchable * memory window, but fall back to the regular memory * window if that fails. Try both windows before * attempting to grow a window in case the firmware * has used a range in the regular memory window to * map a prefetchable BAR. */ if (flags & RF_PREFETCHABLE) { r = pcib_suballoc_resource(sc, &sc->pmem, child, type, rid, start, end, count, flags); if (r != NULL) break; } r = pcib_suballoc_resource(sc, &sc->mem, child, type, rid, start, end, count, flags); if (r != NULL || (sc->flags & PCIB_SUBTRACTIVE) != 0) break; if (flags & RF_PREFETCHABLE) { if (pcib_grow_window(sc, &sc->pmem, type, start, end, count, flags) == 0) { r = pcib_suballoc_resource(sc, &sc->pmem, child, type, rid, start, end, count, flags); if (r != NULL) break; } } if (pcib_grow_window(sc, &sc->mem, type, start, end, count, flags & ~RF_PREFETCHABLE) == 0) r = pcib_suballoc_resource(sc, &sc->mem, child, type, rid, start, end, count, flags); break; default: return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } /* * If attempts to suballocate from the window fail but this is a * subtractive bridge, pass the request up the tree. */ if (sc->flags & PCIB_SUBTRACTIVE && r == NULL) return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); return (r); } int pcib_adjust_resource(device_t bus, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end) { struct pcib_softc *sc; sc = device_get_softc(bus); if (pcib_is_resource_managed(sc, type, r)) return (rman_adjust_resource(r, start, end)); return (bus_generic_adjust_resource(bus, child, type, r, start, end)); } int pcib_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pcib_softc *sc; int error; sc = device_get_softc(dev); if (pcib_is_resource_managed(sc, type, r)) { if (rman_get_flags(r) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } return (bus_generic_release_resource(dev, child, type, rid, r)); } #else /* * We have to trap resource allocation requests and ensure that the bridge * is set up to, or capable of handling them. */ struct resource * pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pcib_softc *sc = device_get_softc(dev); const char *name, *suffix; int ok; /* * Fail the allocation for this range if it's not supported. */ name = device_get_nameunit(child); if (name == NULL) { name = ""; suffix = ""; } else suffix = " "; switch (type) { case SYS_RES_IOPORT: ok = 0; if (!pcib_is_io_open(sc)) break; ok = (start >= sc->iobase && end <= sc->iolimit); /* * Make sure we allow access to VGA I/O addresses when the * bridge has the "VGA Enable" bit set. */ if (!ok && pci_is_vga_ioport_range(start, end)) ok = (sc->bridgectl & PCIB_BCR_VGA_ENABLE) ? 1 : 0; if ((sc->flags & PCIB_SUBTRACTIVE) == 0) { if (!ok) { if (start < sc->iobase) start = sc->iobase; if (end > sc->iolimit) end = sc->iolimit; if (start < end) ok = 1; } } else { ok = 1; #if 0 /* * If we overlap with the subtractive range, then * pick the upper range to use. */ if (start < sc->iolimit && end > sc->iobase) start = sc->iolimit + 1; #endif } if (end < start) { device_printf(dev, "ioport: end (%lx) < start (%lx)\n", end, start); start = 0; end = 0; ok = 0; } if (!ok) { device_printf(dev, "%s%srequested unsupported I/O " "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n", name, suffix, start, end, sc->iobase, sc->iolimit); return (NULL); } if (bootverbose) device_printf(dev, "%s%srequested I/O range 0x%lx-0x%lx: in range\n", name, suffix, start, end); break; case SYS_RES_MEMORY: ok = 0; if (pcib_is_nonprefetch_open(sc)) ok = ok || (start >= sc->membase && end <= sc->memlimit); if (pcib_is_prefetch_open(sc)) ok = ok || (start >= sc->pmembase && end <= sc->pmemlimit); /* * Make sure we allow access to VGA memory addresses when the * bridge has the "VGA Enable" bit set. */ if (!ok && pci_is_vga_memory_range(start, end)) ok = (sc->bridgectl & PCIB_BCR_VGA_ENABLE) ? 1 : 0; if ((sc->flags & PCIB_SUBTRACTIVE) == 0) { if (!ok) { ok = 1; if (flags & RF_PREFETCHABLE) { if (pcib_is_prefetch_open(sc)) { if (start < sc->pmembase) start = sc->pmembase; if (end > sc->pmemlimit) end = sc->pmemlimit; } else { ok = 0; } } else { /* non-prefetchable */ if (pcib_is_nonprefetch_open(sc)) { if (start < sc->membase) start = sc->membase; if (end > sc->memlimit) end = sc->memlimit; } else { ok = 0; } } } } else if (!ok) { ok = 1; /* subtractive bridge: always ok */ #if 0 if (pcib_is_nonprefetch_open(sc)) { if (start < sc->memlimit && end > sc->membase) start = sc->memlimit + 1; } if (pcib_is_prefetch_open(sc)) { if (start < sc->pmemlimit && end > sc->pmembase) start = sc->pmemlimit + 1; } #endif } if (end < start) { device_printf(dev, "memory: end (%lx) < start (%lx)\n", end, start); start = 0; end = 0; ok = 0; } if (!ok && bootverbose) device_printf(dev, "%s%srequested unsupported memory range %#lx-%#lx " "(decoding %#jx-%#jx, %#jx-%#jx)\n", name, suffix, start, end, (uintmax_t)sc->membase, (uintmax_t)sc->memlimit, (uintmax_t)sc->pmembase, (uintmax_t)sc->pmemlimit); if (!ok) return (NULL); if (bootverbose) device_printf(dev,"%s%srequested memory range " "0x%lx-0x%lx: good\n", name, suffix, start, end); break; default: break; } /* * Bridge is OK decoding this resource, so pass it up. */ return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } #endif /* * If ARI is enabled on this downstream port, translate the function number * to the non-ARI slot/function. The downstream port will convert it back in * hardware. If ARI is not enabled slot and func are not modified. */ static __inline void pcib_xlate_ari(device_t pcib, int bus, int *slot, int *func) { struct pcib_softc *sc; int ari_func; sc = device_get_softc(pcib); ari_func = *func; if (sc->flags & PCIB_ENABLE_ARI) { KASSERT(*slot == 0, ("Non-zero slot number with ARI enabled!")); *slot = PCIE_ARI_SLOT(ari_func); *func = PCIE_ARI_FUNC(ari_func); } } static void pcib_enable_ari(struct pcib_softc *sc, uint32_t pcie_pos) { uint32_t ctl2; ctl2 = pci_read_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, 4); ctl2 |= PCIEM_CTL2_ARI; pci_write_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, ctl2, 4); sc->flags |= PCIB_ENABLE_ARI; } /* * PCIB interface. */ int pcib_maxslots(device_t dev) { return (PCI_SLOTMAX); } static int pcib_ari_maxslots(device_t dev) { struct pcib_softc *sc; sc = device_get_softc(dev); if (sc->flags & PCIB_ENABLE_ARI) return (PCIE_ARI_SLOTMAX); else return (PCI_SLOTMAX); } static int pcib_ari_maxfuncs(device_t dev) { struct pcib_softc *sc; sc = device_get_softc(dev); if (sc->flags & PCIB_ENABLE_ARI) return (PCIE_ARI_FUNCMAX); else return (PCI_FUNCMAX); } static void pcib_ari_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, int *func) { struct pcib_softc *sc; sc = device_get_softc(pcib); *bus = PCI_RID2BUS(rid); if (sc->flags & PCIB_ENABLE_ARI) { *slot = PCIE_ARI_RID2SLOT(rid); *func = PCIE_ARI_RID2FUNC(rid); } else { *slot = PCI_RID2SLOT(rid); *func = PCI_RID2FUNC(rid); } } /* * Since we are a child of a PCI bus, its parent must support the pcib interface. */ static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width) { pcib_xlate_ari(dev, b, &s, &f); return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, width)); } static void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width) { pcib_xlate_ari(dev, b, &s, &f); PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, val, width); } /* * Route an interrupt across a PCI bridge. */ int pcib_route_interrupt(device_t pcib, device_t dev, int pin) { device_t bus; int parent_intpin; int intnum; /* * * The PCI standard defines a swizzle of the child-side device/intpin to * the parent-side intpin as follows. * * device = device on child bus * child_intpin = intpin on child bus slot (0-3) * parent_intpin = intpin on parent bus slot (0-3) * * parent_intpin = (device + child_intpin) % 4 */ parent_intpin = (pci_get_slot(dev) + (pin - 1)) % 4; /* * Our parent is a PCI bus. Its parent must export the pcib interface * which includes the ability to route interrupts. */ bus = device_get_parent(pcib); intnum = PCIB_ROUTE_INTERRUPT(device_get_parent(bus), pcib, parent_intpin + 1); if (PCI_INTERRUPT_VALID(intnum) && bootverbose) { device_printf(pcib, "slot %d INT%c is routed to irq %d\n", pci_get_slot(dev), 'A' + pin - 1, intnum); } return(intnum); } /* Pass request to alloc MSI/MSI-X messages up to the parent bridge. */ int pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs) { struct pcib_softc *sc = device_get_softc(pcib); device_t bus; if (sc->flags & PCIB_DISABLE_MSI) return (ENXIO); bus = device_get_parent(pcib); return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount, irqs)); } /* Pass request to release MSI/MSI-X messages up to the parent bridge. */ int pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs) { device_t bus; bus = device_get_parent(pcib); return (PCIB_RELEASE_MSI(device_get_parent(bus), dev, count, irqs)); } /* Pass request to alloc an MSI-X message up to the parent bridge. */ int pcib_alloc_msix(device_t pcib, device_t dev, int *irq) { struct pcib_softc *sc = device_get_softc(pcib); device_t bus; if (sc->flags & PCIB_DISABLE_MSIX) return (ENXIO); bus = device_get_parent(pcib); return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq)); } /* Pass request to release an MSI-X message up to the parent bridge. */ int pcib_release_msix(device_t pcib, device_t dev, int irq) { device_t bus; bus = device_get_parent(pcib); return (PCIB_RELEASE_MSIX(device_get_parent(bus), dev, irq)); } /* Pass request to map MSI/MSI-X message up to parent bridge. */ int pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, uint32_t *data) { device_t bus; int error; bus = device_get_parent(pcib); error = PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data); if (error) return (error); pci_ht_map_msi(pcib, *addr); return (0); } /* Pass request for device power state up to parent bridge. */ int pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate) { device_t bus; bus = device_get_parent(pcib); return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate)); } static int pcib_ari_enabled(device_t pcib) { struct pcib_softc *sc; sc = device_get_softc(pcib); return ((sc->flags & PCIB_ENABLE_ARI) != 0); } static uint16_t pcib_ari_get_rid(device_t pcib, device_t dev) { struct pcib_softc *sc; uint8_t bus, slot, func; sc = device_get_softc(pcib); if (sc->flags & PCIB_ENABLE_ARI) { bus = pci_get_bus(dev); func = pci_get_function(dev); return (PCI_ARI_RID(bus, func)); } else { bus = pci_get_bus(dev); slot = pci_get_slot(dev); func = pci_get_function(dev); return (PCI_RID(bus, slot, func)); } } /* * Check that the downstream port (pcib) and the endpoint device (dev) both * support ARI. If so, enable it and return 0, otherwise return an error. */ static int pcib_try_enable_ari(device_t pcib, device_t dev) { struct pcib_softc *sc; int error; uint32_t cap2; int ari_cap_off; uint32_t ari_ver; uint32_t pcie_pos; sc = device_get_softc(pcib); /* * ARI is controlled in a register in the PCIe capability structure. * If the downstream port does not have the PCIe capability structure * then it does not support ARI. */ error = pci_find_cap(pcib, PCIY_EXPRESS, &pcie_pos); if (error != 0) return (ENODEV); /* Check that the PCIe port advertises ARI support. */ cap2 = pci_read_config(pcib, pcie_pos + PCIER_DEVICE_CAP2, 4); if (!(cap2 & PCIEM_CAP2_ARI)) return (ENODEV); /* * Check that the endpoint device advertises ARI support via the ARI * extended capability structure. */ error = pci_find_extcap(dev, PCIZ_ARI, &ari_cap_off); if (error != 0) return (ENODEV); /* * Finally, check that the endpoint device supports the same version * of ARI that we do. */ ari_ver = pci_read_config(dev, ari_cap_off, 4); if (PCI_EXTCAP_VER(ari_ver) != PCIB_SUPPORTED_ARI_VER) { if (bootverbose) device_printf(pcib, "Unsupported version of ARI (%d) detected\n", PCI_EXTCAP_VER(ari_ver)); return (ENXIO); } pcib_enable_ari(sc, pcie_pos); return (0); }