Add an optionnal controller callback for channel reset. If the callback

is set to NULL, use the generic reset code.
Use this to work around a bug in some Acer IDE controllers (like the
one found in some sparc systems) where a controller disable/enable
is required after a reset to avoid data corruption when Ultra-DMA is
used. Workaround from opensolaris, thanks to Hiroki Sato for testing.
This commit is contained in:
bouyer 2005-08-06 22:07:24 +00:00
parent c1494c99c6
commit d1278fcba8
4 changed files with 104 additions and 41 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: wdc.c,v 1.224 2005/06/19 18:14:27 bouyer Exp $ */
/* $NetBSD: wdc.c,v 1.225 2005/08/06 22:07:24 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001, 2003 Manuel Bouyer. All rights reserved.
@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.224 2005/06/19 18:14:27 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.225 2005/08/06 22:07:24 bouyer Exp $");
#ifndef ATADEBUG
#define ATADEBUG
@ -613,23 +613,10 @@ wdcprobe1(struct ata_channel *chp, int poll)
delay(5000);
#endif
if (wdc->select)
wdc->select(chp,0);
bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, WDSD_IBM);
delay(10); /* 400ns delay */
/* assert SRST, wait for reset to complete */
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_RST | WDCTL_IDS | WDCTL_4BIT);
DELAY(1000);
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_IDS | WDCTL_4BIT);
wdc->reset(chp, RESET_POLL);
DELAY(2000);
(void) bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_error], 0);
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT);
delay(10); /* 400ns delay */
/* ACK interrupt in case there is one pending left (Promise ATA100) */
if (wdc->irqack != NULL)
wdc->irqack(chp);
splx(s);
ret_value = __wdcwait_reset(chp, ret_value, poll);
@ -703,6 +690,9 @@ wdcattach(struct ata_channel *chp)
wdc->datain_pio = wdc_datain_pio;
if (wdc->dataout_pio == NULL)
wdc->dataout_pio = wdc_dataout_pio;
/* default reset method */
if (wdc->reset == NULL)
wdc->reset = wdc_do_reset;
/* initialise global data */
if (atac->atac_bustype_ata == NULL)
@ -948,27 +938,8 @@ wdcreset(struct ata_channel *chp, int poll)
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct wdc_regs *wdr = &wdc->regs[chp->ch_channel];
int drv_mask1, drv_mask2;
int s = 0;
if (wdc->select)
wdc->select(chp,0);
if (poll != RESET_SLEEP)
s = splbio();
/* master */
bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, WDSD_IBM);
delay(10); /* 400ns delay */
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_RST | WDCTL_IDS | WDCTL_4BIT);
delay(2000);
(void) bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_error], 0);
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_4BIT | WDCTL_IDS);
delay(10); /* 400ns delay */
if (poll != RESET_SLEEP) {
if (wdc->irqack)
wdc->irqack(chp);
splx(s);
}
wdc->reset(chp, poll);
drv_mask1 = (chp->ch_drive[0].drive_flags & DRIVE) ? 0x01:0x00;
drv_mask1 |= (chp->ch_drive[1].drive_flags & DRIVE) ? 0x02:0x00;
@ -987,6 +958,35 @@ wdcreset(struct ata_channel *chp, int poll)
return (drv_mask1 != drv_mask2) ? 1 : 0;
}
void
wdc_do_reset(struct ata_channel *chp, int poll)
{
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct wdc_regs *wdr = &wdc->regs[chp->ch_channel];
int s = 0;
if (poll != RESET_SLEEP)
s = splbio();
if (wdc->select)
wdc->select(chp,0);
/* master */
bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, WDSD_IBM);
delay(10); /* 400ns delay */
/* assert SRST, wait for reset to complete */
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_RST | WDCTL_IDS | WDCTL_4BIT);
delay(2000);
(void) bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_error], 0);
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr,
WDCTL_4BIT | WDCTL_IDS);
delay(10); /* 400ns delay */
if (poll != RESET_SLEEP) {
if (wdc->irqack)
wdc->irqack(chp);
splx(s);
}
}
static int
__wdcwait_reset(struct ata_channel *chp, int drv_mask, int poll)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: wdcvar.h,v 1.82 2005/03/02 12:25:28 mycroft Exp $ */
/* $NetBSD: wdcvar.h,v 1.83 2005/08/06 22:07:24 bouyer Exp $ */
/*-
* Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
@ -101,6 +101,9 @@ struct wdc_softc {
/* Optional callback to ack IRQ. */
void (*irqack)(struct ata_channel *);
/* Optional callback to perform a bus reset */
void (*reset)(struct ata_channel *, int);
/* overridden if the backend has a different data transfer method */
void (*datain_pio)(struct ata_channel *, int, void *, size_t);
void (*dataout_pio)(struct ata_channel *, int, void *, size_t);
@ -144,6 +147,7 @@ void wdccommandshort(struct ata_channel *, int, int);
void wdctimeout(void *arg);
void wdc_reset_drive(struct ata_drive_datas *, int);
void wdc_reset_channel(struct ata_channel *, int);
void wdc_do_reset(struct ata_channel *, int);
int wdc_exec_command(struct ata_drive_datas *, struct ata_command*);

