/* * Copyright 1996 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. * * $Id: ide_pci.c,v 1.4 1997/09/20 07:41:55 dyson Exp $ */ #include "pci.h" #if NPCI > 0 #include "opt_wd.h" #include "wd.h" #if NWDC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #define PROMISE_ULTRA33 0x4d33105a struct ide_pci_cookie; /* structs vendor_fns, ide_pci_cookie are recursive */ struct vendor_fns { int (*vendor_dmainit) /* initialize DMA controller and drive */ (struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *); void (*vendor_status) /* prints off DMA timing info */ (struct ide_pci_cookie *cookie); }; /* * XXX the fact that this list keeps all kinds of info on PCI controllers * is pretty grotty-- much of this should be replaced by a proper integration * of PCI probes into the wd driver. * XXX if we're going to support native-PCI controllers, we also need to * keep the address of the IDE control block register, which is something wd.c * needs to know, which is why this info is in the wrong place. */ struct ide_pci_cookie { LIST_ENTRY(ide_pci_cookie) le; int iobase_wd; int ctlr; /* controller 0/1 on PCI IDE interface */ int unit; int iobase_bm; /* SFF-8038 control registers */ int altiobase_wd; pcici_t tag; pcidi_t type; struct ide_pci_prd *prd; struct vendor_fns vs; }; struct ide_pci_softc { LIST_HEAD(, ide_pci_cookie) cookies; }; static int generic_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void generic_status(struct ide_pci_cookie *cookie); static void via_571_status(struct ide_pci_cookie *cookie); static int via_571_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static void intel_piix_dump_drive(char *ctlr, int sitre, int is_piix4, int word40, int word44, int word48, int word4a, int drive); static void intel_piix_status(struct ide_pci_cookie *cookie); static int intel_piix_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo); static struct ide_pci_cookie * mkcookie(int iobase_wd, int ctlr, int unit, int iobase_bm, pcici_t tag, pcidi_t type, struct vendor_fns *vp, int altiobase_wd); static void ide_pci_attach(pcici_t tag, int unit); static void *ide_pci_candma(int, int); static int ide_pci_dmainit(void *, struct wdparams *, int (*)(int, void *), void *); static int ide_pci_dmaverify(void *, char *, u_long, int); static int ide_pci_dmasetup(void *, char *, u_long, int); static void ide_pci_dmastart(void *); static int ide_pci_dmadone(void *); static int ide_pci_status(void *); static int ide_pci_iobase(void *xcp); static int ide_pci_altiobase(void *xcp); static struct ide_pci_softc softc; static int ide_pci_softc_cookies_initted = 0; extern void wdintr(void *); extern struct isa_driver wdcdriver; /* * PRD_ALLOC_SIZE should be something that will not be allocated across a 64k * boundary. * PRD_MAX_SEGS is defined to be the maximum number of segments required for * a transfer on an IDE drive, for an xfer that is linear in virtual memory. * PRD_BUF_SIZE is the size of the buffer needed for a PRD table. */ #define PRD_ALLOC_SIZE PAGE_SIZE #define PRD_MAX_SEGS ((256 * 512 / PAGE_SIZE) + 1) #define PRD_BUF_SIZE PRD_MAX_SEGS * 8 static void *prdbuf = 0; static void *prdbuf_next = 0; /* * Hardware specific IDE controller code. All vendor-specific code * for handling IDE timing and other chipset peculiarities should be * encapsulated here. */ /* helper funcs */ /* * nnn_mode() return the highest valid mode, or -1 if the mode class is * not supported */ static inline int pio_mode(struct wdparams *wp) { if ((wp->wdp_atavalid & 2) == 2) { if ((wp->wdp_eidepiomodes & 2) == 2) return 4; if ((wp->wdp_eidepiomodes & 1) == 1) return 3; } return -1; } #if 0 static inline int dma_mode(struct wdparams *wp) { /* XXX not quite sure how to verify validity on this field */ } #endif static inline int mwdma_mode(struct wdparams *wp) { /* * XXX technically, using wdp_atavalid to test for validity of * this field is not quite correct */ if ((wp->wdp_atavalid & 2) == 2) { if ((wp->wdp_dmamword & 4) == 4) return 2; if ((wp->wdp_dmamword & 2) == 2) return 1; if ((wp->wdp_dmamword & 1) == 1) return 0; } return -1; } static inline int udma_mode(struct wdparams *wp) { if ((wp->wdp_atavalid & 4) == 4) { if ((wp->wdp_udmamode & 4) == 4) return 2; if ((wp->wdp_udmamode & 2) == 2) return 1; if ((wp->wdp_udmamode & 1) == 1) return 0; } return -1; } /* Generic busmastering PCI-IDE */ static int generic_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { /* * punt on the whole timing issue by looking for either a * drive programmed for both PIO4 and mDMA2 (which use similar * timing) or a drive in an UltraDMA mode (hopefully all * controllers have separate timing for UDMA). one hopes that if * the drive's DMA mode has been configured by the BIOS, the * controller's has also. * * XXX there are examples where this approach is now known to be * broken, at least on systems based on Intel chipsets. */ if ((pio_mode(wp) >= 4 && mwdma_mode(wp) >= 2) || (udma_mode(wp) >= 2)) { printf("ide_pci: generic_dmainit %04x:%d: warning, IDE controller timing not set\n", cookie->iobase_wd, cookie->unit); return 1; } #ifdef IDE_PCI_DEBUG printf("pio_mode: %d, mwdma_mode(wp): %d, udma_mode(wp): %d\n", pio_mode(wp), mwdma_mode(wp), udma_mode(wp)); #endif return 0; } static void generic_status(struct ide_pci_cookie *cookie) { printf("generic_status: no PCI IDE timing info available\n"); } static struct vendor_fns vs_generic = { generic_dmainit, generic_status }; /* VIA Technologies "82C571" PCI-IDE controller core */ static void via_571_status(struct ide_pci_cookie *cookie) { int iobase_wd; int ctlr, unit; int iobase_bm; pcici_t tag; pcidi_t type; u_long word40[5]; int i, unitno; iobase_wd = cookie->iobase_wd; unit = cookie->unit; ctlr = cookie->ctlr; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; unitno = ctlr * 2 + unit; for (i=0; i<5; i++) { word40[i] = pci_conf_read(tag, i * 4 + 0x40); } if (ctlr == 0) printf("via_571_status: Primary IDE prefetch/postwrite %s/%s\n", word40[0] & 0x8000 ? "enabled" : "disabled", word40[0] & 0x4000 ? "enabled" : "disabled"); else printf("via_571_status: Secondary IDE prefetch/postwrite %s/%s\n", word40[0] & 0x2000 ? "enabled" : "disabled", word40[0] & 0x1000 ? "enabled" : "disabled"); printf("via_571_status: busmaster status read retry %s\n", (word40[1] & 0x08) ? "enabled" : "disabled"); printf("via_571_status: %s drive %d data setup=%d active=%d recovery=%d\n", unitno < 2 ? "primary" : "secondary", unitno & 1, ((word40[3] >> ((3 - unitno) * 2)) & 3) + 1, ((word40[2] >> (((3 - unitno) * 8) + 4)) & 0x0f) + 1, ((word40[2] >> ((3 - unitno) * 8)) & 0x0f) + 1); if (ctlr == 0) printf("via_571_status: primary ctrl active=%d recovery=%d\n", ((word40[3] >> 28) & 0x0f) + 1, ((word40[2] >> 24) & 0x0f) + 1); else printf("via_571_status: secondary ctrl active=%d recovery=%d\n", ((word40[3] >> 20) & 0x0f) + 1, ((word40[2] >> 16) & 0x0f) + 1); /* UltraDMA dump */ { int foo; foo = word40[4] >> ((3 - unitno) * 8); printf("via_571_status: %s drive %d udma method=%d enable=%d PIOmode=%d cycle=%d\n", i < 2 ? "primary" : "secondary", i & 1, (foo >> 7) & 1, (foo >> 6) & 1, (foo >> 5) & 1, (foo & 3) + 2); } } /* * XXX timing values set here are only good for 30/33MHz buses; should deal * with slower ones too (BTW: you overclock-- you lose) */ static int via_571_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; u_long pci_revision; int unitno; pci_revision = pci_conf_read(cookie->tag, PCI_CLASS_REG) & PCI_REVISION_MASK; unitno = cookie->ctlr * 2 + cookie->unit; /* If it's a UDMA drive on a '590, set it up */ /* * XXX the revision number we check for is of dubious validity. * it's extracted from the AMD 645 datasheet. */ if (pci_revision >= 1 && udma_mode(wp) >= 2) { unsigned int word50, mask, new; word50 = pci_conf_read(cookie->tag, 0x50); /* UDMA enable by SET FEATURES, DMA cycles, cycle time 2T */ mask = 0xe3000000 >> (unitno * 8); new = 0x80000000 >> (unitno * 8); word50 &= ~mask; word50 |= new; pci_conf_write(cookie->tag, 0x50, word50); /* * With the '590, drive configuration should come *after* the * controller configuration, to make sure the controller sees * the SET FEATURES command and does the right thing. */ /* Set UDMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } if (bootverbose) via_571_status(cookie); return 1; } /* otherwise, try and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { u_long workword; /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } /* Configure the controller appropriately for MWDMA mode 2 */ workword = pci_conf_read(cookie->tag, 0x40); /* * enable prefetch/postwrite-- XXX may cause problems * with CD-ROMs? */ workword &= ~(3 << (cookie->ctlr * 2 + 12)); workword |= 3 << (cookie->ctlr * 2 + 12); /* FIFO configurations-- equal split, threshold 1/2 */ workword &= 0x90ffffff; workword |= 0x2a000000; pci_conf_write(cookie->tag, 0x40, workword); workword = pci_conf_read(cookie->tag, 0x44); /* enable status read retry */ workword |= 8; /* enable FIFO flush on interrupt and end of sector */ workword &= 0xff0cffff; workword |= 0x00f00000; pci_conf_write(cookie->tag, 0x44, workword); workword = pci_conf_read(cookie->tag, 0x48); /* set Mode2 timing */ workword &= ~(0xff000000 >> (unitno * 8)); workword |= 0x31000000 >> (unitno * 8); pci_conf_write(cookie->tag, 0x48, workword); /* set sector size */ pci_conf_write(cookie->tag, cookie->ctlr ? 0x68 : 0x60, 0x200); if (bootverbose) via_571_status(cookie); return 1; } return 0; } static struct vendor_fns vs_via_571 = { via_571_dmainit, via_571_status }; static void promise_status(struct ide_pci_cookie *cookie) { pcici_t tag; int i; u_int32_t port0_command, port0_altstatus; u_int32_t port1_command, port1_altstatus; u_int32_t dma_block; u_int32_t lat_and_interrupt; u_int32_t drivetiming; int pa, pb, mb, mc; tag = cookie->tag; port0_command = pci_conf_read(tag, 0x10); port0_altstatus = pci_conf_read(tag, 0x14); port1_command = pci_conf_read(tag, 0x18); port1_altstatus = pci_conf_read(tag, 0x1c); dma_block = pci_conf_read(tag, 0x20); lat_and_interrupt = pci_conf_read(tag, 0x3c); printf("promise_status: port0: 0x%lx, port0_alt: 0x%lx, port1: 0x%lx, port1_alt: 0x%lx\n", port0_command, port0_altstatus, port1_command, port1_altstatus); printf("promise_status: dma control blk address: 0x%lx, int: %d, irq: %d\n", dma_block, (lat_and_interrupt >> 8) & 0xff, lat_and_interrupt & 0xff); for(i=0;i<4;i+=2) { drivetiming = pci_conf_read(tag, 0x60 + i * 4); printf("drivebits%d-%d: %b\n", i, i+1, drivetiming, "\020\05Prefetch\06Iordy\07Errdy\010Sync\025DmaW\026DmaR"); pa = drivetiming & 0xf; pb = (drivetiming >> 8) & 0x1f; mb = (drivetiming >> 13) & 0x7; mc = (drivetiming >> 16) & 0xf; printf("drivetiming%d: pa: 0x%x, pb: 0x%x, mb: 0x%x, mc: 0x%x\n", i, pa, pb, mb, mc); drivetiming = pci_conf_read(tag, 0x60 + (i + 1) * 4); pa = drivetiming & 0xf; pb = (drivetiming >> 8) & 0x1f; mb = (drivetiming >> 13) & 0x7; mc = (drivetiming >> 16) & 0xf; printf("drivetiming%d: pa: 0x%x, pb: 0x%x, mb: 0x%x, mc: 0x%x\n", i + 1, pa, pb, mb, mc); } } static struct vendor_fns vs_promise = { generic_dmainit, promise_status }; /* Intel PIIX, PIIX3, and PIIX4 IDE controller subfunctions */ static void intel_piix_dump_drive(char *ctlr, int sitre, int is_piix4, int word40, int word44, int word48, int word4a, int drive) { char *ms; if (!sitre) ms = "master/slave"; else if (drive == 0) ms = "master"; else ms = "slave"; printf("intel_piix_status: %s %s sample = %d, %s recovery = %d\n", ctlr, ms, 5 - ((sitre && drive) ? ((word44 >> 2) & 3) : ((word40 >> 12) & 3)), ms, 4 - ((sitre && drive) ? ((word44 >> 0) & 3) : ((word40 >> 8) & 3))); word40 >>= (drive * 4); printf("intel_piix_status: %s %s fastDMAonly %s, pre/post %s,\n\ intel_piix_status: IORDY sampling %s,\n\ intel_piix_status: fast PIO %s%s\n", ctlr, (drive == 0) ? "master" : "slave", (word40 & 8) ? "enabled" : "disabled", (word40 & 4) ? "enabled" : "disabled", (word40 & 2) ? "enabled" : "disabled", (word40 & 1) ? "enabled" : "disabled", ((word40 & 9) == 9) ? " (overridden by fastDMAonly)" : "" ); if (is_piix4) printf("intel_piix_status: UltraDMA %s, CT/RP = %d/%d\n", word48 ? "enabled": "disabled", 4 - (word4a & 3), 6 - (word4a & 3)); } static void intel_piix_status(struct ide_pci_cookie *cookie) { int iobase_wd; int unit; int iobase_bm; pcici_t tag; pcidi_t type; int ctlr; u_long word40, word44, word48; int sitre, is_piix4; iobase_wd = cookie->iobase_wd; unit = cookie->unit; iobase_bm = cookie->iobase_bm; tag = cookie->tag; type = cookie->type; ctlr = cookie->ctlr; word40 = pci_conf_read(tag, 0x40); word44 = pci_conf_read(tag, 0x44); word48 = pci_conf_read(tag, 0x48); /* * XXX will not be right for the *next* generation of upward-compatible * intel IDE controllers... */ is_piix4 = pci_conf_read(tag, PCI_CLASS_REG) == 0x71118086; sitre = word40 & 0x4000; switch (ctlr * 2 + unit) { case 0: intel_piix_dump_drive("primary", sitre, is_piix4, word40 & 0xffff, word44 & 0x0f, word48, word48 >> 16, 0); break; case 1: intel_piix_dump_drive("primary", sitre, is_piix4, word40 & 0xffff, word44 & 0x0f, word48 >> 1, word48 >> 20, 1); break; case 2: intel_piix_dump_drive("secondary", sitre, is_piix4, (word40 >> 16) & 0xffff, (word44 >> 4) & 0x0f, word48 >> 2, word48 >> 24, 0); break; case 3: intel_piix_dump_drive("secondary", sitre, is_piix4, (word40 >> 16) & 0xffff, (word44 >> 4) & 0x0f, word48 >> 3, word48 >> 28, 1); break; default: printf("intel_piix_status: bad drive or controller number\n"); } } /* * XXX timing values set hereare only good for 30/33MHz buses; should deal * with slower ones too (BTW: you overclock-- you lose) */ static int intel_piix_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { int r; /* If it's a UDMA drive and a PIIX4, set it up */ if (cookie->type == 0x71118086 && udma_mode(wp) >= 2) { /* Set UDMA mode 2 on controller */ int unitno, mask, new; if (bootverbose) printf("intel_piix_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } unitno = cookie->ctlr * 2 + cookie->unit; mask = 1 << unitno + 3 << (16 + unitno * 4); new = 1 << unitno + 2 << (16 + unitno * 4); pci_conf_write(cookie->tag, 0x48, (pci_conf_read(cookie->tag, 0x48) & ~mask) | new); if (bootverbose) intel_piix_status(cookie); return 1; } /* * if it's an 82371FB, which can't do independent programming of * drive timing, we punt; we're not going to fuss with trying to * coordinate timing modes between drives. if this is you, get a * new motherboard. or contribute patches :) * * we do now at least see if the modes set are OK to use. this should * satisfy the majority of people, with mwdma mode2 drives. */ else if (cookie->type == 0x12308086) { u_long word40; /* can drive do PIO 4 and MW DMA 2? */ if (!(mwdma_mode(wp) >= 4 && pio_mode(wp) >= 4)) return 0; word40 = pci_conf_read(cookie->tag, 0x40); word40 >>= cookie->ctlr * 16; /* Check for timing config usable for DMA on controller */ if (!((word40 & 0x3300) == 0x2300 && ((word40 >> (cookie->unit * 4)) & 1) == 1)) return 0; /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } return 1; } /* otherwise, treat it as a PIIX3 and program it for MW DMA mode 2 */ else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) { u_long mask40, mask44, new40, new44; /* * If SITRE is not set, set it and copy the * appropriate bits into the secondary registers. Do * both controllers at once. */ if (((pci_conf_read(cookie->tag, 0x40) >> (16 * cookie->ctlr)) & 0x4000) == 0) { unsigned int word40, word44; word40 = pci_conf_read(cookie->tag, 0x40); /* copy bits to secondary register */ word44 = pci_conf_read(cookie->tag, 0x44); /* * I've got a Biostar motherboard with Award * BIOS that sets SITRE and secondary timing * on one controller but not the other. * Bizarre. */ if ((word40 & 0x4000) == 0) { word44 &= ~0xf; word44 |= ((word40 & 0x3000) >> 10) | ((word40 & 0x0300) >> 8); } if ((word40 & 0x40000000) == 0) { word44 &= ~0xf0; word44 |= ((word40 & 0x30000000) >> 22) | ((word40 & 0x03000000) >> 20); } /* set SITRE */ word40 |= 0x40004000; pci_conf_write(cookie->tag, 0x40, word40); pci_conf_write(cookie->tag, 0x44, word44); } /* Set multiword DMA mode 2 on drive */ if (bootverbose) printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("intel_piix_dmainit: setting DMA mode failed\n"); return 0; } /* * backward compatible hardware leaves us with such * twisted masses of software (aka twiddle the * extremely weird register layout on a PIIX3, setting * PIO mode 4 and MWDMA mode 2) */ if (cookie->unit == 0) { mask40 = 0x330f; new40 = 0x2307; mask44 = 0; new44 = 0; } else { mask40 = 0x00f0; new40 = 0x0070; mask44 = 0x000f; new44 = 0x000b; } if (cookie->ctlr) { mask40 <<= 16; new40 <<= 16; mask44 <<= 4; new44 <<= 4; } pci_conf_write(cookie->tag, 0x40, (pci_conf_read(cookie->tag, 0x40) & ~mask40) | new40); pci_conf_write(cookie->tag, 0x44, (pci_conf_read(cookie->tag, 0x44) & ~mask44) | new44); if (bootverbose) intel_piix_status(cookie); return 1; } return 0; } static struct vendor_fns vs_intel_piix = { intel_piix_dmainit, intel_piix_status }; /* Generic SFF-8038i code-- all code below here, except for PCI probes, * more or less conforms to the SFF-8038i spec as extended for PCI. * There should be no code that goes beyond that feature set below. */ /* XXX mkcookie is overloaded with too many parameters */ static struct ide_pci_cookie * mkcookie(int iobase_wd, int ctlr, int unit, int iobase_bm, pcici_t tag, pcidi_t type, struct vendor_fns *vp, int altiobase_wd) { struct ide_pci_cookie *cp; cp = malloc(sizeof *cp, M_DEVBUF, M_NOWAIT); if (!cp) return 0; cp->iobase_wd = iobase_wd; cp->ctlr = ctlr; cp->unit = unit; cp->tag = tag; cp->type = type; cp->iobase_bm = iobase_bm; cp->altiobase_wd = altiobase_wd; bcopy(vp, &cp->vs, sizeof(struct vendor_fns)); if (!prdbuf) { prdbuf = malloc(PRD_ALLOC_SIZE, M_DEVBUF, M_NOWAIT); if (!prdbuf) { FREE(cp, M_DEVBUF); return 0; } if (((int)prdbuf >> PAGE_SHIFT) ^ (((int)prdbuf + PRD_ALLOC_SIZE - 1) >> PAGE_SHIFT)) { printf("ide_pci: prdbuf straddles page boundary, no DMA\n"); FREE(cp, M_DEVBUF); FREE(prdbuf, M_DEVBUF); return 0; } prdbuf_next = prdbuf; } if (((char *)prdbuf_next + PRD_BUF_SIZE) > ((char *)prdbuf + PRD_ALLOC_SIZE)) { printf("ide_pci: mkcookie %04x:%d: no more space for PRDs, no DMA\n", iobase_wd, unit); FREE(cp, M_DEVBUF); return 0; } cp->prd = prdbuf_next; (char *)prdbuf_next += PRD_BUF_SIZE; LIST_INSERT_HEAD(&softc.cookies, cp, le); return cp; } static char * ide_pci_probe(pcici_t tag, pcidi_t type) { u_long data; data = pci_conf_read(tag, PCI_CLASS_REG); if ((data & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE && ((data & PCI_SUBCLASS_MASK) == 0x00010000 || ((data & PCI_SUBCLASS_MASK) == 0x00040000))) { if (type == 0x71118086) return ("Intel PIIX4 Bus-master IDE controller"); if (type == 0x70108086) return ("Intel PIIX3 Bus-master IDE controller"); if (type == 0x12308086) return ("Intel PIIX Bus-master IDE controller"); if (type == PROMISE_ULTRA33) return ("Promise Ultra/33 IDE controller"); if (type == 0x05711106) return ("VIA 82C586x (Apollo) Bus-master IDE controller"); if (data & 0x8000) return ("PCI IDE controller (busmaster capable)"); #ifndef CMD640 /* * XXX the CMD640B hack should be better integrated, or * something. */ else return ("PCI IDE controller (not busmaster capable)"); #endif }; return ((char*)0); } static void ide_pci_attach(pcici_t tag, int unit) { u_long class, cmd; int bmista_1, bmista_2; int iobase_wd_1, iobase_wd_2, iobase_bm_1, iobase_bm_2; int altiobase_wd_1, altiobase_wd_2; struct vendor_fns *vp; pcidi_t type; struct ide_pci_cookie *cookie; int ctlridx; ctlridx = unit * 2; /* set up vendor-specific stuff */ type = pci_conf_read(tag, PCI_ID_REG); if (type != PROMISE_ULTRA33) { /* is it busmaster capable? bail if not */ class = pci_conf_read(tag, PCI_CLASS_REG); if (!(class & 0x8000)) { return; } /* is it enabled and is busmastering turned on? */ cmd = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); if ((cmd & 5) != 5) { return; } } switch (type) { case 0x71118086: case 0x70108086: case 0x12308086: /* Intel PIIX, PIIX3, PIIX4 */ vp = &vs_intel_piix; break; case 0x5711106: /* VIA Apollo chipset family */ vp = &vs_via_571; break; case PROMISE_ULTRA33: /* Promise controllers */ vp = &vs_promise; break; default: /* everybody else */ vp = &vs_generic; break; } if (type != PROMISE_ULTRA33) { if ((class & 0x100) == 0) { iobase_wd_1 = IO_WD1; altiobase_wd_1 = iobase_wd_1 + wd_altsts; } else { iobase_wd_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_wd_1 = pci_conf_read(tag, 0x14) & 0xfffc; } if ((class & 0x400) == 0) { iobase_wd_2 = IO_WD2; altiobase_wd_2 = iobase_wd_2 + wd_altsts; } else { iobase_wd_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_wd_2 = pci_conf_read(tag, 0x1c) & 0xfffc; } } else { iobase_wd_1 = pci_conf_read(tag, 0x10) & 0xfffc; altiobase_wd_1 = pci_conf_read(tag, 0x14) & 0xfffc; iobase_wd_2 = pci_conf_read(tag, 0x18) & 0xfffc; altiobase_wd_2 = pci_conf_read(tag, 0x1c) & 0xfffc; } iobase_bm_1 = pci_conf_read(tag, 0x20) & 0xfffc; iobase_bm_2 = iobase_bm_1 + SFF8038_CTLR_1; if (iobase_bm_1 == 0) { printf("ide_pci: BIOS has not configured busmaster I/O address,\n\ ide_pci: giving up\n"); return; } wddma[unit].wdd_candma = ide_pci_candma; wddma[unit].wdd_dmainit = ide_pci_dmainit; wddma[unit].wdd_dmaverify = ide_pci_dmaverify; wddma[unit].wdd_dmaprep = ide_pci_dmasetup; wddma[unit].wdd_dmastart = ide_pci_dmastart; wddma[unit].wdd_dmadone = ide_pci_dmadone; wddma[unit].wdd_dmastatus = ide_pci_status; wddma[unit].wdd_iobase = ide_pci_iobase; wddma[unit].wdd_altiobase = ide_pci_altiobase; /* * This code below is mighty bogus. The config entries for the * isa_devtab_bio are plugged in before the standard ISA bios scan. * This is our "hack" way to simulate a dynamic assignment of I/O * addresses, from a PCI device to an ISA probe. Sorry :-). */ if (iobase_wd_1 != IO_WD1) { struct isa_device *dvp, *dvp1, *dvup; for( dvp = isa_devtab_bio; dvp->id_id != 0; dvp++) { if ((dvp->id_driver == &wdcdriver) && (dvp->id_iobase == 0)) { int biotabunit; biotabunit = dvp->id_unit * 2; dvp->id_iobase = iobase_wd_1; dvp1 = dvp + 1; dvp1->id_iobase = iobase_wd_2; printf("ide_pci%d: adding drives to controller %d:", unit, biotabunit); for(dvup = isa_biotab_wdc; dvup->id_id != 0; dvup++) { if (dvup->id_driver != &wdcdriver) continue; if (dvup->id_unit != biotabunit) continue; dvup->id_iobase = dvp->id_iobase; printf(" %d", dvup->id_unit); dvup++; pci_map_int(tag, wdintr, (void *) dvp->id_unit, &bio_imask); if (dvup->id_id == 0) break; if (dvup->id_unit == biotabunit + 1) { dvup->id_iobase = dvp->id_iobase; printf(" %d", dvup->id_unit); dvup++; if (dvup->id_id == 0) { iobase_wd_2 = 0; break; } } if (dvup->id_unit == biotabunit + 2) { pci_map_int(tag, wdintr, (void *) ((int) dvp->id_unit + 1), &bio_imask); dvup->id_iobase = dvp1->id_iobase; printf(" %d", dvup->id_unit); dvup++; if (dvup->id_id == 0) { break; } } if (dvup->id_unit == biotabunit + 3) { pci_map_int(tag, wdintr, (void *) ((int) dvp->id_unit + 1), &bio_imask); dvup->id_iobase = dvp1->id_iobase; printf(" %d", dvup->id_unit); } break; } printf("\n"); break; } } } bmista_1 = inb(iobase_bm_1 + BMISTA_PORT); bmista_2 = inb(iobase_bm_2 + BMISTA_PORT); if (!ide_pci_softc_cookies_initted) { LIST_INIT(&softc.cookies); ide_pci_softc_cookies_initted = 1; } if (iobase_wd_1 != 0) { cookie = mkcookie(iobase_wd_1, ctlridx, 0, iobase_bm_1, tag, type, vp, altiobase_wd_1); if (bootverbose) vp->vendor_status(cookie); cookie = mkcookie(iobase_wd_1, ctlridx, 1, iobase_bm_1, tag, type, vp, altiobase_wd_1); if (bootverbose) { vp->vendor_status(cookie); printf("ide_pci: busmaster 0 status: %02x from port: %08x\n", bmista_1, iobase_bm_1+BMISTA_PORT); if (bmista_1 & BMISTA_DMA0CAP) printf("ide_pci: ide0:0 has been configured for DMA by BIOS\n"); if (bmista_1 & BMISTA_DMA1CAP) printf("ide_pci: ide0:1 has been configured for DMA by BIOS\n"); } } if (bmista_1 & BMISTA_SIMPLEX || bmista_2 & BMISTA_SIMPLEX) { printf("ide_pci: controller is simplex, no DMA on secondary channel\n"); } else if (iobase_wd_2 != 0) { cookie = mkcookie(iobase_wd_2, ctlridx + 1, 0, iobase_bm_2, tag, type, vp, altiobase_wd_2); if (bootverbose) vp->vendor_status(cookie); cookie = mkcookie(iobase_wd_2, ctlridx + 1, 1, iobase_bm_2, tag, type, vp, altiobase_wd_2); if (bootverbose) { vp->vendor_status(cookie); printf("ide_pci: busmaster 1 status: %02x from port: %08x\n", bmista_2, iobase_bm_2+BMISTA_PORT); if (bmista_2 & BMISTA_DMA0CAP) printf("ide_pci: ide1:0 has been configured for DMA by BIOS\n"); if (bmista_2 & BMISTA_DMA1CAP) printf("ide_pci: ide1:1 has been configured for DMA by BIOS\n"); } } } static u_long ide_pci_count; static struct pci_device ide_pci_device = { "ide_pci", ide_pci_probe, ide_pci_attach, &ide_pci_count, 0 }; DATA_SET(pcidevice_set, ide_pci_device); /* * Return a cookie if we can do DMA on the specified (iobase_wd, unit). */ static void * ide_pci_candma(int iobase_wd, int unit) { struct ide_pci_cookie *cp; cp = softc.cookies.lh_first; while(cp) { if (cp->ctlr == unit && ((iobase_wd == 0) || (cp->iobase_wd == iobase_wd))) break; cp = cp->le.le_next; } return cp; } /* * Initialize controller and drive for DMA operation, including timing modes. * Uses data passed from the wd driver and a callback function to initialize * timing modes on the drive. */ static int ide_pci_dmainit(void *cookie, struct wdparams *wp, int(*wdcmd)(int, void *), void *wdinfo) { struct ide_pci_cookie *cp = cookie; /* * If the controller status indicates that DMA is configured already, * we flounce happily away */ if (inb(cp->iobase_bm + BMISTA_PORT) & ((cp->unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP)) return 1; /* We take a stab at it with device-dependent code */ return(cp->vs.vendor_dmainit(cp, wp, wdcmd, wdinfo)); } /* * Verify that controller can handle a dma request for cp. Should * not affect any hardware or driver state. */ static int ide_pci_dmaverify(void *xcp, char *vaddr, u_long count, int dir) { int badfu; /* * check for nonaligned or odd-length Stuff */ badfu = ((unsigned int)vaddr & 1) || (count & 1); #ifdef DIAGNOSTIC if (badfu) { printf("ide_pci: dmaverify odd vaddr or length, "); printf("vaddr = %08x length = %08x\n", (int)vaddr, count); } #endif return (!badfu); } /* * Set up DMA for cp. It is the responsibility of the caller * to ensure that the controller is idle before this routine * is called. */ static int ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir) { struct ide_pci_cookie *cp = xcp; struct ide_pci_prd *prd; int i; u_long firstpage; u_long prd_base, prd_count; u_long nbase, ncount, nend; int iobase_bm; u_long count, checkcount; prd = cp->prd; count = vcount; i = 0; iobase_bm = cp->iobase_bm; if (count == 0) { printf("ide_pci: dmasetup 0-length transfer, "); printf("vaddr = %08x length = %08x\n", (int)vaddr, count); return 1; } /* Generate first PRD entry, which may be non-aligned. */ firstpage = PAGE_SIZE - ((u_long)vaddr & PAGE_MASK); prd_base = vtophys(vaddr); prd_count = MIN(count, firstpage); vaddr += prd_count; count -= prd_count; /* Step through virtual pages, coalescing as needed. */ while (count) { nbase = vtophys(vaddr); ncount = MIN(count, PAGE_SIZE); nend = nbase + ncount; /* * Coalesce if physically contiguous and not crossing * 64k boundary. */ if ((prd_base + prd_count == nbase) && ((((nend - 1) ^ prd_base) & ~0xffff) == 0)) { prd_count += ncount; } else { prd[i].prd_base = prd_base; prd[i].prd_count = (prd_count & 0xffff); i++; if (i >= PRD_MAX_SEGS) { printf("wd82371: too many segments in PRD table\n"); return 1; } prd_base = nbase; prd_count = ncount; } vaddr += ncount; count -= ncount; } /* Write last PRD entry. */ prd[i].prd_base = prd_base; prd[i].prd_count = (prd_count & 0xffff) | PRD_EOT_BIT; #ifdef DIAGNOSTIC /* sanity check the transfer for length and page-alignment, at least */ checkcount = 0; for (i = 0;; i++) { unsigned int modcount; modcount = prd[i].prd_count & 0xffffe; if (modcount == 0) modcount = 0x10000; checkcount += modcount; if (i != 0 && ((prd[i].prd_base & PAGE_MASK) != 0)) { printf("ide_pci: dmasetup() diagnostic fails-- unaligned page\n"); return 1; } if (prd[i].prd_count & PRD_EOT_BIT) break; } if (checkcount != vcount) { printf("ide_pci: dmasetup() diagnostic fails-- bad length\n"); return 1; } #endif /* Set up PRD base register */ outl(iobase_bm + BMIDTP_PORT, vtophys(prd)); /* Set direction of transfer */ outb(iobase_bm + BMICOM_PORT, (dir == B_READ) ? BMICOM_READ_WRITE : 0); /* Clear interrupt and error bits */ outb(iobase_bm + BMISTA_PORT, (inb(iobase_bm + BMISTA_PORT) | (BMISTA_INTERRUPT | BMISTA_DMA_ERROR))); return 0; } static void ide_pci_dmastart(void *xcp) { struct ide_pci_cookie *cp = xcp; int iobase_bm; iobase_bm = cp->iobase_bm; outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) | BMICOM_STOP_START); } static int ide_pci_dmadone(void *xcp) { struct ide_pci_cookie *cp = xcp; int iobase_bm, status; status = ide_pci_status(xcp); iobase_bm = cp->iobase_bm; outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) & ~BMICOM_STOP_START); return status; } static int ide_pci_status(void *xcp) { struct ide_pci_cookie *cp = xcp; int iobase_bm, status, bmista; status = 0; iobase_bm = cp->iobase_bm; bmista = inb(iobase_bm + BMISTA_PORT); if (bmista & BMISTA_INTERRUPT) status |= WDDS_INTERRUPT; if (bmista & BMISTA_DMA_ERROR) status |= WDDS_ERROR; if (bmista & BMISTA_DMA_ACTIVE) status |= WDDS_ACTIVE; return status; } static int ide_pci_altiobase(void *xcp) { struct ide_pci_cookie *cp = xcp; if (cp == 0) { return 0; } else { return cp->altiobase_wd; } } static int ide_pci_iobase(void *xcp) { struct ide_pci_cookie *cp = xcp; if (cp == 0) { return 0; } else { return cp->iobase_wd; } } #endif #endif /* NPCI > 0 */