ide_pci.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,713 行 · 第 1/3 页
C
1,713 行
/* * 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.28 1999/01/17 05:46:25 bde Exp $ */#include "pci.h"#if NPCI > 0#include "opt_wd.h"#include "wd.h"#if NWDC > 0#include <sys/param.h>#include <sys/systm.h>#include <sys/buf.h>#include <sys/malloc.h>#include <sys/kernel.h>#include <vm/vm.h>#include <vm/pmap.h>#include <i386/isa/wdreg.h>#ifdef PC98#include <pc98/pc98/pc98.h>#else#include <i386/isa/isa.h>#endif#include <i386/isa/isa_device.h>#include <pci/pcivar.h>#include <pci/pcireg.h>#include <pci/ide_pcireg.h>#ifndef MIN#define MIN(a,b) (((a)<(b))?(a):(b))#endif#define PROMISE_ULTRA33 0x4d33105astruct 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 intgeneric_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo);static voidgeneric_status(struct ide_pci_cookie *cookie);static voidvia_571_status(struct ide_pci_cookie *cookie);static intvia_571_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo);static voidacer_status(struct ide_pci_cookie *cookie);static int acer_dmainit(struct ide_pci_cookie *cookie, struct wdparams *wp, int (*wdcmd)(int, void *), void *wdinfo);static voidintel_piix_dump_drive(char *ctlr, int sitre, int is_piix4, int word40, int word44, int word48, int word4a, int drive);static voidintel_piix_status(struct ide_pci_cookie *cookie);static intintel_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, 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 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 * 8static 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 intpio_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 0static __inline intdma_mode(struct wdparams *wp){ /* XXX not quite sure how to verify validity on this field */}#endifstatic __inline intmwdma_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 intudma_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 intgeneric_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); /* If we're here, then this controller is most likely not set for UDMA, even if the drive may be. Make the drive wise up. */ if(!wdcmd(WDDMA_MDMA2, wdinfo)) printf("generic_dmainit: could not set multiword DMA mode!\n"); 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 voidgeneric_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 voidvia_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, ((u_int)(word40[3] >> ((3 - unitno) * 2)) & 3) + 1, ((u_int)(word40[2] >> (((3 - unitno) * 8) + 4)) & 0x0f) + 1, ((u_int)(word40[2] >> ((3 - unitno) * 8)) & 0x0f) + 1); if (ctlr == 0) printf("via_571_status: primary ctrl active=%d recovery=%d\n", ((u_int)(word40[3] >> 28) & 0x0f) + 1, ((u_int)(word40[2] >> 24) & 0x0f) + 1); else printf("via_571_status: secondary ctrl active=%d recovery=%d\n", ((u_int)(word40[3] >> 20) & 0x0f) + 1, ((u_int)(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 intvia_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 = 0x40000000 >> (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("via_571_dmainit: setting ultra DMA mode 2\n"); r = wdcmd(WDDMA_UDMA2, wdinfo); if (!r) { printf("via_571_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("via_571_dmainit: setting multiword DMA mode 2\n"); r = wdcmd(WDDMA_MDMA2, wdinfo); if (!r) { printf("via_571_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 |= 0xc000 >> (cookie->ctlr * 2); /* 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};/* Cyrix Cx5530 Courtesy of Whistle Communications *//* * Verify that controller can handle a dma request for cp. Should * not affect any hardware or driver state. * Special version for 5530 that allows only transfers on 16 byte boundaries.(!) * (Yes the Cyrix 5530 can only UDMA to cache-line boundaries.(bleh!)) * Luckily nearly all disk IO is to kernel bufers which are page alligned. * They may fix this in some other version of the chip, but it's in the latest * at this time (Jan 1999). */static intcyrix_5530_dmaverify(void *xcp, char *vaddr, u_long count, int dir){ int badfu; /* * check for nonaligned or odd-length Stuff */ badfu = ((unsigned int)vaddr & 0xf) || (count & 0xf);#ifdef DIAGNOSTIC if (badfu) { printf("ide_pci: dmaverify odd vaddr or length, "); printf("vaddr = %p length = %08lx\n", (void *)vaddr, count); }#endif return (!badfu);}/* * XXX Unit number handling may be broken in the Cx5530 modules. * It has only been checked with a single drive. * 12MByte/Sec transfer rates were seen with Quantum Fireball drives * with negligable CPU usage. */static voidcyrix_5530_status(struct ide_pci_cookie *cookie){ int iobase_wd; int ctlr, unit; int iobase_bm; pcici_t tag; pcidi_t type; u_long PIO_config; u_long DMA_config; int 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; /* set some values the BIOS should have set */ printf("Using 0x%x\n", cookie->iobase_bm); outl(iobase_bm + (unit * 0x10) + 0x20, 0x00040010); outl(iobase_bm + (unit * 0x10) + 0x24, 0x00911030); /* if ((ctlr == 0) && (unit == 0)) */ /* XXX */ /* outb(iobase_bm + (unit * 0x10) + BMISTA_PORT, 0xe6);*/ PIO_config = inl(iobase_bm + (unit * 0x10) + 0x20); DMA_config = inl(iobase_bm + (unit * 0x10) + 0x24); printf("cyrix_5530_status: %s:%u IDE PIO cfg: 0x%08lx\n", (ctlr ? "Secondary" : "Primary"), unit, PIO_config); printf("cyrix_5530_status: %s:%u IDE DMA cfg: 0x%08lx\n", (ctlr ? "Secondary" : "Primary"), unit, DMA_config);}/*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?