View File

@ -1,4 +1,4 @@
/* $NetBSD: aceride.c,v 1.15 2005/05/24 05:25:15 lukem Exp $ */
/* $NetBSD: aceride.c,v 1.16 2005/08/06 22:07:24 bouyer Exp $ */
/*
* Copyright (c) 1999, 2000, 2001 Manuel Bouyer.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: aceride.c,v 1.15 2005/05/24 05:25:15 lukem Exp $");
__KERNEL_RCSID(0, "$NetBSD: aceride.c,v 1.16 2005/08/06 22:07:24 bouyer Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -41,6 +41,8 @@ __KERNEL_RCSID(0, "$NetBSD: aceride.c,v 1.15 2005/05/24 05:25:15 lukem Exp $");
#include <dev/pci/pciidevar.h>
#include <dev/pci/pciide_acer_reg.h>
static int acer_pcib_match(struct pci_attach_args *);
static void acer_do_reset(struct ata_channel *, int);
static void acer_chip_map(struct pciide_softc*, struct pci_attach_args*);
static void acer_setup_channel(struct ata_channel*);
static int acer_pci_intr(void *);
@ -48,7 +50,12 @@ static int acer_pci_intr(void *);
static int aceride_match(struct device *, struct cfdata *, void *);
static void aceride_attach(struct device *, struct device *, void *);
CFATTACH_DECL(aceride, sizeof(struct pciide_softc),
struct aceride_softc {
struct pciide_softc pciide_sc;
struct pci_attach_args pcib_pa;
};
CFATTACH_DECL(aceride, sizeof(struct aceride_softc),
aceride_match, aceride_attach, NULL, NULL);
static const struct pciide_product_desc pciide_acer_products[] = {
@ -89,6 +96,21 @@ aceride_attach(struct device *parent, struct device *self, void *aux)
}
static int
acer_pcib_match(struct pci_attach_args *pa)
{
/*
* we need to access the PCI config space of the pcib, see
* acer_do_reset()
*/
if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA &&
PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M1543)
return 1;
return 0;
}
static void
acer_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
{
@ -97,6 +119,7 @@ acer_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
pcireg_t cr, interface;
bus_size_t cmdsize, ctlsize;
pcireg_t rev = PCI_REVISION(pa->pa_class);
struct aceride_softc *acer_sc = (struct aceride_softc *)sc;
if (pciide_chipen(sc, pa) == 0)
return;
@ -154,6 +177,14 @@ acer_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
}
wdc_allocate_regs(&sc->sc_wdcdev);
if (rev == 0xC3) {
/* install reset bug workaround */
if (pci_find_device(&acer_sc->pcib_pa, acer_pcib_match) == 0) {
printf("%s: WARNING: can't find pci-isa bridge\n",
sc->sc_wdcdev.sc_atac.atac_dev.dv_xname);
} else
sc->sc_wdcdev.reset = acer_do_reset;
}
for (channel = 0; channel < sc->sc_wdcdev.sc_atac.atac_nchannels;
channel++) {
@ -172,6 +203,30 @@ acer_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
}
}
static void
acer_do_reset(struct ata_channel *chp, int poll)
{
struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
struct aceride_softc *acer_sc = (struct aceride_softc *)sc;
u_int8_t reg;
/*
* From OpenSolaris: after a reset we need to disable/enable the
* corresponding channel, or data corruption will occur in
* UltraDMA modes
*/
wdc_do_reset(chp, poll);
reg = pciide_pci_read(acer_sc->pcib_pa.pa_pc, acer_sc->pcib_pa.pa_tag,
ACER_PCIB_CTRL);
printf("acer_do_reset reg 0x%x\n", reg);
pciide_pci_write(acer_sc->pcib_pa.pa_pc, acer_sc->pcib_pa.pa_tag,
ACER_PCIB_CTRL, reg & ACER_PCIB_CTRL_ENCHAN(chp->ch_channel));
delay(1000);
pciide_pci_write(acer_sc->pcib_pa.pa_pc, acer_sc->pcib_pa.pa_tag,
ACER_PCIB_CTRL, reg);
}
static void
acer_setup_channel(struct ata_channel *chp)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: pciide_acer_reg.h,v 1.8 2005/02/27 00:27:33 perry Exp $ */
/* $NetBSD: pciide_acer_reg.h,v 1.9 2005/08/06 22:07:24 bouyer Exp $ */
/*
* Copyright (c) 1999 Manuel Bouyer.
@ -90,6 +90,10 @@
#define ACER_0x79_REVC2_EN 0x4
#define ACER_0x79_EN 0x2
/* OpenSolaris: channel enable/disable in the PCI-ISA bridge */
#define ACER_PCIB_CTRL 0x58
#define ACER_PCIB_CTRL_ENCHAN(chan) (0x4 << (chan))
/*
* IDE bus frequency (1 byte)
* This should be setup by the BIOS - can we rely on this ?