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 + -
显示快捷键?