diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2005-09-14 06:13:23 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2005-09-14 06:13:23 +0000 |
commit | 933f6db2ed357f6babdfc360c964cd94f010a09d (patch) | |
tree | 3bf6722f4acc464f7b17903c1d9c2019fc1df18c | |
parent | 6957b3ba1447ed2cdcf839e5ca9dd51674edd513 (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.c | 150 | ||||
-rw-r--r-- | sys/dev/pci/pciide_ixp_reg.h | 85 |
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) |