freebsd-skq/sys/pci/ide_pci.c
John Dyson e871e61fcf Addition of support of the slightly rogue Promise IDE interface(Dyson), support
of multiple PCI IDE controllers(Dyson), and some updates and cleanups from
John Hood, who originally made our IDE DMA stuff work :-).

I have run tests with 7 IDE drives connected to my system, all in DMA
mode, with no errors.  Modulo any bugs, this stuff makes IDE look
really good (within it's limitations.)

Submitted by:	John Hood <cgull@smoke.marlboro.vt.us>
1997-09-20 07:41:58 +00:00

1434 lines
35 KiB
C

/*
* 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: wd82371.c,v 1.5.2.1 1996/11/16 21:19:51 phk 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>
#include <i386/isa/isa.h>
#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 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_timing(void *, int);
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,j;
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 idetm;
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, *dvup1;
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;
static int trashmore = 0xdeadbeef;
static int *trashmore_p = 0;
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 */