summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2005-09-14 06:13:23 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2005-09-14 06:13:23 +0000
commit933f6db2ed357f6babdfc360c964cd94f010a09d (patch)
tree3bf6722f4acc464f7b17903c1d9c2019fc1df18c
parent6957b3ba1447ed2cdcf839e5ca9dd51674edd513 (diff)
Support for ATI IXP 200/300/400 IDE mostly based on changes
made by Quentin Garnier to NetBSD. tested by tdeval@ and ian@, ok grange@
-rw-r--r--sys/dev/pci/pciide.c150
-rw-r--r--sys/dev/pci/pciide_ixp_reg.h85
2 files changed, 234 insertions, 1 deletions
diff --git a/sys/dev/pci/pciide.c b/sys/dev/pci/pciide.c
index 6d6cc8f5ec8..e5bf52a04d7 100644
--- a/sys/dev/pci/pciide.c
+++ b/sys/dev/pci/pciide.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pciide.c,v 1.202 2005/09/05 13:21:49 jsg Exp $ */
+/* $OpenBSD: pciide.c,v 1.203 2005/09/14 06:13:22 jsg Exp $ */
/* $NetBSD: pciide.c,v 1.127 2001/08/03 01:31:08 tsutsui Exp $ */
/*
@@ -132,6 +132,7 @@ int wdcdebug_pciide_mask = WDCDEBUG_PCIIDE_MASK;
#include <dev/pci/pciide_nforce_reg.h>
#include <dev/pci/pciide_i31244_reg.h>
#include <dev/pci/pciide_ite_reg.h>
+#include <dev/pci/pciide_ixp_reg.h>
#include <dev/pci/cy82c693var.h>
/* inlines for reading/writing 8-bit PCI registers */
@@ -261,6 +262,9 @@ void artisea_chip_map(struct pciide_softc *, struct pci_attach_args *);
void ite_chip_map(struct pciide_softc *, struct pci_attach_args *);
void ite_setup_channel(struct channel_softc *);
+void ixp_chip_map(struct pciide_softc *, struct pci_attach_args *);
+void ixp_setup_channel(struct channel_softc *);
+
u_int8_t pciide_dmacmd_read(struct pciide_softc *, int);
void pciide_dmacmd_write(struct pciide_softc *, int, u_int8_t);
u_int8_t pciide_dmactl_read(struct pciide_softc *, int);
@@ -736,6 +740,18 @@ const struct pciide_product_desc pciide_ite_products[] = {
};
const struct pciide_product_desc pciide_ati_products[] = {
+ { PCI_PRODUCT_ATI_IXP_IDE_200,
+ 0,
+ ixp_chip_map
+ },
+ { PCI_PRODUCT_ATI_IXP_IDE_300,
+ 0,
+ ixp_chip_map
+ },
+ { PCI_PRODUCT_ATI_IXP_IDE_400,
+ 0,
+ ixp_chip_map
+ },
{ PCI_PRODUCT_ATI_IXP_SATA_400_1,
0,
sii3112_chip_map
@@ -7348,3 +7364,135 @@ pio:
pciide_print_modes(cp);
}
+
+void
+ixp_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
+{
+ struct pciide_channel *cp;
+ int channel;
+ pcireg_t interface = PCI_INTERFACE(pa->pa_class);
+ bus_size_t cmdsize, ctlsize;
+
+ if (pciide_chipen(sc, pa) == 0)
+ return;
+
+ printf(": DMA");
+ pciide_mapreg_dma(sc, pa);
+
+ sc->sc_wdcdev.cap = WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32 |
+ WDC_CAPABILITY_MODE;
+ if (sc->sc_dma_ok) {
+ sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA | WDC_CAPABILITY_UDMA;
+ sc->sc_wdcdev.cap |= WDC_CAPABILITY_IRQACK;
+ sc->sc_wdcdev.irqack = pciide_irqack;
+ }
+ sc->sc_wdcdev.PIO_cap = 4;
+ sc->sc_wdcdev.DMA_cap = 2;
+ sc->sc_wdcdev.UDMA_cap = 6;
+
+ sc->sc_wdcdev.set_modes = ixp_setup_channel;
+ sc->sc_wdcdev.channels = sc->wdc_chanarray;
+ sc->sc_wdcdev.nchannels = PCIIDE_NUM_CHANNELS;
+
+ pciide_print_channels(sc->sc_wdcdev.nchannels, interface);
+
+ for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) {
+ cp = &sc->pciide_channels[channel];
+ if (pciide_chansetup(sc, channel, interface) == 0)
+ continue;
+ pciide_map_compat_intr(pa, cp, channel, interface);
+ if (cp->hw_ok == 0)
+ continue;
+ pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize,
+ pciide_pci_intr);
+ if (cp->hw_ok == 0) {
+ pciide_unmap_compat_intr(pa, cp, channel, interface);
+ continue;
+ }
+ sc->sc_wdcdev.set_modes(&cp->wdc_channel);
+ }
+}
+
+void
+ixp_setup_channel(struct channel_softc *chp)
+{
+ struct ata_drive_datas *drvp;
+ int drive, mode;
+ u_int32_t idedma_ctl;
+ struct pciide_channel *cp = (struct pciide_channel*)chp;
+ struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc;
+ int channel = chp->channel;
+ pcireg_t udma, mdma_timing, pio, pio_timing;
+
+ pio_timing = pci_conf_read(sc->sc_pc, sc->sc_tag, IXP_PIO_TIMING);
+ pio = pci_conf_read(sc->sc_pc, sc->sc_tag, IXP_PIO_CTL);
+ mdma_timing = pci_conf_read(sc->sc_pc, sc->sc_tag, IXP_MDMA_TIMING);
+ udma = pci_conf_read(sc->sc_pc, sc->sc_tag, IXP_UDMA_CTL);
+
+ /* Setup DMA if needed */
+ pciide_channel_dma_setup(cp);
+
+ /* Per channel settings */
+ for (drive = 0; drive < 2; drive++) {
+ drvp = &chp->ch_drive[drive];
+
+ /* If no drive, skip */
+ if ((drvp->drive_flags & DRIVE) == 0)
+ continue;
+ if ((chp->wdc->cap & WDC_CAPABILITY_UDMA) != 0 &&
+ (drvp->drive_flags & DRIVE_UDMA) != 0) {
+ /* Setup UltraDMA mode */
+ idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive);
+ IXP_UDMA_ENABLE(udma, chp->channel, drive);
+ IXP_SET_MODE(udma, chp->channel, drive,
+ drvp->UDMA_mode);
+ mode = drvp->PIO_mode;
+ } else if ((chp->wdc->cap & WDC_CAPABILITY_DMA) != 0 &&
+ (drvp->drive_flags & DRIVE_DMA) != 0) {
+ /* Setup multiword DMA mode */
+ drvp->drive_flags &= ~DRIVE_UDMA;
+ idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive);
+ IXP_UDMA_DISABLE(udma, chp->channel, drive);
+ IXP_SET_TIMING(mdma_timing, chp->channel, drive,
+ ixp_mdma_timings[drvp->DMA_mode]);
+
+ /* mode = min(pio, dma + 2) */
+ if (drvp->PIO_mode <= (drvp->DMA_mode + 2))
+ mode = drvp->PIO_mode;
+ else
+ mode = drvp->DMA_mode + 2;
+ } else {
+ mode = drvp->PIO_mode;
+ }
+
+ /* Setup PIO mode */
+ drvp->PIO_mode = mode;
+ if (mode < 2)
+ drvp->DMA_mode = 0;
+ else
+ drvp->DMA_mode = mode - 2;
+ /*
+ * Set PIO mode and timings
+ * Linux driver avoids PIO mode 1, let's do it too.
+ */
+ if (drvp->PIO_mode == 1)
+ drvp->PIO_mode = 0;
+
+ IXP_SET_MODE(pio, chp->channel, drive, drvp->PIO_mode);
+ IXP_SET_TIMING(pio_timing, chp->channel, drive,
+ ixp_pio_timings[drvp->PIO_mode]);
+ }
+
+ pci_conf_write(sc->sc_pc, sc->sc_tag, IXP_UDMA_CTL, udma);
+ pci_conf_write(sc->sc_pc, sc->sc_tag, IXP_MDMA_TIMING, mdma_timing);
+ pci_conf_write(sc->sc_pc, sc->sc_tag, IXP_PIO_CTL, pio);
+ pci_conf_write(sc->sc_pc, sc->sc_tag, IXP_PIO_TIMING, pio_timing);
+
+ if (idedma_ctl != 0) {
+ /* Add software bits in status register */
+ bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh,
+ IDEDMA_CTL(channel), idedma_ctl);
+ }
+
+ pciide_print_modes(cp);
+}
diff --git a/sys/dev/pci/pciide_ixp_reg.h b/sys/dev/pci/pciide_ixp_reg.h
new file mode 100644
index 00000000000..1f04f07deed
--- /dev/null
+++ b/sys/dev/pci/pciide_ixp_reg.h
@@ -0,0 +1,85 @@
+/* $OpenBSD: pciide_ixp_reg.h,v 1.1 2005/09/14 06:13:22 jsg Exp $ */
+/* $NetBSD: pciide_ixp_reg.h,v 1.2 2005/02/27 00:27:33 perry Exp $ */
+
+/*
+ * Copyright (c) 2004 The NetBSD Foundation.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to the NetBSD Foundation
+ * by Quentin Garnier.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/* All values gathered from the linux driver. */
+
+#define IXP_PIO_TIMING 0x40
+#define IXP_MDMA_TIMING 0x44
+#define IXP_PIO_CTL 0x48
+#define IXP_PIO_MODE 0x4a
+#define IXP_UDMA_CTL 0x54
+#define IXP_UDMA_MODE 0x56
+
+static const uint8_t ixp_pio_timings[] = {
+ 0x5d, 0x47, 0x34, 0x22, 0x20
+};
+
+static const uint8_t ixp_mdma_timings[] = {
+ 0x77, 0x21, 0x20
+};
+
+/* First 4 bits of UDMA_CTL enable or disable UDMA for the drive */
+#define IXP_UDMA_ENABLE(u, c, d) do { \
+ (u) |= (1 << (2 * (c) + (d))); \
+ } while (0)
+#define IXP_UDMA_DISABLE(u, c, d) do { \
+ (u) &= ~(1 << (2 * (c) + (d))); \
+ } while (0)
+
+/*
+ * UDMA_MODE has 4 bits per drive, though only 3 are actually used
+ * Note that in this macro u is the whole
+ * UDMA_CTL+UDMA_MODE register (32bits).
+ * PIO_MODE works just the same.
+ */
+#define IXP_SET_MODE(u, c, d, m) do { \
+ int __ixpshift = 16 + 8*(c) + 4*(d); \
+ (u) &= ~(0x7 << __ixpshift); \
+ (u) |= (((m) & 0x7) << __ixpshift); \
+ } while (0)
+
+/*
+ * MDMA_TIMING has one byte per drive.
+ * PIO_TIMING works just the same.
+ */
+#define IXP_SET_TIMING(m, c, d, t) do { \
+ int __ixpshift = 16*(c) + 8*(d); \
+ (m) &= ~(0xff << __ixpshift); \
+ (m) |= ((t) & 0xff) << __ixpshift; \
+ } while (0)