Module Name: src Committed By: jakllsch Date: Sun Feb 10 21:21:30 UTC 2013
Modified Files: src/sys/conf: files src/sys/dev/ic: mvsata.c Log Message: This change adds SATA port multiplier support to mvsata(4). To generate a diff of this commit: cvs rdiff -u -r1.1062 -r1.1063 src/sys/conf/files cvs rdiff -u -r1.28 -r1.29 src/sys/dev/ic/mvsata.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/conf/files diff -u src/sys/conf/files:1.1062 src/sys/conf/files:1.1063 --- src/sys/conf/files:1.1062 Wed Jan 9 22:23:45 2013 +++ src/sys/conf/files Sun Feb 10 21:21:29 2013 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1062 2013/01/09 22:23:45 skrll Exp $ +# $NetBSD: files,v 1.1063 2013/02/10 21:21:29 jakllsch Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20100430 @@ -994,7 +994,7 @@ device siisata: ata, ata_dma, ata_udma, # Marvell Serial-ATA Host Controller define mvsata file dev/ic/mvsata.c mvsata -device mvsata: ata, ata_dma, ata_udma, wdc_common, sata, mvsata +device mvsata: ata, ata_dma, ata_udma, wdc_common, sata, sata_pmp, mvsata defflag opt_mvsata.h MVSATA_WITHOUTDMA # DECchip 21x4x Ethernet controller family, and assorted clones. Index: src/sys/dev/ic/mvsata.c diff -u src/sys/dev/ic/mvsata.c:1.28 src/sys/dev/ic/mvsata.c:1.29 --- src/sys/dev/ic/mvsata.c:1.28 Sun Feb 10 20:13:53 2013 +++ src/sys/dev/ic/mvsata.c Sun Feb 10 21:21:29 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: mvsata.c,v 1.28 2013/02/10 20:13:53 jakllsch Exp $ */ +/* $NetBSD: mvsata.c,v 1.29 2013/02/10 21:21:29 jakllsch Exp $ */ /* * Copyright (c) 2008 KIYOHARA Takashi * All rights reserved. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.28 2013/02/10 20:13:53 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.29 2013/02/10 21:21:29 jakllsch Exp $"); #include "opt_mvsata.h" @@ -51,6 +51,7 @@ __KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1 #include <dev/ata/atareg.h> #include <dev/ata/atavar.h> #include <dev/ic/wdcvar.h> +#include <dev/ata/satapmpreg.h> #include <dev/ata/satareg.h> #include <dev/ata/satavar.h> @@ -178,8 +179,8 @@ static inline void mvsata_dma_bufunload( static void mvsata_hreset_port(struct mvsata_port *); static void mvsata_reset_port(struct mvsata_port *); static void mvsata_reset_hc(struct mvsata_hc *); +static uint32_t mvsata_softreset(struct mvsata_port *, int); #ifndef MVSATA_WITHOUTDMA -static void mvsata_softreset(struct mvsata_port *, int); static void mvsata_edma_reset_qptr(struct mvsata_port *); static inline void mvsata_edma_enable(struct mvsata_port *); static int mvsata_edma_disable(struct mvsata_port *, int, int); @@ -204,6 +205,7 @@ static void mvsata_print_crpb(struct mvs static void mvsata_print_eprd(struct mvsata_port *, int); #endif +static void mvsata_probe_drive(struct ata_channel *); struct ata_bustype mvsata_ata_bustype = { SCSIPI_BUSTYPE_ATA, @@ -229,6 +231,23 @@ static const struct scsipi_bustype mvsat #endif /* NATAPIBUS */ #endif +static void +mvsata_pmp_select(struct mvsata_port *mvport, int pmpport) +{ + uint32_t ifctl; + + KASSERT(pmpport < PMP_MAX_DRIVES); +#if defined(DIAGNOSTIC) || defined(MVSATA_DEBUG) + if ((MVSATA_EDMA_READ_4(mvport, EDMA_CMD) & EDMA_CMD_EENEDMA) != 0) { + panic("EDMA enabled"); + } +#endif + + ifctl = MVSATA_EDMA_READ_4(mvport, SATA_SATAICTL); + ifctl &= ~0xf; + ifctl |= pmpport; + MVSATA_EDMA_WRITE_4(mvport, SATA_SATAICTL, ifctl); +} int mvsata_attach(struct mvsata_softc *sc, struct mvsata_product *product, @@ -309,7 +328,7 @@ mvsata_attach(struct mvsata_softc *sc, s #endif #endif sc->sc_wdcdev.wdc_maxdrives = 1; /* SATA is always 1 drive */ - sc->sc_wdcdev.sc_atac.atac_probe = wdc_sataprobe; + sc->sc_wdcdev.sc_atac.atac_probe = mvsata_probe_drive; sc->sc_wdcdev.sc_atac.atac_set_modes = mvsata_setup_channel; sc->sc_wdc_regs = @@ -415,9 +434,20 @@ mvsata_error(struct mvsata_port *mvport) { struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport)); uint32_t cause; - int handled = 0; cause = MVSATA_EDMA_READ_4(mvport, EDMA_IEC); + /* + * We must ack SATA_SE and SATA_FISIC before acking coresponding bits + * in EDMA_IEC. + */ + if (cause & EDMA_IE_SERRINT) { + MVSATA_EDMA_WRITE_4(mvport, SATA_SE, + MVSATA_EDMA_READ_4(mvport, SATA_SEIM)); + } + if (cause & EDMA_IE_ETRANSINT) { + MVSATA_EDMA_WRITE_4(mvport, SATA_FISIC, + ~MVSATA_EDMA_READ_4(mvport, SATA_FISIM)); + } MVSATA_EDMA_WRITE_4(mvport, EDMA_IEC, ~cause); DPRINTFN(3, ("%s:%d:%d:" @@ -430,25 +460,16 @@ mvsata_error(struct mvsata_port *mvport) if (!cause) return 0; - /* If PM connected, connect/disconnect interrupts storm could happen */ - if (MVSATA_EDMA_READ_4(mvport, EDMA_IEC) & - (EDMA_IE_EDEVDIS | EDMA_IE_EDEVCON)) - if (sc->sc_gen == gen2 || sc->sc_gen == gen2e) { - delay(20 * 1000); - cause = MVSATA_EDMA_READ_4(mvport, EDMA_IEC); - MVSATA_EDMA_WRITE_4(mvport, EDMA_IEC, ~cause); - } - - if (cause & EDMA_IE_EDEVDIS) + if (cause & EDMA_IE_EDEVDIS) { aprint_normal("%s:%d:%d: device disconnect\n", device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc, mvport->port); + } if (cause & EDMA_IE_EDEVCON) { if (sc->sc_gen == gen1) mvsata_devconn_gen1(mvport); DPRINTFN(3, (" device connected\n")); - handled = 1; } #ifndef MVSATA_WITHOUTDMA if ((sc->sc_gen == gen1 && cause & EDMA_IE_ETRANSINT) || @@ -472,7 +493,6 @@ mvsata_error(struct mvsata_port *mvport) mvport->port_hc->hc, mvport->port, cause); break; } - handled = 1; } #endif if (cause & EDMA_IE_ETRANSINT) { @@ -482,7 +502,7 @@ mvsata_error(struct mvsata_port *mvport) mvport->port_hc->hc, mvport->port); } - return handled; + return 1; } @@ -490,6 +510,25 @@ mvsata_error(struct mvsata_port *mvport) * ATA callback entry points */ +static void +mvsata_probe_drive(struct ata_channel *chp) +{ + struct mvsata_port * const mvport = (struct mvsata_port *)chp; + uint32_t sstat, sig; + + sstat = sata_reset_interface(chp, mvport->port_iot, + mvport->port_sata_scontrol, mvport->port_sata_sstatus); + switch (sstat) { + case SStatus_DET_DEV: + mvsata_pmp_select(mvport, PMP_PORT_CTL); + sig = mvsata_softreset(mvport, AT_WAIT); + sata_interpret_sig(chp, 0, sig); + break; + default: + break; + } +} + #ifndef MVSATA_WITHOUTDMA static int mvsata_bio(struct ata_drive_datas *drvp, struct ata_bio *ata_bio) @@ -529,8 +568,7 @@ mvsata_reset_drive(struct ata_drive_data struct ata_channel *chp = drvp->chnl_softc; struct mvsata_port *mvport = (struct mvsata_port *)chp; uint32_t edma_c; - - KASSERT(sigp == NULL); + uint32_t sig; edma_c = MVSATA_EDMA_READ_4(mvport, EDMA_CMD); @@ -541,7 +579,12 @@ mvsata_reset_drive(struct ata_drive_data if (edma_c & EDMA_CMD_EENEDMA) mvsata_edma_disable(mvport, 10000, flags & AT_WAIT); - mvsata_softreset(mvport, flags & AT_WAIT); + mvsata_pmp_select(mvport, drvp->drive); + + sig = mvsata_softreset(mvport, flags & AT_WAIT); + + if (sigp) + *sigp = sig; if (edma_c & EDMA_CMD_EENEDMA) { mvsata_edma_reset_qptr(mvport); @@ -609,11 +652,11 @@ mvsata_exec_command(struct ata_drive_dat int rv, s; DPRINTFN(1, ("%s:%d: mvsata_exec_command: drive=%d, bcount=%d," - " r_command=0x%x, r_head=0x%x, r_cyl=0x%x, r_sector=0x%x," - " r_count=0x%x, r_features=0x%x\n", + " r_lba=0x%012"PRIx64", r_count=0x%04x, r_features=0x%04x," + " r_device=0x%02x, r_command=0x%02x\n", device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, - drvp->drive, ata_c->bcount, ata_c->r_command, ata_c->r_head, - ata_c->r_cyl, ata_c->r_sector, ata_c->r_count, ata_c->r_features)); + drvp->drive, ata_c->bcount, ata_c->r_lba, ata_c->r_count, + ata_c->r_features, ata_c->r_device, ata_c->r_command)); xfer = ata_get_xfer(ata_c->flags & AT_WAIT ? ATAXF_CANSLEEP : ATAXF_NOSLEEP); @@ -1138,6 +1181,8 @@ do_pio: if (mvport->port_edmamode != nodma) mvsata_edma_disable(mvport, 10 /* ms */, wait_flags); + mvsata_pmp_select(mvport, xfer->c_drive); + /* Do control operations specially. */ if (__predict_false(drvp->state < READY)) { /* @@ -1173,10 +1218,10 @@ do_pio: return; } if (ata_bio->flags & ATA_LBA48) - wdccommandext(chp, xfer->c_drive, atacmd_to48(cmd), + wdccommandext(chp, 0, atacmd_to48(cmd), ata_bio->blkno, nblks, 0, WDSD_LBA); else - wdccommand(chp, xfer->c_drive, cmd, cyl, + wdccommand(chp, 0, cmd, cyl, head, sect, nblks, (ata_bio->lp->d_type == DTYPE_ST506) ? ata_bio->lp->d_precompcyl / 4 : 0); @@ -1423,8 +1468,6 @@ mvsata_bio_ready(struct mvsata_port *mvp struct ata_drive_datas *drvp = &chp->ch_drive[drive]; const char *errstring; - flags |= AT_POLL; /* XXX */ - /* * disable interrupts, all commands here should be quick * enough to be able to poll, and we don't go here that often @@ -1435,8 +1478,8 @@ mvsata_bio_ready(struct mvsata_port *mvp errstring = "wait"; if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags)) goto ctrltimeout; - wdccommandshort(chp, drive, WDCC_RECAL); - /* Wait for at last 400ns for status bit to be valid */ + wdccommandshort(chp, 0, WDCC_RECAL); + /* Wait for at least 400ns for status bit to be valid */ DELAY(1); errstring = "recal"; if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags)) @@ -1449,7 +1492,7 @@ mvsata_bio_ready(struct mvsata_port *mvp /* Also don't try if the drive didn't report its mode */ if ((drvp->drive_flags & ATA_DRIVE_MODE) == 0) goto geometry; - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x08 | drvp->PIO_mode, WDSF_SET_MODE); errstring = "piomode"; if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags)) @@ -1457,10 +1500,10 @@ mvsata_bio_ready(struct mvsata_port *mvp if (chp->ch_status & (WDCS_ERR | WDCS_DWF)) goto ctrlerror; if (drvp->drive_flags & ATA_DRIVE_UDMA) - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); else if (drvp->drive_flags & ATA_DRIVE_DMA) - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x20 | drvp->DMA_mode, WDSF_SET_MODE); else goto geometry; @@ -1472,7 +1515,7 @@ mvsata_bio_ready(struct mvsata_port *mvp geometry: if (ata_bio->flags & ATA_LBA) goto multimode; - wdccommand(chp, drive, WDCC_IDP, ata_bio->lp->d_ncylinders, + wdccommand(chp, 0, WDCC_IDP, ata_bio->lp->d_ncylinders, ata_bio->lp->d_ntracks - 1, 0, ata_bio->lp->d_nsectors, (ata_bio->lp->d_type == DTYPE_ST506) ? ata_bio->lp->d_precompcyl / 4 : 0); @@ -1484,7 +1527,7 @@ geometry: multimode: if (ata_bio->multi == 1) goto ready; - wdccommand(chp, drive, WDCC_SETMULTI, 0, 0, 0, ata_bio->multi, 0); + wdccommand(chp, 0, WDCC_SETMULTI, 0, 0, 0, ata_bio->multi, 0); errstring = "setmulti"; if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags)) goto ctrltimeout; @@ -1536,6 +1579,8 @@ mvsata_wdc_cmd_start(struct ata_channel if (mvport->port_edmamode != nodma) mvsata_edma_disable(mvport, 10 /* ms */, wait_flags); + mvsata_pmp_select(mvport, drive); + MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM); switch(wdcwait(chp, ata_c->r_st_bmask | WDCS_DRQ, ata_c->r_st_bmask, ata_c->timeout, wait_flags)) { @@ -1552,11 +1597,11 @@ mvsata_wdc_cmd_start(struct ata_channel /* polled command, disable interrupts */ MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT | WDCTL_IDS); if ((ata_c->flags & AT_LBA48) != 0) { - wdccommandext(chp, drive, ata_c->r_command, + wdccommandext(chp, 0, ata_c->r_command, ata_c->r_lba, ata_c->r_count, ata_c->r_features, ata_c->r_device & ~0x10); } else { - wdccommand(chp, drive, ata_c->r_command, + wdccommand(chp, 0, ata_c->r_command, (ata_c->r_lba >> 8) & 0xffff, (((ata_c->flags & AT_LBA) != 0) ? WDSD_LBA : 0) | ((ata_c->r_lba >> 24) & 0x0f), @@ -1819,6 +1864,8 @@ mvsata_atapi_start(struct ata_channel *c if (mvport->port_edmamode != nodma) mvsata_edma_disable(mvport, 10 /* ms */, wait_flags); + mvsata_pmp_select(mvport, xfer->c_drive); + if ((xfer->c_flags & C_DMA) && (drvp->n_xfers <= NXFER)) drvp->n_xfers++; @@ -1846,7 +1893,7 @@ mvsata_atapi_start(struct ata_channel *c errstring = "unbusy"; if (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags)) goto timeout; - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x08 | drvp->PIO_mode, WDSF_SET_MODE); errstring = "piomode"; if (wdc_wait_for_unbusy(chp, ATAPI_MODE_DELAY, wait_flags)) @@ -1869,11 +1916,11 @@ mvsata_atapi_start(struct ata_channel *c goto error; } if (drvp->drive_flags & ATA_DRIVE_UDMA) - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); else if (drvp->drive_flags & ATA_DRIVE_DMA) - wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, + wdccommand(chp, 0, SET_FEATURES, 0, 0, 0, 0x20 | drvp->DMA_mode, WDSF_SET_MODE); else goto ready; @@ -1934,7 +1981,7 @@ ready: * data is necessary, multiple data transfer phases will be done. */ - wdccommand(chp, xfer->c_drive, ATAPI_PKT_CMD, + wdccommand(chp, 0, ATAPI_PKT_CMD, xfer->c_bcount <= 0xffff ? xfer->c_bcount : 0xffff, 0, 0, 0, (xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0); @@ -2235,7 +2282,9 @@ mvsata_atapi_reset(struct ata_channel *c struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; struct scsipi_xfer *sc_xfer = xfer->c_cmd; - wdccommandshort(chp, xfer->c_drive, ATAPI_SOFT_RESET); + mvsata_pmp_select(mvport, xfer->c_drive); + + wdccommandshort(chp, 0, ATAPI_SOFT_RESET); drvp->state = 0; if (wdc_wait_for_unbusy(chp, WDC_RESET_WAIT, AT_POLL) != 0) { printf("%s:%d:%d: reset failed\n", device_xname(atac->atac_dev), @@ -3127,6 +3176,9 @@ mvsata_reset_port(struct mvsata_port *mv MVSATA_EDMA_WRITE_4(mvport, EDMA_CFG, EDMA_CFG_RESERVED | EDMA_CFG_RESERVED2); MVSATA_EDMA_WRITE_4(mvport, EDMA_T, 0); + MVSATA_EDMA_WRITE_4(mvport, SATA_SEIM, 0x019c0000); + MVSATA_EDMA_WRITE_4(mvport, SATA_SE, ~0); + MVSATA_EDMA_WRITE_4(mvport, SATA_FISIC, 0); MVSATA_EDMA_WRITE_4(mvport, EDMA_IEC, 0); MVSATA_EDMA_WRITE_4(mvport, EDMA_IEM, 0); MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQBAH, 0); @@ -3138,8 +3190,6 @@ mvsata_reset_port(struct mvsata_port *mv MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, 0); MVSATA_EDMA_WRITE_4(mvport, EDMA_TC, 0); MVSATA_EDMA_WRITE_4(mvport, EDMA_IORT, 0xbc); - - MVSATA_EDMA_WRITE_4(mvport, SATA_FISIC, 0); } static void @@ -3167,33 +3217,50 @@ mvsata_reset_hc(struct mvsata_hc *mvhc) #endif } -#ifndef MVSATA_WITHOUTDMA -static void +#define WDCDELAY 100 /* 100 microseconds */ +#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY) + +static uint32_t mvsata_softreset(struct mvsata_port *mvport, int waitok) { - uint32_t stat; - int i; + uint32_t sig0 = ~0; + int timeout, nloop; + uint8_t st0; - MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_RST | WDCTL_IDS); + MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_RST | WDCTL_IDS | WDCTL_4BIT); + delay(10); + (void) MVSATA_WDC_READ_1(mvport, SRB_FE); + MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_IDS | WDCTL_4BIT); delay(10); - MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_IDS); - delay(2000); - if (waitok) { - /* wait maximum 31sec */ - for (i = 31000; i > 0; i--) { - stat = MVSATA_WDC_READ_1(mvport, SRB_CS); - if (!(stat & WDCS_BSY)) - break; - delay(1000); + if (!waitok) + nloop = WDCNDELAY_RST; + else + nloop = WDC_RESET_WAIT * hz / 1000; + + /* wait for BSY to deassert */ + for (timeout = 0; timeout < nloop; timeout++) { + st0 = MVSATA_WDC_READ_1(mvport, SRB_CS); + + if ((st0 & WDCS_BSY) == 0) { + sig0 = MVSATA_WDC_READ_1(mvport, SRB_SC) << 0; + sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAL) << 8; + sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAM) << 16; + sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAH) << 24; + goto out; } - if (i == 0) - aprint_error("%s:%d:%d: soft reset failed\n", - device_xname(MVSATA_DEV2(mvport)), - mvport->port_hc->hc, mvport->port); + if (!waitok) + delay(WDCDELAY); + else + tsleep(&nloop, PRIBIO, "atarst", 1); } + +out: + MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT); + return sig0; } +#ifndef MVSATA_WITHOUTDMA static void mvsata_edma_reset_qptr(struct mvsata_port *mvport) { @@ -3234,8 +3301,12 @@ mvsata_edma_disable(struct mvsata_port * else delay(1000); } - if (ms == timeout) + if (ms == timeout) { + aprint_error("%s:%d:%d: unable to stop EDMA\n", + device_xname(MVSATA_DEV2(mvport)), + mvport->port_hc->hc, mvport->port); return EBUSY; + } /* The diable bit (eDsEDMA) is self negated. */ MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, EDMA_CMD_EDSEDMA);