Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev Add an optionnal controller callback for channel res...



details:   https://anonhg.NetBSD.org/src/rev/0bca2edd899f
branches:  trunk
changeset: 583434:0bca2edd899f
user:      bouyer <bouyer%NetBSD.org@localhost>
date:      Sat Aug 06 22:07:24 2005 +0000

description:
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.

diffstat:

 sys/dev/ic/wdc.c              |  72 +++++++++++++++++++++---------------------
 sys/dev/ic/wdcvar.h           |   6 ++-
 sys/dev/pci/aceride.c         |  61 ++++++++++++++++++++++++++++++++++-
 sys/dev/pci/pciide_acer_reg.h |   6 ++-
 4 files changed, 104 insertions(+), 41 deletions(-)

diffs (282 lines):

diff -r 7ebb1ef33f1c -r 0bca2edd899f sys/dev/ic/wdc.c
--- a/sys/dev/ic/wdc.c  Sat Aug 06 17:58:13 2005 +0000
+++ b/sys/dev/ic/wdc.c  Sat Aug 06 22:07:24 2005 +0000
@@ -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 @@
        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 @@
                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 @@
        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 @@
        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)
 {
diff -r 7ebb1ef33f1c -r 0bca2edd899f sys/dev/ic/wdcvar.h
--- a/sys/dev/ic/wdcvar.h       Sat Aug 06 17:58:13 2005 +0000
+++ b/sys/dev/ic/wdcvar.h       Sat Aug 06 22:07:24 2005 +0000
@@ -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 @@
        /* 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   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*);
 
diff -r 7ebb1ef33f1c -r 0bca2edd899f sys/dev/pci/aceride.c
--- a/sys/dev/pci/aceride.c     Sat Aug 06 17:58:13 2005 +0000
+++ b/sys/dev/pci/aceride.c     Sat Aug 06 22:07:24 2005 +0000
@@ -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 @@
 #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  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 @@
 
 }
 
+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 @@
        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 @@
        }
 
        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++) {
@@ -173,6 +204,30 @@
 }
 
 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)
 {
        struct ata_drive_datas *drvp;
diff -r 7ebb1ef33f1c -r 0bca2edd899f sys/dev/pci/pciide_acer_reg.h
--- a/sys/dev/pci/pciide_acer_reg.h     Sat Aug 06 17:58:13 2005 +0000
+++ b/sys/dev/pci/pciide_acer_reg.h     Sat Aug 06 22:07:24 2005 +0000
@@ -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 ?



Home | Main Index | Thread Index | Old Index