814 lines
21 KiB
C
814 lines
21 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.
|
|
*
|
|
* From: wd82371.c,v 1.5.2.1 1996/11/16 21:19:51 phk Exp $
|
|
* $Id: ide_pci.c,v 1.1 1997/07/29 12:57:09 sos Exp $
|
|
*/
|
|
|
|
#include "pci.h"
|
|
#if NPCI > 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 <pci/pcivar.h>
|
|
#include <pci/pcireg.h>
|
|
#include <pci/ide_pcireg.h>
|
|
|
|
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 */
|
|
(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type);
|
|
};
|
|
|
|
/*
|
|
* 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 unit;
|
|
int iobase_bm; /* SFF-8038 control registers */
|
|
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(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type);
|
|
static void
|
|
via_571_status(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type);
|
|
static void
|
|
intel_piix_dump_drive(char *ctlr,
|
|
int sitre,
|
|
int word40,
|
|
int word44,
|
|
int drive);
|
|
static void
|
|
intel_piix_status(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type);
|
|
|
|
static struct ide_pci_cookie *
|
|
mkcookie(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type,
|
|
struct vendor_fns *vp);
|
|
|
|
|
|
|
|
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 struct ide_pci_softc softc;
|
|
|
|
static int ide_pci_softc_cookies_initted = 0;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* Generic busmastering PCI-IDE */
|
|
|
|
static int
|
|
generic_dmainit(struct ide_pci_cookie *cookie,
|
|
struct wdparams *wp,
|
|
int(*wdcmd)(int, void *),
|
|
void *wdinfo)
|
|
{
|
|
int mode, r;
|
|
/*
|
|
* XXX 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. this code may eventually be replaced
|
|
* by gunk in the hw-specific code to deal with specific
|
|
* controllers.
|
|
*/
|
|
/* XXX way too sick and twisted conditional */
|
|
if (!((((wp->wdp_atavalid & 2) == 2) &&
|
|
((wp->wdp_dmamword & 0x404) == 0x404) &&
|
|
((wp->wdp_eidepiomodes & 2) == 2)) ||
|
|
(((wp->wdp_atavalid & 4) == 4) &&
|
|
(wp->wdp_udmamode == 4))))
|
|
return 0;
|
|
|
|
#if 0
|
|
/*
|
|
* XXX flesh this out into real code that actually
|
|
* does something-- this was just testing gunk.
|
|
*/
|
|
if (((wp->wdp_atavalid & 0x4) == 0x4) &&
|
|
(wp->wdp_udmamode == 4)) {
|
|
printf("UDMA mode\n");
|
|
mode = 0x42; /* XXX where's the #defines... */
|
|
}
|
|
else {
|
|
printf("MDMA mode\n");
|
|
mode = 0x24;
|
|
}
|
|
|
|
r = wdcmd(mode, wdinfo);
|
|
printf("dmainit out like we expect\n");
|
|
if (!r)
|
|
return 0;
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
generic_status(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type)
|
|
{
|
|
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(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type)
|
|
{
|
|
unsigned int word40[5];
|
|
int i;
|
|
|
|
/* XXX how to handle four calls for one controller? */
|
|
if (iobase_wd != 0x1f0 || unit != 0)
|
|
return;
|
|
|
|
for (i=0; i<5; i++) {
|
|
word40[i] = pci_conf_read(tag, i * 4 + 0x40);
|
|
}
|
|
|
|
printf("via_571_status: Primary IDE prefetch/postwrite %s/%s\n",
|
|
word40[0] & 0x8000 ? "enabled" : "disabled",
|
|
word40[0] & 0x4000 ? "enabled" : "disabled");
|
|
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: Master %d read/%d write IRDY# wait states\n",
|
|
(word40[1] & 0x40) >> 6,
|
|
(word40[1] & 0x20) >> 5);
|
|
printf("via_571_status: busmaster status read retry %s\n",
|
|
(word40[1] & 0x10) ? "enabled" : "disabled");
|
|
|
|
for (i=0; i<4; i++)
|
|
printf("via_571_status: %s drive %d setup=%d active=%d recovery=%d\n",
|
|
i < 2 ? "primary" : "secondary",
|
|
i & 1,
|
|
((word40[3] >> ((3 - i) * 2)) & 3) + 1,
|
|
((word40[2] >> (((3 - i) * 8) + 4)) & 0x0f) + 1,
|
|
((word40[2] >> ((3 - i) * 8)) & 0x0f) + 1);
|
|
|
|
|
|
/* XXX could go on and do UDMA status for '586B */
|
|
}
|
|
|
|
static struct vendor_fns vs_via_571 =
|
|
{
|
|
generic_dmainit,
|
|
via_571_status
|
|
};
|
|
|
|
/* Intel PIIX, PIIX3, and PIIX4 IDE controller subfunctions */
|
|
|
|
static void
|
|
intel_piix_dump_drive(char *ctlr,
|
|
int sitre,
|
|
int word40,
|
|
int word44,
|
|
int drive)
|
|
{
|
|
char *ms;
|
|
|
|
if (!sitre)
|
|
ms = "master/slave";
|
|
else if (drive == 0)
|
|
ms = "master";
|
|
else
|
|
ms = "slave";
|
|
|
|
if (sitre || drive == 0)
|
|
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)" : "" );
|
|
|
|
/* XXX extend to dump 82371AB's UltraDMA modes */
|
|
}
|
|
|
|
static void
|
|
intel_piix_status(int iobase_wd,
|
|
int unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type)
|
|
{
|
|
unsigned int word40, word44;
|
|
int sitre;
|
|
|
|
/* XXX how to handle four calls for one controller? */
|
|
if (iobase_wd != 0x1f0 || unit != 0)
|
|
return;
|
|
|
|
word40 = pci_conf_read(tag, 0x40);
|
|
word44 = pci_conf_read(tag, 0x44);
|
|
|
|
sitre = word40 & 0x4000;
|
|
|
|
intel_piix_dump_drive("primary", sitre, word40 & 0xffff, word44 & 0x0f, 0);
|
|
intel_piix_dump_drive("primary", sitre, word40 & 0xffff, word44 & 0x0f, 1);
|
|
intel_piix_dump_drive("secondary",
|
|
sitre,
|
|
(word40 >> 16) & 0xffff,
|
|
(word44 >> 4) & 0x0f,0);
|
|
intel_piix_dump_drive("secondary",
|
|
sitre,
|
|
(word40 >> 16) & 0xffff,
|
|
(word44 >> 4) & 0x0f,1);
|
|
}
|
|
|
|
static struct vendor_fns vs_intel_piix =
|
|
{
|
|
generic_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 unit,
|
|
int iobase_bm,
|
|
pcici_t tag,
|
|
pcidi_t type,
|
|
struct vendor_fns *vp)
|
|
{
|
|
struct ide_pci_cookie *cp;
|
|
|
|
cp = malloc(sizeof *cp, M_DEVBUF, M_NOWAIT);
|
|
if (!cp) return cp;
|
|
|
|
cp->iobase_wd = iobase_wd;
|
|
cp->unit = unit;
|
|
cp->tag = tag;
|
|
cp->type = type;
|
|
cp->iobase_bm = iobase_bm;
|
|
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");
|
|
FREE(cp, M_DEVBUF);
|
|
FREE(prdbuf, M_DEVBUF);
|
|
return 0;
|
|
}
|
|
|
|
prdbuf_next = prdbuf;
|
|
}
|
|
cp->prd = prdbuf_next;
|
|
(char *)prdbuf_next += PRD_BUF_SIZE;
|
|
|
|
if ((char *)prdbuf_next > ((char *)prdbuf + PRD_ALLOC_SIZE))
|
|
panic("ide_pci: too many prdbufs allocated");
|
|
|
|
if (bootverbose)
|
|
printf("ide_pci: mkcookie %04x:%d: PRD vstart = %08x vend = %08x\n",
|
|
iobase_wd, unit, (int)cp->prd, ((int)cp->prd)+PRD_BUF_SIZE);
|
|
LIST_INSERT_HEAD(&softc.cookies, cp, le);
|
|
return cp;
|
|
}
|
|
|
|
static char *
|
|
ide_pci_probe(pcici_t tag, pcidi_t type)
|
|
{
|
|
int data = pci_conf_read(tag, PCI_CLASS_REG);
|
|
|
|
switch (data & PCI_CLASS_MASK) {
|
|
|
|
case PCI_CLASS_MASS_STORAGE:
|
|
if ((data & PCI_SUBCLASS_MASK) == 0x00010000) {
|
|
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 == 0x05711106)
|
|
return ("VIA 82C586x (Apollo) Bus-master IDE controller");
|
|
if (data & 0x8000)
|
|
return ("PCI IDE controller (busmaster capable)");
|
|
/*
|
|
* XXX leave this out for now, to allow CMD640B hack to work. said
|
|
* hack should be better integrated, or something.
|
|
*/
|
|
#if 0
|
|
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;
|
|
int class;
|
|
int bmista;
|
|
int iobase_wd, iobase_bm;
|
|
int cmd;
|
|
struct vendor_fns *vp;
|
|
pcidi_t type;
|
|
|
|
if (unit) return;
|
|
|
|
/* 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;
|
|
|
|
/* set up vendor-specific stuff */
|
|
type = pci_conf_read(tag, PCI_ID_REG);
|
|
|
|
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;
|
|
|
|
default:
|
|
/* everybody else */
|
|
vp = &vs_generic;
|
|
break;
|
|
}
|
|
|
|
iobase_wd = (class & 0x100) ?
|
|
(pci_conf_read(tag, 0x10) & 0xfffc) :
|
|
0x1f0;
|
|
iobase_bm = pci_conf_read(tag, 0x20) & 0xfffc;
|
|
|
|
if (!ide_pci_softc_cookies_initted) {
|
|
LIST_INIT(&softc.cookies);
|
|
ide_pci_softc_cookies_initted = 1;
|
|
}
|
|
|
|
bmista = inb(iobase_bm + BMISTA_PORT);
|
|
|
|
if (bootverbose)
|
|
printf("ide_pci: busmaster 0 status: %02x from port: %08x\n",
|
|
bmista, iobase_bm+BMISTA_PORT);
|
|
|
|
if (!(bmista & BMISTA_DMA0CAP))
|
|
printf("ide_pci: warning, ide0:0 not configured for DMA?\n");
|
|
mkcookie(iobase_wd, 0, iobase_bm, tag, type, vp);
|
|
if (bootverbose)
|
|
vp->vendor_status(iobase_wd, 0, iobase_bm, tag, type);
|
|
|
|
if (!(bmista & BMISTA_DMA1CAP))
|
|
printf("ide_pci: warning, ide0:1 not configured for DMA?\n");
|
|
mkcookie(iobase_wd, 1, iobase_bm, tag, type, vp);
|
|
if (bootverbose)
|
|
vp->vendor_status(iobase_wd, 1, iobase_bm, tag, type);
|
|
|
|
if (bmista & BMISTA_SIMPLEX) {
|
|
printf("ide_pci: primary is simplex-only, no DMA on secondary\n");
|
|
} else {
|
|
iobase_wd = (class & 0x400) ?
|
|
(pci_conf_read(tag, 0x10) & 0xfffc) :
|
|
0x170;
|
|
iobase_bm += SFF8038_CTLR_1;
|
|
bmista = inb(iobase_bm + BMISTA_PORT);
|
|
|
|
if (bootverbose)
|
|
printf("ide_pci: busmaster 1 status: %02x from port: %08x\n",
|
|
bmista, iobase_bm+BMISTA_PORT);
|
|
|
|
if (bmista & BMISTA_SIMPLEX) {
|
|
printf("ide_pci: secondary is simplex-only, no DMA on secondary\n");
|
|
} else {
|
|
if (!(bmista & BMISTA_DMA0CAP))
|
|
printf("ide_pci: warning, ide1:0 not configured for DMA?\n");
|
|
mkcookie(iobase_wd, 0, iobase_bm, tag, type, vp);
|
|
if (bootverbose)
|
|
vp->vendor_status(iobase_wd, 0, iobase_bm, tag, type);
|
|
if (!(bmista & BMISTA_DMA1CAP))
|
|
printf("ide_pci: warning, ide1:1 not configured for DMA?\n");
|
|
mkcookie(iobase_wd, 1, iobase_bm, tag, type, vp);
|
|
if (bootverbose)
|
|
vp->vendor_status(iobase_wd, 1, iobase_bm, tag, type);
|
|
}
|
|
}
|
|
|
|
wddma.wdd_candma = ide_pci_candma;
|
|
wddma.wdd_dmainit = ide_pci_dmainit;
|
|
wddma.wdd_dmaverify = ide_pci_dmaverify;
|
|
wddma.wdd_dmaprep = ide_pci_dmasetup;
|
|
wddma.wdd_dmastart = ide_pci_dmastart;
|
|
wddma.wdd_dmadone = ide_pci_dmadone;
|
|
wddma.wdd_dmastatus = ide_pci_status;
|
|
}
|
|
|
|
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->unit == unit && 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;
|
|
|
|
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);
|
|
#if 1
|
|
if (badfu) {
|
|
printf("ide_pci: dmaverify odd vaddr or length, ");
|
|
printf("vaddr = %08x length = %08x\n", (int)vaddr, count);
|
|
}
|
|
#endif
|
|
/*
|
|
* XXX should perhaps be checking that length of generated table
|
|
* does not exceed space available, but that Would Be Hairy
|
|
*/
|
|
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 count, int dir)
|
|
{
|
|
struct ide_pci_cookie *cp = xcp;
|
|
struct ide_pci_prd *prd;
|
|
int i;
|
|
u_long pgresid;
|
|
int iobase_bm;
|
|
static int trashmore;
|
|
static int *trashmore_p = 0;
|
|
|
|
|
|
prd = cp->prd;
|
|
i = 0;
|
|
|
|
iobase_bm = cp->iobase_bm;
|
|
/*
|
|
* ensure that 0-length transfers get a PRD that won't smash much
|
|
*/
|
|
if (!trashmore_p)
|
|
trashmore_p = (void *)vtophys(&trashmore);
|
|
|
|
prd[0].prd_base = (unsigned int)trashmore_p;
|
|
prd[0].prd_count = 0x80000002;
|
|
|
|
if (count == 0) {
|
|
printf("ide_pci: dmasetup 0-length transfer, ");
|
|
printf("vaddr = %08x length = %08x\n", (int)vaddr, count);
|
|
}
|
|
|
|
/*
|
|
* XXX the PRD generation code is somewhat ugly and will not
|
|
* port easily to big endian systems.
|
|
*
|
|
* but it works.
|
|
*/
|
|
|
|
/*
|
|
* Deal with transfers that don't start on a page
|
|
* boundary.
|
|
*/
|
|
pgresid = (u_long)vaddr % PAGE_SIZE;
|
|
if (pgresid) {
|
|
prd[i].prd_base = vtophys(vaddr);
|
|
if (count >= (PAGE_SIZE - pgresid))
|
|
prd[i].prd_count = PAGE_SIZE - pgresid;
|
|
else
|
|
prd[i].prd_count = count;
|
|
vaddr += prd[i].prd_count;
|
|
count -= prd[i].prd_count;
|
|
i++;
|
|
}
|
|
|
|
/*
|
|
* We have now ensured that vaddr is page-aligned, so just
|
|
* step through the pages adding each one onto the list.
|
|
*/
|
|
while(count) {
|
|
u_long phys, n;
|
|
|
|
phys = vtophys(vaddr);
|
|
n = ((count > PAGE_SIZE) ? PAGE_SIZE : count);
|
|
/*
|
|
* If the current page is physically contiguous with
|
|
* whatever we have in the previous PRD, just tack it
|
|
* onto the end.
|
|
* CAVEAT: due to a hardware deficiency, PRDs
|
|
* cannot cross a 64K boundary.
|
|
* XXX should we bother with this collapsing? scattered
|
|
* pages appear to be the common case anyway.
|
|
*/
|
|
if (i > 0
|
|
&& (phys == prd[i - 1].prd_base + prd[i - 1].prd_count)
|
|
&& ((prd[i - 1].prd_base & 0xffff)
|
|
+ prd[i - 1].prd_count + n) <= 65535) {
|
|
|
|
prd[i - 1].prd_count += n;
|
|
} else {
|
|
prd[i].prd_base = phys;
|
|
prd[i].prd_count = n;
|
|
i++;
|
|
if (i >= PRD_MAX_SEGS)
|
|
panic("wd82371: too many segments\n");
|
|
}
|
|
count -= n;
|
|
vaddr += n;
|
|
}
|
|
|
|
/* put a sign at the edge of the cliff... */
|
|
prd[(i>0) ? (i-1) : 0].prd_count |= PRD_EOT_BIT;
|
|
|
|
if (i == 0)
|
|
printf("ide_pci: dmasetup 0-length PRD???\n");
|
|
|
|
/* Set up PRD base register */
|
|
outl(iobase_bm + BMIDTP_PORT, vtophys(prd));
|
|
|
|
/* Set direction of transfer */
|
|
if (dir == B_READ) {
|
|
outb(iobase_bm + BMICOM_PORT, BMICOM_READ_WRITE);
|
|
} else {
|
|
outb(iobase_bm + BMICOM_PORT, 0);
|
|
}
|
|
|
|
/* Clear interrupt and error bits */
|
|
outb(iobase_bm + BMISTA_PORT,
|
|
(inb(iobase_bm + BMISTA_PORT)
|
|
| (BMISTA_INTERRUPT | BMISTA_DMA_ERROR)));
|
|
|
|
/* printf("dma enable: iobase_bm = %08x command/status = %08x pointer = %08x\n", iobase_bm, inl(iobase_bm + BMICOM_PORT), inl(iobase_bm + BMIDTP_PORT)); */
|
|
|
|
/* printf("P"); */
|
|
|
|
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);
|
|
|
|
/* printf("["); */
|
|
}
|
|
|
|
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);
|
|
|
|
/* printf("]"); */
|
|
|
|
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);
|
|
|
|
/* printf("dmastatus: iobase_bm = %08x status = %02x command/status = %08x pointer = %08x\n", iobase_bm, bmista, inl(iobase_bm + BMICOM_PORT), inl(iobase_bm + BMIDTP_PORT)); */
|
|
|
|
if (bmista & BMISTA_INTERRUPT)
|
|
status |= WDDS_INTERRUPT;
|
|
if (bmista & BMISTA_DMA_ERROR)
|
|
status |= WDDS_ERROR;
|
|
if (bmista & BMISTA_DMA_ACTIVE)
|
|
status |= WDDS_ACTIVE;
|
|
|
|
/* printf( (bmista == BMISTA_INTERRUPT)? "?":"!"); */
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif /* NPCI > 0 */
|