Index: sys/conf/files =================================================================== RCS file: /cvsroot/src/sys/conf/files,v retrieving revision 1.944 diff -u -r1.944 files --- sys/conf/files 12 Mar 2009 00:15:07 -0000 1.944 +++ sys/conf/files 19 Apr 2009 22:10:28 -0000 @@ -1292,6 +1292,11 @@ # device-mapper driver for LVM include "dev/dm/files.dm" +# Initio INIC SATA controllers +define inicsata_core +file dev/ic/inicsata_core.c inicsata_core +device inicsata: ata, ata_dma, ata_udma, sata, inicsata_core + # # File systems # Index: sys/dev/pci/files.pci =================================================================== RCS file: /cvsroot/src/sys/dev/pci/files.pci,v retrieving revision 1.312 diff -u -r1.312 files.pci --- sys/dev/pci/files.pci 16 Jan 2009 21:11:27 -0000 1.312 +++ sys/dev/pci/files.pci 19 Apr 2009 22:10:38 -0000 @@ -969,3 +969,7 @@ # attach bwi at pci with bwi_pci file dev/pci/if_bwi_pci.c bwi_pci + +# Initio INIC-1622 SATA Controller +attach inicsata at pci with inicsata_pci +file dev/pci/inicsata_pci.c inicsata_pci --- /dev/null 2009-04-19 23:15:36.000000000 +0100 +++ share/man/man4/inicsata.4 2009-04-04 23:47:42.000000000 +0100 @@ -0,0 +1,66 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2009 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Matt Fleming. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd March 1, 2009 +.Dt INICSATA 4 +.Os +.Sh NAME +.Nm inicsata +.Nd Initio INIC-1622 Serial ATA disk controller driver +.Sh SYNOPSIS +.Cd "inicsata* at pci? dev ? function ? +.Sh DESCRIPTION +The +.Nm +driver supports the Initio INIC-1622 Serial ATA controller +and provides the interface with the hardware for +the +.Xr ata 4 +driver. +.Pp +.Sh SEE ALSO +.Xr ata 4 , +.Xr atapi 4 , +.Xr intro 4 , +.Xr pci 4 , +.Xr wd 4 , +.Xr wdc 4 +.Sh HISTORY +The +.Nm +driver appeared in +.Nx 5.0 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Matt Fleming +.Aq mjf@NetBSD.org . +.Sh BUGS +Native Command Queuing is not yet supported. --- /dev/null 2009-04-19 23:15:36.000000000 +0100 +++ sys/dev/ic/inicsata_core.c 2009-04-19 22:12:53.000000000 +0100 @@ -0,0 +1,1586 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Fleming. + * + * 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. + * + * 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. + */ + +/* + * References: INIC-1622 - PCI to Serial ATA Host Adapter IC, + * Version 1.0, September 2003. Available at + * http://www.innortech.com/pdf/INIC1622.PDF + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include /* for SCSI status */ +#include "atapibus.h" + +#define ATA_DELAY 100000 + +#ifdef INICSATA_DEBUG_FUNC +#define FUNC_ENTRY printf("%s:%d\n", __func__, __LINE__); +#else +#define FUNC_ENTRY +#endif + +#ifdef INICSATA_DEBUG_CMD +#define CMD_PRINTF(arg) printf arg +#else +#define CMD_PRINTF(arg) +#endif + +#ifdef INICSATA_DEBUG_DMA +#define DMA_PRINTF(arg) printf arg +#else +#define DMA_PRINTF(arg) +#endif + +#ifdef INICSATA_DEBUG_BIO +#define BIO_PRINTF(arg) printf arg +#else +#define BIO_PRINTF(arg) +#endif + +#ifdef INICSATA_DEBUG_INTR +#define INTR_PRINTF(arg) printf arg +#else +#define INTR_PRINTF(arg) +#endif + +void inicsata_enable_intrs(struct inicsata_softc *); +int inicsata_ata_bio(struct ata_drive_datas *, struct ata_bio *); +void inicsata_reset_drive(struct ata_drive_datas *, int); +void inicsata_reset_channel(struct ata_channel *, int); +int inicsata_exec_command(struct ata_drive_datas *, struct ata_command *); +int inicsata_ata_addref(struct ata_drive_datas *); +void inicsata_ata_delref(struct ata_drive_datas *); +void inicsata_atapi_kill_pending(struct scsipi_periph *); +void inicsata_killpending(struct ata_drive_datas *); +void inicsata_setup_channel(struct ata_channel *); +void inicsata_channel_stop(struct inicsata_softc *, struct ata_channel *); +void inicsata_cmd_start(struct ata_channel *, struct ata_xfer *); +int inicsata_cmd_complete(struct ata_channel *, struct ata_xfer *, int); +void inicsata_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *, int); +int inicsata_dma_setup(struct ata_channel *, void *, size_t, int, + struct inicsata_prd *, bool); +void inicsata_cmd_done(struct ata_channel *, struct ata_xfer *); +void inicsata_timeout(void *); +void inicsata_bio_start(struct ata_channel *, struct ata_xfer *); +int inicsata_bio_complete(struct ata_channel *, struct ata_xfer *, int); +void inicsata_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int); +void inicsata_probe_drive(struct ata_channel *); +void inicsata_reset(struct inicsata_softc *); +static void _inicsata_setup_secret_prdtbl(struct inicsata_softc *, + struct inicsata_channel *); +static void inicsata_activate_xfer(struct inicsata_softc *, int); + +#if NATAPIBUS > 0 +void inicsata_atapibus_attach(struct atabus_softc *); +void inicsata_atapi_probe_device(struct atapibus_softc *, int); +void inicsata_atapi_minphys(struct buf *); +void inicsata_atapi_start(struct ata_channel *, struct ata_xfer *); +int inicsata_atapi_complete(struct ata_channel *, struct ata_xfer *, int); +void inicsata_atapi_kill_xfer(struct ata_channel *, struct ata_xfer *, int); +void inicsata_atapi_done(struct ata_channel *, struct ata_xfer *); +void inicsata_atapi_reset(struct ata_channel *, struct ata_xfer *); +void inicsata_atapi_scsipi_request(struct scsipi_channel *, + scsipi_adapter_req_t, void *); +void inicsata_atapi_kill_pending(struct scsipi_periph *); +#endif /* NATAPIBUS */ + + +#if NATAPIBUS > 0 +static const struct scsipi_bustype inicsata_atapi_bustype = { + SCSIPI_BUSTYPE_ATAPI, + atapi_scsipi_cmd, + atapi_interpret_sense, + atapi_print_addr, + inicsata_atapi_kill_pending, +}; +#endif /* NATAPIBUS */ + +const struct ata_bustype inicsata_ata_bustype = { + SCSIPI_BUSTYPE_ATA, + inicsata_ata_bio, + inicsata_reset_drive, + inicsata_reset_channel, + inicsata_exec_command, + ata_get_params, + inicsata_ata_addref, + inicsata_ata_delref, + inicsata_killpending, +}; + + +static void +inicsata_setup_port(struct inicsata_softc *sc, int i) +{ + struct inicsata_channel *ichp = &sc->sc_channels[i]; + + /*XXXMJF memsets needed? */ + memset(ichp->ic_descr, 0, INICSATA_DESCRIPTOR_SIZE); + memset(ichp->ic_cpb_tbl, 0, INICSATA_CPBTBL_SIZE); + + printf("%s: 0x%lx and 0x%lx\n", __func__, + ichp->ic_bus_cpb_tbl, ichp->ic_bus_prdtbl); + + IS_WRITE4(sc, IS_P_PRDTP(i), ichp->ic_bus_prdtbl); + IS_WRITE4(sc, IS_P_CPBLAR(i), ichp->ic_bus_cpb_tbl); +} + +void +inicsata_enable_intrs(struct inicsata_softc *sc) +{ + int i; + uint32_t gctrl; + + gctrl = IS_READ2(sc, IS_GCTRL); + gctrl &= ~(IS_GC_GINTDIS|IS_GC_PWRDWN|IS_GC_RPGSEL); + + /* Soft reset */ + IS_WRITE2(sc, IS_GCTRL, gctrl | IS_GC_SOFTRST); + IS_READ2(sc, IS_GCTRL); /* flush */ + + for (i = 0; i < 10; i++) { + DELAY(1000); + gctrl = IS_READ2(sc, IS_GCTRL); + if ((gctrl & IS_GC_SOFTRST) == 0) + break; + } + + /* mask port IRQs (we only use the global interrupt) */ + for (i = 0; i < INICSATA_MAX_PORTS; i++) { + IS_WRITE1(sc, IS_P_MINTSTAT(i), 0xff); + IS_READ2(sc, IS_P_IDMAC(i)); /* flush */ + DELAY(1000); + IS_WRITE2(sc, IS_P_IDMAC(i), IS_P_ID_RSTADM); + IS_READ2(sc, IS_P_IDMAC(i)); /* flush */ + DELAY(1000); + IS_WRITE2(sc, IS_P_IDMAC(i), 0); + + /* clear port interrupt register */ + IS_WRITE1(sc, IS_P_IS(i), 0xff); + IS_WRITE1(sc, IS_P_MINTSTAT(i),IS_P_IX_MCHUIRQ|IS_P_IX_MCHQINT); + } + + /* enable global interrupt */ + IS_WRITE2(sc, IS_GCTRL, gctrl & ~IS_GC_GINTDIS); + IS_WRITE2(sc, IS_GIMSK, + IS_READ2(sc, IS_GIMSK) & ~(IS_GM_MCH0INT | IS_GM_MCH1INT)); +} + +void +inicsata_reset(struct inicsata_softc *sc) +{ + int i; + + FUNC_ENTRY; + for (i = 0; i < INICSATA_MAX_PORTS; i++) { + IS_WRITE2(sc, IS_P_IDMAC(i), IS_P_ID_RSTA); + IS_READ2(sc, IS_P_IDMAC(i)); /* flush */ + } + + delay(1000); + + for (i = 0; i < INICSATA_MAX_PORTS; i++) + IS_WRITE2(sc, IS_P_IDMAC(i), 0); +} + +static void +_inicsata_setup_secret_prdtbl(struct inicsata_softc *sc, + struct inicsata_channel *ichp) +{ + bus_dma_segment_t seg; + int error; + int rseg; + int dmasize; + void *prdtblp; + + dmasize = 2048; /* XXXMJF SSSH! Stolen from linux */ + error = bus_dmamem_alloc(sc->sc_dmat, dmasize, + PAGE_SIZE, INICSATA_DMA_BOUNDARY, + &seg, 1, &rseg, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't allocate memory for command table\n"); + return; + } + + error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + dmasize, &prdtblp, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't map command table memory\n"); + return; + } + + error = bus_dmamap_create(sc->sc_dmat, + dmasize, 1, dmasize, INICSATA_DMA_BOUNDARY, + BUS_DMA_NOWAIT, &ichp->ic_prdtbld); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't create command table map\n"); + return; + } + + error = bus_dmamap_load(sc->sc_dmat, ichp->ic_prdtbld, + prdtblp, dmasize, NULL, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't load command table map\n"); + return; + } + + ichp->ic_bus_prdtbl = ichp->ic_prdtbld->dm_segs[0].ds_addr; + ichp->ic_prdtbl = prdtblp; +} + +void +inicsata_attach(struct inicsata_softc *sc) +{ + struct inicsata_channel *ichp; + struct ata_channel *chp; + int i, port, error; + bus_dma_segment_t seg; + int rseg; + int dmasize; + void *cpb_tblp; + void *descrp; + + inicsata_enable_intrs(sc); + + sc->sc_atac.atac_cap = ATAC_CAP_DMA | ATAC_CAP_UDMA; + sc->sc_atac.atac_pio_cap = 4; + sc->sc_atac.atac_nchannels = 2; + sc->sc_atac.atac_dma_cap = 2; + sc->sc_atac.atac_udma_cap = 6; + sc->sc_atac.atac_channels = sc->sc_chanarray; + sc->sc_atac.atac_probe = inicsata_probe_drive; + sc->sc_atac.atac_bustype_ata = &inicsata_ata_bustype; + sc->sc_atac.atac_set_modes = inicsata_setup_channel; +#if NATAPIBUS > 0 + sc->sc_atac.atac_atapibus_attach = inicsata_atapibus_attach; +#endif + + for (i = 0, port = 0; i < INICSATA_MAX_PORTS; i++) { + ichp = &sc->sc_channels[i]; + chp = (struct ata_channel *)ichp; + sc->sc_chanarray[i] = chp; + chp->ch_channel = i; + chp->ch_atac = &sc->sc_atac; + + chp->ch_queue = + kmem_alloc(sizeof(struct ata_queue), KM_NOSLEEP); + if (chp->ch_queue == NULL) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't allocate memory for command queue\n"); + break; + } + + dmasize = INICSATA_CPBTBL_SIZE; + error = bus_dmamem_alloc(sc->sc_dmat, dmasize, + PAGE_SIZE, INICSATA_DMA_BOUNDARY, + &seg, 1, &rseg, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't allocate memory for command table\n"); + break; + } + + error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + dmasize, &cpb_tblp, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't map command table memory\n"); + break; + } + + error = bus_dmamap_create(sc->sc_dmat, + dmasize, 1, dmasize, INICSATA_DMA_BOUNDARY, + BUS_DMA_NOWAIT, &ichp->ic_cpb_tbld); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't create command table map\n"); + break; + } + + error = bus_dmamap_load(sc->sc_dmat, ichp->ic_cpb_tbld, + cpb_tblp, dmasize, NULL, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't load command table map\n"); + break; + } + + ichp->ic_cpb_tbl = cpb_tblp; + ichp->ic_bus_cpb_tbl = + ichp->ic_cpb_tbld->dm_segs[0].ds_addr; + + dmasize = INICSATA_DESCRIPTOR_SIZE; + error = bus_dmamem_alloc(sc->sc_dmat, dmasize, + PAGE_SIZE, INICSATA_DMA_BOUNDARY, &seg, + 1, &rseg, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't allocate memory for command table\n"); + break; + } + + error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + dmasize, &descrp, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't map command table memory\n"); + break; + } + + error = bus_dmamap_create(sc->sc_dmat, + dmasize, 1, dmasize, INICSATA_DMA_BOUNDARY, + BUS_DMA_NOWAIT, &ichp->ic_descrd); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't create command table map\n"); + break; + } + + error = bus_dmamap_load(sc->sc_dmat, ichp->ic_descrd, + descrp, dmasize, NULL, BUS_DMA_NOWAIT); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't load command table map\n"); + break; + } + + ichp->ic_bus_descr = ichp->ic_descrd->dm_segs[0].ds_addr; + ichp->ic_descr = descrp; + + /* The xfer DMA map */ + error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, + INICSATA_NPRD, INICSATA_DMA_MAXSEGSZ, + INICSATA_DMA_BOUNDARY, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &ichp->ic_datad); + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "can't create memory for CDB\n"); + break; + } + + _inicsata_setup_secret_prdtbl(sc, ichp); + + inicsata_setup_port(sc, i); + chp->ch_ndrive = 1; + + if (bus_space_subregion(sc->sc_it, sc->sc_ih, IS_P_SSTS(i), + 4, &ichp->ic_sstatus) != 0) { + aprint_error_dev(sc->sc_atac.atac_dev, + "couldn't map channel %d status reg\n", i); + break; + } + + if (bus_space_subregion(sc->sc_it, sc->sc_ih, IS_P_SCTRL(i), + 4, &ichp->ic_scontrol) != 0) { + aprint_error_dev(sc->sc_atac.atac_dev, + "couldn't map channel %d control reg\n", i); + break; + } + + if (bus_space_subregion(sc->sc_it, sc->sc_ih, IS_P_SERR(i), + 4, &ichp->ic_serror) != 0) { + aprint_error_dev(sc->sc_atac.atac_dev, + "couldn't map channel %d error reg\n", i); + break; + } + + ata_channel_attach(chp); + port++; + continue; + } + + FUNC_ENTRY; + return; +} + +int +inicsata_intr(void *v) +{ + struct inicsata_softc *sc = v; + uint32_t is; + int i, r = 0; + + while ((is = IS_READ4(sc, IS_GIS))) { + r = 1; + for (i = 0; i < INICSATA_MAX_PORTS; i++) { + if (is & (IS_P0 << i)) + inicsata_intr_port(sc, &sc->sc_channels[i]); + } + } + + return r; +} + +void +inicsata_intr_port(struct inicsata_softc *sc, struct inicsata_channel *ichp) +{ + uint32_t is, idma_is, error; + struct ata_channel *chp = &ichp->ata_channel; + struct ata_xfer *xfer = chp->ch_queue->active_xfer; + + /* Clear IRQ status */ + is = IS_READ1(sc, IS_P_IS(chp->ch_channel)); + IS_WRITE1(sc, IS_P_IS(chp->ch_channel), is); + + idma_is = IS_READ2(sc, IS_P_IDMAS(chp->ch_channel)); + + if ((is & (IS_P_IX_CHOFF | IS_P_IX_CHON | IS_P_IX_FTLINT)) || + (idma_is & (IS_P_ID_PERR | IS_P_ID_CPBERR))) { + printf("is_err=0x%x, idma_err=0x%x\n", is, idma_is); + inicsata_channel_stop(sc, chp); + } else if (idma_is & IS_P_ID_DONE) { + /* XXXMJF I know, this is weird */ + inicsata_channel_stop(sc, chp); + + error = IS_READ1(sc, IS_P_TF_CMD(chp->ch_channel)); + if (error & (WDCS_DWF | WDCS_ERR)) + chp->ch_flags |= error; + + xfer->c_intr(chp, xfer, 0); + } +} + +void +inicsata_reset_drive(struct ata_drive_datas *drvp, int flags) +{ + struct ata_channel *chp = drvp->chnl_softc; + ata_reset_channel(chp, flags); + return; +} + +void +inicsata_reset_channel(struct ata_channel *chp, int flags) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + + inicsata_channel_stop(sc, chp); + + if (sata_reset_interface(chp, sc->sc_it, ichp->ic_scontrol, + ichp->ic_sstatus) != SStatus_DET_DEV) { + aprint_error_dev(sc->sc_atac.atac_dev, + "port reset failed\n"); + /* XXX and then? */ + } + + /* clear port interrupt register */ + IS_WRITE1(sc, IS_P_IS(chp->ch_channel), 0xff); + IS_WRITE1(sc, IS_P_MINTSTAT(chp->ch_channel), 0xff); + return; +} + +int +inicsata_ata_addref(struct ata_drive_datas *drvp) +{ + return 0; +} + +void +inicsata_ata_delref(struct ata_drive_datas *drvp) +{ +} + +void +inicsata_killpending(struct ata_drive_datas *drvp) +{ +} + +void +inicsata_probe_drive(struct ata_channel *chp) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + int i, s; + + /* XXX This should be done by other code. */ + for (i = 0; i < chp->ch_ndrive; i++) { + chp->ch_drive[i].chnl_softc = chp; + chp->ch_drive[i].drive = i; + } + + switch (sata_reset_interface(chp, sc->sc_it, ichp->ic_scontrol, + ichp->ic_sstatus)) { + case SStatus_DET_DEV: + s = splbio(); + chp->ch_drive[0].drive_flags |= DRIVE_ATA; + splx(s); + + break; + default: + break; + } +} + +void +inicsata_setup_channel(struct ata_channel *chp) +{ + return; +} + +int +inicsata_exec_command(struct ata_drive_datas *drvp, struct ata_command *ata_c) +{ + struct ata_channel *chp = drvp->chnl_softc; + struct ata_xfer *xfer; + int s, rv; + + FUNC_ENTRY; + xfer = ata_get_xfer(ata_c->flags & AT_WAIT ? ATAXF_CANSLEEP : + ATAXF_NOSLEEP); + if (xfer == NULL) + return ATACMD_TRY_AGAIN; + + if (ata_c->flags & AT_POLL) + xfer->c_flags |= C_POLL; + if (ata_c->flags & AT_WAIT) + xfer->c_flags |= C_WAIT; + + xfer->c_drive = drvp->drive; + xfer->c_databuf = ata_c->data; + xfer->c_bcount = ata_c->bcount; + xfer->c_cmd = ata_c; + xfer->c_start = inicsata_cmd_start; + xfer->c_intr = inicsata_cmd_complete; + xfer->c_kill_xfer = inicsata_cmd_kill_xfer; + + s = splbio(); + ata_exec_xfer(chp, xfer); + + if (ata_c->flags & AT_DONE) + rv = ATACMD_COMPLETE; + else { + if (ata_c->flags & AT_WAIT) { + while ((ata_c->flags & AT_DONE) == 0) + tsleep(ata_c, PRIBIO, "inicsatacmd", 0); + + rv = ATACMD_COMPLETE; + } else + rv = ATACMD_QUEUED; + } + + splx(s); + return rv; +} + +static void +inicsata_activate_xfer(struct inicsata_softc *sc, int channel) +{ + struct inicsata_channel *ichp = &sc->sc_channels[channel]; + + ichp->ic_cpb_tbl[0] = ichp->ic_descrd->dm_segs[0].ds_addr; + IS_CPBTBL_SYNC(sc, ichp, BUS_DMASYNC_PREWRITE); + + IS_WRITE2(sc, IS_GCTRL, IS_GC_SWLED | IS_GC_FIFO0); + IS_WRITE2(sc, IS_P_IDMAC(channel), IS_P_ID_GO); + IS_WRITE1(sc, IS_P_PTQFIFO(channel), 0); +} + +void +inicsata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct ata_command *ata_c = xfer->c_cmd; + struct inicsata_r_fis *fis; + struct inicsata_cpb *cpb; + struct inicsata_prd *prd; + int channel = chp->ch_channel; + int i; + + FUNC_ENTRY; + + /* XXXMJF needed? */ + memset(ichp->ic_descr, 0, INICSATA_DESCRIPTOR_SIZE); + + prd = &ichp->ic_descr->d_prd[0]; + cpb = &ichp->ic_descr->d_cpb; + + cpb->cpb_cmdh.cmdh_flags = IS_CPB_VLD | IS_CPB_IEN; + if (ata_c->flags & (AT_READ|AT_WRITE)) + cpb->cpb_cmdh.cmdh_flags |= IS_CPB_DATA; + + cpb->cpb_cmdh.cmdh_len = htole32(ata_c->bcount); + CMD_PRINTF(("%s: bcount=%d\n", __func__, ata_c->bcount)); + cpb->cpb_cmdh.cmdh_prd = + htole32(ichp->ic_bus_descr + INICSATA_CPB_SIZE); + CMD_PRINTF(("%s: ic_bus_descr=0x%lx, INICSATA_RFIS_SIZE=%lu\n", + __func__, (unsigned long)ichp->ic_bus_descr, + INICSATA_RFIS_SIZE)); + + CMD_PRINTF(("%s: flags=0x%x, len=%u, cmdh_prd=0x%lx\n", __func__, + cpb->cpb_cmdh.cmdh_flags, cpb->cpb_cmdh.cmdh_len, + (unsigned long)cpb->cpb_cmdh.cmdh_prd)); + fis = &cpb->cpb_rfis; + fis->rfis_cmd = ata_c->r_command; + CMD_PRINTF(("%s: command=0x%x\n", __func__, fis->rfis_cmd)); + fis->rfis_features = ata_c->r_features; + fis->rfis_secnum = ata_c->r_sector; + fis->rfis_clo = ata_c->r_cyl & 0xff; + fis->rfis_chi = (ata_c->r_cyl >> 8) & 0xff; + fis->rfis_dev = ata_c->r_head & 0xff; + CMD_PRINTF(("%s: r_head=0x%x\n", __func__, ata_c->r_head)); + fis->rfis_seccnt = ata_c->r_count; + + CMD_PRINTF(("%s: rfis_features=0x%x, rfis_secnum=0x%x," + " rfis_clo=0x%x, rfis_chi=0x%x, rfis_dev=0x%x, " + "rfis_seccnt=0x%x\n", __func__, + fis->rfis_features, + fis->rfis_secnum, + fis->rfis_clo, + fis->rfis_chi, + fis->rfis_dev, + fis->rfis_seccnt)); + + if (inicsata_dma_setup(chp, + (ata_c->flags & (AT_READ|AT_WRITE)) ? ata_c->data : NULL, + ata_c->bcount, + (ata_c->flags & AT_READ) ? BUS_DMA_READ : BUS_DMA_WRITE, + prd, (xfer->c_flags & C_DMA))) { + ata_c->flags |= AT_DF; + inicsata_cmd_complete(chp, xfer, 0); + return; + } + + IS_CPB_SYNC(sc, ichp, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + if (ata_c->flags & AT_POLL) { + /* polled command, disable interrupts */ + CMD_PRINTF(("%s: disabling intrs\n", __func__)); + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) | IS_GC_GINTDIS); + } + + chp->ch_flags |= ATACH_IRQ_WAIT; + chp->ch_status = 0; + + /* start command */ + inicsata_activate_xfer(sc, channel); + + if (!(ata_c->flags & AT_POLL)) { + chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for timeout */ + callout_reset(&chp->ch_callout, mstohz(ata_c->timeout), + inicsata_timeout, chp); + return; + } + + /* + * Polled command. + */ + for (i = 0; i < ata_c->timeout / 10; i++) { + if (ata_c->flags & AT_DONE) + break; + inicsata_intr_port(sc, ichp); + if (ata_c->flags & AT_WAIT) + tsleep(&xfer, PRIBIO, "inicsatapl", mstohz(10)); + else + delay(10000); + } + + if ((ata_c->flags & AT_DONE) == 0) { + ata_c->flags |= AT_TIMEOU; + inicsata_cmd_complete(chp, xfer, 0); + } + + /* reenable interrupts */ + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) & ~IS_GC_GINTDIS); +} + +void +inicsata_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, + int reason) +{ + struct ata_command *ata_c = xfer->c_cmd; + + FUNC_ENTRY; + switch(reason) { + case KILL_GONE: + ata_c->flags |= AT_GONE; + break; + case KILL_RESET: + ata_c->flags |= AT_RESET; + break; + default: + panic("%s: unknonwn reason %d\n", __func__, reason); + } + + inicsata_cmd_done(chp, xfer); +} + +int +inicsata_cmd_complete(struct ata_channel *chp, struct ata_xfer *xfer, int is) +{ + struct ata_command *ata_c = xfer->c_cmd; + + chp->ch_flags &= ~ATACH_IRQ_WAIT; + + FUNC_ENTRY; + if (xfer->c_flags & C_TIMEOU) + ata_c->flags |= AT_TIMEOU; + else + callout_stop(&chp->ch_callout); + + chp->ch_queue->active_xfer = NULL; + + if (chp->ch_drive[xfer->c_drive].drive_flags & DRIVE_WAITDRAIN) { + inicsata_cmd_kill_xfer(chp, xfer, KILL_GONE); + chp->ch_drive[xfer->c_drive].drive_flags &= ~DRIVE_WAITDRAIN; + wakeup(&chp->ch_queue->active_xfer); + return 0; + } + + if (is) { + ata_c->r_head = 0; + ata_c->r_count = 0; + ata_c->r_sector = 0; + ata_c->r_cyl = 0; + if (chp->ch_status & WDCS_BSY) + ata_c->flags |= AT_TIMEOU; + else if (chp->ch_status & WDCS_ERR) { + ata_c->r_error = chp->ch_error; + ata_c->flags |= AT_ERROR; + } + } + inicsata_cmd_done(chp, xfer); + return 0; +} + +void +inicsata_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct ata_command *ata_c = xfer->c_cmd; + int i; + + FUNC_ENTRY; + if (ata_c->flags & (AT_READ|AT_WRITE)) { + bus_dmamap_sync(sc->sc_dmat, ichp->ic_datad, 0, + ichp->ic_datad->dm_mapsize, + (ata_c->flags & AT_READ) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + for (i = 0; i < ichp->ic_datad->dm_nsegs; i++) { + CMD_PRINTF(("%s: p_mad=0x%lx, p_len=%u\n", __func__, + (unsigned long)htole32(ichp->ic_datad->dm_segs[i].ds_addr), + (unsigned int)htole16(ichp->ic_datad->dm_segs[i].ds_len))); + + } + bus_dmamap_unload(sc->sc_dmat, ichp->ic_datad); + } + + IS_CPB_SYNC(sc, ichp, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + IS_PRD_SYNC(sc, ichp, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + ata_c->flags |= AT_DONE|AT_XFDONE; /* XXXMJF is this right? */ + ata_free_xfer(chp, xfer); + + if (ata_c->flags & AT_WAIT) + wakeup(ata_c); + else if (ata_c->callback) + ata_c->callback(ata_c->callback_arg); + + atastart(chp); + return; +} + +int +inicsata_ata_bio(struct ata_drive_datas *drvp, struct ata_bio *ata_bio) +{ + struct ata_channel *chp = drvp->chnl_softc; + struct ata_xfer *xfer; + int rv; + + FUNC_ENTRY; + xfer = ata_get_xfer(ATAXF_NOSLEEP); + if (xfer == NULL) + return ATACMD_TRY_AGAIN; + + if (ata_bio->flags & ATA_POLL) + xfer->c_flags |= C_POLL; + + xfer->c_drive = drvp->drive; + xfer->c_cmd = ata_bio; + xfer->c_databuf = ata_bio->databuf; + xfer->c_bcount = ata_bio->bcount; + xfer->c_start = inicsata_bio_start; + xfer->c_intr = inicsata_bio_complete; + xfer->c_kill_xfer = inicsata_bio_kill_xfer; + ata_exec_xfer(chp, xfer); + + if (ata_bio->flags & ATA_ITSDONE) + rv = ATACMD_COMPLETE; + else + rv = ATACMD_QUEUED; + + return rv; +} + +void +inicsata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct ata_bio *ata_bio = xfer->c_cmd; + struct inicsata_r_fis *fis; + struct inicsata_cpb *cpb; + struct inicsata_prd *prd; + int i, nblks; + int channel = chp->ch_channel; + + FUNC_ENTRY; + nblks = xfer->c_bcount / ata_bio->lp->d_secsize; + + memset(ichp->ic_descr, 0, INICSATA_DESCRIPTOR_SIZE); + + prd = &ichp->ic_descr->d_prd[0]; + cpb = &ichp->ic_descr->d_cpb; + cpb->cpb_cmdh.cmdh_flags = IS_CPB_VLD | IS_CPB_IEN | IS_CPB_DATA; + cpb->cpb_cmdh.cmdh_len = htole32(ata_bio->bcount); + cpb->cpb_cmdh.cmdh_prd = + htole32(ichp->ic_bus_descr + + offsetof(struct inicsata_descriptor, d_prd)); + + BIO_PRINTF(("%s: flags=0x%x, len=%u, cmdh_prd=0x%lx\n", __func__, + cpb->cpb_cmdh.cmdh_flags, cpb->cpb_cmdh.cmdh_len, + (unsigned long)cpb->cpb_cmdh.cmdh_prd)); + + fis = &cpb->cpb_rfis; + + if (ata_bio->flags & ATA_LBA48) { + fis->rfis_cmd = (ata_bio->flags & ATA_READ) ? + WDCC_READDMA_EXT : WDCC_WRITEDMA_EXT; + fis->rfis_dev = WDSD_LBA; + fis->rfis_xsecnum = (ata_bio->blkno >> 24) & 0xff; + fis->rfis_xclo = (ata_bio->blkno >> 32) & 0xff; + fis->rfis_xchi = (ata_bio->blkno >> 40) & 0xff; + } else { + fis->rfis_cmd = (ata_bio->flags & ATA_READ) ? + WDCC_READDMA : WDCC_WRITEDMA; + fis->rfis_dev = ((ata_bio->blkno >> 24) & 0x0f) | WDSD_LBA; + fis->rfis_xsecnum = 0; + fis->rfis_xclo = 0; + fis->rfis_xchi = 0; + } + + fis->rfis_secnum = ata_bio->blkno & 0xff; + fis->rfis_xfeatures = 0; + fis->rfis_mctl = 0; + fis->rfis_seccnt = nblks & 0xff; + fis->rfis_serror = 0; + fis->rfis_sstatus = 0; + fis->rfis_clo = (ata_bio->blkno >> 8) & 0xff; + fis->rfis_chi = (ata_bio->blkno >> 16) & 0xff; + fis->rfis_xseccnt = (ata_bio->flags & ATA_LBA48) ? + ((nblks >> 8) & 0xff) : 0; + + BIO_PRINTF(("%s: rfis_features=0x%x, rfis_secnum=0x%x," + " rfis_clo=0x%x, rfis_chi=0x%x, rfis_dev=0x%x, " + "rfis_seccnt=0x%x, rfis_xfeatures=0x%x, " + "rfis_mctl=0x%x, rfis_ctrl=0x%x, " + "rfis_cmd=0x%x, rfis_xsecnum=0x%x, rfis_xclo=0x%x, " + "rfis_xchi=0x%x\n", __func__, + fis->rfis_features, + fis->rfis_secnum, + fis->rfis_clo, + fis->rfis_chi, + fis->rfis_dev, + fis->rfis_seccnt, + fis->rfis_xfeatures, + fis->rfis_mctl, + fis->rfis_ctrl, + fis->rfis_cmd, + fis->rfis_xsecnum, + fis->rfis_xclo, + fis->rfis_xchi)); + + if (inicsata_dma_setup(chp, ata_bio->databuf, ata_bio->bcount, + (ata_bio->flags & ATA_READ) ? BUS_DMA_READ : BUS_DMA_WRITE, + prd, + /* (xfer->c_flags & C_DMA) */ true )) { + ata_bio->error = ERR_DMA; + ata_bio->r_error = 0; + printf("Error, calling bio_complete\n"); + inicsata_bio_complete(chp, xfer, 0); + return; + } + + IS_CPB_SYNC(sc, ichp, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + if (xfer->c_flags & C_POLL) { + /* polled command, disable interrupts */ + BIO_PRINTF(("%s: disabling intrs\n", __func__)); + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) | IS_GC_GINTDIS); + } + + chp->ch_flags |= ATACH_IRQ_WAIT; + chp->ch_status = 0; + + /* start command */ + inicsata_activate_xfer(sc, channel); + + if (!(xfer->c_flags & C_POLL)) { + chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */ + callout_reset(&chp->ch_callout, mstohz(ATA_DELAY), + inicsata_timeout, chp); + return; + } + + /* + * Polled command. + */ + for (i = 0; i < ATA_DELAY / 10; i++) { + if (ata_bio->flags & ATA_ITSDONE) + break; + inicsata_intr_port(sc, ichp); + if (ata_bio->flags & ATA_NOSLEEP) + delay(10000); + else + tsleep(&xfer, PRIBIO, "inicsatapl", mstohz(10)); + } + + if ((ata_bio->flags & ATA_ITSDONE) == 0) { + ata_bio->error = TIMEOUT; + printf("Error, calling bio_complete\n"); + inicsata_bio_complete(chp, xfer, 0); + } + + /* reenable interrupts */ + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) & ~IS_GC_GINTDIS); +} + +void +inicsata_bio_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, + int reason) +{ + int drive = xfer->c_drive; + struct ata_bio *ata_bio = xfer->c_cmd; + + FUNC_ENTRY; + ata_free_xfer(chp, xfer); + ata_bio->flags |= ATA_ITSDONE; + + switch(reason) { + case KILL_GONE: + ata_bio->error = ERR_NODEV; + break; + case KILL_RESET: + ata_bio->error = ERR_RESET; + break; + default: + panic("%s: unknown reason %d\n", __func__, reason); + } + + ata_bio->r_error = WDCE_ABRT; + (*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc); +} + +int +inicsata_bio_complete(struct ata_channel *chp, struct ata_xfer *xfer, int is) +{ + struct ata_bio *ata_bio = xfer->c_cmd; + int drive = xfer->c_drive; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + + FUNC_ENTRY; + chp->ch_flags &= ~ATACH_IRQ_WAIT; + if (xfer->c_flags & C_TIMEOU) + ata_bio->error = TIMEOUT; + else { + callout_stop(&chp->ch_callout); + ata_bio->error = NOERROR; + } + + chp->ch_queue->active_xfer = NULL; + bus_dmamap_sync(sc->sc_dmat, ichp->ic_datad, 0, + ichp->ic_datad->dm_mapsize, + (ata_bio->flags & ATA_READ) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ichp->ic_datad); + + if (chp->ch_drive[xfer->c_drive].drive_flags & DRIVE_WAITDRAIN) { + inicsata_bio_kill_xfer(chp, xfer, KILL_GONE); + chp->ch_drive[xfer->c_drive].drive_flags &= ~DRIVE_WAITDRAIN; + wakeup(&chp->ch_queue->active_xfer); + return 0; + } + + ata_free_xfer(chp, xfer); + ata_bio->flags |= ATA_ITSDONE; + + if (chp->ch_status & WDCS_DWF) + ata_bio->error = ERR_DF; + else if (chp->ch_status & WDCS_ERR) { + ata_bio->error = ERROR; + ata_bio->r_error = chp->ch_error; + } else if (chp->ch_status & WDCS_CORR) + ata_bio->flags |= ATA_CORR; + + IS_CPB_SYNC(sc, ichp, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + if ((ata_bio->flags & ATA_READ) || ata_bio->error == NOERROR) + ata_bio->bcount = 0; + (*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc); + atastart(chp); + + return 0; +} + +void +inicsata_channel_stop(struct inicsata_softc *sc, struct ata_channel *chp) +{ + int channel = chp->ch_channel; + + FUNC_ENTRY; + /* flush */ + IS_READ1(sc, IS_P_RPQFIFO(channel)); + IS_READ1(sc, IS_P_RPQCNT(channel)); + + IS_WRITE1(sc, IS_P_IDMAC(channel), 0); +} + +void +inicsata_timeout(void *v) +{ + struct ata_channel *chp = (struct ata_channel *)v; + struct ata_xfer *xfer = chp->ch_queue->active_xfer; + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + int s; + + /* XXXMJF REMOVE! */ + sc = sc; + INTR_PRINTF(("%s:%d:0x%x:0x%x:0x%x:0x%x:0x%x:0x%x:0x%x:0x%x:0x%x\n", __func__, __LINE__, + IS_READ2(sc, IS_GCTRL), IS_READ4(sc, IS_GIS), + IS_READ4(sc, IS_GIMSK), + IS_READ1(sc, IS_P_IS(chp->ch_channel)), + IS_READ1(sc, IS_P_MINTSTAT(chp->ch_channel)), + IS_READ2(sc, IS_P_IDMAS(chp->ch_channel)), + IS_READ1(sc, IS_P_IDMAC(chp->ch_channel)), + IS_READ4(sc, IS_P_SSTS(chp->ch_channel)), + IS_READ4(sc, IS_P_SERR(chp->ch_channel)))); + + s = splbio(); + if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0) { + xfer->c_flags |= C_TIMEOU; + xfer->c_intr(chp, xfer, 0); + } + splx(s); +} + +int +inicsata_dma_setup(struct ata_channel *chp, void *data, + size_t count, int op, struct inicsata_prd *prd, bool dma) +{ + int error; + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct inicsata_cpb *cpb; + uint8_t flags = 0; + int seg; + + cpb = &ichp->ic_descr->d_cpb; + + if (dma) + flags = IS_PRD_ORD; + + if (op == BUS_DMA_WRITE) + flags |= IS_PRD_WRITE; + + if (data == NULL) + goto out; + + error = bus_dmamap_load(sc->sc_dmat, ichp->ic_datad, + data, count, NULL, + BUS_DMA_NOWAIT | BUS_DMA_STREAMING | op); + + if (error) { + aprint_error_dev(sc->sc_atac.atac_dev, + "port %d: failed to load xfer: %d\n", + chp->ch_channel, error); + return error; + } + + bus_dmamap_sync(sc->sc_dmat, ichp->ic_datad, 0, + ichp->ic_datad->dm_mapsize, + (op = BUS_DMA_READ) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + DMA_PRINTF(("%s: nsegs = %d\n", __func__, ichp->ic_datad->dm_nsegs)); + + for (seg = 0; seg < ichp->ic_datad->dm_nsegs; seg++) { + prd[seg].p_mad = + htole32(ichp->ic_datad->dm_segs[seg].ds_addr); + prd[seg].p_len = + htole16(ichp->ic_datad->dm_segs[seg].ds_len); + prd[seg].p_flags = flags; + DMA_PRINTF(("%s: p_mad=0x%lx, p_len=%u,p_flags=0x%x\n", + __func__, (unsigned long)prd[seg].p_mad, + prd[seg].p_len, prd[seg].p_flags)); + } + + prd[seg - 1].p_flags |= IS_PRD_END; + IS_PRD_SYNC(sc, ichp, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + +out: + return 0; +} + +#if NATAPIBUS > 0 +void +inicsata_atapibus_attach(struct atabus_softc *ata_sc) +{ + struct ata_channel *chp = ata_sc->sc_chan; + struct atac_softc *atac = chp->ch_atac; + struct scsipi_adapter *adapt = &atac->atac_atapi_adapter._generic; + struct scsipi_channel *chan = &chp->ch_atapi_channel; + + FUNC_ENTRY; + /* + * Fill in the scsipi_adapter. + */ + adapt->adapt_dev = atac->atac_dev; + adapt->adapt_nchannels = atac->atac_nchannels; + adapt->adapt_request = inicsata_atapi_scsipi_request; + adapt->adapt_minphys = inicsata_atapi_minphys; + atac->atac_atapi_adapter.atapi_probe_device = + inicsata_atapi_probe_device; + + /* + * Fill in the scsipi_channel. + */ + memset(chan, 0, sizeof(*chan)); + chan->chan_adapter = adapt; + chan->chan_bustype = &inicsata_atapi_bustype; + chan->chan_channel = chp->ch_channel; + chan->chan_flags = SCSIPI_CHAN_OPENINGS; + chan->chan_openings = 1; + chan->chan_max_periph = 1; + chan->chan_ntargets = 1; + chan->chan_nluns = 1; + chp->atapibus = config_found_ia(ata_sc->sc_dev, "atapi", chan, + atapiprint); +} + +void +inicsata_atapi_minphys(struct buf *bp) +{ + if (bp->b_bcount > MAXPHYS) + bp->b_bcount = MAXPHYS; + minphys(bp); +} + +/* + * Kill off all pending xfers for a periph. + * + * Must be called at splbio(). + */ +void +inicsata_atapi_kill_pending(struct scsipi_periph *periph) +{ + struct atac_softc *atac = + device_private(periph->periph_channel->chan_adapter->adapt_dev); + struct ata_channel *chp = + atac->atac_channels[periph->periph_channel->chan_channel]; + FUNC_ENTRY; + + ata_kill_pending(&chp->ch_drive[periph->periph_target]); +} + +void +inicsata_atapi_scsipi_request(struct scsipi_channel *chan, + scsipi_adapter_req_t req, void *arg) +{ + struct scsipi_adapter *adapt = chan->chan_adapter; + struct scsipi_periph *periph; + struct scsipi_xfer *sc_xfer; + struct inicsata_softc *sc = device_private(adapt->adapt_dev); + struct atac_softc *atac = &sc->sc_atac; + struct ata_xfer *xfer; + int channel = chan->chan_channel; + int drive, s; + + FUNC_ENTRY; + switch (req) { + case ADAPTER_REQ_RUN_XFER: + sc_xfer = arg; + periph = sc_xfer->xs_periph; + drive = periph->periph_target; + + if (!device_is_active(atac->atac_dev)) { + sc_xfer->error = XS_DRIVER_STUFFUP; + scsipi_done(sc_xfer); + return; + } + + xfer = ata_get_xfer(ATAXF_NOSLEEP); + if (xfer == NULL) { + sc_xfer->error = XS_RESOURCE_SHORTAGE; + scsipi_done(sc_xfer); + return; + } + + if (sc_xfer->xs_control & XS_CTL_POLL) + xfer->c_flags |= C_POLL; + + xfer->c_drive = drive; + xfer->c_flags |= C_ATAPI; + xfer->c_cmd = sc_xfer; + xfer->c_databuf = sc_xfer->data; + xfer->c_bcount = sc_xfer->datalen; + xfer->c_start = inicsata_atapi_start; + xfer->c_intr = inicsata_atapi_complete; + xfer->c_kill_xfer = inicsata_atapi_kill_xfer; + xfer->c_dscpoll = 0; + s = splbio(); + ata_exec_xfer(atac->atac_channels[channel], xfer); +#ifdef DIAGNOSTIC + if ((sc_xfer->xs_control & XS_CTL_POLL) != 0 && + (sc_xfer->xs_status & XS_STS_DONE) == 0) + panic("%s: polled command not note", __func__); +#endif + splx(s); + return; + default: + /* Not supported, nothing to do. */ + ; + } +} + +void +inicsata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer) +{ + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct scsipi_xfer *sc_xfer = xfer->c_cmd; + struct inicsata_cpb *cpb; + struct inicsata_prd *prd; + struct inicsata_r_fis *fis; + int i, seg = 0; + int channel = chp->ch_channel; + + FUNC_ENTRY; + + /* XXXMJF needed? */ + memset(ichp->ic_descr, 0, INICSATA_DESCRIPTOR_SIZE); + + prd = &ichp->ic_descr->d_prd[0]; + cpb = &ichp->ic_descr->d_cpb; + cpb->cpb_cmdh.cmdh_flags = IS_CPB_VLD | IS_CPB_IEN | IS_CPB_DATA; + cpb->cpb_cmdh.cmdh_len = htole32(sc_xfer->cmdlen + sc_xfer->datalen); + cpb->cpb_cmdh.cmdh_prd = + htole32(ichp->ic_bus_descr + + offsetof(struct inicsata_descriptor, d_prd)); + + fis = &cpb->cpb_rfis; + fis->rfis_cmd = ATAPI_PKT_CMD; + fis->rfis_features = (sc_xfer->datalen ? ATAPI_PKT_CMD_FTRE_DMA : 0); + fis->rfis_secnum = 0; + fis->rfis_clo = 0; + fis->rfis_chi = 0; + fis->rfis_dev = WDSD_LBA; + fis->rfis_xsecnum = 0; + fis->rfis_xclo = 0; + fis->rfis_xfeatures = 0; + fis->rfis_seccnt = 0; + fis->rfis_xseccnt = 0; + + /* copy over ATAPI command */ + KASSERT(sc_xfer->cmdlen < INICSATA_CDB_SIZE); + + memcpy(ichp->ic_descr->d_atapi, sc_xfer->cmd, sc_xfer->cmdlen); + prd[seg].p_mad = + htole32(ichp->ic_bus_descr + + offsetof(struct inicsata_descriptor, d_atapi)); + + prd[seg].p_len = htole16(sc_xfer->cmdlen); + prd[seg].p_flags = IS_PRD_PKT | IS_PRD_WRITE; + if (!(sc_xfer->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT))) + prd[seg].p_flags |= IS_PRD_END; + seg++; + + if (inicsata_dma_setup(chp, + (sc_xfer->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) ? + xfer->c_databuf : NULL, + xfer->c_bcount, (sc_xfer->xs_control & XS_CTL_DATA_IN) ? + BUS_DMA_READ : BUS_DMA_WRITE, &prd[seg], (xfer->c_flags & C_DMA))) { + sc_xfer->error = XS_DRIVER_STUFFUP; + inicsata_atapi_complete(chp, xfer, 0); + return; + } + + IS_CPB_SYNC(sc, ichp, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + if (xfer->c_flags & C_POLL) { + /* polled command, disable interrupts */ + printf("%s: disabling intrs\n", __func__); + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) | IS_GC_GINTDIS); + } + + chp->ch_flags |= ATACH_IRQ_WAIT; + chp->ch_status = 0; + + /* start command */ + inicsata_activate_xfer(sc, channel); + + if (!(xfer->c_flags & C_POLL)) { + chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */ + callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout), + inicsata_timeout, chp); + return; + } + + /* + * Polled command. + */ + for (i = 0; i < ATA_DELAY / 10; i++) { + if (sc_xfer->xs_status & XS_STS_DONE) + break; + inicsata_intr_port(sc, ichp); + delay(10000); + } + + if ((sc_xfer->xs_status & XS_STS_DONE) == 0) { + sc_xfer->error = XS_TIMEOUT; + inicsata_atapi_complete(chp, xfer, 0); + } + + /* reenable interrupts */ + IS_WRITE2(sc, IS_GCTRL, IS_READ2(sc, IS_GCTRL) & ~IS_GC_GINTDIS); +} + +int +inicsata_atapi_complete(struct ata_channel *chp, struct ata_xfer *xfer, int irq) +{ + struct scsipi_xfer *sc_xfer = xfer->c_cmd; + int drive = xfer->c_drive; + struct inicsata_channel *ichp = (struct inicsata_channel *)chp; + struct inicsata_softc *sc = (struct inicsata_softc *)chp->ch_atac; + + FUNC_ENTRY; + chp->ch_flags &= ~ATACH_IRQ_WAIT; + if (xfer->c_flags & C_TIMEOU) + sc_xfer->error = XS_TIMEOUT; + else { + callout_stop(&chp->ch_callout); + sc_xfer->error = 0; + } + + chp->ch_queue->active_xfer = NULL; + bus_dmamap_sync(sc->sc_dmat, ichp->ic_datad, 0, + ichp->ic_datad->dm_mapsize, + (sc_xfer->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ichp->ic_datad); + + if (chp->ch_drive[0].drive_flags & DRIVE_WAITDRAIN) { + inicsata_atapi_kill_xfer(chp, xfer, KILL_GONE); + chp->ch_drive[drive].drive_flags &= ~DRIVE_WAITDRAIN; + wakeup(&chp->ch_queue->active_xfer); + return 0; + } + + ata_free_xfer(chp, xfer); + + IS_CPB_SYNC(sc, ichp, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + sc_xfer->resid = sc_xfer->datalen; + /* XXXMJF sc_xfer->resid -= le32toh(ichp->ic_cmdh[slot].cmdh_prdbc); */ + + if (chp->ch_status & WDCS_ERR && + ((sc_xfer->xs_control & XS_CTL_REQSENSE) == 0 || + sc_xfer->resid == sc_xfer->datalen)) { + sc_xfer->error = XS_SHORTSENSE; + sc_xfer->sense.atapi_sense = chp->ch_error; + if ((sc_xfer->xs_periph->periph_quirks & + PQUIRK_NOSENSE) == 0) { + /* ask scsipi to send REQUEST_SENSE */ + sc_xfer->error = XS_BUSY; + sc_xfer->status = SCSI_CHECK; + } + } + + scsipi_done(sc_xfer); + atastart(chp); + return 0; +} + +void +inicsata_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, + int reason) +{ + struct scsipi_xfer *sc_xfer = xfer->c_cmd; + + FUNC_ENTRY; + /* remove this command from xfer queue */ + switch (reason) { + case KILL_GONE: + sc_xfer->error = XS_DRIVER_STUFFUP; + break; + case KILL_RESET: + sc_xfer->error = XS_RESET; + break; + default: + panic("%s: unknown reason %d\n", __func__, reason); + } + + ata_free_xfer(chp, xfer); + scsipi_done(sc_xfer); +} + +void +inicsata_atapi_probe_device(struct atapibus_softc *sc, int target) +{ + struct scsipi_channel *chan = sc->sc_channel; + struct scsipi_periph *periph; + struct ataparams ids; + struct ataparams *id = &ids; + struct inicsata_softc *ic = + device_private(chan->chan_adapter->adapt_dev); + struct atac_softc *atac = &ic->sc_atac; + struct ata_channel *chp = atac->atac_channels[chan->chan_channel]; + struct ata_drive_datas *drvp = &chp->ch_drive[target]; + struct scsipibus_attach_args sa; + char serial_number[21], model[41], firmware_revision[9]; + int s; + + FUNC_ENTRY; + /* skip if already attached */ + if (scsipi_lookup_periph(chan, target, 0) != NULL) + return; + + FUNC_ENTRY; + /* if no ATAPI devices detected at attach time, skip */ + if ((drvp->drive_flags & DRIVE_ATAPI) == 0) + return; + + /* Some ATAPI devices need a bit more time after software reset. */ + delay(5000); + FUNC_ENTRY; + if (ata_get_params(drvp, AT_WAIT, id) == 0) { + FUNC_ENTRY; + periph = scsipi_alloc_periph(M_NOWAIT); + FUNC_ENTRY; + if (periph == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to allocate periph for drive %d\n", target); + return; + } + + periph->periph_dev = NULL; + periph->periph_channel = chan; + periph->periph_switch = &atapi_probe_periphsw; + periph->periph_lun = 0; + periph->periph_quirks = PQUIRK_ONLYBIG; + +#ifdef SCSIPI_DEBUG + if (SCSIPI_DEBUG_TYPE == SCSIPI_BUSTYPE_ATAPI && + SCSIPI_DEBUG_TARGET == target) + periph->periph_dbflags |= SCSIPI_DEBUG_FLAGS; +#endif + periph->periph_type = ATAPI_CFG_TYPE(id->atap_config); + if (id->atap_config & ATAPI_CFG_REMOV) + periph->periph_flags |= PERIPH_REMOVABLE; + if (periph->periph_type == T_SEQUENTIAL) { + s = splbio(); + drvp->drive_flags |= DRIVE_ATAPIST; + splx(s); + } + + sa.sa_periph = periph; + sa.sa_inqbuf.type = ATAPI_CFG_TYPE(id->atap_config); + sa.sa_inqbuf.removable = id->atap_config & ATAPI_CFG_REMOV ? + T_REMOV : T_FIXED; + scsipi_strvis((u_char *)model, 40, id->atap_model, 40); + scsipi_strvis((u_char *)serial_number, 20, id->atap_serial, 20); + scsipi_strvis((u_char *)firmware_revision, 8, + id->atap_revision, 8); + sa.sa_inqbuf.vendor = model; + sa.sa_inqbuf.product = serial_number; + sa.sa_inqbuf.revision = firmware_revision; + + /* + * Determine the operating mode capabilities of the device. + */ + if ((id->atap_config & ATAPI_CFG_CMD_MASK) == ATAPI_CFG_CMD_16) + periph->periph_cap |= PERIPH_CAP_CMD16; + /* XXX This is gross. */ + periph->periph_cap |= (id->atap_config & ATAPI_CFG_DRQ_MASK); + + FUNC_ENTRY; + drvp->drv_softc = atapi_probe_device(sc, target, periph, &sa); + FUNC_ENTRY; + + if (drvp->drv_softc) { + FUNC_ENTRY; + ata_probe_caps(drvp); + } else { + s = splbio(); + drvp->drive_flags &= ~DRIVE_ATAPI; + splx(s); + } + } else { + s = splbio(); + drvp->drive_flags &= ~DRIVE_ATAPI; + splx(s); + } +} +#endif /* NATAPIBUS */ --- /dev/null 2009-04-19 23:15:36.000000000 +0100 +++ sys/dev/ic/inicsatareg.h 2009-04-19 19:44:33.000000000 +0100 @@ -0,0 +1,133 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Fleming. + * + * 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. + * + * 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. + */ + +/* Command Paramter Block flags */ +#define IS_CPB_VLD 0x00000001 /* CPB valid */ +#define IS_CPB_QUE 0x00000002 /* Queued command */ +#define IS_CPB_DATA 0x00000004 /* Data (rsvd in datasheet) */ +#define IS_CPB_IEN 0x00000008 /* PCI interrupt enable */ +#define IS_CPB_DEVDIR 0x00000010 /* Device direction control */ + +#define IS_GCTRL 0x7c +#define IS_GC_MRMUL 0x00000001 /* enable mem read multiple */ +#define IS_GC_EEPRG 0x00000002 /* enable EEEPROM programming */ +#define IS_GC_MIREN 0x00000004 /* Mirroring enable */ +#define IS_GC_SWLED 0x00000008 /* Software LED enable */ +#define IS_GC_LED0 0x00000010 /* LED0 control */ +#define IS_GC_LED1 0x00000020 /* LED1 control */ +#define IS_GC_GINTDIS 0x00000100 /* Global interrupt disable */ +#define IS_GC_FIFO0 0x00000400 /* FIFO threshold 0 */ +#define IS_GC_FIFO1 0x00000800 /* FIFO threshold 1 */ +#define IS_GC_PWRDWN 0x00001000 /* Powerdown */ +#define IS_GC_SOFTRST 0x00002000 /* Software reset */ +#define IS_GC_SINT 0x00004000 /* Software interrupt enable */ +#define IS_GC_RPGSEL 0x00008000 /* Register page select */ + +/* Global Interrupt Status */ +#define IS_GIS 0xbc +#define IS_P0 0x00000001 /* Port 0 interrupt */ +#define IS_P1 0x00000002 /* Port 1 interrupt */ +#define IS_SI 0x00004000 /* Software interrupt */ +#define IS_GI 0x00008000 /* Global interrupt */ + +/* Global Interrupt Mask */ +#define IS_GIMSK 0xbe +#define IS_GM_MCH0INT 0x00000001 /* Mask channel 0 interrupt */ +#define IS_GM_MCH1INT 0x00000002 /* Mask channel 1 interrupt */ +#define IS_GM_MSOFTINT 0x00004000 /* Mask software interrupt */ + +/* Per-port registers */ +#define IS_P_OFFSET(port) (0x40 * (port)) + +#define IS_P_TF_CMD(p) (0x07 + IS_P_OFFSET(p)) + +#define IS_P_IS(p) (0x09 + IS_P_OFFSET(p)) /* Port Interrupt status */ +#define IS_P_IX_CHOFF 0x00000001 /* SATA channel off */ +#define IS_P_IX_CHON 0x00000002 /* SATA channel on */ +#define IS_P_IX_CHCINT 0x00000004 /* Channel complete */ +#define IS_P_IX_FTLINT 0x00000008 /* Fatal error */ +#define IS_P_IX_CHUIRQ 0x00000010 /* Channel unsolicited */ +#define IS_P_IX_CHQINT 0x00000020 /* Channel queue */ +#define IS_P_IX_CHINTP 0x00000040 /* Channel interrupt pending */ + +#define IS_P_MINTSTAT(p) (0x0a + IS_P_OFFSET(p)) +#define IS_P_IX_MCHOFF 0x00000001 /* Mask SATA channel off */ +#define IS_P_IX_MCHON 0x00000002 /* Mask SATA chanenl on */ +#define IS_P_IX_MCHCINT 0x00000004 /* Mask channel complete */ +#define IS_P_IX_MFLTINT 0x00000008 /* Mask fatal error */ +#define IS_P_IX_MCHUIRQ 0x00000010 /* Mask channel unsolicited */ +#define IS_P_IX_MCHQINT 0x00000020 /* Mask channel queue */ + +/* PRD Table Pointer */ +#define IS_P_PRDTP(p) (0x0c + IS_P_OFFSET(p)) + +/* IDMA control */ +#define IS_P_IDMAC(p) (0x14 + IS_P_OFFSET(p)) +#define IS_P_ID_FREZEN 0x00000001 /* Freeze enable */ +#define IS_P_ID_UNFREZ 0x00000002 /* Unfreeze IDMA */ +#define IS_P_ID_RSTA 0x00000004 /* ATA hard reset */ +#define IS_P_ID_AUTEN 0x00000008 /* ADMA auto-poll enable */ +#define IS_P_ID_ABT 0x00000010 /* Abort pending commands */ +#define IS_P_ID_RSTADM 0x00000020 /* Reset ADMA */ +#define IS_P_ID_PSE 0x00000040 /* ADMA pause */ +#define IS_P_ID_GO 0x00000080 /* ADMA go */ +#define IS_P_ID_IEN 0x00000100 /* PCI chan interrupt disable */ +#define IS_P_ID_HWFREZEN 0x00000200 /* HW freeze enable */ +#define IS_P_ID_MSLV 0x00001000 /* Mirror slave */ +#define IS_P_ID_MSLVSEL __BITS(13,14) /* Slave index */ +#define IS_P_ID_MMSTR 0x00008000 /* Mirror master */ + +/* IDMA status */ +#define IS_P_IDMAS(p) (0x16 + IS_P_OFFSET(p)) +#define IS_P_ID_PERR 0x00000001 /* PCI Error mode */ +#define IS_P_ID_CPBERR 0x00000002 /* ADMA CPB error */ +#define IS_P_ID_LGCY 0x00000008 /* ADMA legacy */ +#define IS_P_ID_UIRQ 0x00000010 /* ATA unsolicited IRQ */ +#define IS_P_ID_STPD 0x00000020 /* ADMA stopped */ +#define IS_P_ID_PSD 0x00000040 /* ADMA pause */ +#define IS_P_ID_DONE 0x00000080 /* ADMA done */ + +/* CPB lookup table addr */ +#define IS_P_CPBLAR(p) (0x18 + IS_P_OFFSET(p)) + +/* Posting Queue FIFO */ +#define IS_P_PTQFIFO(p) (0x1c + IS_P_OFFSET(p)) + +/* Reply Queue FIFO */ +#define IS_P_RPQFIFO(p) (0x1e + IS_P_OFFSET(p)) + +/* Reply Queue FIFO Count */ +#define IS_P_RPQCNT(p) (0x1f + IS_P_OFFSET(p)) + +#define IS_P_SSTS(p) (0x20 + IS_P_OFFSET(p)) /* Serial ATA status */ +#define IS_P_SERR(p) (0x24 + IS_P_OFFSET(p)) /* Serial ATA error */ +#define IS_P_SCTRL(p) (0x28 + IS_P_OFFSET(p)) /* Serial ATA control */ +#define IS_P_SACT(p) (0x2c + IS_P_OFFSET(p)) /* Serial ATA active */ --- /dev/null 2009-04-19 23:15:36.000000000 +0100 +++ sys/dev/ic/inicsatavar.h 2009-04-19 14:59:29.000000000 +0100 @@ -0,0 +1,191 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Fleming. + * + * 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. + * + * 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. + */ + +#include + +#define IS_READ1(sc, reg) bus_space_read_1((sc)->sc_it, \ + (sc)->sc_ih, (reg)) +#define IS_WRITE1(sc, reg, val) bus_space_write_1((sc)->sc_it, \ + (sc)->sc_ih, (reg), (val)) +#define IS_READ2(sc, reg) bus_space_read_2((sc)->sc_it, \ + (sc)->sc_ih, (reg)) +#define IS_WRITE2(sc, reg, val) bus_space_write_2((sc)->sc_it, \ + (sc)->sc_ih, (reg), (val)) +#define IS_READ4(sc, reg) bus_space_read_4((sc)->sc_it, \ + (sc)->sc_ih, (reg)) +#define IS_WRITE4(sc, reg, val) bus_space_write_4((sc)->sc_it, \ + (sc)->sc_ih, (reg), (val)) + +/* + * Command Parameter Block. + */ +struct inicsata_cmd_header { + uint8_t cmdh_rflags; /* response flags */ + uint8_t cmdh_error; /* ATA error */ + uint8_t cmdh_status; /* ATA status */ + uint8_t cmdh_flags; /* control flags */ + uint32_t cmdh_len; /* total transfer length */ + uint32_t cmdh_prd; /* first PRD pointer */ + uint8_t cmdh_res[4]; /* reserved */ +} __packed; + +#if 0 +#define INICSATA_NPRD (129) /* XXXMJF linux has + 1 */ +#else +#define INICSATA_NPRD ((MAXPHYS / PAGE_SIZE) + 1) +#endif +#define INICSATA_MAX_PORTS 2 +#define INICSATA_MAX_CMDS 4 +#define INICSATA_CPBTBL_SIZE (INICSATA_MAX_CMDS * \ + (sizeof(struct inicsata_cmd_header) + sizeof(struct inicsata_r_fis))) + +struct inicsata_r_fis { + uint8_t rfis_features; /* ATA features */ + uint8_t rfis_xfeatures; /* ATA ex features */ + uint8_t rfis_dev; /* ATA device/head */ + uint8_t rfis_mctl; /* mirror control */ + uint8_t rfis_seccnt; /* ATA sector count */ + uint8_t rfis_xseccnt; /* ATA ex. sector count */ + uint8_t rfis_secnum; /* ATA sector number */ + uint8_t rfis_xsecnum; /* ATA ex. sector number */ + uint8_t rfis_clo; /* ATA cylinder low */ + uint8_t rfis_xclo; /* ATA ex. cylinder low */ + uint8_t rfis_chi; /* ATA cylinder high */ + uint8_t rfis_xchi; /* ATA ex. cylinder high */ + uint8_t rfis_cmd; /* ATA command */ + uint8_t rfis_ctrl; /* ATA control */ + uint8_t rfis_serror; /* Slave ATA error */ + uint8_t rfis_sstatus; /* Slave ATA status */ +} __packed; + +#define INICSATA_RFIS_SIZE (sizeof(struct inicsata_r_fis)) + +/* Physical Region Descvriptor. */ +struct inicsata_prd { + uint32_t p_mad; /* physical memory addrses */ + uint16_t p_len; /* transfer length */ + uint8_t p_rsvd; + uint8_t p_flags; /* control flags */ +#define IS_PRD_IGEX 0x00000002 /* Ignore data excess */ +#define IS_PRD_PKT 0x00000004 /* Packet command pointer */ +#define IS_PRD_DINT 0x00000008 /* Direct interrupt */ +#define IS_PRD_ORD 0x00000010 /* Data transfer method */ +#define IS_PRD_WRITE 0x00000020 /* Data direction, rsvd in datasheet */ +#define IS_PRD_IOM 0x00000040 /* I/O memory transfer */ +#define IS_PRD_END 0x00000080 /* APRD chain end */ +}; + +#define INICSATA_PRD_SIZE (sizeof(struct inicsata_prd)) + +#define INICSATA_PRDTBL_SIZE (INICSATA_NPRD * INICSATA_PRD_SIZE) + +#define IS_PRD_SYNC(sc, ichp, op) bus_dmamap_sync((sc)->sc_dmat, \ + (ichp)->ic_descrd, INICSATA_CPB_SIZE, \ + INICSATA_PRD_SIZE * INICSATA_NPRD, (op)) + +/* + * Command Parameter Block + */ +struct inicsata_cpb { + struct inicsata_cmd_header cpb_cmdh; + struct inicsata_r_fis cpb_rfis; +} __packed; + +#define INICSATA_CPB_SIZE (sizeof(struct inicsata_cpb)) + +#define INICSATA_CDB_SIZE 16 /* - Stolen from Linux */ + +struct inicsata_descriptor { + struct inicsata_cpb d_cpb; + struct inicsata_prd d_prd[INICSATA_NPRD]; + uint8_t d_atapi[INICSATA_CDB_SIZE]; /* zero for non-ATAPI */ +} __packed; + +#define INICSATA_DESCRIPTOR_SIZE (sizeof(struct inicsata_descriptor)) + +#define IS_CPB_SYNC(sc, ichp, op) bus_dmamap_sync((sc)->sc_dmat, \ + (ichp)->ic_descrd, 0, INICSATA_CPB_SIZE, (op)) + +#define INICSATA_DMA_BOUNDARY (1 << 24) + +/* + * The Linux sata_inic162x driver claims that this controller can + * lockup the whole machine if given a 65536 byte PRD entry, so reduce + * the maximum segment size. + */ +#define INICSATA_DMA_MAXSEGSZ (65536 - 512) + +/* + * Synchronise the entire CPB table. + */ +#define IS_CPBTBL_SYNC(sc, ichp, op) bus_dmamap_sync((sc)->sc_dmat, \ + (ichp)->ic_cpb_tbld, 0, INICSATA_CPBTBL_SIZE, (op)) + +struct inicsata_softc { + struct atac_softc sc_atac; + bus_space_tag_t sc_it; /* inicsata register mapping */ + bus_space_handle_t sc_ih; + bus_dma_tag_t sc_dmat; /* DMA memory mapping */ + int sc_atac_capflags; + + struct ata_channel *sc_chanarray[INICSATA_MAX_PORTS]; + struct inicsata_channel { + struct ata_channel ata_channel; /* generic part */ + + bus_dmamap_t ic_descrd; + bus_addr_t ic_bus_descr; + struct inicsata_descriptor *ic_descr; + + /* command tables (allocated per-channel */ + bus_dmamap_t ic_cpb_tbld; + uint32_t *ic_cpb_tbl; + bus_addr_t ic_bus_cpb_tbl; + + /* For CDB */ + bus_dmamap_t ic_datad; + + bus_dmamap_t ic_prdtbld; + bus_addr_t ic_bus_prdtbl; + struct inicsata_prd *ic_prdtbl; + + bus_space_handle_t ic_scontrol; + bus_space_handle_t ic_sstatus; + bus_space_handle_t ic_serror; + } sc_channels[INICSATA_MAX_PORTS]; + + /* PCI stuff */ + pci_chipset_tag_t sc_pc; + pcitag_t sc_pcitag; +}; + +void inicsata_attach(struct inicsata_softc *); +void inicsata_intr_port(struct inicsata_softc *, struct inicsata_channel *); +int inicsata_intr(void *); --- /dev/null 2009-04-19 23:15:36.000000000 +0100 +++ sys/dev/pci/inicsata_pci.c 2009-04-13 13:06:03.000000000 +0100 @@ -0,0 +1,124 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Fleming. + * + * 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. + * + * 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. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +static int inicsata_match(device_t, cfdata_t, void *); +static void inicsata_pci_attach(device_t, device_t, void *); + +CFATTACH_DECL_NEW(inicsata_pci, sizeof(struct inicsata_softc), + inicsata_match, inicsata_pci_attach, NULL, NULL); + +static int +inicsata_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INITIO && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INITIO_1622) + return (1); + + return (0); +} + +static void +inicsata_pci_attach(device_t parent, device_t self, void *aux) +{ + struct pci_attach_args *pa = aux; + struct inicsata_softc *sc = device_private(self); + pci_intr_handle_t intrhandle; + const char *intrstr; + char devinfo[256]; + void *ih; + pcireg_t csr; + + sc->sc_atac.atac_dev = self; + + /* Map BAR 5 (ATA Bus Master Memory Mapped Registers) */ + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x14, + PCI_MAPREG_TYPE_MEM | + PCI_MAPREG_MEM_TYPE_32BIT, 0, + &sc->sc_it, &sc->sc_ih, + NULL, NULL) != 0) { + aprint_error(": unable to map BA5 register space\n"); + return; + } + sc->sc_pc = pa->pa_pc; + sc->sc_pcitag = pa->pa_tag; + + pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo)); + aprint_normal(": %s\n", devinfo); + + /* Map and establish interrupt handler. */ + if (pci_intr_map(pa, &intrhandle) != 0) { + aprint_error_dev(sc->sc_atac.atac_dev, + "couldn't map interrupt\n"); + return; + } + intrstr = pci_intr_string(pa->pa_pc, intrhandle); + ih = pci_intr_establish(pa->pa_pc, intrhandle, IPL_BIO, + inicsata_intr, sc); + + if (ih == NULL) { + aprint_error_dev(sc->sc_atac.atac_dev, + "couldn't establish interrupt"); + if (intrstr != NULL) + aprint_normal(" at %s", intrstr); + aprint_normal("\n"); + return; + } + aprint_normal_dev(sc->sc_atac.atac_dev, + "interrupting at %s\n", intrstr ? intrstr : "unknown interrupt"); + + sc->sc_dmat = pa->pa_dmat; + + /* set the necessary bits in case the firmware didn't */ + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + csr |= PCI_COMMAND_MASTER_ENABLE; + csr |= PCI_COMMAND_MEM_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr); + + inicsata_attach(sc); +}