Index: stable/8/sys/amd64/include/xen =================================================================== --- stable/8/sys/amd64/include/xen (revision 208447) +++ stable/8/sys/amd64/include/xen (revision 208448) Property changes on: stable/8/sys/amd64/include/xen ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/amd64/include/xen:r208375,208393 Index: stable/8/sys/cddl/contrib/opensolaris =================================================================== --- stable/8/sys/cddl/contrib/opensolaris (revision 208447) +++ stable/8/sys/cddl/contrib/opensolaris (revision 208448) Property changes on: stable/8/sys/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/cddl/contrib/opensolaris:r208375,208393 Index: stable/8/sys/contrib/dev/acpica =================================================================== --- stable/8/sys/contrib/dev/acpica (revision 208447) +++ stable/8/sys/contrib/dev/acpica (revision 208448) Property changes on: stable/8/sys/contrib/dev/acpica ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/contrib/dev/acpica:r208375,208393 Index: stable/8/sys/contrib/pf =================================================================== --- stable/8/sys/contrib/pf (revision 208447) +++ stable/8/sys/contrib/pf (revision 208448) Property changes on: stable/8/sys/contrib/pf ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/contrib/pf:r208375,208393 Index: stable/8/sys/dev/ahci/ahci.c =================================================================== --- stable/8/sys/dev/ahci/ahci.c (revision 208447) +++ stable/8/sys/dev/ahci/ahci.c (revision 208448) @@ -1,2595 +1,2624 @@ /*- * Copyright (c) 2009 Alexander Motin * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 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 AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" #include #include #include #include #include /* local prototypes */ static int ahci_setup_interrupt(device_t dev); static void ahci_intr(void *data); static void ahci_intr_one(void *data); static int ahci_suspend(device_t dev); static int ahci_resume(device_t dev); +static int ahci_ch_init(device_t dev); +static int ahci_ch_deinit(device_t dev); static int ahci_ch_suspend(device_t dev); static int ahci_ch_resume(device_t dev); static void ahci_ch_pm(void *arg); static void ahci_ch_intr_locked(void *data); static void ahci_ch_intr(void *data); static int ahci_ctlr_reset(device_t dev); static int ahci_ctlr_setup(device_t dev); static void ahci_begin_transaction(device_t dev, union ccb *ccb); static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_execute_transaction(struct ahci_slot *slot); static void ahci_timeout(struct ahci_slot *slot); static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et); static int ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag); static void ahci_dmainit(device_t dev); static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_dmafini(device_t dev); static void ahci_slotsalloc(device_t dev); static void ahci_slotsfree(device_t dev); static void ahci_reset(device_t dev); static void ahci_start(device_t dev, int fbs); static void ahci_stop(device_t dev); static void ahci_clo(device_t dev); static void ahci_start_fr(device_t dev); static void ahci_stop_fr(device_t dev); static int ahci_sata_connect(struct ahci_channel *ch); static int ahci_sata_phy_reset(device_t dev); static int ahci_wait_ready(device_t dev, int t); static void ahci_issue_read_log(device_t dev); static void ahci_process_read_log(device_t dev, union ccb *ccb); static void ahciaction(struct cam_sim *sim, union ccb *ccb); static void ahcipoll(struct cam_sim *sim); MALLOC_DEFINE(M_AHCI, "AHCI driver", "AHCI driver data buffers"); static struct { uint32_t id; uint8_t rev; const char *name; int quirks; #define AHCI_Q_NOFORCE 1 #define AHCI_Q_NOPMP 2 #define AHCI_Q_NONCQ 4 #define AHCI_Q_1CH 8 #define AHCI_Q_2CH 16 #define AHCI_Q_4CH 32 #define AHCI_Q_EDGEIS 64 #define AHCI_Q_SATA2 128 #define AHCI_Q_NOBSYRES 256 #define AHCI_Q_NOAA 512 } ahci_ids[] = { {0x43801002, 0x00, "ATI IXP600", 0}, {0x43901002, 0x00, "ATI IXP700", 0}, {0x43911002, 0x00, "ATI IXP700", 0}, {0x43921002, 0x00, "ATI IXP700", 0}, {0x43931002, 0x00, "ATI IXP700", 0}, {0x43941002, 0x00, "ATI IXP800", 0}, {0x43951002, 0x00, "ATI IXP800", 0}, {0x26528086, 0x00, "Intel ICH6", AHCI_Q_NOFORCE}, {0x26538086, 0x00, "Intel ICH6M", AHCI_Q_NOFORCE}, {0x26818086, 0x00, "Intel ESB2", 0}, {0x26828086, 0x00, "Intel ESB2", 0}, {0x26838086, 0x00, "Intel ESB2", 0}, {0x27c18086, 0x00, "Intel ICH7", 0}, {0x27c38086, 0x00, "Intel ICH7", 0}, {0x27c58086, 0x00, "Intel ICH7M", 0}, {0x27c68086, 0x00, "Intel ICH7M", 0}, {0x28218086, 0x00, "Intel ICH8", 0}, {0x28228086, 0x00, "Intel ICH8", 0}, {0x28248086, 0x00, "Intel ICH8", 0}, {0x28298086, 0x00, "Intel ICH8M", 0}, {0x282a8086, 0x00, "Intel ICH8M", 0}, {0x29228086, 0x00, "Intel ICH9", 0}, {0x29238086, 0x00, "Intel ICH9", 0}, {0x29248086, 0x00, "Intel ICH9", 0}, {0x29258086, 0x00, "Intel ICH9", 0}, {0x29278086, 0x00, "Intel ICH9", 0}, {0x29298086, 0x00, "Intel ICH9M", 0}, {0x292a8086, 0x00, "Intel ICH9M", 0}, {0x292b8086, 0x00, "Intel ICH9M", 0}, {0x292c8086, 0x00, "Intel ICH9M", 0}, {0x292f8086, 0x00, "Intel ICH9M", 0}, {0x294d8086, 0x00, "Intel ICH9", 0}, {0x294e8086, 0x00, "Intel ICH9M", 0}, {0x3a058086, 0x00, "Intel ICH10", 0}, {0x3a228086, 0x00, "Intel ICH10", 0}, {0x3a258086, 0x00, "Intel ICH10", 0}, {0x3b228086, 0x00, "Intel PCH", 0}, {0x3b238086, 0x00, "Intel PCH", 0}, {0x3b248086, 0x00, "Intel PCH", 0}, {0x3b258086, 0x00, "Intel PCH", 0}, {0x3b298086, 0x00, "Intel PCH", 0}, {0x3b2b8086, 0x00, "Intel PCH", 0}, {0x3b2c8086, 0x00, "Intel PCH", 0}, {0x3b2f8086, 0x00, "Intel PCH", 0}, {0x2361197b, 0x00, "JMicron JMB361", AHCI_Q_NOFORCE}, {0x2363197b, 0x00, "JMicron JMB363", AHCI_Q_NOFORCE}, {0x2365197b, 0x00, "JMicron JMB365", AHCI_Q_NOFORCE}, {0x2366197b, 0x00, "JMicron JMB366", AHCI_Q_NOFORCE}, {0x2368197b, 0x00, "JMicron JMB368", AHCI_Q_NOFORCE}, {0x611111ab, 0x00, "Marvell 88SX6111", AHCI_Q_NOFORCE|AHCI_Q_1CH|AHCI_Q_EDGEIS}, {0x612111ab, 0x00, "Marvell 88SX6121", AHCI_Q_NOFORCE|AHCI_Q_2CH|AHCI_Q_EDGEIS}, {0x614111ab, 0x00, "Marvell 88SX6141", AHCI_Q_NOFORCE|AHCI_Q_4CH|AHCI_Q_EDGEIS}, {0x614511ab, 0x00, "Marvell 88SX6145", AHCI_Q_NOFORCE|AHCI_Q_4CH|AHCI_Q_EDGEIS}, {0x91231b4b, 0x11, "Marvell 88SE912x", AHCI_Q_NOBSYRES}, {0x91231b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS|AHCI_Q_SATA2|AHCI_Q_NOBSYRES}, {0x044c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x055010de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055110de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055210de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055310de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055510de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055610de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055710de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055810de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055910de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055A10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055B10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x058410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x07f010de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f110de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f210de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f310de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f410de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f510de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f610de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f710de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f810de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f910de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fa10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fb10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x0ad010de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad110de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad210de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad310de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad410de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad510de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad610de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad710de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad810de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad910de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ada10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0adb10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ab410de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab510de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab610de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab710de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab810de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab910de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0aba10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abb10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abc10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abd10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abe10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abf10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0d8410de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8510de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8610de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8710de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8810de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8910de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8a10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8b10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8c10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8d10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8e10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8f10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x33491106, 0x00, "VIA VT8251", 0}, {0x62871106, 0x00, "VIA VT8251", 0}, {0x11841039, 0x00, "SiS 966", 0}, {0x11851039, 0x00, "SiS 968", 0}, {0x01861039, 0x00, "SiS 968", 0}, {0x00000000, 0x00, NULL, 0} }; static int ahci_probe(device_t dev) { char buf[64]; int i, valid = 0; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); /* Is this a possible AHCI candidate? */ if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_SATA && pci_get_progif(dev) == PCIP_STORAGE_SATA_AHCI_1_0) valid = 1; /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid && (valid || !(ahci_ids[i].quirks & AHCI_Q_NOFORCE))) { /* Do not attach JMicrons with single PCI function. */ if (pci_get_vendor(dev) == 0x197b && (pci_read_config(dev, 0xdf, 1) & 0x40) == 0) return (ENXIO); snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } if (!valid) return (ENXIO); device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_ata_probe(device_t dev) { char buf[64]; int i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); if ((intptr_t)device_get_ivars(dev) >= 0) return (ENXIO); /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid) { snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); device_t child; int error, unit, speed, i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); u_int32_t version; ctlr->dev = dev; i = 0; while (ahci_ids[i].id != 0 && (ahci_ids[i].id != devid || ahci_ids[i].rev > revid)) i++; ctlr->quirks = ahci_ids[i].quirks; resource_int_value(device_get_name(dev), device_get_unit(dev), "ccc", &ctlr->ccc); /* if we have a memory BAR(5) we are likely on an AHCI part */ ctlr->r_rid = PCIR_BAR(5); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return ENXIO; /* Setup our own memory management for channels. */ ctlr->sc_iomem.rm_type = RMAN_ARRAY; ctlr->sc_iomem.rm_descr = "I/O memory addresses"; if ((error = rman_init(&ctlr->sc_iomem)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } if ((error = rman_manage_region(&ctlr->sc_iomem, rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); } pci_enable_busmaster(dev); /* Reset controller */ if ((error = ahci_ctlr_reset(dev)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); }; /* Get the HW capabilities */ version = ATA_INL(ctlr->r_mem, AHCI_VS); ctlr->caps = ATA_INL(ctlr->r_mem, AHCI_CAP); if (version >= 0x00010020) ctlr->caps2 = ATA_INL(ctlr->r_mem, AHCI_CAP2); if (ctlr->caps & AHCI_CAP_EMS) ctlr->capsem = ATA_INL(ctlr->r_mem, AHCI_EM_CTL); ctlr->ichannels = ATA_INL(ctlr->r_mem, AHCI_PI); if (ctlr->quirks & AHCI_Q_1CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->ichannels &= 0x01; } if (ctlr->quirks & AHCI_Q_2CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 1; ctlr->ichannels &= 0x03; } if (ctlr->quirks & AHCI_Q_4CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 3; ctlr->ichannels &= 0x0f; } ctlr->channels = MAX(flsl(ctlr->ichannels), (ctlr->caps & AHCI_CAP_NPMASK) + 1); if (ctlr->quirks & AHCI_Q_NOPMP) ctlr->caps &= ~AHCI_CAP_SPM; if (ctlr->quirks & AHCI_Q_NONCQ) ctlr->caps &= ~AHCI_CAP_SNCQ; if ((ctlr->caps & AHCI_CAP_CCCS) == 0) ctlr->ccc = 0; ahci_ctlr_setup(dev); /* Setup interrupts. */ if (ahci_setup_interrupt(dev)) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return ENXIO; } /* Announce HW capabilities. */ speed = (ctlr->caps & AHCI_CAP_ISS) >> AHCI_CAP_ISS_SHIFT; device_printf(dev, "AHCI v%x.%02x with %d %sGbps ports, Port Multiplier %s%s\n", ((version >> 20) & 0xf0) + ((version >> 16) & 0x0f), ((version >> 4) & 0xf0) + (version & 0x0f), (ctlr->caps & AHCI_CAP_NPMASK) + 1, ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?"))), (ctlr->caps & AHCI_CAP_SPM) ? "supported" : "not supported", (ctlr->caps & AHCI_CAP_FBSS) ? " with FBS" : ""); if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s%s%s%s %sGbps", (ctlr->caps & AHCI_CAP_64BIT) ? " 64bit":"", (ctlr->caps & AHCI_CAP_SNCQ) ? " NCQ":"", (ctlr->caps & AHCI_CAP_SSNTF) ? " SNTF":"", (ctlr->caps & AHCI_CAP_SMPS) ? " MPS":"", (ctlr->caps & AHCI_CAP_SSS) ? " SS":"", (ctlr->caps & AHCI_CAP_SALP) ? " ALP":"", (ctlr->caps & AHCI_CAP_SAL) ? " AL":"", (ctlr->caps & AHCI_CAP_SCLO) ? " CLO":"", ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?")))); printf("%s%s%s%s%s%s %dcmd%s%s%s %dports\n", (ctlr->caps & AHCI_CAP_SAM) ? " AM":"", (ctlr->caps & AHCI_CAP_SPM) ? " PM":"", (ctlr->caps & AHCI_CAP_FBSS) ? " FBS":"", (ctlr->caps & AHCI_CAP_PMD) ? " PMD":"", (ctlr->caps & AHCI_CAP_SSC) ? " SSC":"", (ctlr->caps & AHCI_CAP_PSC) ? " PSC":"", ((ctlr->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1, (ctlr->caps & AHCI_CAP_CCCS) ? " CCC":"", (ctlr->caps & AHCI_CAP_EMS) ? " EM":"", (ctlr->caps & AHCI_CAP_SXS) ? " eSATA":"", (ctlr->caps & AHCI_CAP_NPMASK) + 1); } if (bootverbose && version >= 0x00010020) { device_printf(dev, "Caps2:%s%s%s\n", (ctlr->caps2 & AHCI_CAP2_APST) ? " APST":"", (ctlr->caps2 & AHCI_CAP2_NVMP) ? " NVMP":"", (ctlr->caps2 & AHCI_CAP2_BOH) ? " BOH":""); } if (bootverbose && (ctlr->caps & AHCI_CAP_EMS)) { device_printf(dev, "EM Caps:%s%s%s%s%s%s%s%s\n", (ctlr->capsem & AHCI_EM_PM) ? " PM":"", (ctlr->capsem & AHCI_EM_ALHD) ? " ALHD":"", (ctlr->capsem & AHCI_EM_XMT) ? " XMT":"", (ctlr->capsem & AHCI_EM_SMB) ? " SMB":"", (ctlr->capsem & AHCI_EM_SGPIO) ? " SGPIO":"", (ctlr->capsem & AHCI_EM_SES2) ? " SES-2":"", (ctlr->capsem & AHCI_EM_SAFTE) ? " SAF-TE":"", (ctlr->capsem & AHCI_EM_LED) ? " LED":""); } /* Attach all channels on this controller */ for (unit = 0; unit < ctlr->channels; unit++) { if ((ctlr->ichannels & (1 << unit)) == 0) continue; child = device_add_child(dev, "ahcich", -1); if (child == NULL) device_printf(dev, "failed to add channel device\n"); else device_set_ivars(child, (void *)(intptr_t)unit); } bus_generic_attach(dev); return 0; } static int ahci_detach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); device_t *children; int nchildren, i; /* Detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* Free interrupts. */ for (i = 0; i < ctlr->numirqs; i++) { if (ctlr->irqs[i].r_irq) { bus_teardown_intr(dev, ctlr->irqs[i].r_irq, ctlr->irqs[i].handle); bus_release_resource(dev, SYS_RES_IRQ, ctlr->irqs[i].r_irq_rid, ctlr->irqs[i].r_irq); } } pci_release_msi(dev); /* Free memory. */ rman_fini(&ctlr->sc_iomem); if (ctlr->r_mem) bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (0); } static int ahci_ctlr_reset(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int timeout; if (pci_read_config(dev, 0x00, 4) == 0x28298086 && (pci_read_config(dev, 0x92, 1) & 0xfe) == 0x04) pci_write_config(dev, 0x92, 0x01, 1); /* Enable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); /* Reset AHCI controller */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE|AHCI_GHC_HR); for (timeout = 1000; timeout > 0; timeout--) { DELAY(1000); if ((ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_HR) == 0) break; } if (timeout == 0) { device_printf(dev, "AHCI controller reset failure\n"); return ENXIO; } /* Reenable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); return (0); } static int ahci_ctlr_setup(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); /* Clear interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_IS, ATA_INL(ctlr->r_mem, AHCI_IS)); /* Configure CCC */ if (ctlr->ccc) { ATA_OUTL(ctlr->r_mem, AHCI_CCCP, ATA_INL(ctlr->r_mem, AHCI_PI)); ATA_OUTL(ctlr->r_mem, AHCI_CCCC, (ctlr->ccc << AHCI_CCCC_TV_SHIFT) | (4 << AHCI_CCCC_CC_SHIFT) | AHCI_CCCC_EN); ctlr->cccv = (ATA_INL(ctlr->r_mem, AHCI_CCCC) & AHCI_CCCC_INT_MASK) >> AHCI_CCCC_INT_SHIFT; if (bootverbose) { device_printf(dev, "CCC with %dms/4cmd enabled on vector %d\n", ctlr->ccc, ctlr->cccv); } } /* Enable AHCI interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) | AHCI_GHC_IE); return (0); } static int ahci_suspend(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); bus_generic_suspend(dev); /* Disable interupts, so the state change(s) doesn't trigger */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); return 0; } static int ahci_resume(device_t dev) { int res; if ((res = ahci_ctlr_reset(dev)) != 0) return (res); ahci_ctlr_setup(dev); return (bus_generic_resume(dev)); } static int ahci_setup_interrupt(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int i, msi = 1; /* Process hints. */ resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &msi); if (msi < 0) msi = 0; else if (msi == 1) msi = min(1, pci_msi_count(dev)); else if (msi > 1) msi = pci_msi_count(dev); /* Allocate MSI if needed/present. */ if (msi && pci_alloc_msi(dev, &msi) == 0) { ctlr->numirqs = msi; } else { msi = 0; ctlr->numirqs = 1; } /* Check for single MSI vector fallback. */ if (ctlr->numirqs > 1 && (ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_MRSM) != 0) { device_printf(dev, "Falling back to one MSI\n"); ctlr->numirqs = 1; } /* Allocate all IRQs. */ for (i = 0; i < ctlr->numirqs; i++) { ctlr->irqs[i].ctlr = ctlr; ctlr->irqs[i].r_irq_rid = i + (msi ? 1 : 0); if (ctlr->numirqs == 1 || i >= ctlr->channels || (ctlr->ccc && i == ctlr->cccv)) ctlr->irqs[i].mode = AHCI_IRQ_MODE_ALL; else if (i == ctlr->numirqs - 1) ctlr->irqs[i].mode = AHCI_IRQ_MODE_AFTER; else ctlr->irqs[i].mode = AHCI_IRQ_MODE_ONE; if (!(ctlr->irqs[i].r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &ctlr->irqs[i].r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "unable to map interrupt\n"); return ENXIO; } if ((bus_setup_intr(dev, ctlr->irqs[i].r_irq, ATA_INTR_FLAGS, NULL, (ctlr->irqs[i].mode == AHCI_IRQ_MODE_ONE) ? ahci_intr_one : ahci_intr, &ctlr->irqs[i], &ctlr->irqs[i].handle))) { /* SOS XXX release r_irq */ device_printf(dev, "unable to setup interrupt\n"); return ENXIO; } } return (0); } /* * Common case interrupt handler. */ static void ahci_intr(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; u_int32_t is, ise = 0; void *arg; int unit; if (irq->mode == AHCI_IRQ_MODE_ALL) { unit = 0; if (ctlr->ccc) is = ctlr->ichannels; else is = ATA_INL(ctlr->r_mem, AHCI_IS); } else { /* AHCI_IRQ_MODE_AFTER */ unit = irq->r_irq_rid - 1; is = ATA_INL(ctlr->r_mem, AHCI_IS); } /* CCC interrupt is edge triggered. */ if (ctlr->ccc) ise = 1 << ctlr->cccv; /* Some controllers have edge triggered IS. */ if (ctlr->quirks & AHCI_Q_EDGEIS) ise |= is; if (ise != 0) ATA_OUTL(ctlr->r_mem, AHCI_IS, ise); for (; unit < ctlr->channels; unit++) { if ((is & (1 << unit)) != 0 && (arg = ctlr->interrupt[unit].argument)) { ctlr->interrupt[unit].function(arg); } } /* AHCI declares level triggered IS. */ if (!(ctlr->quirks & AHCI_Q_EDGEIS)) ATA_OUTL(ctlr->r_mem, AHCI_IS, is); } /* * Simplified interrupt handler for multivector MSI mode. */ static void ahci_intr_one(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; void *arg; int unit; unit = irq->r_irq_rid - 1; /* Some controllers have edge triggered IS. */ if (ctlr->quirks & AHCI_Q_EDGEIS) ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); if ((arg = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(arg); /* AHCI declares level triggered IS. */ if (!(ctlr->quirks & AHCI_Q_EDGEIS)) ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); } static struct resource * ahci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = ((struct ahci_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = AHCI_OFFSET + (unit << 7); long st; switch (type) { case SYS_RES_MEMORY: st = rman_get_start(ctlr->r_mem); res = rman_reserve_resource(&ctlr->sc_iomem, st + offset, st + offset + 127, 128, RF_ACTIVE, child); if (res) { bus_space_handle_t bsh; bus_space_tag_t bst; bsh = rman_get_bushandle(ctlr->r_mem); bst = rman_get_bustag(ctlr->r_mem); bus_space_subregion(bst, bsh, offset, 128, &bsh); rman_set_bushandle(res, bsh); rman_set_bustag(res, bst); } break; case SYS_RES_IRQ: if (*rid == ATA_IRQ_RID) res = ctlr->irqs[0].r_irq; break; } return (res); } static int ahci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { switch (type) { case SYS_RES_MEMORY: rman_release_resource(r); return (0); case SYS_RES_IRQ: if (rid != ATA_IRQ_RID) return ENOENT; return (0); } return (EINVAL); } static int ahci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); if (filter != NULL) { printf("ahci.c: we cannot use a filter here\n"); return (EINVAL); } ctlr->interrupt[unit].function = function; ctlr->interrupt[unit].argument = argument; return (0); } static int ahci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); ctlr->interrupt[unit].function = NULL; ctlr->interrupt[unit].argument = NULL; return (0); } static int ahci_print_child(device_t dev, device_t child) { int retval; retval = bus_print_child_header(dev, child); retval += printf(" at channel %d", (int)(intptr_t)device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } devclass_t ahci_devclass; static device_method_t ahci_methods[] = { DEVMETHOD(device_probe, ahci_probe), DEVMETHOD(device_attach, ahci_attach), DEVMETHOD(device_detach, ahci_detach), DEVMETHOD(device_suspend, ahci_suspend), DEVMETHOD(device_resume, ahci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), { 0, 0 } }; static driver_t ahci_driver = { "ahci", ahci_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0); static device_method_t ahci_ata_methods[] = { DEVMETHOD(device_probe, ahci_ata_probe), DEVMETHOD(device_attach, ahci_attach), DEVMETHOD(device_detach, ahci_detach), DEVMETHOD(device_suspend, ahci_suspend), DEVMETHOD(device_resume, ahci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), { 0, 0 } }; static driver_t ahci_ata_driver = { "ahci", ahci_ata_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, atapci, ahci_ata_driver, ahci_devclass, 0, 0); MODULE_VERSION(ahci, 1); MODULE_DEPEND(ahci, cam, 1, 1, 1); static int ahci_ch_probe(device_t dev) { device_set_desc_copy(dev, "AHCI channel"); return (0); } static int ahci_ch_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ahci_channel *ch = device_get_softc(dev); struct cam_devq *devq; int rid, error, i, sata_rev = 0; u_int32_t version; ch->dev = dev; ch->unit = (intptr_t)device_get_ivars(dev); ch->caps = ctlr->caps; ch->caps2 = ctlr->caps2; ch->quirks = ctlr->quirks; ch->numslots = ((ch->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1, mtx_init(&ch->mtx, "AHCI channel lock", NULL, MTX_DEF); resource_int_value(device_get_name(dev), device_get_unit(dev), "pm_level", &ch->pm_level); if (ch->pm_level > 3) callout_init_mtx(&ch->pm_timer, &ch->mtx, 0); /* Limit speed for my onboard JMicron external port. * It is not eSATA really. */ if (pci_get_devid(ctlr->dev) == 0x2363197b && pci_get_subvendor(ctlr->dev) == 0x1043 && pci_get_subdevice(ctlr->dev) == 0x81e4 && ch->unit == 0) sata_rev = 1; if (ch->quirks & AHCI_Q_SATA2) sata_rev = 2; resource_int_value(device_get_name(dev), device_get_unit(dev), "sata_rev", &sata_rev); for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; ch->user[i].bytecount = 8192; ch->user[i].tags = ch->numslots; ch->user[i].caps = 0; ch->curr[i] = ch->user[i]; if (ch->pm_level) { ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ | CTS_SATA_CAPS_H_APST | CTS_SATA_CAPS_D_PMREQ | CTS_SATA_CAPS_D_APST; } ch->user[i].caps |= CTS_SATA_CAPS_H_DMAAA; } rid = ch->unit; if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); ahci_dmainit(dev); ahci_slotsalloc(dev); - ahci_ch_resume(dev); + ahci_ch_init(dev); mtx_lock(&ch->mtx); rid = ATA_IRQ_RID; if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); device_printf(dev, "Unable to map interrupt\n"); return (ENXIO); } if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ahci_ch_intr_locked, dev, &ch->ih))) { device_printf(dev, "Unable to setup interrupt\n"); error = ENXIO; goto err1; } ch->chcaps = ATA_INL(ch->r_mem, AHCI_P_CMD); version = ATA_INL(ctlr->r_mem, AHCI_VS); if (version < 0x00010020 && (ctlr->caps & AHCI_CAP_FBSS)) ch->chcaps |= AHCI_P_CMD_FBSCP; if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s\n", (ch->chcaps & AHCI_P_CMD_HPCP) ? " HPCP":"", (ch->chcaps & AHCI_P_CMD_MPSP) ? " MPSP":"", (ch->chcaps & AHCI_P_CMD_CPD) ? " CPD":"", (ch->chcaps & AHCI_P_CMD_ESP) ? " ESP":"", (ch->chcaps & AHCI_P_CMD_FBSCP) ? " FBSCP":""); } /* Create the device queue for our SIM. */ devq = cam_simq_alloc(ch->numslots); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ahciaction, ahcipoll, "ahcich", ch, device_get_unit(dev), &ch->mtx, min(2, ch->numslots), (ch->caps & AHCI_CAP_SNCQ) ? ch->numslots : 0, devq); if (ch->sim == NULL) { device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err2; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } if (ch->pm_level > 3) { callout_reset(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8, ahci_ch_pm, dev); } mtx_unlock(&ch->mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_unlock(&ch->mtx); return (error); } static int ahci_ch_detach(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->mtx); if (ch->pm_level > 3) callout_drain(&ch->pm_timer); bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); - ahci_ch_suspend(dev); + ahci_ch_deinit(dev); ahci_slotsfree(dev); ahci_dmafini(dev); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_destroy(&ch->mtx); return (0); } static int -ahci_ch_suspend(device_t dev) +ahci_ch_init(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); + uint64_t work; + /* Disable port interrupts */ + ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); + /* Setup work areas */ + work = ch->dma.work_bus + AHCI_CL_OFFSET; + ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff); + ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32); + work = ch->dma.rfis_bus; + ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff); + ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32); + /* Activate the channel and power/spin up device */ + ATA_OUTL(ch->r_mem, AHCI_P_CMD, + (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD | + ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) | + ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 ))); + ahci_start_fr(dev); + ahci_start(dev, 1); + return (0); +} + +static int +ahci_ch_deinit(device_t dev) +{ + struct ahci_channel *ch = device_get_softc(dev); + /* Disable port interrupts. */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset command register. */ ahci_stop(dev); ahci_stop_fr(dev); ATA_OUTL(ch->r_mem, AHCI_P_CMD, 0); /* Allow everything, including partial and slumber modes. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, 0); /* Request slumber mode transition and give some time to get there. */ ATA_OUTL(ch->r_mem, AHCI_P_CMD, AHCI_P_CMD_SLUMBER); DELAY(100); /* Disable PHY. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } static int +ahci_ch_suspend(device_t dev) +{ + struct ahci_channel *ch = device_get_softc(dev); + + mtx_lock(&ch->mtx); + xpt_freeze_simq(ch->sim, 1); + while (ch->oslots) + msleep(ch, &ch->mtx, PRIBIO, "ahcisusp", hz/100); + ahci_ch_deinit(dev); + mtx_unlock(&ch->mtx); + return (0); +} + +static int ahci_ch_resume(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); - uint64_t work; - /* Disable port interrupts */ - ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); - /* Setup work areas */ - work = ch->dma.work_bus + AHCI_CL_OFFSET; - ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff); - ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32); - work = ch->dma.rfis_bus; - ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff); - ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32); - /* Activate the channel and power/spin up device */ - ATA_OUTL(ch->r_mem, AHCI_P_CMD, - (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD | - ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) | - ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 ))); - ahci_start_fr(dev); - ahci_start(dev, 1); + mtx_lock(&ch->mtx); + ahci_ch_init(dev); + ahci_reset(dev); + xpt_release_simq(ch->sim, TRUE); + mtx_unlock(&ch->mtx); return (0); } devclass_t ahcich_devclass; static device_method_t ahcich_methods[] = { DEVMETHOD(device_probe, ahci_ch_probe), DEVMETHOD(device_attach, ahci_ch_attach), DEVMETHOD(device_detach, ahci_ch_detach), DEVMETHOD(device_suspend, ahci_ch_suspend), DEVMETHOD(device_resume, ahci_ch_resume), { 0, 0 } }; static driver_t ahcich_driver = { "ahcich", ahcich_methods, sizeof(struct ahci_channel) }; DRIVER_MODULE(ahcich, ahci, ahcich_driver, ahcich_devclass, 0, 0); struct ahci_dc_cb_args { bus_addr_t maddr; int error; }; static void ahci_dmainit(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_dc_cb_args dcba; size_t rfsize; if (ch->caps & AHCI_CAP_64BIT) ch->dma.max_address = BUS_SPACE_MAXADDR; else ch->dma.max_address = BUS_SPACE_MAXADDR_32BIT; /* Command area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_WORK_SIZE, 1, AHCI_WORK_SIZE, 0, NULL, NULL, &ch->dma.work_tag)) goto error; if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, 0, &ch->dma.work_map)) goto error; if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map, ch->dma.work, AHCI_WORK_SIZE, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); goto error; } ch->dma.work_bus = dcba.maddr; /* FIS receive area. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) rfsize = 4096; else rfsize = 256; if (bus_dma_tag_create(bus_get_dma_tag(dev), rfsize, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, rfsize, 1, rfsize, 0, NULL, NULL, &ch->dma.rfis_tag)) goto error; if (bus_dmamem_alloc(ch->dma.rfis_tag, (void **)&ch->dma.rfis, 0, &ch->dma.rfis_map)) goto error; if (bus_dmamap_load(ch->dma.rfis_tag, ch->dma.rfis_map, ch->dma.rfis, rfsize, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); goto error; } ch->dma.rfis_bus = dcba.maddr; /* Data area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_SG_ENTRIES * PAGE_SIZE * ch->numslots, AHCI_SG_ENTRIES, AHCI_PRD_MAX, 0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) { goto error; } return; error: device_printf(dev, "WARNING - DMA initialization failed\n"); ahci_dmafini(dev); } static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_dc_cb_args *dcba = (struct ahci_dc_cb_args *)xsc; if (!(dcba->error = error)) dcba->maddr = segs[0].ds_addr; } static void ahci_dmafini(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); if (ch->dma.data_tag) { bus_dma_tag_destroy(ch->dma.data_tag); ch->dma.data_tag = NULL; } if (ch->dma.rfis_bus) { bus_dmamap_unload(ch->dma.rfis_tag, ch->dma.rfis_map); bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); ch->dma.rfis_bus = 0; ch->dma.rfis_map = NULL; ch->dma.rfis = NULL; } if (ch->dma.work_bus) { bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map); bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); ch->dma.work_bus = 0; ch->dma.work_map = NULL; ch->dma.work = NULL; } if (ch->dma.work_tag) { bus_dma_tag_destroy(ch->dma.work_tag); ch->dma.work_tag = NULL; } } static void ahci_slotsalloc(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Alloc and setup command/dma slots */ bzero(ch->slot, sizeof(ch->slot)); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; slot->dev = dev; slot->slot = i; slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; callout_init_mtx(&slot->timeout, &ch->mtx, 0); if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map)) device_printf(ch->dev, "FAILURE - create data_map\n"); } } static void ahci_slotsfree(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Free all dma slots */ for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; callout_drain(&slot->timeout); if (slot->dma.data_map) { bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map); slot->dma.data_map = NULL; } } } static void ahci_phy_check_events(device_t dev, u_int32_t serr) { struct ahci_channel *ch = device_get_softc(dev); if ((serr & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) { u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS); union ccb *ccb; if (bootverbose) { if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) { device_printf(dev, "CONNECT requested\n"); } else device_printf(dev, "DISCONNECT requested\n"); } ahci_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } } static void ahci_notify_events(device_t dev, u_int32_t status) { struct ahci_channel *ch = device_get_softc(dev); struct cam_path *dpath; int i; if (ch->caps & AHCI_CAP_SSNTF) ATA_OUTL(ch->r_mem, AHCI_P_SNTF, status); if (bootverbose) device_printf(dev, "SNTF 0x%04x\n", status); for (i = 0; i < 16; i++) { if ((status & (1 << i)) == 0) continue; if (xpt_create_path(&dpath, NULL, xpt_path_path_id(ch->path), i, 0) == CAM_REQ_CMP) { xpt_async(AC_SCSI_AEN, dpath, NULL); xpt_free_path(dpath); } } } static void ahci_ch_intr_locked(void *data) { device_t dev = (device_t)data; struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); ahci_ch_intr(data); mtx_unlock(&ch->mtx); } static void ahci_ch_pm(void *arg) { device_t dev = (device_t)arg; struct ahci_channel *ch = device_get_softc(dev); uint32_t work; if (ch->numrslots != 0) return; work = ATA_INL(ch->r_mem, AHCI_P_CMD); if (ch->pm_level == 4) work |= AHCI_P_CMD_PARTIAL; else work |= AHCI_P_CMD_SLUMBER; ATA_OUTL(ch->r_mem, AHCI_P_CMD, work); } static void ahci_ch_intr(void *data) { device_t dev = (device_t)data; struct ahci_channel *ch = device_get_softc(dev); uint32_t istatus, sstatus, cstatus, serr = 0, sntf = 0, ok, err; enum ahci_err_type et; int i, ccs, port; /* Read and clear interrupt statuses. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); if (istatus == 0) return; ATA_OUTL(ch->r_mem, AHCI_P_IS, istatus); /* Read command statuses. */ sstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); cstatus = ATA_INL(ch->r_mem, AHCI_P_CI); if (istatus & AHCI_P_IX_SDB) { if (ch->caps & AHCI_CAP_SSNTF) sntf = ATA_INL(ch->r_mem, AHCI_P_SNTF); else if (ch->fbs_enabled) { u_int8_t *fis = ch->dma.rfis + 0x58; for (i = 0; i < 16; i++) { if (fis[1] & 0x80) { fis[1] &= 0x7f; sntf |= 1 << i; } fis += 256; } } else { u_int8_t *fis = ch->dma.rfis + 0x58; if (fis[1] & 0x80) sntf = (1 << (fis[1] & 0x0f)); } } /* Process PHY events */ if (istatus & (AHCI_P_IX_PC | AHCI_P_IX_PRC | AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { serr = ATA_INL(ch->r_mem, AHCI_P_SERR); if (serr) { ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr); ahci_phy_check_events(dev, serr); } } /* Process command errors */ if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; //device_printf(dev, "%s ERROR is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x fbs %08x ccs %d\n", // __func__, istatus, cstatus, sstatus, ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), // serr, ATA_INL(ch->r_mem, AHCI_P_FBS), ccs); port = -1; if (ch->fbs_enabled) { uint32_t fbs = ATA_INL(ch->r_mem, AHCI_P_FBS); if (fbs & AHCI_P_FBS_SDE) { port = (fbs & AHCI_P_FBS_DWE) >> AHCI_P_FBS_DWE_SHIFT; } else { for (i = 0; i < 16; i++) { if (ch->numrslotspd[i] == 0) continue; if (port == -1) port = i; else if (port != i) { port = -2; break; } } } } err = ch->rslots & (cstatus | sstatus); } else { ccs = 0; err = 0; port = -1; } /* Complete all successfull commands. */ ok = ch->rslots & ~(cstatus | sstatus); for (i = 0; i < ch->numslots; i++) { if ((ok >> i) & 1) ahci_end_transaction(&ch->slot[i], AHCI_ERR_NONE); } /* On error, complete the rest of commands with error statuses. */ if (err) { if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } for (i = 0; i < ch->numslots; i++) { /* XXX: reqests in loading state. */ if (((err >> i) & 1) == 0) continue; if (port >= 0 && ch->slot[i].ccb->ccb_h.target_id != port) continue; if (istatus & AHCI_P_IX_TFE) { if (port != -2) { /* Task File Error */ if (ch->numtslotspd[ ch->slot[i].ccb->ccb_h.target_id] == 0) { /* Untagged operation. */ if (i == ccs) et = AHCI_ERR_TFE; else et = AHCI_ERR_INNOCENT; } else { /* Tagged operation. */ et = AHCI_ERR_NCQ; } } else { et = AHCI_ERR_TFE; ch->fatalerr = 1; } } else if (istatus & AHCI_P_IX_IF) { if (ch->numtslots == 0 && i != ccs && port != -2) et = AHCI_ERR_INNOCENT; else et = AHCI_ERR_SATA; } else et = AHCI_ERR_INVALID; ahci_end_transaction(&ch->slot[i], et); } /* * We can't reinit port if there are some other * commands active, use resume to complete them. */ if (ch->rslots != 0) ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | AHCI_P_FBS_DEC); } /* Process NOTIFY events */ if (sntf) ahci_notify_events(dev, sntf); } /* Must be called with channel locked. */ static int ahci_check_collision(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); int t = ccb->ccb_h.target_id; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { /* Tagged command while we have no supported tag free. */ if (((~ch->oslots) & (0xffffffff >> (32 - ch->curr[t].tags))) == 0) return (1); /* If we have FBS */ if (ch->fbs_enabled) { /* Tagged command while untagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] == 0) return (1); } else { /* Tagged command while untagged are active. */ if (ch->numrslots != 0 && ch->numtslots == 0) return (1); /* Tagged command while tagged to other target is active. */ if (ch->numtslots != 0 && ch->taggedtarget != ccb->ccb_h.target_id) return (1); } } else { /* If we have FBS */ if (ch->fbs_enabled) { /* Untagged command while tagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] != 0) return (1); } else { /* Untagged command while tagged are active. */ if (ch->numrslots != 0 && ch->numtslots != 0) return (1); } } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) { /* Atomic command while anything active. */ if (ch->numrslots != 0) return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) return (1); return (0); } /* Must be called with channel locked. */ static void ahci_begin_transaction(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_slot *slot; int tag, tags; /* Choose empty slot. */ tags = ch->numslots; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) tags = ch->curr[ccb->ccb_h.target_id].tags; tag = ch->lastslot; while (1) { if (tag >= tags) tag = 0; if (ch->slot[tag].state == AHCI_SLOT_EMPTY) break; tag++; }; ch->lastslot = tag; /* Occupy chosen slot. */ slot = &ch->slot[tag]; slot->ccb = ccb; /* Stop PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3) callout_stop(&ch->pm_timer); /* Update channel stats. */ ch->oslots |= (1 << slot->slot); ch->numrslots++; ch->numrslotspd[ccb->ccb_h.target_id]++; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots++; ch->numtslotspd[ccb->ccb_h.target_id]++; ch->taggedtarget = ccb->ccb_h.target_id; } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) ch->aslots |= (1 << slot->slot); slot->dma.nsegs = 0; /* If request moves data, setup and load SG list */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { void *buf; bus_size_t size; slot->state = AHCI_SLOT_LOADING; if (ccb->ccb_h.func_code == XPT_ATA_IO) { buf = ccb->ataio.data_ptr; size = ccb->ataio.dxfer_len; } else { buf = ccb->csio.data_ptr; size = ccb->csio.dxfer_len; } bus_dmamap_load(ch->dma.data_tag, slot->dma.data_map, buf, size, ahci_dmasetprd, slot, 0); } else ahci_execute_transaction(slot); } /* Locked by busdma engine. */ static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_slot *slot = arg; struct ahci_channel *ch = device_get_softc(slot->dev); struct ahci_cmd_tab *ctp; struct ahci_dma_prd *prd; int i; if (error) { device_printf(slot->dev, "DMA load error\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } KASSERT(nsegs <= AHCI_SG_ENTRIES, ("too many DMA segment entries\n")); /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Fill S/G table */ prd = &ctp->prd_tab[0]; for (i = 0; i < nsegs; i++) { prd[i].dba = htole64(segs[i].ds_addr); prd[i].dbc = htole32((segs[i].ds_len - 1) & AHCI_PRD_MASK); } slot->dma.nsegs = nsegs; bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, ((slot->ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); ahci_execute_transaction(slot); } /* Must be called with channel locked. */ static void ahci_execute_transaction(struct ahci_slot *slot) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); struct ahci_cmd_tab *ctp; struct ahci_cmd_list *clp; union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; int fis_size, i; uint8_t *fis = ch->dma.rfis + 0x40; uint8_t val; /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Setup the FIS for this request */ if (!(fis_size = ahci_setup_fis(dev, ctp, ccb, slot->slot))) { device_printf(ch->dev, "Setting up SATA FIS failed\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } /* Setup the command list entry */ clp = (struct ahci_cmd_list *) (ch->dma.work + AHCI_CL_OFFSET + (AHCI_CL_SIZE * slot->slot)); clp->prd_length = slot->dma.nsegs; clp->cmd_flags = (ccb->ccb_h.flags & CAM_DIR_OUT ? AHCI_CMD_WRITE : 0) | (ccb->ccb_h.func_code == XPT_SCSI_IO ? (AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH) : 0) | (fis_size / sizeof(u_int32_t)) | (port << 12); /* Special handling for Soft Reset command. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL)) { if (ccb->ataio.cmd.control & ATA_A_RESET) { /* Kick controller into sane state */ ahci_stop(dev); ahci_clo(dev); ahci_start(dev, 0); clp->cmd_flags |= AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY; } else { /* Prepare FIS receive area for check. */ for (i = 0; i < 20; i++) fis[i] = 0xff; } } clp->bytecount = 0; clp->cmd_table_phys = htole64(ch->dma.work_bus + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_PREREAD); /* Set ACTIVE bit for NCQ commands. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ATA_OUTL(ch->r_mem, AHCI_P_SACT, 1 << slot->slot); } /* If FBS is enabled, set PMP port. */ if (ch->fbs_enabled) { ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | (port << AHCI_P_FBS_DEV_SHIFT)); } /* Issue command to the controller. */ slot->state = AHCI_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); ATA_OUTL(ch->r_mem, AHCI_P_CI, (1 << slot->slot)); /* Device reset commands doesn't interrupt. Poll them. */ if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.command == ATA_DEVICE_RESET || (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL))) { int count, timeout = ccb->ccb_h.timeout; enum ahci_err_type et = AHCI_ERR_NONE; for (count = 0; count < timeout; count++) { DELAY(1000); if (!(ATA_INL(ch->r_mem, AHCI_P_CI) & (1 << slot->slot))) break; if (ATA_INL(ch->r_mem, AHCI_P_TFD) & ATA_S_ERROR) { device_printf(ch->dev, "Poll error on slot %d, TFD: %04x\n", slot->slot, ATA_INL(ch->r_mem, AHCI_P_TFD)); et = AHCI_ERR_TFE; break; } /* Workaround for ATI SB600/SB700 chipsets. */ if (ccb->ccb_h.target_id == 15 && pci_get_vendor(device_get_parent(dev)) == 0x1002 && (ATA_INL(ch->r_mem, AHCI_P_IS) & AHCI_P_IX_IPM)) { et = AHCI_ERR_TIMEOUT; break; } } if (timeout && (count >= timeout)) { device_printf(ch->dev, "Poll timeout on slot %d\n", slot->slot); device_printf(dev, "is %08x cs %08x ss %08x " "rs %08x tfd %02x serr %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR)); et = AHCI_ERR_TIMEOUT; } /* Marvell controllers do not wait for readyness. */ if ((ch->quirks & AHCI_Q_NOBSYRES) && (ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) == 0) { while ((val = fis[2]) & (ATA_S_BUSY | ATA_S_DRQ)) { DELAY(1000); if (count++ >= timeout) { device_printf(dev, "device is not " "ready after soft-reset: " "tfd = %08x\n", val); et = AHCI_ERR_TIMEOUT; break; } } } ahci_end_transaction(slot, et); /* Kick controller into sane state and enable FBS. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) == 0) { ahci_stop(ch->dev); ahci_start(ch->dev, 1); } return; } /* Start command execution timeout */ callout_reset(&slot->timeout, (int)ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); return; } /* Must be called with channel locked. */ static void ahci_process_timeout(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_TIMEOUT); } } /* Must be called with channel locked. */ static void ahci_rearm_timeout(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; /* Do we have a running request on slot? */ if (slot->state < AHCI_SLOT_RUNNING) continue; if ((ch->toslots & (1 << i)) == 0) continue; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); } } /* Locked by callout mechanism. */ static void ahci_timeout(struct ahci_slot *slot) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); uint32_t sstatus; int ccs; int i; /* Check for stale timeout. */ if (slot->state < AHCI_SLOT_RUNNING) return; /* Check if slot was not being executed last time we checked. */ if (slot->state < AHCI_SLOT_EXECUTING) { /* Check if slot started executing. */ sstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; if ((sstatus & (1 << slot->slot)) != 0 || ccs == slot->slot || ch->fbs_enabled) slot->state = AHCI_SLOT_EXECUTING; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); return; } device_printf(dev, "Timeout on slot %d\n", slot->slot); device_printf(dev, "is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR)); /* Handle frozen command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } if (!ch->fbs_enabled) { /* Without FBS we know real timeout source. */ ch->fatalerr = 1; /* Handle command with timeout. */ ahci_end_transaction(&ch->slot[slot->slot], AHCI_ERR_TIMEOUT); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } } else { /* With FBS we wait for other commands timeout and pray. */ if (ch->toslots == 0) xpt_freeze_simq(ch->sim, 1); ch->toslots |= (1 << slot->slot); if ((ch->rslots & ~ch->toslots) == 0) ahci_process_timeout(dev); else device_printf(dev, " ... waiting for slots %08x\n", ch->rslots & ~ch->toslots); } } /* Must be called with channel locked. */ static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); union ccb *ccb = slot->ccb; bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_POSTWRITE); /* Read result registers to the result struct * May be incorrect if several commands finished same time, * so read only when sure or have to. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { struct ata_res *res = &ccb->ataio.res; if ((et == AHCI_ERR_TFE) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT)) { u_int8_t *fis = ch->dma.rfis + 0x40; bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_POSTREAD); if (ch->fbs_enabled) { fis += ccb->ccb_h.target_id * 256; res->status = fis[2]; res->error = fis[3]; } else { uint16_t tfd = ATA_INL(ch->r_mem, AHCI_P_TFD); res->status = tfd; res->error = tfd >> 8; } res->lba_low = fis[4]; res->lba_mid = fis[5]; res->lba_high = fis[6]; res->device = fis[7]; res->lba_low_exp = fis[8]; res->lba_mid_exp = fis[9]; res->lba_high_exp = fis[10]; res->sector_count = fis[12]; res->sector_count_exp = fis[13]; } else bzero(res, sizeof(*res)); } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, (ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ch->dma.data_tag, slot->dma.data_map); } if (et != AHCI_ERR_NONE) ch->eslots |= (1 << slot->slot); /* In case of error, freeze device for proper recovery. */ if ((et != AHCI_ERR_NONE) && (!ch->readlog) && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } /* Set proper result status. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (et) { case AHCI_ERR_NONE: ccb->ccb_h.status |= CAM_REQ_CMP; if (ccb->ccb_h.func_code == XPT_SCSI_IO) ccb->csio.scsi_status = SCSI_STATUS_OK; break; case AHCI_ERR_INVALID: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_INVALID; break; case AHCI_ERR_INNOCENT: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; case AHCI_ERR_TFE: case AHCI_ERR_NCQ: if (ccb->ccb_h.func_code == XPT_SCSI_IO) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } break; case AHCI_ERR_SATA: ch->fatalerr = 1; if (!ch->readlog) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_UNCOR_PARITY; break; case AHCI_ERR_TIMEOUT: if (!ch->readlog) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; default: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } /* Free slot. */ ch->oslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot); if (et != AHCI_ERR_TIMEOUT) { if (ch->toslots == (1 << slot->slot)) xpt_release_simq(ch->sim, TRUE); ch->toslots &= ~(1 << slot->slot); } slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; /* Update channel stats. */ ch->numrslots--; ch->numrslotspd[ccb->ccb_h.target_id]--; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots--; ch->numtslotspd[ccb->ccb_h.target_id]--; } /* If it was first request of reset sequence and there is no error, * proceed to second request. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) && et == AHCI_ERR_NONE) { ccb->ataio.cmd.control &= ~ATA_A_RESET; ahci_begin_transaction(dev, ccb); return; } /* If it was our READ LOG command - process it. */ if (ch->readlog) { ahci_process_read_log(dev, ccb); /* If it was NCQ command error, put result on hold. */ } else if (et == AHCI_ERR_NCQ) { ch->hold[slot->slot] = ccb; ch->numhslots++; } else xpt_done(ccb); /* Unfreeze frozen command. */ if (ch->frozen && !ahci_check_collision(dev, ch->frozen)) { union ccb *fccb = ch->frozen; ch->frozen = NULL; ahci_begin_transaction(dev, fccb); xpt_release_simq(ch->sim, TRUE); } /* If we have no other active commands, ... */ if (ch->rslots == 0) { /* if there was fatal error - reset port. */ if (ch->toslots != 0 || ch->fatalerr) { ahci_reset(dev); } else { /* if we have slots in error, we can reinit port. */ if (ch->eslots != 0) { ahci_stop(dev); ahci_start(dev, 1); } /* if there commands on hold, we can do READ LOG. */ if (!ch->readlog && ch->numhslots) ahci_issue_read_log(dev); } /* If all the rest of commands are in timeout - give them chance. */ } else if ((ch->rslots & ~ch->toslots) == 0 && et != AHCI_ERR_TIMEOUT) ahci_rearm_timeout(dev); /* Start PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3 && (ch->curr[ch->pm_present ? 15 : 0].caps & CTS_SATA_CAPS_D_PMREQ)) { callout_schedule(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8); } } static void ahci_issue_read_log(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); union ccb *ccb; struct ccb_ataio *ataio; int i; ch->readlog = 1; /* Find some holden command. */ for (i = 0; i < ch->numslots; i++) { if (ch->hold[i]) break; } ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { device_printf(dev, "Unable allocate READ LOG command"); return; /* XXX */ } ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */ ccb->ccb_h.func_code = XPT_ATA_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ ataio = &ccb->ataio; ataio->data_ptr = malloc(512, M_AHCI, M_NOWAIT); if (ataio->data_ptr == NULL) { device_printf(dev, "Unable allocate memory for READ LOG command"); return; /* XXX */ } ataio->dxfer_len = 512; bzero(&ataio->cmd, sizeof(ataio->cmd)); ataio->cmd.flags = CAM_ATAIO_48BIT; ataio->cmd.command = 0x2F; /* READ LOG EXT */ ataio->cmd.sector_count = 1; ataio->cmd.sector_count_exp = 0; ataio->cmd.lba_low = 0x10; ataio->cmd.lba_mid = 0; ataio->cmd.lba_mid_exp = 0; /* Freeze SIM while doing READ LOG EXT. */ xpt_freeze_simq(ch->sim, 1); ahci_begin_transaction(dev, ccb); } static void ahci_process_read_log(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); uint8_t *data; struct ata_res *res; int i; ch->readlog = 0; data = ccb->ataio.data_ptr; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (data[0] & 0x80) == 0) { for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; if ((data[0] & 0x1F) == i) { res = &ch->hold[i]->ataio.res; res->status = data[2]; res->error = data[3]; res->lba_low = data[4]; res->lba_mid = data[5]; res->lba_high = data[6]; res->device = data[7]; res->lba_low_exp = data[8]; res->lba_mid_exp = data[9]; res->lba_high_exp = data[10]; res->sector_count = data[12]; res->sector_count_exp = data[13]; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_REQUEUE_REQ; } xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } else { if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) device_printf(dev, "Error while READ LOG EXT\n"); else if ((data[0] & 0x80) == 0) { device_printf(dev, "Non-queued command error in READ LOG EXT\n"); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } free(ccb->ataio.data_ptr, M_AHCI); xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static void ahci_start(device_t dev, int fbs) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xFFFFFFFF); /* Clear any interrupts pending on this channel */ ATA_OUTL(ch->r_mem, AHCI_P_IS, 0xFFFFFFFF); /* Configure FIS-based switching if supported. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) { ch->fbs_enabled = (fbs && ch->pm_present) ? 1 : 0; ATA_OUTL(ch->r_mem, AHCI_P_FBS, ch->fbs_enabled ? AHCI_P_FBS_EN : 0); } /* Start operations on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd &= ~AHCI_P_CMD_PMA; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_ST | (ch->pm_present ? AHCI_P_CMD_PMA : 0)); } static void ahci_stop(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Kill all activity on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_ST); /* Wait for activity stop. */ timeout = 0; do { DELAY(1000); if (timeout++ > 1000) { device_printf(dev, "stopping AHCI engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CR); ch->eslots = 0; } static void ahci_clo(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Issue Command List Override if supported */ if (ch->caps & AHCI_CAP_SCLO) { cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd |= AHCI_P_CMD_CLO; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd); timeout = 0; do { DELAY(1000); if (timeout++ > 1000) { device_printf(dev, "executing CLO failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CLO); } } static void ahci_stop_fr(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Kill all FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_FRE); /* Wait for FIS reception stop. */ timeout = 0; do { DELAY(1000); if (timeout++ > 1000) { device_printf(dev, "stopping AHCI FR engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_FR); } static void ahci_start_fr(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; /* Start FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_FRE); } static int ahci_wait_ready(device_t dev, int t) { struct ahci_channel *ch = device_get_softc(dev); int timeout = 0; uint32_t val; while ((val = ATA_INL(ch->r_mem, AHCI_P_TFD)) & (ATA_S_BUSY | ATA_S_DRQ)) { DELAY(1000); if (timeout++ > t) { device_printf(dev, "device is not ready (timeout %dms) " "tfd = %08x\n", t, val); return (EBUSY); } } if (bootverbose) device_printf(dev, "ready wait time=%dms\n", timeout); return (0); } static void ahci_reset(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev)); int i; xpt_freeze_simq(ch->sim, 1); if (bootverbose) device_printf(dev, "AHCI reset...\n"); /* Requeue freezed command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } /* Kill the engine and requeue all running commands. */ ahci_stop(dev); for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; /* XXX; Commands in loading state. */ ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } if (ch->toslots != 0) xpt_release_simq(ch->sim, TRUE); ch->eslots = 0; ch->toslots = 0; ch->fatalerr = 0; /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); /* Disable port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset and reconnect PHY, */ if (!ahci_sata_phy_reset(dev)) { if (bootverbose) device_printf(dev, "AHCI reset done: phy reset found no device\n"); ch->devices = 0; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (AHCI_P_IX_CPD | AHCI_P_IX_PRC | AHCI_P_IX_PC)); xpt_release_simq(ch->sim, TRUE); return; } /* Wait for clearing busy status. */ if (ahci_wait_ready(dev, 15000)) ahci_clo(dev); ahci_start(dev, 1); ch->devices = 1; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (AHCI_P_IX_CPD | AHCI_P_IX_TFE | AHCI_P_IX_HBF | AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF | ((ch->pm_level == 0) ? AHCI_P_IX_PRC | AHCI_P_IX_PC : 0) | AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) | AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR))); if (bootverbose) device_printf(dev, "AHCI reset done: device found\n"); xpt_release_simq(ch->sim, TRUE); } static int ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag) { struct ahci_channel *ch = device_get_softc(dev); u_int8_t *fis = &ctp->cfis[0]; bzero(ctp->cfis, 64); fis[0] = 0x27; /* host to device */ fis[1] = (ccb->ccb_h.target_id & 0x0f); if (ccb->ccb_h.func_code == XPT_SCSI_IO) { fis[1] |= 0x80; fis[2] = ATA_PACKET_CMD; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) fis[3] = ATA_F_DMA; else { fis[5] = ccb->csio.dxfer_len; fis[6] = ccb->csio.dxfer_len >> 8; } fis[7] = ATA_D_LBA; fis[15] = ATA_A_4BIT; bzero(ctp->acmd, 32); bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, ctp->acmd, ccb->csio.cdb_len); } else if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) == 0) { fis[1] |= 0x80; fis[2] = ccb->ataio.cmd.command; fis[3] = ccb->ataio.cmd.features; fis[4] = ccb->ataio.cmd.lba_low; fis[5] = ccb->ataio.cmd.lba_mid; fis[6] = ccb->ataio.cmd.lba_high; fis[7] = ccb->ataio.cmd.device; fis[8] = ccb->ataio.cmd.lba_low_exp; fis[9] = ccb->ataio.cmd.lba_mid_exp; fis[10] = ccb->ataio.cmd.lba_high_exp; fis[11] = ccb->ataio.cmd.features_exp; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { fis[12] = tag << 3; fis[13] = 0; } else { fis[12] = ccb->ataio.cmd.sector_count; fis[13] = ccb->ataio.cmd.sector_count_exp; } fis[15] = ATA_A_4BIT; } else { fis[15] = ccb->ataio.cmd.control; } return (20); } static int ahci_sata_connect(struct ahci_channel *ch) { u_int32_t status; int timeout; /* Wait up to 100ms for "connect well" */ for (timeout = 0; timeout < 100 ; timeout++) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS); if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) break; if ((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_OFFLINE) { if (bootverbose) { device_printf(ch->dev, "SATA offline status=%08x\n", status); } return (0); } DELAY(1000); } if (timeout >= 100) { if (bootverbose) { device_printf(ch->dev, "SATA connect timeout status=%08x\n", status); } return (0); } if (bootverbose) { device_printf(ch->dev, "SATA connect time=%dms status=%08x\n", timeout, status); } /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xffffffff); return (1); } static int ahci_sata_phy_reset(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int sata_rev; uint32_t val; sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = ATA_SC_SPD_SPEED_GEN1; else if (sata_rev == 2) val = ATA_SC_SPD_SPEED_GEN2; else if (sata_rev == 3) val = ATA_SC_SPD_SPEED_GEN3; else val = 0; ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_RESET | val | ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER); DELAY(5000); ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); DELAY(5000); if (!ahci_sata_connect(ch)) { if (ch->pm_level > 0) ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } return (1); } static int ahci_check_ids(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); if (ccb->ccb_h.target_id > ((ch->caps & AHCI_CAP_SPM) ? 15 : 0)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void ahciaction(struct cam_sim *sim, union ccb *ccb) { device_t dev; struct ahci_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahciaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ahci_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (ahci_check_ids(dev, ccb)) return; if (ch->devices == 0 || (ch->pm_present == 0 && ccb->ccb_h.target_id > 0 && ccb->ccb_h.target_id < 15)) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } /* Check for command collision. */ if (ahci_check_collision(dev, ccb)) { /* Freeze command. */ ch->frozen = ccb; /* We have only one frozen slot, so freeze simq also. */ xpt_freeze_simq(ch->sim, 1); return; } ahci_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; if (ahci_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(ch->numslots, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) ch->pm_present = cts->xport_specific.sata.pm_present; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; uint32_t status; if (ahci_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->proto_specific.valid = 0; cts->xport_specific.sata.valid = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (ccb->ccb_h.target_id == 15 || (ccb->ccb_h.target_id == 0 && !ch->pm_present))) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS) & ATA_SS_SPD_MASK; if (status & 0x0f0) { cts->xport_specific.sata.revision = (status & 0x0f0) >> 4; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; if (ch->pm_level) { if (ch->caps & (AHCI_CAP_PSC | AHCI_CAP_SSC)) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; if (ch->caps2 & AHCI_CAP2_APST) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_APST; } if ((ch->caps & AHCI_CAP_SNCQ) && (ch->quirks & AHCI_Q_NOAA) == 0) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_DMAAA; cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; cts->xport_specific.sata.pm_present = ch->pm_present; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.tags = d->tags; cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS; cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ahci_reset(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; if (ch->caps & AHCI_CAP_SNCQ) cpi->hba_inquiry |= PI_TAG_ABLE; if (ch->caps & AHCI_CAP_SPM) cpi->hba_inquiry |= PI_SATAPM; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (ch->caps & AHCI_CAP_SPM) cpi->max_target = 15; else cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "AHCI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = MAXPHYS; /* ATI SB600 can't handle 256 sectors with FPDMA (NCQ). */ if (pci_get_devid(device_get_parent(dev)) == 0x43801002) cpi->maxio = min(cpi->maxio, 128 * 512); cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void ahcipoll(struct cam_sim *sim) { struct ahci_channel *ch = (struct ahci_channel *)cam_sim_softc(sim); ahci_ch_intr(ch->dev); } Index: stable/8/sys/dev/ata/ata-all.c =================================================================== --- stable/8/sys/dev/ata/ata-all.c (revision 208447) +++ stable/8/sys/dev/ata/ata-all.c (revision 208448) @@ -1,1721 +1,1732 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 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 AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATA_CAM #include #include #include #include #include #endif #ifndef ATA_CAM /* device structure */ static d_ioctl_t ata_ioctl; static struct cdevsw ata_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; #endif /* prototypes */ #ifndef ATA_CAM static void ata_boot_attach(void); static device_t ata_add_child(device_t, struct ata_device *, int); #else static void ataaction(struct cam_sim *sim, union ccb *ccb); static void atapoll(struct cam_sim *sim); #endif static void ata_conn_event(void *, int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); static void ata_interrupt_locked(void *data); /* global vars */ MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer"); int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL; struct intr_config_hook *ata_delayed_attach = NULL; devclass_t ata_devclass; uma_zone_t ata_request_zone; uma_zone_t ata_composite_zone; int ata_wc = 1; int ata_setmax = 0; int ata_dma_check_80pin = 1; /* local vars */ static int ata_dma = 1; static int atapi_dma = 1; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); TUNABLE_INT("hw.ata.ata_dma", &ata_dma); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0, "ATA disk DMA mode control"); TUNABLE_INT("hw.ata.ata_dma_check_80pin", &ata_dma_check_80pin); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma_check_80pin, CTLFLAG_RDTUN, &ata_dma_check_80pin, 1, "Check for 80pin cable before setting ATA DMA mode"); TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0, "ATAPI device DMA mode control"); TUNABLE_INT("hw.ata.wc", &ata_wc); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0, "ATA disk write caching"); TUNABLE_INT("hw.ata.setmax", &ata_setmax); SYSCTL_INT(_hw_ata, OID_AUTO, setmax, CTLFLAG_RDTUN, &ata_setmax, 0, "ATA disk set max native address"); /* * newbus device interface related functions */ int ata_probe(device_t dev) { return 0; } int ata_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); int error, rid; #ifdef ATA_CAM struct cam_devq *devq; int i; #endif /* check that we have a virgin channel to attach */ if (ch->r_irq) return EEXIST; /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); TASK_INIT(&ch->conntask, 0, ata_conn_event, dev); #ifdef ATA_CAM for (i = 0; i < 16; i++) { ch->user[i].mode = 0; if (ch->flags & ATA_SATA) ch->user[i].bytecount = 8192; else ch->user[i].bytecount = MAXPHYS; ch->curr[i] = ch->user[i]; } #endif /* reset the controller HW, the channel and device(s) */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("ataatch", 1); #ifndef ATA_CAM ATA_RESET(dev); #endif ATA_LOCKING(dev, ATA_LF_UNLOCK); /* allocate DMA resources if DMA HW present*/ if (ch->dma.alloc) ch->dma.alloc(dev); /* setup interrupt delivery */ rid = ATA_IRQ_RID; ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (!ch->r_irq) { device_printf(dev, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ata_interrupt, ch, &ch->ih))) { device_printf(dev, "unable to setup interrupt\n"); return error; } #ifndef ATA_CAM /* probe and attach devices on this channel unless we are in early boot */ if (!ata_delayed_attach) ata_identify(dev); return (0); #else mtx_lock(&ch->state_mtx); /* Create the device queue for our SIM. */ devq = cam_simq_alloc(1); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ataaction, atapoll, "ata", ch, device_get_unit(dev), &ch->state_mtx, 1, 0, devq); if (ch->sim == NULL) { device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err2; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } mtx_unlock(&ch->state_mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); mtx_unlock(&ch->state_mtx); return (error); #endif } int ata_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); #ifndef ATA_CAM device_t *children; int nchildren, i; #endif /* check that we have a valid channel to detach */ if (!ch->r_irq) return ENXIO; /* grap the channel lock so no new requests gets launched */ mtx_lock(&ch->state_mtx); ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); #ifndef ATA_CAM /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } #endif taskqueue_drain(taskqueue_thread, &ch->conntask); #ifdef ATA_CAM mtx_lock(&ch->state_mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->state_mtx); #endif /* release resources */ bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ch->r_irq = NULL; /* free DMA resources if DMA HW present*/ if (ch->dma.free) ch->dma.free(dev); mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } static void ata_conn_event(void *context, int dummy) { device_t dev = (device_t)context; #ifdef ATA_CAM struct ata_channel *ch = device_get_softc(dev); union ccb *ccb; mtx_lock(&ch->state_mtx); ata_reinit(dev); mtx_unlock(&ch->state_mtx); if ((ccb = xpt_alloc_ccb()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); #else ata_reinit(dev); #endif } int ata_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; #ifndef ATA_CAM device_t *children; int nchildren, i; /* check that we have a valid channel to reinit */ if (!ch || !ch->r_irq) return ENXIO; if (bootverbose) device_printf(dev, "reiniting channel ..\n"); /* poll for locking the channel */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("atarini", 1); /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); if (ch->state & ATA_STALL_QUEUE) { /* Recursive reinits and reinits during detach prohobited. */ mtx_unlock(&ch->state_mtx); return (ENXIO); } if ((request = ch->running)) callout_stop(&request->callout); ch->running = NULL; /* unconditionally grap the channel lock */ ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* reinit the children and delete any that fails */ if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { /* did any children go missing ? */ if (children[i] && device_is_attached(children[i]) && ATA_REINIT(children[i])) { /* * if we had a running request and its device matches * this child we need to inform the request that the * device is gone. */ if (request && request->dev == children[i]) { request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); /* if not timeout finish request here */ if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); request = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* if we still have a good request put it on the queue again */ if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= ATA_R_REQUEUE; ata_queue_request(request); } /* we're done release the channel for new work */ mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); /* Add new children. */ /* ata_identify(dev); */ if (bootverbose) device_printf(dev, "reinit done ..\n"); /* kick off requests on the queue */ ata_start(dev); #else xpt_freeze_simq(ch->sim, 1); if ((request = ch->running)) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; callout_stop(&request->callout); if (ch->dma.unload) ch->dma.unload(request); request->result = ERESTART; ata_cam_end_transaction(dev, request); } /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); xpt_release_simq(ch->sim, TRUE); #endif return(0); } int ata_suspend(device_t dev) { struct ata_channel *ch; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; -#ifndef ATA_CAM +#ifdef ATA_CAM + mtx_lock(&ch->state_mtx); + xpt_freeze_simq(ch->sim, 1); + while (ch->state != ATA_IDLE) + msleep(ch, &ch->state_mtx, PRIBIO, "atasusp", hz/100); + mtx_unlock(&ch->state_mtx); +#else /* wait for the channel to be IDLE or detached before suspending */ while (ch->r_irq) { mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE) { ch->state = ATA_ACTIVE; mtx_unlock(&ch->state_mtx); break; } mtx_unlock(&ch->state_mtx); tsleep(ch, PRIBIO, "atasusp", hz/10); } ATA_LOCKING(dev, ATA_LF_UNLOCK); #endif return(0); } int ata_resume(device_t dev) { + struct ata_channel *ch; int error; /* check for valid device */ - if (!dev || !device_get_softc(dev)) + if (!dev || !(ch = device_get_softc(dev))) return ENXIO; +#ifdef ATA_CAM + mtx_lock(&ch->state_mtx); + error = ata_reinit(dev); + xpt_release_simq(ch->sim, TRUE); + mtx_unlock(&ch->state_mtx); +#else /* reinit the devices, we dont know what mode/state they are in */ error = ata_reinit(dev); - -#ifndef ATA_CAM /* kick off requests on the queue */ ata_start(dev); #endif return error; } void ata_interrupt(void *data) { #ifdef ATA_CAM struct ata_channel *ch = (struct ata_channel *)data; mtx_lock(&ch->state_mtx); #endif ata_interrupt_locked(data); #ifdef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } static void ata_interrupt_locked(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; #ifndef ATA_CAM mtx_lock(&ch->state_mtx); #endif do { /* ignore interrupt if its not for us */ if (ch->hw.status && !ch->hw.status(ch->dev)) break; /* do we have a running request */ if (!(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } /* * we have the HW locks, so end the transaction for this request * if it finishes immediately otherwise wait for next interrupt */ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; #ifdef ATA_CAM ata_cam_end_transaction(ch->dev, request); #else mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); #endif return; } } while (0); #ifndef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } void ata_print_cable(device_t dev, u_int8_t *who) { device_printf(dev, "DMA limited to UDMA33, %s found non-ATA66 cable\n", who); } int ata_check_80pin(device_t dev, int mode) { struct ata_device *atadev = device_get_softc(dev); if (!ata_dma_check_80pin) { if (bootverbose) device_printf(dev, "Skipping 80pin cable check\n"); return mode; } if (mode > ATA_UDMA2 && !(atadev->param.hwres & ATA_CABLE_ID)) { ata_print_cable(dev, "device"); mode = ATA_UDMA2; } return mode; } void ata_setmode(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); int error, mode, pmode; mode = atadev->mode; do { pmode = mode = ata_limit_mode(dev, mode, ATA_DMA_MAX); mode = ATA_SETMODE(device_get_parent(dev), atadev->unit, mode); if ((ch->flags & (ATA_CHECKS_CABLE | ATA_SATA)) == 0) mode = ata_check_80pin(dev, mode); } while (pmode != mode); /* Interate till successfull negotiation. */ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode); if (bootverbose) device_printf(dev, "%ssetting %s\n", (error) ? "FAILURE " : "", ata_mode2str(mode)); atadev->mode = mode; } /* * device related interfaces */ #ifndef ATA_CAM static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag, struct thread *td) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data; int *value = (int *)data; int i, nchildren, error = ENOTTY; switch (cmd) { case IOCATAGMAXCHANNEL: /* In case we have channel 0..n this will return n+1. */ *value = devclass_get_maxunit(ata_devclass); error = 0; break; case IOCATAREINIT: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = ata_reinit(device); break; case IOCATAATTACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_ATTACH(device); break; case IOCATADETACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_DETACH(device); break; case IOCATADEVICES: if (devices->channel >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, devices->channel)) || !device_is_attached(device)) return ENXIO; bzero(devices->name[0], 32); bzero(&devices->params[0], sizeof(struct ata_params)); bzero(devices->name[1], 32); bzero(&devices->params[1], sizeof(struct ata_params)); if (!device_get_children(device, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && device_is_attached(children[i])) { struct ata_device *atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) { /* XXX SOS PM */ strncpy(devices->name[0], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { /* XXX SOS PM */ strncpy(devices->name[1], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[1], sizeof(struct ata_params)); } } } free(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(cmd, data); } return error; } #endif int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) { struct ata_device *atadev = device_get_softc(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data; struct ata_params *params = (struct ata_params *)data; int *mode = (int *)data; struct ata_request *request; caddr_t buf; int error; switch (cmd) { case IOCATAREQUEST: if (ioc_request->count > (ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS)) { return (EFBIG); } if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) { return ENOMEM; } if (!(request = ata_alloc_request())) { free(buf, M_ATA); return ENOMEM; } request->dev = atadev->dev; if (ioc_request->flags & ATA_CMD_WRITE) { error = copyin(ioc_request->data, buf, ioc_request->count); if (error) { free(buf, M_ATA); ata_free_request(request); return error; } } if (ioc_request->flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = ioc_request->u.ata.command; request->u.ata.feature = ioc_request->u.ata.feature; request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; if (ioc_request->flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (ioc_request->flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (ioc_request->flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); if (request->flags & ATA_R_ATAPI) { bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense, sizeof(struct atapi_sense)); } else { ioc_request->u.ata.command = request->u.ata.command; ioc_request->u.ata.feature = request->u.ata.feature; ioc_request->u.ata.lba = request->u.ata.lba; ioc_request->u.ata.count = request->u.ata.count; } ioc_request->error = request->result; if (ioc_request->flags & ATA_CMD_READ) error = copyout(buf, ioc_request->data, ioc_request->count); else error = 0; free(buf, M_ATA); ata_free_request(request); return error; case IOCATAGPARM: ata_getparam(atadev, 0); bcopy(&atadev->param, params, sizeof(struct ata_params)); return 0; case IOCATASMODE: atadev->mode = *mode; ata_setmode(dev); return 0; case IOCATAGMODE: *mode = atadev->mode | (ATA_GETREV(device_get_parent(dev), atadev->unit) << 8); return 0; case IOCATASSPINDOWN: atadev->spindown = *mode; return 0; case IOCATAGSPINDOWN: *mode = atadev->spindown; return 0; default: return ENOTTY; } } #ifndef ATA_CAM static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; mtx_lock(&Giant); /* newbus suckage it needs Giant */ /* kick of probe and attach on all channels */ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) { if ((ch = devclass_get_softc(ata_devclass, ctlr))) { ata_identify(ch->dev); } } /* release the hook that got us here, we are only needed once during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } #endif /* * misc support functions */ #ifndef ATA_CAM static device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit) { device_t child; if ((child = device_add_child(parent, NULL, unit))) { device_set_softc(child, atadev); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; } return child; } #endif int ata_getparam(struct ata_device *atadev, int init) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); struct ata_request *request; u_int8_t command = 0; int error = ENOMEM, retries = 2; if (ch->devices & (ATA_ATA_MASTER << atadev->unit)) command = ATA_ATA_IDENTIFY; if (ch->devices & (ATA_ATAPI_MASTER << atadev->unit)) command = ATA_ATAPI_IDENTIFY; if (!command) return ENXIO; while (retries-- > 0 && error) { if (!(request = ata_alloc_request())) break; request->dev = atadev->dev; request->timeout = 1; request->retries = 0; request->u.ata.command = command; request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT); if (!bootverbose) request->flags |= ATA_R_QUIET; request->data = (void *)&atadev->param; request->bytecount = sizeof(struct ata_params); request->donecount = 0; request->transfersize = DEV_BSIZE; ata_queue_request(request); error = request->result; ata_free_request(request); } if (!error && (isprint(atadev->param.model[0]) || isprint(atadev->param.model[1]))) { struct ata_params *atacap = &atadev->param; int16_t *ptr; for (ptr = (int16_t *)atacap; ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) { *ptr = le16toh(*ptr); } if (!(!strncmp(atacap->model, "FX", 2) || !strncmp(atacap->model, "NEC", 3) || !strncmp(atacap->model, "Pioneer", 7) || !strncmp(atacap->model, "SHARP", 5))) { bswap(atacap->model, sizeof(atacap->model)); bswap(atacap->revision, sizeof(atacap->revision)); bswap(atacap->serial, sizeof(atacap->serial)); } btrim(atacap->model, sizeof(atacap->model)); bpack(atacap->model, atacap->model, sizeof(atacap->model)); btrim(atacap->revision, sizeof(atacap->revision)); bpack(atacap->revision, atacap->revision, sizeof(atacap->revision)); btrim(atacap->serial, sizeof(atacap->serial)); bpack(atacap->serial, atacap->serial, sizeof(atacap->serial)); if (bootverbose) printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n", device_get_unit(ch->dev), ata_unit2str(atadev), ata_mode2str(ata_pmode(atacap)), ata_mode2str(ata_wmode(atacap)), ata_mode2str(ata_umode(atacap)), (atacap->hwres & ATA_CABLE_ID) ? "80":"40"); if (init) { char buffer[64]; sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision); device_set_desc_copy(atadev->dev, buffer); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config != ATA_CFA_MAGIC1) && (atadev->param.config != ATA_CFA_MAGIC2)) { if (atapi_dma && (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR && ata_umode(&atadev->param) >= ATA_UDMA2) atadev->mode = ATA_DMA_MAX; } else { if (ata_dma && (ata_umode(&atadev->param) > 0 || ata_wmode(&atadev->param) > 0)) atadev->mode = ATA_DMA_MAX; } } } else { if (!error) error = ENXIO; } return error; } #ifndef ATA_CAM int ata_identify(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_device *atadev; device_t *children; device_t child, master = NULL; int nchildren, i, n = ch->devices; if (bootverbose) device_printf(dev, "Identifying devices: %08x\n", ch->devices); mtx_lock(&Giant); /* Skip existing devices. */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && (atadev = device_get_softc(children[i]))) n &= ~((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << atadev->unit); } free(children, M_TEMP); } /* Create new devices. */ if (bootverbose) device_printf(dev, "New devices: %08x\n", n); if (n == 0) { mtx_unlock(&Giant); return (0); } for (i = 0; i < ATA_PM; ++i) { if (n & (((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << i))) { int unit = -1; if (!(atadev = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } atadev->unit = i; #ifdef ATA_STATIC_ID if (n & (ATA_ATA_MASTER << i)) unit = (device_get_unit(dev) << 1) + i; #endif if ((child = ata_add_child(dev, atadev, unit))) { /* * PATA slave should be identified first, to allow * device cable detection on master to work properly. */ if (i == 0 && (n & ATA_PORTMULTIPLIER) == 0 && (n & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << 1)) != 0) { master = child; continue; } if (ata_getparam(atadev, 1)) { device_delete_child(dev, child); free(atadev, M_ATA); } } else free(atadev, M_ATA); } } if (master) { atadev = device_get_softc(master); if (ata_getparam(atadev, 1)) { device_delete_child(dev, master); free(atadev, M_ATA); } } bus_generic_probe(dev); bus_generic_attach(dev); mtx_unlock(&Giant); return 0; } #endif void ata_default_registers(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* fill in the defaults from whats setup already */ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res; ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset; ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res; ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset; ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res; ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset; ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res; ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset; } void ata_modify_if_48bit(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); request->flags &= ~ATA_R_48BIT; if (((request->u.ata.lba + request->u.ata.count) >= ATA_MAX_28BIT_LBA || request->u.ata.count > 256) && atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_READ: request->u.ata.command = ATA_READ48; break; case ATA_READ_MUL: request->u.ata.command = ATA_READ_MUL48; break; case ATA_READ_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA48; break; case ATA_READ_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA_QUEUED48; break; case ATA_WRITE: request->u.ata.command = ATA_WRITE48; break; case ATA_WRITE_MUL: request->u.ata.command = ATA_WRITE_MUL48; break; case ATA_WRITE_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA48; break; case ATA_WRITE_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA_QUEUED48; break; case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } else if (atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_READ_NATIVE_MAX_ADDRESS: request->u.ata.command = ATA_READ_NATIVE_MAX_ADDRESS48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } } void ata_udelay(int interval) { /* for now just use DELAY, the timer/sleep subsytems are not there yet */ if (1 || interval < (1000000/hz) || ata_delayed_attach) DELAY(interval); else pause("ataslp", interval/(1000000/hz)); } char * ata_unit2str(struct ata_device *atadev) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); static char str[8]; if (ch->devices & ATA_PORTMULTIPLIER) sprintf(str, "port%d", atadev->unit); else sprintf(str, "%s", atadev->unit == ATA_MASTER ? "master" : "slave"); return str; } const char * ata_mode2str(int mode) { switch (mode) { case -1: return "UNSUPPORTED"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA16"; case ATA_UDMA1: return "UDMA25"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA40"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_SA150: return "SATA150"; case ATA_SA300: return "SATA300"; default: if (mode & ATA_DMA_MASK) return "BIOSDMA"; else return "BIOSPIO"; } } const char * ata_satarev2str(int rev) { switch (rev) { case 0: return ""; case 1: return "SATA 1.5Gb/s"; case 2: return "SATA 3Gb/s"; case 3: return "SATA 6Gb/s"; case 0xff: return "SATA"; default: return "???"; } } int ata_atapi(device_t dev, int target) { struct ata_channel *ch = device_get_softc(dev); return (ch->devices & (ATA_ATAPI_MASTER << target)); } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 0x02) return ATA_PIO4; if (ap->apiomodes & 0x01) return ATA_PIO3; } if (ap->mwdmamodes & 0x04) return ATA_PIO4; if (ap->mwdmamodes & 0x02) return ATA_PIO3; if (ap->mwdmamodes & 0x01) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100) return ATA_PIO1; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000) return ATA_PIO0; return ATA_PIO0; } int ata_wmode(struct ata_params *ap) { if (ap->mwdmamodes & 0x04) return ATA_WDMA2; if (ap->mwdmamodes & 0x02) return ATA_WDMA1; if (ap->mwdmamodes & 0x01) return ATA_WDMA0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x40) return ATA_UDMA6; if (ap->udmamodes & 0x20) return ATA_UDMA5; if (ap->udmamodes & 0x10) return ATA_UDMA4; if (ap->udmamodes & 0x08) return ATA_UDMA3; if (ap->udmamodes & 0x04) return ATA_UDMA2; if (ap->udmamodes & 0x02) return ATA_UDMA1; if (ap->udmamodes & 0x01) return ATA_UDMA0; } return -1; } int ata_limit_mode(device_t dev, int mode, int maxmode) { struct ata_device *atadev = device_get_softc(dev); if (maxmode && mode > maxmode) mode = maxmode; if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0) return min(mode, ata_umode(&atadev->param)); if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0) return min(mode, ata_wmode(&atadev->param)); if (mode > ata_pmode(&atadev->param)) return min(mode, ata_pmode(&atadev->param)); return mode; } static void bswap(int8_t *buf, int len) { u_int16_t *ptr = (u_int16_t*)(buf + len); while (--ptr >= (u_int16_t*)buf) *ptr = ntohs(*ptr); } static void btrim(int8_t *buf, int len) { int8_t *ptr; for (ptr = buf; ptr < buf+len; ++ptr) if (!*ptr || *ptr == '_') *ptr = ' '; for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) *ptr = 0; } static void bpack(int8_t *src, int8_t *dst, int len) { int i, j, blank; for (i = j = blank = 0 ; i < len; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') { blank = 1; if (i == 0) continue; } dst[j++] = src[i]; } if (j < len) dst[j] = 0x00; } #ifdef ATA_CAM void ata_cam_begin_transaction(device_t dev, union ccb *ccb) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } bzero(request, sizeof(*request)); /* setup request */ request->dev = NULL; request->parent = dev; request->unit = ccb->ccb_h.target_id; if (ccb->ccb_h.func_code == XPT_ATA_IO) { request->data = ccb->ataio.data_ptr; request->bytecount = ccb->ataio.dxfer_len; request->u.ata.command = ccb->ataio.cmd.command; request->u.ata.feature = ((uint16_t)ccb->ataio.cmd.features_exp << 8) | (uint16_t)ccb->ataio.cmd.features; request->u.ata.count = ((uint16_t)ccb->ataio.cmd.sector_count_exp << 8) | (uint16_t)ccb->ataio.cmd.sector_count; if (ccb->ataio.cmd.flags & CAM_ATAIO_48BIT) { request->flags |= ATA_R_48BIT; request->u.ata.lba = ((uint64_t)ccb->ataio.cmd.lba_high_exp << 40) | ((uint64_t)ccb->ataio.cmd.lba_mid_exp << 32) | ((uint64_t)ccb->ataio.cmd.lba_low_exp << 24); } else { request->u.ata.lba = ((uint64_t)(ccb->ataio.cmd.device & 0x0f) << 24); } request->u.ata.lba |= ((uint64_t)ccb->ataio.cmd.lba_high << 16) | ((uint64_t)ccb->ataio.cmd.lba_mid << 8) | (uint64_t)ccb->ataio.cmd.lba_low; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ccb->ataio.cmd.flags & CAM_ATAIO_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } else { request->data = ccb->csio.data_ptr; request->bytecount = ccb->csio.dxfer_len; bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, request->u.atapi.ccb, ccb->csio.cdb_len); request->flags |= ATA_R_ATAPI; if (ch->curr[ccb->ccb_h.target_id].atapi == 16) request->flags |= ATA_R_ATAPI16; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } request->transfersize = min(request->bytecount, ch->curr[ccb->ccb_h.target_id].bytecount); request->retries = 0; request->timeout = (ccb->ccb_h.timeout + 999) / 1000; callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); request->ccb = ccb; ch->running = request; ch->state = ATA_ACTIVE; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; ata_cam_end_transaction(dev, request); return; } } void ata_cam_end_transaction(device_t dev, struct ata_request *request) { struct ata_channel *ch = device_get_softc(dev); union ccb *ccb = request->ccb; int fatalerr = 0; ccb->ccb_h.status &= ~CAM_STATUS_MASK; if (request->flags & ATA_R_TIMEOUT) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT | CAM_RELEASE_SIMQ; fatalerr = 1; } else if (request->status & ATA_S_ERROR) { if (ccb->ccb_h.func_code == XPT_ATA_IO) { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } else { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } } else if (request->result == ERESTART) ccb->ccb_h.status |= CAM_REQUEUE_REQ; else if (request->result != 0) ccb->ccb_h.status |= CAM_REQ_CMP_ERR; else ccb->ccb_h.status |= CAM_REQ_CMP; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } if (ccb->ccb_h.func_code == XPT_ATA_IO && ((request->status & ATA_S_ERROR) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT))) { struct ata_res *res = &ccb->ataio.res; res->status = request->status; res->error = request->error; res->lba_low = request->u.ata.lba; res->lba_mid = request->u.ata.lba >> 8; res->lba_high = request->u.ata.lba >> 16; res->device = request->u.ata.lba >> 24; res->lba_low_exp = request->u.ata.lba >> 24; res->lba_mid_exp = request->u.ata.lba >> 32; res->lba_high_exp = request->u.ata.lba >> 40; res->sector_count = request->u.ata.count; res->sector_count_exp = request->u.ata.count >> 8; } ata_free_request(request); xpt_done(ccb); /* Do error recovery if needed. */ if (fatalerr) ata_reinit(dev); } static int ata_check_ids(device_t dev, union ccb *ccb) { struct ata_channel *ch = device_get_softc(dev); if (ccb->ccb_h.target_id > ((ch->flags & ATA_NO_SLAVE) ? 0 : 1)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void ataaction(struct cam_sim *sim, union ccb *ccb) { device_t dev; struct ata_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ataaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ata_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (ata_check_ids(dev, ccb)) return; if ((ch->devices & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << ccb->ccb_h.target_id)) == 0) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } if (ch->running) device_printf(dev, "already running!\n"); if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET)) { struct ata_res *res = &ccb->ataio.res; bzero(res, sizeof(*res)); if (ch->devices & (ATA_ATA_MASTER << ccb->ccb_h.target_id)) { res->lba_high = 0; res->lba_mid = 0; } else { res->lba_high = 0xeb; res->lba_mid = 0x14; } ccb->ccb_h.status = CAM_REQ_CMP; break; } ata_cam_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (ata_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (ch->flags & ATA_SATA) { if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.sata.mode); } else d->mode = cts->xport_specific.sata.mode; } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; } else { if (cts->xport_specific.ata.valid & CTS_ATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.ata.mode); } else d->mode = cts->xport_specific.ata.mode; } if (cts->xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) d->bytecount = cts->xport_specific.ata.bytecount; if (cts->xport_specific.ata.valid & CTS_ATA_VALID_ATAPI) d->atapi = cts->xport_specific.ata.atapi; } ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (ata_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; if (ch->flags & ATA_SATA) { cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.sata.valid = 0; cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { cts->xport_specific.sata.revision = ATA_GETREV(dev, ccb->ccb_h.target_id); if (cts->xport_specific.sata.revision != 0xff) { cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; } else { cts->transport = XPORT_ATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.ata.valid = 0; cts->xport_specific.ata.mode = d->mode; cts->xport_specific.ata.valid |= CTS_ATA_VALID_MODE; cts->xport_specific.ata.bytecount = d->bytecount; cts->xport_specific.ata.valid |= CTS_ATA_VALID_BYTECOUNT; cts->xport_specific.ata.atapi = d->atapi; cts->xport_specific.ata.valid |= CTS_ATA_VALID_ATAPI; } ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ata_reinit(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (ch->flags & ATA_NO_SLAVE) cpi->max_target = 0; else cpi->max_target = 1; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); if (ch->flags & ATA_SATA) cpi->base_transfer_speed = 150000; else cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "ATA", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); if (ch->flags & ATA_SATA) cpi->transport = XPORT_SATA; else cpi->transport = XPORT_ATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void atapoll(struct cam_sim *sim) { struct ata_channel *ch = (struct ata_channel *)cam_sim_softc(sim); ata_interrupt_locked(ch); } #endif /* * module handeling */ static int ata_module_event_handler(module_t mod, int what, void *arg) { #ifndef ATA_CAM static struct cdev *atacdev; #endif switch (what) { case MOD_LOAD: #ifndef ATA_CAM /* register controlling device */ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); if (cold) { /* register boot attach to be run when interrupts are enabled */ if (!(ata_delayed_attach = (struct intr_config_hook *) malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO))) { printf("ata: malloc of delayed attach hook failed\n"); return EIO; } ata_delayed_attach->ich_func = (void*)ata_boot_attach; if (config_intrhook_establish(ata_delayed_attach) != 0) { printf("ata: config_intrhook_establish failed\n"); free(ata_delayed_attach, M_TEMP); } } #endif return 0; case MOD_UNLOAD: #ifndef ATA_CAM /* deregister controlling device */ destroy_dev(atacdev); #endif return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(ata, 1); #ifdef ATA_CAM MODULE_DEPEND(ata, cam, 1, 1, 1); #endif static void ata_init(void) { ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); ata_composite_zone = uma_zcreate("ata_composite", sizeof(struct ata_composite), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL); static void ata_uninit(void) { uma_zdestroy(ata_composite_zone); uma_zdestroy(ata_request_zone); } SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL); Index: stable/8/sys/dev/mvs/mvs.c =================================================================== --- stable/8/sys/dev/mvs/mvs.c (revision 208447) +++ stable/8/sys/dev/mvs/mvs.c (revision 208448) @@ -1,2173 +1,2202 @@ /*- * Copyright (c) 2010 Alexander Motin * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 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 AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mvs.h" #include #include #include #include #include /* local prototypes */ +static int mvs_ch_init(device_t dev); +static int mvs_ch_deinit(device_t dev); static int mvs_ch_suspend(device_t dev); static int mvs_ch_resume(device_t dev); static void mvs_dmainit(device_t dev); static void mvs_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static void mvs_dmafini(device_t dev); static void mvs_slotsalloc(device_t dev); static void mvs_slotsfree(device_t dev); static void mvs_setup_edma_queues(device_t dev); static void mvs_set_edma_mode(device_t dev, enum mvs_edma_mode mode); static void mvs_ch_pm(void *arg); static void mvs_ch_intr_locked(void *data); static void mvs_ch_intr(void *data); static void mvs_reset(device_t dev); static void mvs_softreset(device_t dev, union ccb *ccb); static int mvs_sata_connect(struct mvs_channel *ch); static int mvs_sata_phy_reset(device_t dev); static int mvs_wait(device_t dev, u_int s, u_int c, int t); static void mvs_tfd_read(device_t dev, union ccb *ccb); static void mvs_tfd_write(device_t dev, union ccb *ccb); static void mvs_legacy_intr(device_t dev); static void mvs_crbq_intr(device_t dev); static void mvs_begin_transaction(device_t dev, union ccb *ccb); static void mvs_legacy_execute_transaction(struct mvs_slot *slot); static void mvs_timeout(struct mvs_slot *slot); static void mvs_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void mvs_requeue_frozen(device_t dev); static void mvs_execute_transaction(struct mvs_slot *slot); static void mvs_end_transaction(struct mvs_slot *slot, enum mvs_err_type et); static void mvs_issue_read_log(device_t dev); static void mvs_process_read_log(device_t dev, union ccb *ccb); static void mvsaction(struct cam_sim *sim, union ccb *ccb); static void mvspoll(struct cam_sim *sim); MALLOC_DEFINE(M_MVS, "MVS driver", "MVS driver data buffers"); static int mvs_ch_probe(device_t dev) { device_set_desc_copy(dev, "Marvell SATA channel"); return (0); } static int mvs_ch_attach(device_t dev) { struct mvs_controller *ctlr = device_get_softc(device_get_parent(dev)); struct mvs_channel *ch = device_get_softc(dev); struct cam_devq *devq; int rid, error, i, sata_rev = 0; ch->dev = dev; ch->unit = (intptr_t)device_get_ivars(dev); ch->quirks = ctlr->quirks; mtx_init(&ch->mtx, "MVS channel lock", NULL, MTX_DEF); resource_int_value(device_get_name(dev), device_get_unit(dev), "pm_level", &ch->pm_level); if (ch->pm_level > 3) callout_init_mtx(&ch->pm_timer, &ch->mtx, 0); resource_int_value(device_get_name(dev), device_get_unit(dev), "sata_rev", &sata_rev); for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; ch->user[i].bytecount = (ch->quirks & MVS_Q_GENIIE) ? 8192 : 2048; ch->user[i].tags = MVS_MAX_SLOTS; ch->curr[i] = ch->user[i]; if (ch->pm_level) { ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ | CTS_SATA_CAPS_H_APST | CTS_SATA_CAPS_D_PMREQ | CTS_SATA_CAPS_D_APST; } } rid = ch->unit; if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); mvs_dmainit(dev); mvs_slotsalloc(dev); - mvs_ch_resume(dev); + mvs_ch_init(dev); mtx_lock(&ch->mtx); rid = ATA_IRQ_RID; if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "Unable to map interrupt\n"); error = ENXIO; goto err0; } if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, mvs_ch_intr_locked, dev, &ch->ih))) { device_printf(dev, "Unable to setup interrupt\n"); error = ENXIO; goto err1; } /* Create the device queue for our SIM. */ devq = cam_simq_alloc(MVS_MAX_SLOTS - 1); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(mvsaction, mvspoll, "mvsch", ch, device_get_unit(dev), &ch->mtx, 2, (ch->quirks & MVS_Q_GENI) ? 0 : MVS_MAX_SLOTS - 1, devq); if (ch->sim == NULL) { cam_simq_free(devq); device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err1; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } if (ch->pm_level > 3) { callout_reset(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8, mvs_ch_pm, dev); } mtx_unlock(&ch->mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); err0: bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_unlock(&ch->mtx); mtx_destroy(&ch->mtx); return (error); } static int mvs_ch_detach(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->mtx); if (ch->pm_level > 3) callout_drain(&ch->pm_timer); bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); - mvs_ch_suspend(dev); + mvs_ch_deinit(dev); mvs_slotsfree(dev); mvs_dmafini(dev); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_destroy(&ch->mtx); return (0); } static int -mvs_ch_suspend(device_t dev) +mvs_ch_init(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); - - /* Stop EDMA */ - mvs_set_edma_mode(dev, MVS_EDMA_OFF); - /* Disable port interrupts. */ - ATA_OUTL(ch->r_mem, EDMA_IEM, 0); - return (0); -} - -static int -mvs_ch_resume(device_t dev) -{ - struct mvs_channel *ch = device_get_softc(dev); uint32_t reg; /* Disable port interrupts */ ATA_OUTL(ch->r_mem, EDMA_IEM, 0); /* Stop EDMA */ ch->curr_mode = MVS_EDMA_UNKNOWN; mvs_set_edma_mode(dev, MVS_EDMA_OFF); /* Clear and configure FIS interrupts. */ ATA_OUTL(ch->r_mem, SATA_FISIC, 0); reg = ATA_INL(ch->r_mem, SATA_FISC); reg |= SATA_FISC_FISWAIT4HOSTRDYEN_B1; ATA_OUTL(ch->r_mem, SATA_FISC, reg); reg = ATA_INL(ch->r_mem, SATA_FISIM); reg |= SATA_FISC_FISWAIT4HOSTRDYEN_B1; ATA_OUTL(ch->r_mem, SATA_FISC, reg); /* Clear SATA error register. */ ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff); /* Clear any outstanding error interrupts. */ ATA_OUTL(ch->r_mem, EDMA_IEC, 0); /* Unmask all error interrupts */ ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT); + return (0); +} + +static int +mvs_ch_deinit(device_t dev) +{ + struct mvs_channel *ch = device_get_softc(dev); + + /* Stop EDMA */ + mvs_set_edma_mode(dev, MVS_EDMA_OFF); + /* Disable port interrupts. */ + ATA_OUTL(ch->r_mem, EDMA_IEM, 0); + return (0); +} + +static int +mvs_ch_suspend(device_t dev) +{ + struct mvs_channel *ch = device_get_softc(dev); + + mtx_lock(&ch->mtx); + xpt_freeze_simq(ch->sim, 1); + while (ch->oslots) + msleep(ch, &ch->mtx, PRIBIO, "mvssusp", hz/100); + mvs_ch_deinit(dev); + mtx_unlock(&ch->mtx); + return (0); +} + +static int +mvs_ch_resume(device_t dev) +{ + struct mvs_channel *ch = device_get_softc(dev); + + mtx_lock(&ch->mtx); + mvs_ch_init(dev); + mvs_reset(dev); + xpt_release_simq(ch->sim, TRUE); + mtx_unlock(&ch->mtx); return (0); } struct mvs_dc_cb_args { bus_addr_t maddr; int error; }; static void mvs_dmainit(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); struct mvs_dc_cb_args dcba; /* EDMA command request area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MVS_WORKRQ_SIZE, 1, MVS_WORKRQ_SIZE, 0, NULL, NULL, &ch->dma.workrq_tag)) goto error; if (bus_dmamem_alloc(ch->dma.workrq_tag, (void **)&ch->dma.workrq, 0, &ch->dma.workrq_map)) goto error; if (bus_dmamap_load(ch->dma.workrq_tag, ch->dma.workrq_map, ch->dma.workrq, MVS_WORKRQ_SIZE, mvs_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.workrq_tag, ch->dma.workrq, ch->dma.workrq_map); goto error; } ch->dma.workrq_bus = dcba.maddr; /* EDMA command response area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 256, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MVS_WORKRP_SIZE, 1, MVS_WORKRP_SIZE, 0, NULL, NULL, &ch->dma.workrp_tag)) goto error; if (bus_dmamem_alloc(ch->dma.workrp_tag, (void **)&ch->dma.workrp, 0, &ch->dma.workrp_map)) goto error; if (bus_dmamap_load(ch->dma.workrp_tag, ch->dma.workrp_map, ch->dma.workrp, MVS_WORKRP_SIZE, mvs_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.workrp_tag, ch->dma.workrp, ch->dma.workrp_map); goto error; } ch->dma.workrp_bus = dcba.maddr; /* Data area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 2, MVS_EPRD_MAX, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MVS_SG_ENTRIES * PAGE_SIZE * MVS_MAX_SLOTS, MVS_SG_ENTRIES, MVS_EPRD_MAX, 0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) { goto error; } return; error: device_printf(dev, "WARNING - DMA initialization failed\n"); mvs_dmafini(dev); } static void mvs_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct mvs_dc_cb_args *dcba = (struct mvs_dc_cb_args *)xsc; if (!(dcba->error = error)) dcba->maddr = segs[0].ds_addr; } static void mvs_dmafini(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); if (ch->dma.data_tag) { bus_dma_tag_destroy(ch->dma.data_tag); ch->dma.data_tag = NULL; } if (ch->dma.workrp_bus) { bus_dmamap_unload(ch->dma.workrp_tag, ch->dma.workrp_map); bus_dmamem_free(ch->dma.workrp_tag, ch->dma.workrp, ch->dma.workrp_map); ch->dma.workrp_bus = 0; ch->dma.workrp_map = NULL; ch->dma.workrp = NULL; } if (ch->dma.workrp_tag) { bus_dma_tag_destroy(ch->dma.workrp_tag); ch->dma.workrp_tag = NULL; } if (ch->dma.workrq_bus) { bus_dmamap_unload(ch->dma.workrq_tag, ch->dma.workrq_map); bus_dmamem_free(ch->dma.workrq_tag, ch->dma.workrq, ch->dma.workrq_map); ch->dma.workrq_bus = 0; ch->dma.workrq_map = NULL; ch->dma.workrq = NULL; } if (ch->dma.workrq_tag) { bus_dma_tag_destroy(ch->dma.workrq_tag); ch->dma.workrq_tag = NULL; } } static void mvs_slotsalloc(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int i; /* Alloc and setup command/dma slots */ bzero(ch->slot, sizeof(ch->slot)); for (i = 0; i < MVS_MAX_SLOTS; i++) { struct mvs_slot *slot = &ch->slot[i]; slot->dev = dev; slot->slot = i; slot->state = MVS_SLOT_EMPTY; slot->ccb = NULL; callout_init_mtx(&slot->timeout, &ch->mtx, 0); if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map)) device_printf(ch->dev, "FAILURE - create data_map\n"); } } static void mvs_slotsfree(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int i; /* Free all dma slots */ for (i = 0; i < MVS_MAX_SLOTS; i++) { struct mvs_slot *slot = &ch->slot[i]; callout_drain(&slot->timeout); if (slot->dma.data_map) { bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map); slot->dma.data_map = NULL; } } } static void mvs_setup_edma_queues(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); uint64_t work; /* Requests queue. */ work = ch->dma.workrq_bus; ATA_OUTL(ch->r_mem, EDMA_REQQBAH, work >> 32); ATA_OUTL(ch->r_mem, EDMA_REQQIP, work & 0xffffffff); ATA_OUTL(ch->r_mem, EDMA_REQQOP, work & 0xffffffff); bus_dmamap_sync(ch->dma.workrq_tag, ch->dma.workrq_map, BUS_DMASYNC_PREWRITE); /* Reponses queue. */ bzero(ch->dma.workrp, 256); work = ch->dma.workrp_bus; ATA_OUTL(ch->r_mem, EDMA_RESQBAH, work >> 32); ATA_OUTL(ch->r_mem, EDMA_RESQIP, work & 0xffffffff); ATA_OUTL(ch->r_mem, EDMA_RESQOP, work & 0xffffffff); bus_dmamap_sync(ch->dma.workrp_tag, ch->dma.workrp_map, BUS_DMASYNC_PREREAD); ch->out_idx = 0; ch->in_idx = 0; } static void mvs_set_edma_mode(device_t dev, enum mvs_edma_mode mode) { struct mvs_channel *ch = device_get_softc(dev); int timeout; uint32_t ecfg, fcfg, hc, ltm, unkn; if (mode == ch->curr_mode) return; /* If we are running, we should stop first. */ if (ch->curr_mode != MVS_EDMA_OFF) { ATA_OUTL(ch->r_mem, EDMA_CMD, EDMA_CMD_EDSEDMA); timeout = 0; while (ATA_INL(ch->r_mem, EDMA_CMD) & EDMA_CMD_EENEDMA) { DELAY(1000); if (timeout++ > 1000) { device_printf(dev, "stopping EDMA engine failed\n"); break; } }; } ch->curr_mode = mode; ch->fbs_enabled = 0; ch->fake_busy = 0; /* Report mode to controller. Needed for correct CCC operation. */ MVS_EDMA(device_get_parent(dev), dev, mode); /* Configure new mode. */ ecfg = EDMA_CFG_RESERVED | EDMA_CFG_RESERVED2 | EDMA_CFG_EHOSTQUEUECACHEEN; if (ch->pm_present) { ecfg |= EDMA_CFG_EMASKRXPM; if (ch->quirks & MVS_Q_GENIIE) { ecfg |= EDMA_CFG_EEDMAFBS; ch->fbs_enabled = 1; } } if (ch->quirks & MVS_Q_GENI) ecfg |= EDMA_CFG_ERDBSZ; else if (ch->quirks & MVS_Q_GENII) ecfg |= EDMA_CFG_ERDBSZEXT | EDMA_CFG_EWRBUFFERLEN; if (ch->quirks & MVS_Q_CT) ecfg |= EDMA_CFG_ECUTTHROUGHEN; if (mode != MVS_EDMA_OFF) ecfg |= EDMA_CFG_EEARLYCOMPLETIONEN; if (mode == MVS_EDMA_QUEUED) ecfg |= EDMA_CFG_EQUE; else if (mode == MVS_EDMA_NCQ) ecfg |= EDMA_CFG_ESATANATVCMDQUE; ATA_OUTL(ch->r_mem, EDMA_CFG, ecfg); mvs_setup_edma_queues(dev); if (ch->quirks & MVS_Q_GENIIE) { /* Configure FBS-related registers */ fcfg = ATA_INL(ch->r_mem, SATA_FISC); ltm = ATA_INL(ch->r_mem, SATA_LTM); hc = ATA_INL(ch->r_mem, EDMA_HC); if (ch->fbs_enabled) { fcfg |= SATA_FISC_FISDMAACTIVATESYNCRESP; if (mode == MVS_EDMA_NCQ) { fcfg &= ~SATA_FISC_FISWAIT4HOSTRDYEN_B0; hc &= ~EDMA_IE_EDEVERR; } else { fcfg |= SATA_FISC_FISWAIT4HOSTRDYEN_B0; hc |= EDMA_IE_EDEVERR; } ltm |= (1 << 8); } else { fcfg &= ~SATA_FISC_FISDMAACTIVATESYNCRESP; fcfg &= ~SATA_FISC_FISWAIT4HOSTRDYEN_B0; hc |= EDMA_IE_EDEVERR; ltm &= ~(1 << 8); } ATA_OUTL(ch->r_mem, SATA_FISC, fcfg); ATA_OUTL(ch->r_mem, SATA_LTM, ltm); ATA_OUTL(ch->r_mem, EDMA_HC, hc); /* This is some magic, required to handle several DRQs * with basic DMA. */ unkn = ATA_INL(ch->r_mem, EDMA_UNKN_RESD); if (mode == MVS_EDMA_OFF) unkn |= 1; else unkn &= ~1; ATA_OUTL(ch->r_mem, EDMA_UNKN_RESD, unkn); } /* Run EDMA. */ if (mode != MVS_EDMA_OFF) ATA_OUTL(ch->r_mem, EDMA_CMD, EDMA_CMD_EENEDMA); } devclass_t mvs_devclass; devclass_t mvsch_devclass; static device_method_t mvsch_methods[] = { DEVMETHOD(device_probe, mvs_ch_probe), DEVMETHOD(device_attach, mvs_ch_attach), DEVMETHOD(device_detach, mvs_ch_detach), DEVMETHOD(device_suspend, mvs_ch_suspend), DEVMETHOD(device_resume, mvs_ch_resume), { 0, 0 } }; static driver_t mvsch_driver = { "mvsch", mvsch_methods, sizeof(struct mvs_channel) }; DRIVER_MODULE(mvsch, mvs, mvsch_driver, mvsch_devclass, 0, 0); DRIVER_MODULE(mvsch, sata, mvsch_driver, mvsch_devclass, 0, 0); static void mvs_phy_check_events(device_t dev, u_int32_t serr) { struct mvs_channel *ch = device_get_softc(dev); if (ch->pm_level == 0) { u_int32_t status = ATA_INL(ch->r_mem, SATA_SS); union ccb *ccb; if (bootverbose) { if (((status & SATA_SS_DET_MASK) == SATA_SS_DET_PHY_ONLINE) && ((status & SATA_SS_SPD_MASK) != SATA_SS_SPD_NO_SPEED) && ((status & SATA_SS_IPM_MASK) == SATA_SS_IPM_ACTIVE)) { device_printf(dev, "CONNECT requested\n"); } else device_printf(dev, "DISCONNECT requested\n"); } mvs_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } } static void mvs_notify_events(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); struct cam_path *dpath; uint32_t fis; int d; /* Try to read PMP field from SDB FIS. Present only for Gen-IIe. */ fis = ATA_INL(ch->r_mem, SATA_FISDW0); if ((fis & 0x80ff) == 0x80a1) d = (fis & 0x0f00) >> 8; else d = ch->pm_present ? 15 : 0; if (bootverbose) device_printf(dev, "SNTF %d\n", d); if (xpt_create_path(&dpath, NULL, xpt_path_path_id(ch->path), d, 0) == CAM_REQ_CMP) { xpt_async(AC_SCSI_AEN, dpath, NULL); xpt_free_path(dpath); } } static void mvs_ch_intr_locked(void *data) { struct mvs_intr_arg *arg = (struct mvs_intr_arg *)data; device_t dev = (device_t)arg->arg; struct mvs_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); mvs_ch_intr(data); mtx_unlock(&ch->mtx); } static void mvs_ch_pm(void *arg) { device_t dev = (device_t)arg; struct mvs_channel *ch = device_get_softc(dev); uint32_t work; if (ch->numrslots != 0) return; /* If we are idle - request power state transition. */ work = ATA_INL(ch->r_mem, SATA_SC); work &= ~SATA_SC_SPM_MASK; if (ch->pm_level == 4) work |= SATA_SC_SPM_PARTIAL; else work |= SATA_SC_SPM_SLUMBER; ATA_OUTL(ch->r_mem, SATA_SC, work); } static void mvs_ch_pm_wake(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); uint32_t work; int timeout = 0; work = ATA_INL(ch->r_mem, SATA_SS); if (work & SATA_SS_IPM_ACTIVE) return; /* If we are not in active state - request power state transition. */ work = ATA_INL(ch->r_mem, SATA_SC); work &= ~SATA_SC_SPM_MASK; work |= SATA_SC_SPM_ACTIVE; ATA_OUTL(ch->r_mem, SATA_SC, work); /* Wait for transition to happen. */ while ((ATA_INL(ch->r_mem, SATA_SS) & SATA_SS_IPM_ACTIVE) == 0 && timeout++ < 100) { DELAY(100); } } static void mvs_ch_intr(void *data) { struct mvs_intr_arg *arg = (struct mvs_intr_arg *)data; device_t dev = (device_t)arg->arg; struct mvs_channel *ch = device_get_softc(dev); uint32_t iec, serr = 0, fisic = 0; enum mvs_err_type et; int i, ccs, port = -1, selfdis = 0; int edma = (ch->numtslots != 0 || ch->numdslots != 0); //device_printf(dev, "irq cause %02x EDMA %d IEC %08x\n", // arg->cause, edma, ATA_INL(ch->r_mem, EDMA_IEC)); /* New item in response queue. */ if ((arg->cause & 2) && edma) mvs_crbq_intr(dev); /* Some error or special event. */ if (arg->cause & 1) { iec = ATA_INL(ch->r_mem, EDMA_IEC); //device_printf(dev, "irq cause %02x EDMA %d IEC %08x\n", // arg->cause, edma, iec); if (iec & EDMA_IE_SERRINT) { serr = ATA_INL(ch->r_mem, SATA_SE); ATA_OUTL(ch->r_mem, SATA_SE, serr); //device_printf(dev, "SERR %08x\n", serr); } /* EDMA self-disabled due to error. */ if (iec & EDMA_IE_ESELFDIS) selfdis = 1; /* Transport interrupt. */ if (iec & EDMA_IE_ETRANSINT) { /* For Gen-I this bit means self-disable. */ if (ch->quirks & MVS_Q_GENI) selfdis = 1; /* For Gen-II this bit means SDB-N. */ else if (ch->quirks & MVS_Q_GENII) fisic = SATA_FISC_FISWAIT4HOSTRDYEN_B1; else /* For Gen-IIe - read FIS interrupt cause. */ fisic = ATA_INL(ch->r_mem, SATA_FISIC); //device_printf(dev, "FISIC %08x\n", fisic); } if (selfdis) ch->curr_mode = MVS_EDMA_UNKNOWN; ATA_OUTL(ch->r_mem, EDMA_IEC, ~iec); /* Interface errors or Device error. */ if (iec & (0xfc1e9000 | EDMA_IE_EDEVERR)) { port = -1; if (ch->numpslots != 0) { ccs = 0; } else { if (ch->quirks & MVS_Q_GENIIE) ccs = EDMA_S_EIOID(ATA_INL(ch->r_mem, EDMA_S)); else ccs = EDMA_S_EDEVQUETAG(ATA_INL(ch->r_mem, EDMA_S)); /* Check if error is one-PMP-port-specific, */ if (ch->fbs_enabled) { /* Which ports were active. */ for (i = 0; i < 16; i++) { if (ch->numrslotspd[i] == 0) continue; if (port == -1) port = i; else if (port != i) { port = -2; break; } } /* If several ports were active and EDMA still enabled - * other ports are probably unaffected and may continue. */ if (port == -2 && !selfdis) { uint16_t p = ATA_INL(ch->r_mem, SATA_SATAITC) >> 16; port = ffs(p) - 1; if (port != (fls(p) - 1)) port = -2; } } } //device_printf(dev, "err slot %d port %d\n", ccs, port); mvs_requeue_frozen(dev); for (i = 0; i < MVS_MAX_SLOTS; i++) { /* XXX: reqests in loading state. */ if (((ch->rslots >> i) & 1) == 0) continue; if (port >= 0 && ch->slot[i].ccb->ccb_h.target_id != port) continue; if (iec & EDMA_IE_EDEVERR) { /* Device error. */ if (port != -2) { if (ch->numtslots == 0) { /* Untagged operation. */ if (i == ccs) et = MVS_ERR_TFE; else et = MVS_ERR_INNOCENT; } else { /* Tagged operation. */ et = MVS_ERR_NCQ; } } else { et = MVS_ERR_TFE; ch->fatalerr = 1; } } else if (iec & 0xfc1e9000) { if (ch->numtslots == 0 && i != ccs && port != -2) et = MVS_ERR_INNOCENT; else et = MVS_ERR_SATA; } else et = MVS_ERR_INVALID; mvs_end_transaction(&ch->slot[i], et); } } /* Process SDB-N. */ if (fisic & SATA_FISC_FISWAIT4HOSTRDYEN_B1) mvs_notify_events(dev); if (fisic) ATA_OUTL(ch->r_mem, SATA_FISIC, ~fisic); /* Process hot-plug. */ if ((iec & (EDMA_IE_EDEVDIS | EDMA_IE_EDEVCON)) || (serr & SATA_SE_PHY_CHANGED)) mvs_phy_check_events(dev, serr); } /* Legacy mode device interrupt. */ if ((arg->cause & 2) && !edma) mvs_legacy_intr(dev); } static uint8_t mvs_getstatus(device_t dev, int clear) { struct mvs_channel *ch = device_get_softc(dev); uint8_t status = ATA_INB(ch->r_mem, clear ? ATA_STATUS : ATA_ALTSTAT); if (ch->fake_busy) { if (status & (ATA_S_BUSY | ATA_S_DRQ | ATA_S_ERROR)) ch->fake_busy = 0; else status |= ATA_S_BUSY; } return (status); } static void mvs_legacy_intr(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); struct mvs_slot *slot = &ch->slot[0]; /* PIO is always in slot 0. */ union ccb *ccb = slot->ccb; enum mvs_err_type et = MVS_ERR_NONE; int port; u_int length; uint8_t status, ireason; /* Clear interrupt and get status. */ status = mvs_getstatus(dev, 1); // device_printf(dev, "Legacy intr status %02x\n", // status); if (slot->state < MVS_SLOT_RUNNING) return; port = ccb->ccb_h.target_id & 0x0f; /* Wait a bit for late !BUSY status update. */ if (status & ATA_S_BUSY) { DELAY(100); if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) { DELAY(1000); if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) return; } } /* If we got an error, we are done. */ if (status & ATA_S_ERROR) { et = MVS_ERR_TFE; goto end_finished; } if (ccb->ccb_h.func_code == XPT_ATA_IO) { /* ATA PIO */ ccb->ataio.res.status = status; /* Are we moving data? */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { /* If data read command - get them. */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { device_printf(dev, "timeout waiting for read DRQ\n"); et = MVS_ERR_TIMEOUT; goto end_finished; } ATA_INSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), ch->transfersize / 2); } /* Update how far we've gotten. */ ch->donecount += ch->transfersize; /* Do we need more? */ if (ccb->ataio.dxfer_len > ch->donecount) { /* Set this transfer size according to HW capabilities */ ch->transfersize = min(ccb->ataio.dxfer_len - ch->donecount, ch->curr[ccb->ccb_h.target_id].bytecount); /* If data write command - put them */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { device_printf(dev, "timeout waiting for write DRQ\n"); et = MVS_ERR_TIMEOUT; goto end_finished; } ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), ch->transfersize / 2); return; } /* If data read command, return & wait for interrupt */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) return; } } } else if (ch->basic_dma) { /* ATAPI DMA */ if (status & ATA_S_DWF) et = MVS_ERR_TFE; else if (ATA_INL(ch->r_mem, DMA_S) & DMA_S_ERR) et = MVS_ERR_TFE; /* Stop basic DMA. */ ATA_OUTL(ch->r_mem, DMA_C, 0); goto end_finished; } else { /* ATAPI PIO */ length = ATA_INB(ch->r_mem,ATA_CYL_LSB) | (ATA_INB(ch->r_mem,ATA_CYL_MSB) << 8); ireason = ATA_INB(ch->r_mem,ATA_IREASON); //device_printf(dev, "status %02x, ireason %02x, length %d\n", status, ireason, length); switch ((ireason & (ATA_I_CMD | ATA_I_IN)) | (status & ATA_S_DRQ)) { case ATAPI_P_CMDOUT: device_printf(dev, "ATAPI CMDOUT\n"); /* Return wait for interrupt */ return; case ATAPI_P_WRITE: //device_printf(dev, "ATAPI WRITE\n"); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { device_printf(dev, "trying to write on read buffer\n"); et = MVS_ERR_TFE; goto end_finished; break; } ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->csio.data_ptr + ch->donecount), length / 2); ch->donecount += length; /* Set next transfer size according to HW capabilities */ ch->transfersize = min(ccb->csio.dxfer_len - ch->donecount, ch->curr[ccb->ccb_h.target_id].bytecount); /* Return wait for interrupt */ return; case ATAPI_P_READ: //device_printf(dev, "ATAPI READ\n"); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { device_printf(dev, "trying to read on write buffer\n"); et = MVS_ERR_TFE; goto end_finished; } ATA_INSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->csio.data_ptr + ch->donecount), length / 2); ch->donecount += length; /* Set next transfer size according to HW capabilities */ ch->transfersize = min(ccb->csio.dxfer_len - ch->donecount, ch->curr[ccb->ccb_h.target_id].bytecount); /* Return wait for interrupt */ return; case ATAPI_P_DONEDRQ: device_printf(dev, "ATAPI DONEDRQ\n"); device_printf(dev, "WARNING - DONEDRQ non conformant device\n"); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { ATA_INSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->csio.data_ptr + ch->donecount), length / 2); ch->donecount += length; } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->csio.data_ptr + ch->donecount), length / 2); ch->donecount += length; } else et = MVS_ERR_TFE; /* FALLTHROUGH */ case ATAPI_P_ABORT: case ATAPI_P_DONE: //device_printf(dev, "ATAPI ABORT/DONE\n"); if (status & (ATA_S_ERROR | ATA_S_DWF)) et = MVS_ERR_TFE; goto end_finished; default: device_printf(dev, "unknown transfer phase (status %02x, ireason %02x)\n", status, ireason); et = MVS_ERR_TFE; } } end_finished: mvs_end_transaction(slot, et); } static void mvs_crbq_intr(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); struct mvs_crpb *crpb; union ccb *ccb; int in_idx, cin_idx, slot; uint16_t flags; in_idx = (ATA_INL(ch->r_mem, EDMA_RESQIP) & EDMA_RESQP_ERPQP_MASK) >> EDMA_RESQP_ERPQP_SHIFT; bus_dmamap_sync(ch->dma.workrp_tag, ch->dma.workrp_map, BUS_DMASYNC_POSTREAD); cin_idx = ch->in_idx; ch->in_idx = in_idx; while (in_idx != cin_idx) { crpb = (struct mvs_crpb *) (ch->dma.workrp + MVS_CRPB_OFFSET + (MVS_CRPB_SIZE * cin_idx)); slot = le16toh(crpb->id) & MVS_CRPB_TAG_MASK; flags = le16toh(crpb->rspflg); //device_printf(dev, "CRPB %d %d %04x\n", cin_idx, slot, flags); /* * Handle only successfull completions here. * Errors will be handled by main intr handler. */ if (ch->numtslots != 0 || (flags & EDMA_IE_EDEVERR) == 0) { if ((flags >> 8) & ATA_S_ERROR) device_printf(dev, "ERROR STATUS CRPB %d %d %04x\n", cin_idx, slot, flags); if (ch->slot[slot].state >= MVS_SLOT_RUNNING) { ccb = ch->slot[slot].ccb; ccb->ataio.res.status = (flags & MVS_CRPB_ATASTS_MASK) >> MVS_CRPB_ATASTS_SHIFT; mvs_end_transaction(&ch->slot[slot], MVS_ERR_NONE); } else device_printf(dev, "EMPTY CRPB %d (->%d) %d %04x\n", cin_idx, in_idx, slot, flags); } else device_printf(dev, "ERROR FLAGS CRPB %d %d %04x\n", cin_idx, slot, flags); cin_idx = (cin_idx + 1) & (MVS_MAX_SLOTS - 1); } bus_dmamap_sync(ch->dma.workrp_tag, ch->dma.workrp_map, BUS_DMASYNC_PREREAD); if (cin_idx == ch->in_idx) { ATA_OUTL(ch->r_mem, EDMA_RESQOP, ch->dma.workrp_bus | (cin_idx << EDMA_RESQP_ERPQP_SHIFT)); } } /* Must be called with channel locked. */ static int mvs_check_collision(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); if (ccb->ccb_h.func_code == XPT_ATA_IO) { /* NCQ DMA */ if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { /* Can't mix NCQ and non-NCQ DMA commands. */ if (ch->numdslots != 0) return (1); /* Can't mix NCQ and PIO commands. */ if (ch->numpslots != 0) return (1); /* If we have no FBS */ if (!ch->fbs_enabled) { /* Tagged command while tagged to other target is active. */ if (ch->numtslots != 0 && ch->taggedtarget != ccb->ccb_h.target_id) return (1); } /* Non-NCQ DMA */ } else if (ccb->ataio.cmd.flags & CAM_ATAIO_DMA) { /* Can't mix non-NCQ DMA and NCQ commands. */ if (ch->numtslots != 0) return (1); /* Can't mix non-NCQ DMA and PIO commands. */ if (ch->numpslots != 0) return (1); /* PIO */ } else { /* Can't mix PIO with anything. */ if (ch->numrslots != 0) return (1); } if (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT)) { /* Atomic command while anything active. */ if (ch->numrslots != 0) return (1); } } else { /* ATAPI */ /* ATAPI goes without EDMA, so can't mix it with anything. */ if (ch->numrslots != 0) return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) return (1); return (0); } static void mvs_tfd_read(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); struct ata_res *res = &ccb->ataio.res; res->status = ATA_INB(ch->r_mem, ATA_ALTSTAT); res->error = ATA_INB(ch->r_mem, ATA_ERROR); res->device = ATA_INB(ch->r_mem, ATA_DRIVE); ATA_OUTB(ch->r_mem, ATA_CONTROL, ATA_A_HOB); res->sector_count_exp = ATA_INB(ch->r_mem, ATA_COUNT); res->lba_low_exp = ATA_INB(ch->r_mem, ATA_SECTOR); res->lba_mid_exp = ATA_INB(ch->r_mem, ATA_CYL_LSB); res->lba_high_exp = ATA_INB(ch->r_mem, ATA_CYL_MSB); ATA_OUTB(ch->r_mem, ATA_CONTROL, 0); res->sector_count = ATA_INB(ch->r_mem, ATA_COUNT); res->lba_low = ATA_INB(ch->r_mem, ATA_SECTOR); res->lba_mid = ATA_INB(ch->r_mem, ATA_CYL_LSB); res->lba_high = ATA_INB(ch->r_mem, ATA_CYL_MSB); } static void mvs_tfd_write(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); struct ata_cmd *cmd = &ccb->ataio.cmd; ATA_OUTB(ch->r_mem, ATA_DRIVE, cmd->device); ATA_OUTB(ch->r_mem, ATA_CONTROL, cmd->control); ATA_OUTB(ch->r_mem, ATA_FEATURE, cmd->features_exp); ATA_OUTB(ch->r_mem, ATA_FEATURE, cmd->features); ATA_OUTB(ch->r_mem, ATA_COUNT, cmd->sector_count_exp); ATA_OUTB(ch->r_mem, ATA_COUNT, cmd->sector_count); ATA_OUTB(ch->r_mem, ATA_SECTOR, cmd->lba_low_exp); ATA_OUTB(ch->r_mem, ATA_SECTOR, cmd->lba_low); ATA_OUTB(ch->r_mem, ATA_CYL_LSB, cmd->lba_mid_exp); ATA_OUTB(ch->r_mem, ATA_CYL_LSB, cmd->lba_mid); ATA_OUTB(ch->r_mem, ATA_CYL_MSB, cmd->lba_high_exp); ATA_OUTB(ch->r_mem, ATA_CYL_MSB, cmd->lba_high); ATA_OUTB(ch->r_mem, ATA_COMMAND, cmd->command); } /* Must be called with channel locked. */ static void mvs_begin_transaction(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); struct mvs_slot *slot; int slotn, tag; if (ch->pm_level > 0) mvs_ch_pm_wake(dev); /* Softreset is a special case. */ if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL)) { mvs_softreset(dev, ccb); return; } /* Choose empty slot. */ slotn = ffs(~ch->oslots) - 1; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { if (ch->quirks & MVS_Q_GENIIE) tag = ffs(~ch->otagspd[ccb->ccb_h.target_id]) - 1; else tag = slotn; } else tag = 0; /* Occupy chosen slot. */ slot = &ch->slot[slotn]; slot->ccb = ccb; slot->tag = tag; /* Stop PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3) callout_stop(&ch->pm_timer); /* Update channel stats. */ ch->oslots |= (1 << slot->slot); ch->numrslots++; ch->numrslotspd[ccb->ccb_h.target_id]++; if (ccb->ccb_h.func_code == XPT_ATA_IO) { if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { ch->otagspd[ccb->ccb_h.target_id] |= (1 << slot->tag); ch->numtslots++; ch->numtslotspd[ccb->ccb_h.target_id]++; ch->taggedtarget = ccb->ccb_h.target_id; mvs_set_edma_mode(dev, MVS_EDMA_NCQ); } else if (ccb->ataio.cmd.flags & CAM_ATAIO_DMA) { ch->numdslots++; mvs_set_edma_mode(dev, MVS_EDMA_ON); } else { ch->numpslots++; mvs_set_edma_mode(dev, MVS_EDMA_OFF); } if (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT)) { ch->aslots |= (1 << slot->slot); } } else { uint8_t *cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes; ch->numpslots++; /* Use ATAPI DMA only for commands without under-/overruns. */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA && (ch->quirks & MVS_Q_SOC) == 0 && (cdb[0] == 0x08 || cdb[0] == 0x0a || cdb[0] == 0x28 || cdb[0] == 0x2a || cdb[0] == 0x88 || cdb[0] == 0x8a || cdb[0] == 0xa8 || cdb[0] == 0xaa || cdb[0] == 0xbe)) { ch->basic_dma = 1; } mvs_set_edma_mode(dev, MVS_EDMA_OFF); } if (ch->numpslots == 0 || ch->basic_dma) { void *buf; bus_size_t size; slot->state = MVS_SLOT_LOADING; if (ccb->ccb_h.func_code == XPT_ATA_IO) { buf = ccb->ataio.data_ptr; size = ccb->ataio.dxfer_len; } else { buf = ccb->csio.data_ptr; size = ccb->csio.dxfer_len; } bus_dmamap_load(ch->dma.data_tag, slot->dma.data_map, buf, size, mvs_dmasetprd, slot, 0); } else mvs_legacy_execute_transaction(slot); } /* Locked by busdma engine. */ static void mvs_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct mvs_slot *slot = arg; struct mvs_channel *ch = device_get_softc(slot->dev); struct mvs_eprd *eprd; int i; if (error) { device_printf(slot->dev, "DMA load error\n"); mvs_end_transaction(slot, MVS_ERR_INVALID); return; } KASSERT(nsegs <= MVS_SG_ENTRIES, ("too many DMA segment entries\n")); /* If there is only one segment - no need to use S/G table on Gen-IIe. */ if (nsegs == 1 && ch->basic_dma == 0 && (ch->quirks & MVS_Q_GENIIE)) { slot->dma.addr = segs[0].ds_addr; slot->dma.len = segs[0].ds_len; } else { slot->dma.addr = 0; /* Get a piece of the workspace for this EPRD */ eprd = (struct mvs_eprd *) (ch->dma.workrq + MVS_EPRD_OFFSET + (MVS_EPRD_SIZE * slot->slot)); /* Fill S/G table */ for (i = 0; i < nsegs; i++) { eprd[i].prdbal = htole32(segs[i].ds_addr); eprd[i].bytecount = htole32(segs[i].ds_len & MVS_EPRD_MASK); eprd[i].prdbah = htole32((segs[i].ds_addr >> 16) >> 16); } eprd[i - 1].bytecount |= htole32(MVS_EPRD_EOF); } bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, ((slot->ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); if (ch->basic_dma) mvs_legacy_execute_transaction(slot); else mvs_execute_transaction(slot); } static void mvs_legacy_execute_transaction(struct mvs_slot *slot) { device_t dev = slot->dev; struct mvs_channel *ch = device_get_softc(dev); bus_addr_t eprd; union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; int timeout; slot->state = MVS_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); ATA_OUTB(ch->r_mem, SATA_SATAICTL, port << SATA_SATAICTL_PMPTX_SHIFT); if (ccb->ccb_h.func_code == XPT_ATA_IO) { // device_printf(dev, "%d Legacy command %02x size %d\n", // port, ccb->ataio.cmd.command, ccb->ataio.dxfer_len); mvs_tfd_write(dev, ccb); /* Device reset doesn't interrupt. */ if (ccb->ataio.cmd.command == ATA_DEVICE_RESET) { int timeout = 1000000; do { DELAY(10); ccb->ataio.res.status = ATA_INB(ch->r_mem, ATA_STATUS); } while (ccb->ataio.res.status & ATA_S_BUSY && timeout--); mvs_legacy_intr(dev); return; } ch->donecount = 0; ch->transfersize = min(ccb->ataio.dxfer_len, ch->curr[port].bytecount); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) ch->fake_busy = 1; /* If data write command - output the data */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { if (mvs_wait(dev, ATA_S_DRQ, ATA_S_BUSY, 1000) < 0) { device_printf(dev, "timeout waiting for write DRQ\n"); mvs_end_transaction(slot, MVS_ERR_TIMEOUT); return; } ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)(ccb->ataio.data_ptr + ch->donecount), ch->transfersize / 2); } } else { // device_printf(dev, "%d ATAPI command %02x size %d dma %d\n", // port, ccb->csio.cdb_io.cdb_bytes[0], ccb->csio.dxfer_len, // ch->basic_dma); ch->donecount = 0; ch->transfersize = min(ccb->csio.dxfer_len, ch->curr[port].bytecount); /* Write ATA PACKET command. */ if (ch->basic_dma) { ATA_OUTB(ch->r_mem, ATA_FEATURE, ATA_F_DMA); ATA_OUTB(ch->r_mem, ATA_CYL_LSB, 0); ATA_OUTB(ch->r_mem, ATA_CYL_MSB, 0); } else { ATA_OUTB(ch->r_mem, ATA_FEATURE, 0); ATA_OUTB(ch->r_mem, ATA_CYL_LSB, ch->transfersize); ATA_OUTB(ch->r_mem, ATA_CYL_MSB, ch->transfersize >> 8); } ATA_OUTB(ch->r_mem, ATA_COMMAND, ATA_PACKET_CMD); ch->fake_busy = 1; /* Wait for ready to write ATAPI command block */ if (mvs_wait(dev, 0, ATA_S_BUSY, 1000) < 0) { device_printf(dev, "timeout waiting for ATAPI !BUSY\n"); mvs_end_transaction(slot, MVS_ERR_TIMEOUT); return; } timeout = 5000; while (timeout--) { int reason = ATA_INB(ch->r_mem, ATA_IREASON); int status = ATA_INB(ch->r_mem, ATA_STATUS); if (((reason & (ATA_I_CMD | ATA_I_IN)) | (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT) break; DELAY(20); } if (timeout <= 0) { device_printf(dev, "timeout waiting for ATAPI command ready\n"); mvs_end_transaction(slot, MVS_ERR_TIMEOUT); return; } /* Write ATAPI command. */ ATA_OUTSW_STRM(ch->r_mem, ATA_DATA, (uint16_t *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes), ch->curr[port].atapi / 2); DELAY(10); if (ch->basic_dma) { /* Start basic DMA. */ eprd = ch->dma.workrq_bus + MVS_EPRD_OFFSET + (MVS_EPRD_SIZE * slot->slot); ATA_OUTL(ch->r_mem, DMA_DTLBA, eprd); ATA_OUTL(ch->r_mem, DMA_DTHBA, (eprd >> 16) >> 16); ATA_OUTL(ch->r_mem, DMA_C, DMA_C_START | (((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) ? DMA_C_READ : 0)); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) ch->fake_busy = 1; } /* Start command execution timeout */ callout_reset(&slot->timeout, (int)ccb->ccb_h.timeout * hz / 1000, (timeout_t*)mvs_timeout, slot); } /* Must be called with channel locked. */ static void mvs_execute_transaction(struct mvs_slot *slot) { device_t dev = slot->dev; struct mvs_channel *ch = device_get_softc(dev); bus_addr_t eprd; struct mvs_crqb *crqb; struct mvs_crqb_gen2e *crqb2e; union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; int i; // device_printf(dev, "%d EDMA command %02x size %d slot %d tag %d\n", // port, ccb->ataio.cmd.command, ccb->ataio.dxfer_len, slot->slot, slot->tag); /* Get address of the prepared EPRD */ eprd = ch->dma.workrq_bus + MVS_EPRD_OFFSET + (MVS_EPRD_SIZE * slot->slot); /* Prepare CRQB. Gen IIe uses different CRQB format. */ if (ch->quirks & MVS_Q_GENIIE) { crqb2e = (struct mvs_crqb_gen2e *) (ch->dma.workrq + MVS_CRQB_OFFSET + (MVS_CRQB_SIZE * ch->out_idx)); crqb2e->ctrlflg = htole32( ((ccb->ccb_h.flags & CAM_DIR_IN) ? MVS_CRQB2E_READ : 0) | (slot->tag << MVS_CRQB2E_DTAG_SHIFT) | (port << MVS_CRQB2E_PMP_SHIFT) | (slot->slot << MVS_CRQB2E_HTAG_SHIFT)); /* If there is only one segment - no need to use S/G table. */ if (slot->dma.addr != 0) { eprd = slot->dma.addr; crqb2e->ctrlflg |= htole32(MVS_CRQB2E_CPRD); crqb2e->drbc = slot->dma.len; } crqb2e->cprdbl = htole32(eprd); crqb2e->cprdbh = htole32((eprd >> 16) >> 16); crqb2e->cmd[0] = 0; crqb2e->cmd[1] = 0; crqb2e->cmd[2] = ccb->ataio.cmd.command; crqb2e->cmd[3] = ccb->ataio.cmd.features; crqb2e->cmd[4] = ccb->ataio.cmd.lba_low; crqb2e->cmd[5] = ccb->ataio.cmd.lba_mid; crqb2e->cmd[6] = ccb->ataio.cmd.lba_high; crqb2e->cmd[7] = ccb->ataio.cmd.device; crqb2e->cmd[8] = ccb->ataio.cmd.lba_low_exp; crqb2e->cmd[9] = ccb->ataio.cmd.lba_mid_exp; crqb2e->cmd[10] = ccb->ataio.cmd.lba_high_exp; crqb2e->cmd[11] = ccb->ataio.cmd.features_exp; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { crqb2e->cmd[12] = slot->tag << 3; crqb2e->cmd[13] = 0; } else { crqb2e->cmd[12] = ccb->ataio.cmd.sector_count; crqb2e->cmd[13] = ccb->ataio.cmd.sector_count_exp; } crqb2e->cmd[14] = 0; crqb2e->cmd[15] = 0; } else { crqb = (struct mvs_crqb *) (ch->dma.workrq + MVS_CRQB_OFFSET + (MVS_CRQB_SIZE * ch->out_idx)); crqb->cprdbl = htole32(eprd); crqb->cprdbh = htole32((eprd >> 16) >> 16); crqb->ctrlflg = htole16( ((ccb->ccb_h.flags & CAM_DIR_IN) ? MVS_CRQB_READ : 0) | (slot->slot << MVS_CRQB_TAG_SHIFT) | (port << MVS_CRQB_PMP_SHIFT)); i = 0; /* * Controller can handle only 11 of 12 ATA registers, * so we have to choose which one to skip. */ if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { crqb->cmd[i++] = ccb->ataio.cmd.features_exp; crqb->cmd[i++] = 0x11; } crqb->cmd[i++] = ccb->ataio.cmd.features; crqb->cmd[i++] = 0x11; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { crqb->cmd[i++] = slot->tag << 3; crqb->cmd[i++] = 0x12; } else { crqb->cmd[i++] = ccb->ataio.cmd.sector_count_exp; crqb->cmd[i++] = 0x12; crqb->cmd[i++] = ccb->ataio.cmd.sector_count; crqb->cmd[i++] = 0x12; } crqb->cmd[i++] = ccb->ataio.cmd.lba_low_exp; crqb->cmd[i++] = 0x13; crqb->cmd[i++] = ccb->ataio.cmd.lba_low; crqb->cmd[i++] = 0x13; crqb->cmd[i++] = ccb->ataio.cmd.lba_mid_exp; crqb->cmd[i++] = 0x14; crqb->cmd[i++] = ccb->ataio.cmd.lba_mid; crqb->cmd[i++] = 0x14; crqb->cmd[i++] = ccb->ataio.cmd.lba_high_exp; crqb->cmd[i++] = 0x15; crqb->cmd[i++] = ccb->ataio.cmd.lba_high; crqb->cmd[i++] = 0x15; crqb->cmd[i++] = ccb->ataio.cmd.device; crqb->cmd[i++] = 0x16; crqb->cmd[i++] = ccb->ataio.cmd.command; crqb->cmd[i++] = 0x97; } bus_dmamap_sync(ch->dma.workrq_tag, ch->dma.workrq_map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ch->dma.workrp_tag, ch->dma.workrp_map, BUS_DMASYNC_PREREAD); slot->state = MVS_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); /* Issue command to the controller. */ ch->out_idx = (ch->out_idx + 1) & (MVS_MAX_SLOTS - 1); ATA_OUTL(ch->r_mem, EDMA_REQQIP, ch->dma.workrq_bus + MVS_CRQB_OFFSET + (MVS_CRQB_SIZE * ch->out_idx)); /* Start command execution timeout */ callout_reset(&slot->timeout, (int)ccb->ccb_h.timeout * hz / 1000, (timeout_t*)mvs_timeout, slot); return; } /* Must be called with channel locked. */ static void mvs_process_timeout(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); /* Handle the rest of commands. */ for (i = 0; i < MVS_MAX_SLOTS; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < MVS_SLOT_RUNNING) continue; mvs_end_transaction(&ch->slot[i], MVS_ERR_TIMEOUT); } } /* Must be called with channel locked. */ static void mvs_rearm_timeout(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); for (i = 0; i < MVS_MAX_SLOTS; i++) { struct mvs_slot *slot = &ch->slot[i]; /* Do we have a running request on slot? */ if (slot->state < MVS_SLOT_RUNNING) continue; if ((ch->toslots & (1 << i)) == 0) continue; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 2000, (timeout_t*)mvs_timeout, slot); } } /* Locked by callout mechanism. */ static void mvs_timeout(struct mvs_slot *slot) { device_t dev = slot->dev; struct mvs_channel *ch = device_get_softc(dev); /* Check for stale timeout. */ if (slot->state < MVS_SLOT_RUNNING) return; device_printf(dev, "Timeout on slot %d\n", slot->slot); device_printf(dev, "iec %08x sstat %08x serr %08x edma_s %08x " "dma_c %08x dma_s %08x rs %08x status %02x\n", ATA_INL(ch->r_mem, EDMA_IEC), ATA_INL(ch->r_mem, SATA_SS), ATA_INL(ch->r_mem, SATA_SE), ATA_INL(ch->r_mem, EDMA_S), ATA_INL(ch->r_mem, DMA_C), ATA_INL(ch->r_mem, DMA_S), ch->rslots, ATA_INB(ch->r_mem, ATA_ALTSTAT)); /* Handle frozen command. */ mvs_requeue_frozen(dev); /* We wait for other commands timeout and pray. */ if (ch->toslots == 0) xpt_freeze_simq(ch->sim, 1); ch->toslots |= (1 << slot->slot); if ((ch->rslots & ~ch->toslots) == 0) mvs_process_timeout(dev); else device_printf(dev, " ... waiting for slots %08x\n", ch->rslots & ~ch->toslots); } /* Must be called with channel locked. */ static void mvs_end_transaction(struct mvs_slot *slot, enum mvs_err_type et) { device_t dev = slot->dev; struct mvs_channel *ch = device_get_softc(dev); union ccb *ccb = slot->ccb; //device_printf(dev, "cmd done status %d\n", et); bus_dmamap_sync(ch->dma.workrq_tag, ch->dma.workrq_map, BUS_DMASYNC_POSTWRITE); /* Read result registers to the result struct * May be incorrect if several commands finished same time, * so read only when sure or have to. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { struct ata_res *res = &ccb->ataio.res; if ((et == MVS_ERR_TFE) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT)) { mvs_tfd_read(dev, ccb); } else bzero(res, sizeof(*res)); } if (ch->numpslots == 0 || ch->basic_dma) { if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, (ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ch->dma.data_tag, slot->dma.data_map); } } if (et != MVS_ERR_NONE) ch->eslots |= (1 << slot->slot); /* In case of error, freeze device for proper recovery. */ if ((et != MVS_ERR_NONE) && (!ch->readlog) && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } /* Set proper result status. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (et) { case MVS_ERR_NONE: ccb->ccb_h.status |= CAM_REQ_CMP; if (ccb->ccb_h.func_code == XPT_SCSI_IO) ccb->csio.scsi_status = SCSI_STATUS_OK; break; case MVS_ERR_INVALID: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_INVALID; break; case MVS_ERR_INNOCENT: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; case MVS_ERR_TFE: case MVS_ERR_NCQ: if (ccb->ccb_h.func_code == XPT_SCSI_IO) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } break; case MVS_ERR_SATA: ch->fatalerr = 1; if (!ch->readlog) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_UNCOR_PARITY; break; case MVS_ERR_TIMEOUT: if (!ch->readlog) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; default: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } /* Free slot. */ ch->oslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot); if (et != MVS_ERR_TIMEOUT) { if (ch->toslots == (1 << slot->slot)) xpt_release_simq(ch->sim, TRUE); ch->toslots &= ~(1 << slot->slot); } slot->state = MVS_SLOT_EMPTY; slot->ccb = NULL; /* Update channel stats. */ ch->numrslots--; ch->numrslotspd[ccb->ccb_h.target_id]--; if (ccb->ccb_h.func_code == XPT_ATA_IO) { if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { ch->otagspd[ccb->ccb_h.target_id] &= ~(1 << slot->tag); ch->numtslots--; ch->numtslotspd[ccb->ccb_h.target_id]--; } else if (ccb->ataio.cmd.flags & CAM_ATAIO_DMA) { ch->numdslots--; } else { ch->numpslots--; } } else { ch->numpslots--; ch->basic_dma = 0; } /* If it was our READ LOG command - process it. */ if (ch->readlog) { mvs_process_read_log(dev, ccb); /* If it was NCQ command error, put result on hold. */ } else if (et == MVS_ERR_NCQ) { ch->hold[slot->slot] = ccb; ch->holdtag[slot->slot] = slot->tag; ch->numhslots++; } else xpt_done(ccb); /* Unfreeze frozen command. */ if (ch->frozen && !mvs_check_collision(dev, ch->frozen)) { union ccb *fccb = ch->frozen; ch->frozen = NULL; mvs_begin_transaction(dev, fccb); xpt_release_simq(ch->sim, TRUE); } /* If we have no other active commands, ... */ if (ch->rslots == 0) { /* if there was fatal error - reset port. */ if (ch->toslots != 0 || ch->fatalerr) { mvs_reset(dev); } else { /* if we have slots in error, we can reinit port. */ if (ch->eslots != 0) { mvs_set_edma_mode(dev, MVS_EDMA_OFF); ch->eslots = 0; } /* if there commands on hold, we can do READ LOG. */ if (!ch->readlog && ch->numhslots) mvs_issue_read_log(dev); } /* If all the rest of commands are in timeout - give them chance. */ } else if ((ch->rslots & ~ch->toslots) == 0 && et != MVS_ERR_TIMEOUT) mvs_rearm_timeout(dev); /* Start PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3 && (ch->curr[ch->pm_present ? 15 : 0].caps & CTS_SATA_CAPS_D_PMREQ)) { callout_schedule(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8); } } static void mvs_issue_read_log(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); union ccb *ccb; struct ccb_ataio *ataio; int i; ch->readlog = 1; /* Find some holden command. */ for (i = 0; i < MVS_MAX_SLOTS; i++) { if (ch->hold[i]) break; } ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { device_printf(dev, "Unable allocate READ LOG command"); return; /* XXX */ } ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */ ccb->ccb_h.func_code = XPT_ATA_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ ataio = &ccb->ataio; ataio->data_ptr = malloc(512, M_MVS, M_NOWAIT); if (ataio->data_ptr == NULL) { device_printf(dev, "Unable allocate memory for READ LOG command"); return; /* XXX */ } ataio->dxfer_len = 512; bzero(&ataio->cmd, sizeof(ataio->cmd)); ataio->cmd.flags = CAM_ATAIO_48BIT; ataio->cmd.command = 0x2F; /* READ LOG EXT */ ataio->cmd.sector_count = 1; ataio->cmd.sector_count_exp = 0; ataio->cmd.lba_low = 0x10; ataio->cmd.lba_mid = 0; ataio->cmd.lba_mid_exp = 0; /* Freeze SIM while doing READ LOG EXT. */ xpt_freeze_simq(ch->sim, 1); mvs_begin_transaction(dev, ccb); } static void mvs_process_read_log(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); uint8_t *data; struct ata_res *res; int i; ch->readlog = 0; data = ccb->ataio.data_ptr; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (data[0] & 0x80) == 0) { for (i = 0; i < MVS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.target_id != ccb->ccb_h.target_id) continue; if ((data[0] & 0x1F) == ch->holdtag[i]) { res = &ch->hold[i]->ataio.res; res->status = data[2]; res->error = data[3]; res->lba_low = data[4]; res->lba_mid = data[5]; res->lba_high = data[6]; res->device = data[7]; res->lba_low_exp = data[8]; res->lba_mid_exp = data[9]; res->lba_high_exp = data[10]; res->sector_count = data[12]; res->sector_count_exp = data[13]; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_REQUEUE_REQ; } xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } else { if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) device_printf(dev, "Error while READ LOG EXT\n"); else if ((data[0] & 0x80) == 0) { device_printf(dev, "Non-queued command error in READ LOG EXT\n"); } for (i = 0; i < MVS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.target_id != ccb->ccb_h.target_id) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } free(ccb->ataio.data_ptr, M_MVS); xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static int mvs_wait(device_t dev, u_int s, u_int c, int t) { int timeout = 0; uint8_t st; while (((st = mvs_getstatus(dev, 0)) & (s | c)) != s) { DELAY(1000); if (timeout++ > t) { device_printf(dev, "Wait status %02x\n", st); return (-1); } } return (timeout); } static void mvs_requeue_frozen(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); union ccb *fccb = ch->frozen; if (fccb) { ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } } static void mvs_reset(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int i; xpt_freeze_simq(ch->sim, 1); if (bootverbose) device_printf(dev, "MVS reset...\n"); /* Requeue freezed command. */ mvs_requeue_frozen(dev); /* Kill the engine and requeue all running commands. */ mvs_set_edma_mode(dev, MVS_EDMA_OFF); ATA_OUTL(ch->r_mem, DMA_C, 0); for (i = 0; i < MVS_MAX_SLOTS; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < MVS_SLOT_RUNNING) continue; /* XXX; Commands in loading state. */ mvs_end_transaction(&ch->slot[i], MVS_ERR_INNOCENT); } for (i = 0; i < MVS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } if (ch->toslots != 0) xpt_release_simq(ch->sim, TRUE); ch->eslots = 0; ch->toslots = 0; ch->fatalerr = 0; /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); ATA_OUTL(ch->r_mem, EDMA_IEM, 0); ATA_OUTL(ch->r_mem, EDMA_CMD, EDMA_CMD_EATARST); DELAY(25); ATA_OUTL(ch->r_mem, EDMA_CMD, 0); /* Reset and reconnect PHY, */ if (!mvs_sata_phy_reset(dev)) { if (bootverbose) device_printf(dev, "MVS reset done: phy reset found no device\n"); ch->devices = 0; ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff); ATA_OUTL(ch->r_mem, EDMA_IEC, 0); ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT); xpt_release_simq(ch->sim, TRUE); return; } /* Wait for clearing busy status. */ if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, 15000)) < 0) device_printf(dev, "device is not ready\n"); else if (bootverbose) device_printf(dev, "ready wait time=%dms\n", i); ch->devices = 1; ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff); ATA_OUTL(ch->r_mem, EDMA_IEC, 0); ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT); if (bootverbose) device_printf(dev, "MVS reset done: device found\n"); xpt_release_simq(ch->sim, TRUE); } static void mvs_softreset(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); int port = ccb->ccb_h.target_id & 0x0f; int i; mvs_set_edma_mode(dev, MVS_EDMA_OFF); ATA_OUTB(ch->r_mem, SATA_SATAICTL, port << SATA_SATAICTL_PMPTX_SHIFT); ATA_OUTB(ch->r_mem, ATA_CONTROL, ATA_A_RESET); DELAY(10000); ATA_OUTB(ch->r_mem, ATA_CONTROL, 0); ccb->ccb_h.status &= ~CAM_STATUS_MASK; /* Wait for clearing busy status. */ if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, ccb->ccb_h.timeout)) < 0) { ccb->ccb_h.status |= CAM_CMD_TIMEOUT; } else { ccb->ccb_h.status |= CAM_REQ_CMP; } mvs_tfd_read(dev, ccb); xpt_done(ccb); } static int mvs_sata_connect(struct mvs_channel *ch) { u_int32_t status; int timeout; /* Wait up to 100ms for "connect well" */ for (timeout = 0; timeout < 100 ; timeout++) { status = ATA_INL(ch->r_mem, SATA_SS); if (((status & SATA_SS_DET_MASK) == SATA_SS_DET_PHY_ONLINE) && ((status & SATA_SS_SPD_MASK) != SATA_SS_SPD_NO_SPEED) && ((status & SATA_SS_IPM_MASK) == SATA_SS_IPM_ACTIVE)) break; if ((status & SATA_SS_DET_MASK) == SATA_SS_DET_PHY_OFFLINE) { if (bootverbose) { device_printf(ch->dev, "SATA offline status=%08x\n", status); } return (0); } DELAY(1000); } if (timeout >= 100) { if (bootverbose) { device_printf(ch->dev, "SATA connect timeout status=%08x\n", status); } return (0); } if (bootverbose) { device_printf(ch->dev, "SATA connect time=%dms status=%08x\n", timeout, status); } /* Clear SATA error register */ ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff); return (1); } static int mvs_sata_phy_reset(device_t dev) { struct mvs_channel *ch = device_get_softc(dev); int sata_rev; uint32_t val; sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = SATA_SC_SPD_SPEED_GEN1; else if (sata_rev == 2) val = SATA_SC_SPD_SPEED_GEN2; else if (sata_rev == 3) val = SATA_SC_SPD_SPEED_GEN3; else val = 0; ATA_OUTL(ch->r_mem, SATA_SC, SATA_SC_DET_RESET | val | SATA_SC_IPM_DIS_PARTIAL | SATA_SC_IPM_DIS_SLUMBER); DELAY(5000); ATA_OUTL(ch->r_mem, SATA_SC, SATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : (SATA_SC_IPM_DIS_PARTIAL | SATA_SC_IPM_DIS_SLUMBER))); DELAY(5000); if (!mvs_sata_connect(ch)) { if (ch->pm_level > 0) ATA_OUTL(ch->r_mem, SATA_SC, SATA_SC_DET_DISABLE); return (0); } return (1); } static int mvs_check_ids(device_t dev, union ccb *ccb) { struct mvs_channel *ch = device_get_softc(dev); if (ccb->ccb_h.target_id > ((ch->quirks & MVS_Q_GENI) ? 0 : 15)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void mvsaction(struct cam_sim *sim, union ccb *ccb) { device_t dev; struct mvs_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("mvsaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct mvs_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (mvs_check_ids(dev, ccb)) return; if (ch->devices == 0 || (ch->pm_present == 0 && ccb->ccb_h.target_id > 0 && ccb->ccb_h.target_id < 15)) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } /* Check for command collision. */ if (mvs_check_collision(dev, ccb)) { /* Freeze command. */ ch->frozen = ccb; /* We have only one frozen slot, so freeze simq also. */ xpt_freeze_simq(ch->sim, 1); return; } mvs_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct mvs_device *d; if (mvs_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) { d->bytecount = min((ch->quirks & MVS_Q_GENIIE) ? 8192 : 2048, cts->xport_specific.sata.bytecount); } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(MVS_MAX_SLOTS, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) ch->pm_present = cts->xport_specific.sata.pm_present; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct mvs_device *d; uint32_t status; if (mvs_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->proto_specific.valid = 0; cts->xport_specific.sata.valid = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (ccb->ccb_h.target_id == 15 || (ccb->ccb_h.target_id == 0 && !ch->pm_present))) { status = ATA_INL(ch->r_mem, SATA_SS) & SATA_SS_SPD_MASK; if (status & 0x0f0) { cts->xport_specific.sata.revision = (status & 0x0f0) >> 4; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; // if (ch->pm_level) // cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; cts->xport_specific.sata.pm_present = ch->pm_present; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.tags = d->tags; cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS; cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ mvs_reset(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; if (!(ch->quirks & MVS_Q_GENI)) { cpi->hba_inquiry |= PI_SATAPM; /* Gen-II is extremely slow with NCQ on PMP. */ if ((ch->quirks & MVS_Q_GENIIE) || ch->pm_present == 0) cpi->hba_inquiry |= PI_TAG_ABLE; } cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (!(ch->quirks & MVS_Q_GENI)) cpi->max_target = 15; else cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Marvell", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = MAXPHYS; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void mvspoll(struct cam_sim *sim) { struct mvs_channel *ch = (struct mvs_channel *)cam_sim_softc(sim); struct mvs_intr_arg arg; arg.arg = ch->dev; arg.cause = 2; /* XXX */ mvs_ch_intr(&arg); } Index: stable/8/sys/dev/siis/siis.c =================================================================== --- stable/8/sys/dev/siis/siis.c (revision 208447) +++ stable/8/sys/dev/siis/siis.c (revision 208448) @@ -1,1810 +1,1839 @@ /*- * Copyright (c) 2009 Alexander Motin * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 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 AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "siis.h" #include #include #include #include #include /* local prototypes */ static int siis_setup_interrupt(device_t dev); static void siis_intr(void *data); static int siis_suspend(device_t dev); static int siis_resume(device_t dev); +static int siis_ch_init(device_t dev); +static int siis_ch_deinit(device_t dev); static int siis_ch_suspend(device_t dev); static int siis_ch_resume(device_t dev); static void siis_ch_intr_locked(void *data); static void siis_ch_intr(void *data); static void siis_begin_transaction(device_t dev, union ccb *ccb); static void siis_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void siis_execute_transaction(struct siis_slot *slot); static void siis_timeout(struct siis_slot *slot); static void siis_end_transaction(struct siis_slot *slot, enum siis_err_type et); static int siis_setup_fis(device_t dev, struct siis_cmd *ctp, union ccb *ccb, int tag); static void siis_dmainit(device_t dev); static void siis_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static void siis_dmafini(device_t dev); static void siis_slotsalloc(device_t dev); static void siis_slotsfree(device_t dev); static void siis_reset(device_t dev); static void siis_portinit(device_t dev); static int siis_wait_ready(device_t dev, int t); static int siis_sata_connect(struct siis_channel *ch); static void siis_issue_read_log(device_t dev); static void siis_process_read_log(device_t dev, union ccb *ccb); static void siisaction(struct cam_sim *sim, union ccb *ccb); static void siispoll(struct cam_sim *sim); MALLOC_DEFINE(M_SIIS, "SIIS driver", "SIIS driver data buffers"); static struct { uint32_t id; const char *name; int ports; int quirks; #define SIIS_Q_SNTF 1 #define SIIS_Q_NOMSI 2 } siis_ids[] = { {0x31241095, "SiI3124", 4, 0}, {0x31248086, "SiI3124", 4, 0}, {0x31321095, "SiI3132", 2, SIIS_Q_SNTF|SIIS_Q_NOMSI}, {0x02421095, "SiI3132", 2, SIIS_Q_SNTF|SIIS_Q_NOMSI}, {0x02441095, "SiI3132", 2, SIIS_Q_SNTF|SIIS_Q_NOMSI}, {0x31311095, "SiI3131", 1, SIIS_Q_SNTF|SIIS_Q_NOMSI}, {0x35311095, "SiI3531", 1, SIIS_Q_SNTF|SIIS_Q_NOMSI}, {0, NULL, 0, 0} }; static int siis_probe(device_t dev) { char buf[64]; int i; uint32_t devid = pci_get_devid(dev); for (i = 0; siis_ids[i].id != 0; i++) { if (siis_ids[i].id == devid) { snprintf(buf, sizeof(buf), "%s SATA controller", siis_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } return (ENXIO); } static int siis_attach(device_t dev) { struct siis_controller *ctlr = device_get_softc(dev); uint32_t devid = pci_get_devid(dev); device_t child; int error, i, unit; ctlr->dev = dev; for (i = 0; siis_ids[i].id != 0; i++) { if (siis_ids[i].id == devid) break; } ctlr->quirks = siis_ids[i].quirks; /* Global memory */ ctlr->r_grid = PCIR_BAR(0); if (!(ctlr->r_gmem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_grid, RF_ACTIVE))) return (ENXIO); ctlr->gctl = ATA_INL(ctlr->r_gmem, SIIS_GCTL); /* Channels memory */ ctlr->r_rid = PCIR_BAR(2); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return (ENXIO); /* Setup our own memory management for channels. */ ctlr->sc_iomem.rm_type = RMAN_ARRAY; ctlr->sc_iomem.rm_descr = "I/O memory addresses"; if ((error = rman_init(&ctlr->sc_iomem)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_grid, ctlr->r_gmem); return (error); } if ((error = rman_manage_region(&ctlr->sc_iomem, rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_grid, ctlr->r_gmem); rman_fini(&ctlr->sc_iomem); return (error); } pci_enable_busmaster(dev); /* Reset controller */ siis_resume(dev); /* Number of HW channels */ ctlr->channels = siis_ids[i].ports; /* Setup interrupts. */ if (siis_setup_interrupt(dev)) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_grid, ctlr->r_gmem); rman_fini(&ctlr->sc_iomem); return ENXIO; } /* Attach all channels on this controller */ for (unit = 0; unit < ctlr->channels; unit++) { child = device_add_child(dev, "siisch", -1); if (child == NULL) device_printf(dev, "failed to add channel device\n"); else device_set_ivars(child, (void *)(intptr_t)unit); } bus_generic_attach(dev); return 0; } static int siis_detach(device_t dev) { struct siis_controller *ctlr = device_get_softc(dev); device_t *children; int nchildren, i; /* Detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* Free interrupts. */ if (ctlr->irq.r_irq) { bus_teardown_intr(dev, ctlr->irq.r_irq, ctlr->irq.handle); bus_release_resource(dev, SYS_RES_IRQ, ctlr->irq.r_irq_rid, ctlr->irq.r_irq); } pci_release_msi(dev); /* Free memory. */ rman_fini(&ctlr->sc_iomem); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_grid, ctlr->r_gmem); return (0); } static int siis_suspend(device_t dev) { struct siis_controller *ctlr = device_get_softc(dev); bus_generic_suspend(dev); /* Put controller into reset state. */ ctlr->gctl |= SIIS_GCTL_GRESET; ATA_OUTL(ctlr->r_gmem, SIIS_GCTL, ctlr->gctl); return 0; } static int siis_resume(device_t dev) { struct siis_controller *ctlr = device_get_softc(dev); /* Set PCIe max read request size to at least 1024 bytes */ if (pci_get_max_read_req(dev) < 1024) pci_set_max_read_req(dev, 1024); /* Put controller into reset state. */ ctlr->gctl |= SIIS_GCTL_GRESET; ATA_OUTL(ctlr->r_gmem, SIIS_GCTL, ctlr->gctl); DELAY(10000); /* Get controller out of reset state and enable port interrupts. */ ctlr->gctl &= ~(SIIS_GCTL_GRESET | SIIS_GCTL_I2C_IE); ctlr->gctl |= 0x0000000f; ATA_OUTL(ctlr->r_gmem, SIIS_GCTL, ctlr->gctl); return (bus_generic_resume(dev)); } static int siis_setup_interrupt(device_t dev) { struct siis_controller *ctlr = device_get_softc(dev); int msi = ctlr->quirks & SIIS_Q_NOMSI ? 0 : 1; /* Process hints. */ resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &msi); if (msi < 0) msi = 0; else if (msi > 0) msi = min(1, pci_msi_count(dev)); /* Allocate MSI if needed/present. */ if (msi && pci_alloc_msi(dev, &msi) != 0) msi = 0; /* Allocate all IRQs. */ ctlr->irq.r_irq_rid = msi ? 1 : 0; if (!(ctlr->irq.r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &ctlr->irq.r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "unable to map interrupt\n"); return ENXIO; } if ((bus_setup_intr(dev, ctlr->irq.r_irq, ATA_INTR_FLAGS, NULL, siis_intr, ctlr, &ctlr->irq.handle))) { /* SOS XXX release r_irq */ device_printf(dev, "unable to setup interrupt\n"); return ENXIO; } return (0); } /* * Common case interrupt handler. */ static void siis_intr(void *data) { struct siis_controller *ctlr = (struct siis_controller *)data; u_int32_t is; void *arg; int unit; is = ATA_INL(ctlr->r_gmem, SIIS_IS); for (unit = 0; unit < ctlr->channels; unit++) { if ((is & SIIS_IS_PORT(unit)) != 0 && (arg = ctlr->interrupt[unit].argument)) { ctlr->interrupt[unit].function(arg); } } /* Acknowledge interrupt, if MSI enabled. */ if (ctlr->irq.r_irq_rid) { ATA_OUTL(ctlr->r_gmem, SIIS_GCTL, ctlr->gctl | SIIS_GCTL_MSIACK); } } static struct resource * siis_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct siis_controller *ctlr = device_get_softc(dev); int unit = ((struct siis_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = unit << 13; long st; switch (type) { case SYS_RES_MEMORY: st = rman_get_start(ctlr->r_mem); res = rman_reserve_resource(&ctlr->sc_iomem, st + offset, st + offset + 0x2000, 0x2000, RF_ACTIVE, child); if (res) { bus_space_handle_t bsh; bus_space_tag_t bst; bsh = rman_get_bushandle(ctlr->r_mem); bst = rman_get_bustag(ctlr->r_mem); bus_space_subregion(bst, bsh, offset, 0x2000, &bsh); rman_set_bushandle(res, bsh); rman_set_bustag(res, bst); } break; case SYS_RES_IRQ: if (*rid == ATA_IRQ_RID) res = ctlr->irq.r_irq; break; } return (res); } static int siis_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { switch (type) { case SYS_RES_MEMORY: rman_release_resource(r); return (0); case SYS_RES_IRQ: if (rid != ATA_IRQ_RID) return ENOENT; return (0); } return (EINVAL); } static int siis_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep) { struct siis_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); if (filter != NULL) { printf("siis.c: we cannot use a filter here\n"); return (EINVAL); } ctlr->interrupt[unit].function = function; ctlr->interrupt[unit].argument = argument; return (0); } static int siis_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct siis_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); ctlr->interrupt[unit].function = NULL; ctlr->interrupt[unit].argument = NULL; return (0); } static int siis_print_child(device_t dev, device_t child) { int retval; retval = bus_print_child_header(dev, child); retval += printf(" at channel %d", (int)(intptr_t)device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } devclass_t siis_devclass; static device_method_t siis_methods[] = { DEVMETHOD(device_probe, siis_probe), DEVMETHOD(device_attach, siis_attach), DEVMETHOD(device_detach, siis_detach), DEVMETHOD(device_suspend, siis_suspend), DEVMETHOD(device_resume, siis_resume), DEVMETHOD(bus_print_child, siis_print_child), DEVMETHOD(bus_alloc_resource, siis_alloc_resource), DEVMETHOD(bus_release_resource, siis_release_resource), DEVMETHOD(bus_setup_intr, siis_setup_intr), DEVMETHOD(bus_teardown_intr,siis_teardown_intr), { 0, 0 } }; static driver_t siis_driver = { "siis", siis_methods, sizeof(struct siis_controller) }; DRIVER_MODULE(siis, pci, siis_driver, siis_devclass, 0, 0); MODULE_VERSION(siis, 1); MODULE_DEPEND(siis, cam, 1, 1, 1); static int siis_ch_probe(device_t dev) { device_set_desc_copy(dev, "SIIS channel"); return (0); } static int siis_ch_attach(device_t dev) { struct siis_controller *ctlr = device_get_softc(device_get_parent(dev)); struct siis_channel *ch = device_get_softc(dev); struct cam_devq *devq; int rid, error, i, sata_rev = 0; ch->dev = dev; ch->unit = (intptr_t)device_get_ivars(dev); ch->quirks = ctlr->quirks; resource_int_value(device_get_name(dev), device_get_unit(dev), "pm_level", &ch->pm_level); resource_int_value(device_get_name(dev), device_get_unit(dev), "sata_rev", &sata_rev); for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; ch->user[i].bytecount = 8192; ch->user[i].tags = SIIS_MAX_SLOTS; ch->curr[i] = ch->user[i]; if (ch->pm_level) ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ; } mtx_init(&ch->mtx, "SIIS channel lock", NULL, MTX_DEF); rid = ch->unit; if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); siis_dmainit(dev); siis_slotsalloc(dev); - siis_ch_resume(dev); + siis_ch_init(dev); mtx_lock(&ch->mtx); rid = ATA_IRQ_RID; if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); device_printf(dev, "Unable to map interrupt\n"); return (ENXIO); } if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, siis_ch_intr_locked, dev, &ch->ih))) { device_printf(dev, "Unable to setup interrupt\n"); error = ENXIO; goto err1; } /* Create the device queue for our SIM. */ devq = cam_simq_alloc(SIIS_MAX_SLOTS); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(siisaction, siispoll, "siisch", ch, device_get_unit(dev), &ch->mtx, 2, SIIS_MAX_SLOTS, devq); if (ch->sim == NULL) { device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err2; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } mtx_unlock(&ch->mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_unlock(&ch->mtx); return (error); } static int siis_ch_detach(device_t dev) { struct siis_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->mtx); bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); - siis_ch_suspend(dev); + siis_ch_deinit(dev); siis_slotsfree(dev); siis_dmafini(dev); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_destroy(&ch->mtx); return (0); } static int -siis_ch_suspend(device_t dev) +siis_ch_init(device_t dev) { struct siis_channel *ch = device_get_softc(dev); + /* Get port out of reset state. */ + ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET); + ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT); + if (ch->pm_present) + ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME); + else + ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); + /* Enable port interrupts */ + ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED); + return (0); +} + +static int +siis_ch_deinit(device_t dev) +{ + struct siis_channel *ch = device_get_softc(dev); + /* Put port into reset state. */ ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PORT_RESET); return (0); } static int +siis_ch_suspend(device_t dev) +{ + struct siis_channel *ch = device_get_softc(dev); + + mtx_lock(&ch->mtx); + xpt_freeze_simq(ch->sim, 1); + while (ch->oslots) + msleep(ch, &ch->mtx, PRIBIO, "siissusp", hz/100); + siis_ch_deinit(dev); + mtx_unlock(&ch->mtx); + return (0); +} + +static int siis_ch_resume(device_t dev) { struct siis_channel *ch = device_get_softc(dev); - /* Get port out of reset state. */ - ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET); - ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT); - if (ch->pm_present) - ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME); - else - ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); - /* Enable port interrupts */ - ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED); + mtx_lock(&ch->mtx); + siis_ch_init(dev); + siis_reset(dev); + xpt_release_simq(ch->sim, TRUE); + mtx_unlock(&ch->mtx); return (0); } devclass_t siisch_devclass; static device_method_t siisch_methods[] = { DEVMETHOD(device_probe, siis_ch_probe), DEVMETHOD(device_attach, siis_ch_attach), DEVMETHOD(device_detach, siis_ch_detach), DEVMETHOD(device_suspend, siis_ch_suspend), DEVMETHOD(device_resume, siis_ch_resume), { 0, 0 } }; static driver_t siisch_driver = { "siisch", siisch_methods, sizeof(struct siis_channel) }; DRIVER_MODULE(siisch, siis, siisch_driver, siis_devclass, 0, 0); struct siis_dc_cb_args { bus_addr_t maddr; int error; }; static void siis_dmainit(device_t dev) { struct siis_channel *ch = device_get_softc(dev); struct siis_dc_cb_args dcba; /* Command area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, SIIS_WORK_SIZE, 1, SIIS_WORK_SIZE, 0, NULL, NULL, &ch->dma.work_tag)) goto error; if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, 0, &ch->dma.work_map)) goto error; if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map, ch->dma.work, SIIS_WORK_SIZE, siis_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); goto error; } ch->dma.work_bus = dcba.maddr; /* Data area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, SIIS_SG_ENTRIES * PAGE_SIZE * SIIS_MAX_SLOTS, SIIS_SG_ENTRIES, 0xFFFFFFFF, 0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) { goto error; } return; error: device_printf(dev, "WARNING - DMA initialization failed\n"); siis_dmafini(dev); } static void siis_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct siis_dc_cb_args *dcba = (struct siis_dc_cb_args *)xsc; if (!(dcba->error = error)) dcba->maddr = segs[0].ds_addr; } static void siis_dmafini(device_t dev) { struct siis_channel *ch = device_get_softc(dev); if (ch->dma.data_tag) { bus_dma_tag_destroy(ch->dma.data_tag); ch->dma.data_tag = NULL; } if (ch->dma.work_bus) { bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map); bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); ch->dma.work_bus = 0; ch->dma.work_map = NULL; ch->dma.work = NULL; } if (ch->dma.work_tag) { bus_dma_tag_destroy(ch->dma.work_tag); ch->dma.work_tag = NULL; } } static void siis_slotsalloc(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i; /* Alloc and setup command/dma slots */ bzero(ch->slot, sizeof(ch->slot)); for (i = 0; i < SIIS_MAX_SLOTS; i++) { struct siis_slot *slot = &ch->slot[i]; slot->dev = dev; slot->slot = i; slot->state = SIIS_SLOT_EMPTY; slot->ccb = NULL; callout_init_mtx(&slot->timeout, &ch->mtx, 0); if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map)) device_printf(ch->dev, "FAILURE - create data_map\n"); } } static void siis_slotsfree(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i; /* Free all dma slots */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { struct siis_slot *slot = &ch->slot[i]; callout_drain(&slot->timeout); if (slot->dma.data_map) { bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map); slot->dma.data_map = NULL; } } } static void siis_notify_events(device_t dev) { struct siis_channel *ch = device_get_softc(dev); struct cam_path *dpath; u_int32_t status; int i; if (ch->quirks & SIIS_Q_SNTF) { status = ATA_INL(ch->r_mem, SIIS_P_SNTF); ATA_OUTL(ch->r_mem, SIIS_P_SNTF, status); } else { /* * Without SNTF we have no idea which device sent notification. * If PMP is connected, assume it, else - device. */ status = (ch->pm_present) ? 0x8000 : 0x0001; } if (bootverbose) device_printf(dev, "SNTF 0x%04x\n", status); for (i = 0; i < 16; i++) { if ((status & (1 << i)) == 0) continue; if (xpt_create_path(&dpath, NULL, xpt_path_path_id(ch->path), i, 0) == CAM_REQ_CMP) { xpt_async(AC_SCSI_AEN, dpath, NULL); xpt_free_path(dpath); } } } static void siis_phy_check_events(device_t dev) { struct siis_channel *ch = device_get_softc(dev); /* If we have a connection event, deal with it */ if (ch->pm_level == 0) { u_int32_t status = ATA_INL(ch->r_mem, SIIS_P_SSTS); union ccb *ccb; if (bootverbose) { if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) { device_printf(dev, "CONNECT requested\n"); } else device_printf(dev, "DISCONNECT requested\n"); } siis_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } } static void siis_ch_intr_locked(void *data) { device_t dev = (device_t)data; struct siis_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); siis_ch_intr(data); mtx_unlock(&ch->mtx); } static void siis_ch_intr(void *data) { device_t dev = (device_t)data; struct siis_channel *ch = device_get_softc(dev); uint32_t istatus, sstatus, ctx, estatus, ok, err = 0; enum siis_err_type et; int i, ccs, port, tslots; mtx_assert(&ch->mtx, MA_OWNED); /* Read command statuses. */ sstatus = ATA_INL(ch->r_mem, SIIS_P_SS); ok = ch->rslots & ~sstatus; /* Complete all successfull commands. */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { if ((ok >> i) & 1) siis_end_transaction(&ch->slot[i], SIIS_ERR_NONE); } /* Do we have any other events? */ if ((sstatus & SIIS_P_SS_ATTN) == 0) return; /* Read and clear interrupt statuses. */ istatus = ATA_INL(ch->r_mem, SIIS_P_IS) & (0xFFFF & ~SIIS_P_IX_COMMCOMP); ATA_OUTL(ch->r_mem, SIIS_P_IS, istatus); /* Process PHY events */ if (istatus & SIIS_P_IX_PHYRDYCHG) siis_phy_check_events(dev); /* Process NOTIFY events */ if (istatus & SIIS_P_IX_SDBN) siis_notify_events(dev); /* Process command errors */ if (istatus & SIIS_P_IX_COMMERR) { estatus = ATA_INL(ch->r_mem, SIIS_P_CMDERR); ctx = ATA_INL(ch->r_mem, SIIS_P_CTX); ccs = (ctx & SIIS_P_CTX_SLOT) >> SIIS_P_CTX_SLOT_SHIFT; port = (ctx & SIIS_P_CTX_PMP) >> SIIS_P_CTX_PMP_SHIFT; err = ch->rslots & sstatus; //device_printf(dev, "%s ERROR ss %08x is %08x rs %08x es %d act %d port %d serr %08x\n", // __func__, sstatus, istatus, ch->rslots, estatus, ccs, port, // ATA_INL(ch->r_mem, SIIS_P_SERR)); if (!ch->readlog && !ch->recovery) { xpt_freeze_simq(ch->sim, ch->numrslots); ch->recovery = 1; } if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status &= ~CAM_STATUS_MASK; fccb->ccb_h.status |= CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } if (estatus == SIIS_P_CMDERR_DEV || estatus == SIIS_P_CMDERR_SDB || estatus == SIIS_P_CMDERR_DATAFIS) { tslots = ch->numtslots[port]; for (i = 0; i < SIIS_MAX_SLOTS; i++) { /* XXX: requests in loading state. */ if (((ch->rslots >> i) & 1) == 0) continue; if (ch->slot[i].ccb->ccb_h.target_id != port) continue; if (tslots == 0) { /* Untagged operation. */ if (i == ccs) et = SIIS_ERR_TFE; else et = SIIS_ERR_INNOCENT; } else { /* Tagged operation. */ et = SIIS_ERR_NCQ; } siis_end_transaction(&ch->slot[i], et); } /* * We can't reinit port if there are some other * commands active, use resume to complete them. */ if (ch->rslots != 0) ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_RESUME); } else { if (estatus == SIIS_P_CMDERR_SENDFIS || estatus == SIIS_P_CMDERR_INCSTATE || estatus == SIIS_P_CMDERR_PPE || estatus == SIIS_P_CMDERR_SERVICE) { et = SIIS_ERR_SATA; } else et = SIIS_ERR_INVALID; for (i = 0; i < SIIS_MAX_SLOTS; i++) { /* XXX: requests in loading state. */ if (((ch->rslots >> i) & 1) == 0) continue; siis_end_transaction(&ch->slot[i], et); } } } } /* Must be called with channel locked. */ static int siis_check_collision(device_t dev, union ccb *ccb) { struct siis_channel *ch = device_get_softc(dev); mtx_assert(&ch->mtx, MA_OWNED); if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { /* Tagged command while we have no supported tag free. */ if (((~ch->oslots) & (0x7fffffff >> (31 - ch->curr[ccb->ccb_h.target_id].tags))) == 0) return (1); } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) { /* Atomic command while anything active. */ if (ch->numrslots != 0) return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) return (1); return (0); } /* Must be called with channel locked. */ static void siis_begin_transaction(device_t dev, union ccb *ccb) { struct siis_channel *ch = device_get_softc(dev); struct siis_slot *slot; int tag, tags; mtx_assert(&ch->mtx, MA_OWNED); /* Choose empty slot. */ tags = SIIS_MAX_SLOTS; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) tags = ch->curr[ccb->ccb_h.target_id].tags; tag = fls((~ch->oslots) & (0x7fffffff >> (31 - tags))) - 1; /* Occupy chosen slot. */ slot = &ch->slot[tag]; slot->ccb = ccb; /* Update channel stats. */ ch->oslots |= (1 << slot->slot); ch->numrslots++; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots[ccb->ccb_h.target_id]++; } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) ch->aslots |= (1 << slot->slot); slot->dma.nsegs = 0; /* If request moves data, setup and load SG list */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { void *buf; bus_size_t size; slot->state = SIIS_SLOT_LOADING; if (ccb->ccb_h.func_code == XPT_ATA_IO) { buf = ccb->ataio.data_ptr; size = ccb->ataio.dxfer_len; } else { buf = ccb->csio.data_ptr; size = ccb->csio.dxfer_len; } bus_dmamap_load(ch->dma.data_tag, slot->dma.data_map, buf, size, siis_dmasetprd, slot, 0); } else siis_execute_transaction(slot); } /* Locked by busdma engine. */ static void siis_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct siis_slot *slot = arg; struct siis_channel *ch = device_get_softc(slot->dev); struct siis_cmd *ctp; struct siis_dma_prd *prd; int i; mtx_assert(&ch->mtx, MA_OWNED); if (error) { device_printf(slot->dev, "DMA load error\n"); if (!ch->readlog) xpt_freeze_simq(ch->sim, 1); siis_end_transaction(slot, SIIS_ERR_INVALID); return; } KASSERT(nsegs <= SIIS_SG_ENTRIES, ("too many DMA segment entries\n")); /* Get a piece of the workspace for this request */ ctp = (struct siis_cmd *) (ch->dma.work + SIIS_CT_OFFSET + (SIIS_CT_SIZE * slot->slot)); /* Fill S/G table */ if (slot->ccb->ccb_h.func_code == XPT_ATA_IO) prd = &ctp->u.ata.prd[0]; else prd = &ctp->u.atapi.prd[0]; for (i = 0; i < nsegs; i++) { prd[i].dba = htole64(segs[i].ds_addr); prd[i].dbc = htole32(segs[i].ds_len); prd[i].control = 0; } prd[nsegs - 1].control = htole32(SIIS_PRD_TRM); slot->dma.nsegs = nsegs; bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, ((slot->ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); siis_execute_transaction(slot); } /* Must be called with channel locked. */ static void siis_execute_transaction(struct siis_slot *slot) { device_t dev = slot->dev; struct siis_channel *ch = device_get_softc(dev); struct siis_cmd *ctp; union ccb *ccb = slot->ccb; u_int64_t prb_bus; mtx_assert(&ch->mtx, MA_OWNED); /* Get a piece of the workspace for this request */ ctp = (struct siis_cmd *) (ch->dma.work + SIIS_CT_OFFSET + (SIIS_CT_SIZE * slot->slot)); ctp->control = 0; ctp->protocol_override = 0; ctp->transfer_count = 0; /* Special handling for Soft Reset command. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { if (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) { ctp->control |= htole16(SIIS_PRB_SOFT_RESET); } else { ctp->control |= htole16(SIIS_PRB_PROTOCOL_OVERRIDE); if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { ctp->protocol_override |= htole16(SIIS_PRB_PROTO_NCQ); } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { ctp->protocol_override |= htole16(SIIS_PRB_PROTO_READ); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { ctp->protocol_override |= htole16(SIIS_PRB_PROTO_WRITE); } } } else if (ccb->ccb_h.func_code == XPT_SCSI_IO) { if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) ctp->control |= htole16(SIIS_PRB_PACKET_READ); else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) ctp->control |= htole16(SIIS_PRB_PACKET_WRITE); } /* Special handling for Soft Reset command. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET)) { /* Kick controller into sane state */ siis_portinit(dev); } /* Setup the FIS for this request */ if (!siis_setup_fis(dev, ctp, ccb, slot->slot)) { device_printf(ch->dev, "Setting up SATA FIS failed\n"); if (!ch->readlog) xpt_freeze_simq(ch->sim, 1); siis_end_transaction(slot, SIIS_ERR_INVALID); return; } bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_PREWRITE); /* Issue command to the controller. */ slot->state = SIIS_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); prb_bus = ch->dma.work_bus + SIIS_CT_OFFSET + (SIIS_CT_SIZE * slot->slot); ATA_OUTL(ch->r_mem, SIIS_P_CACTL(slot->slot), prb_bus); ATA_OUTL(ch->r_mem, SIIS_P_CACTH(slot->slot), prb_bus >> 32); /* Start command execution timeout */ callout_reset(&slot->timeout, (int)ccb->ccb_h.timeout * hz / 1000, (timeout_t*)siis_timeout, slot); return; } /* Must be called with channel locked. */ static void siis_process_timeout(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); if (!ch->readlog && !ch->recovery) { xpt_freeze_simq(ch->sim, ch->numrslots); ch->recovery = 1; } /* Handle the rest of commands. */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < SIIS_SLOT_RUNNING) continue; siis_end_transaction(&ch->slot[i], SIIS_ERR_TIMEOUT); } } /* Must be called with channel locked. */ static void siis_rearm_timeout(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); for (i = 0; i < SIIS_MAX_SLOTS; i++) { struct siis_slot *slot = &ch->slot[i]; /* Do we have a running request on slot? */ if (slot->state < SIIS_SLOT_RUNNING) continue; if ((ch->toslots & (1 << i)) == 0) continue; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 1000, (timeout_t*)siis_timeout, slot); } } /* Locked by callout mechanism. */ static void siis_timeout(struct siis_slot *slot) { device_t dev = slot->dev; struct siis_channel *ch = device_get_softc(dev); mtx_assert(&ch->mtx, MA_OWNED); /* Check for stale timeout. */ if (slot->state < SIIS_SLOT_RUNNING) return; device_printf(dev, "Timeout on slot %d\n", slot->slot); device_printf(dev, "%s is %08x ss %08x rs %08x es %08x sts %08x serr %08x\n", __func__, ATA_INL(ch->r_mem, SIIS_P_IS), ATA_INL(ch->r_mem, SIIS_P_SS), ch->rslots, ATA_INL(ch->r_mem, SIIS_P_CMDERR), ATA_INL(ch->r_mem, SIIS_P_STS), ATA_INL(ch->r_mem, SIIS_P_SERR)); if (ch->toslots == 0) xpt_freeze_simq(ch->sim, 1); ch->toslots |= (1 << slot->slot); if ((ch->rslots & ~ch->toslots) == 0) siis_process_timeout(dev); else device_printf(dev, " ... waiting for slots %08x\n", ch->rslots & ~ch->toslots); } /* Must be called with channel locked. */ static void siis_end_transaction(struct siis_slot *slot, enum siis_err_type et) { device_t dev = slot->dev; struct siis_channel *ch = device_get_softc(dev); union ccb *ccb = slot->ccb; mtx_assert(&ch->mtx, MA_OWNED); bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_POSTWRITE); /* Read result registers to the result struct * May be incorrect if several commands finished same time, * so read only when sure or have to. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { struct ata_res *res = &ccb->ataio.res; if ((et == SIIS_ERR_TFE) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT)) { int offs = SIIS_P_LRAM_SLOT(slot->slot) + 8; res->status = ATA_INB(ch->r_mem, offs + 2); res->error = ATA_INB(ch->r_mem, offs + 3); res->lba_low = ATA_INB(ch->r_mem, offs + 4); res->lba_mid = ATA_INB(ch->r_mem, offs + 5); res->lba_high = ATA_INB(ch->r_mem, offs + 6); res->device = ATA_INB(ch->r_mem, offs + 7); res->lba_low_exp = ATA_INB(ch->r_mem, offs + 8); res->lba_mid_exp = ATA_INB(ch->r_mem, offs + 9); res->lba_high_exp = ATA_INB(ch->r_mem, offs + 10); res->sector_count = ATA_INB(ch->r_mem, offs + 12); res->sector_count_exp = ATA_INB(ch->r_mem, offs + 13); } else bzero(res, sizeof(*res)); } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, (ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ch->dma.data_tag, slot->dma.data_map); } /* Set proper result status. */ if (et != SIIS_ERR_NONE || ch->recovery) { ch->eslots |= (1 << slot->slot); ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } /* In case of error, freeze device for proper recovery. */ if (et != SIIS_ERR_NONE && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (et) { case SIIS_ERR_NONE: ccb->ccb_h.status |= CAM_REQ_CMP; if (ccb->ccb_h.func_code == XPT_SCSI_IO) ccb->csio.scsi_status = SCSI_STATUS_OK; break; case SIIS_ERR_INVALID: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_INVALID; break; case SIIS_ERR_INNOCENT: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; case SIIS_ERR_TFE: case SIIS_ERR_NCQ: if (ccb->ccb_h.func_code == XPT_SCSI_IO) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } break; case SIIS_ERR_SATA: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_UNCOR_PARITY; break; case SIIS_ERR_TIMEOUT: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; default: ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } /* Free slot. */ ch->oslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot); if (et != SIIS_ERR_TIMEOUT) { if (ch->toslots == (1 << slot->slot)) xpt_release_simq(ch->sim, TRUE); ch->toslots &= ~(1 << slot->slot); } slot->state = SIIS_SLOT_EMPTY; slot->ccb = NULL; /* Update channel stats. */ ch->numrslots--; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots[ccb->ccb_h.target_id]--; } /* If it was our READ LOG command - process it. */ if (ch->readlog) { siis_process_read_log(dev, ccb); /* If it was NCQ command error, put result on hold. */ } else if (et == SIIS_ERR_NCQ) { ch->hold[slot->slot] = ccb; ch->numhslots++; } else xpt_done(ccb); /* Unfreeze frozen command. */ if (ch->frozen && !siis_check_collision(dev, ch->frozen)) { union ccb *fccb = ch->frozen; ch->frozen = NULL; siis_begin_transaction(dev, fccb); xpt_release_simq(ch->sim, TRUE); } /* If we have no other active commands, ... */ if (ch->rslots == 0) { /* if there were timeouts or fatal error - reset port. */ if (ch->toslots != 0 || ch->fatalerr) { siis_reset(dev); } else { /* if we have slots in error, we can reinit port. */ if (ch->eslots != 0) siis_portinit(dev); /* if there commands on hold, we can do READ LOG. */ if (!ch->readlog && ch->numhslots) siis_issue_read_log(dev); } /* If all the reset of commands are in timeout - abort them. */ } else if ((ch->rslots & ~ch->toslots) == 0 && et != SIIS_ERR_TIMEOUT) siis_rearm_timeout(dev); } static void siis_issue_read_log(device_t dev) { struct siis_channel *ch = device_get_softc(dev); union ccb *ccb; struct ccb_ataio *ataio; int i; /* Find some holden command. */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { if (ch->hold[i]) break; } if (i == SIIS_MAX_SLOTS) return; ch->readlog = 1; ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { device_printf(dev, "Unable allocate READ LOG command"); return; /* XXX */ } ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */ ccb->ccb_h.func_code = XPT_ATA_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ ataio = &ccb->ataio; ataio->data_ptr = malloc(512, M_SIIS, M_NOWAIT); if (ataio->data_ptr == NULL) { device_printf(dev, "Unable allocate memory for READ LOG command"); return; /* XXX */ } ataio->dxfer_len = 512; bzero(&ataio->cmd, sizeof(ataio->cmd)); ataio->cmd.flags = CAM_ATAIO_48BIT; ataio->cmd.command = 0x2F; /* READ LOG EXT */ ataio->cmd.sector_count = 1; ataio->cmd.sector_count_exp = 0; ataio->cmd.lba_low = 0x10; ataio->cmd.lba_mid = 0; ataio->cmd.lba_mid_exp = 0; siis_begin_transaction(dev, ccb); } static void siis_process_read_log(device_t dev, union ccb *ccb) { struct siis_channel *ch = device_get_softc(dev); uint8_t *data; struct ata_res *res; int i; ch->readlog = 0; data = ccb->ataio.data_ptr; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (data[0] & 0x80) == 0) { for (i = 0; i < SIIS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.target_id != ccb->ccb_h.target_id) continue; if ((data[0] & 0x1F) == i) { res = &ch->hold[i]->ataio.res; res->status = data[2]; res->error = data[3]; res->lba_low = data[4]; res->lba_mid = data[5]; res->lba_high = data[6]; res->device = data[7]; res->lba_low_exp = data[8]; res->lba_mid_exp = data[9]; res->lba_high_exp = data[10]; res->sector_count = data[12]; res->sector_count_exp = data[13]; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_REQUEUE_REQ; } xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } else { if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) device_printf(dev, "Error while READ LOG EXT\n"); else if ((data[0] & 0x80) == 0) { device_printf(dev, "Non-queued command error in READ LOG EXT\n"); } for (i = 0; i < SIIS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.target_id != ccb->ccb_h.target_id) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } free(ccb->ataio.data_ptr, M_SIIS); xpt_free_ccb(ccb); } static void siis_portinit(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i; ch->eslots = 0; ch->recovery = 0; ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_RESUME); for (i = 0; i < 16; i++) { ATA_OUTL(ch->r_mem, SIIS_P_PMPSTS(i), 0), ATA_OUTL(ch->r_mem, SIIS_P_PMPQACT(i), 0); } ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PORT_INIT); siis_wait_ready(dev, 1000); } static int siis_devreset(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int timeout = 0; uint32_t val; ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_DEV_RESET); while (((val = ATA_INL(ch->r_mem, SIIS_P_STS)) & SIIS_P_CTL_DEV_RESET) != 0) { DELAY(1000); if (timeout++ > 100) { device_printf(dev, "device reset stuck (timeout %dms) " "status = %08x\n", timeout, val); return (EBUSY); } } return (0); } static int siis_wait_ready(device_t dev, int t) { struct siis_channel *ch = device_get_softc(dev); int timeout = 0; uint32_t val; while (((val = ATA_INL(ch->r_mem, SIIS_P_STS)) & SIIS_P_CTL_READY) == 0) { DELAY(1000); if (timeout++ > t) { device_printf(dev, "port is not ready (timeout %dms) " "status = %08x\n", t, val); return (EBUSY); } } return (0); } static void siis_reset(device_t dev) { struct siis_channel *ch = device_get_softc(dev); int i, retry = 0, sata_rev; uint32_t val; xpt_freeze_simq(ch->sim, 1); if (bootverbose) device_printf(dev, "SIIS reset...\n"); if (!ch->readlog && !ch->recovery) xpt_freeze_simq(ch->sim, ch->numrslots); /* Requeue frozen command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status &= ~CAM_STATUS_MASK; fccb->ccb_h.status |= CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } /* Requeue all running commands. */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < SIIS_SLOT_RUNNING) continue; /* XXX; Commands in loading state. */ siis_end_transaction(&ch->slot[i], SIIS_ERR_INNOCENT); } /* Finish all holden commands as-is. */ for (i = 0; i < SIIS_MAX_SLOTS; i++) { if (!ch->hold[i]) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } if (ch->toslots != 0) xpt_release_simq(ch->sim, TRUE); ch->eslots = 0; ch->recovery = 0; ch->toslots = 0; ch->fatalerr = 0; /* Disable port interrupts */ ATA_OUTL(ch->r_mem, SIIS_P_IECLR, 0x0000FFFF); /* Set speed limit. */ sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = ATA_SC_SPD_SPEED_GEN1; else if (sata_rev == 2) val = ATA_SC_SPD_SPEED_GEN2; else if (sata_rev == 3) val = ATA_SC_SPD_SPEED_GEN3; else val = 0; ATA_OUTL(ch->r_mem, SIIS_P_SCTL, ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); retry: siis_devreset(dev); /* Reset and reconnect PHY, */ if (!siis_sata_connect(ch)) { ch->devices = 0; /* Enable port interrupts */ ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED); if (bootverbose) device_printf(dev, "SIIS reset done: phy reset found no device\n"); /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); xpt_release_simq(ch->sim, TRUE); return; } /* Wait for clearing busy status. */ if (siis_wait_ready(dev, 10000)) { device_printf(dev, "device ready timeout\n"); if (!retry) { device_printf(dev, "trying full port reset ...\n"); /* Get port to the reset state. */ ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PORT_RESET); DELAY(10000); /* Get port out of reset state. */ ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET); ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT); if (ch->pm_present) ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME); else ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); siis_wait_ready(dev, 5000); retry = 1; goto retry; } } ch->devices = 1; /* Enable port interrupts */ ATA_OUTL(ch->r_mem, SIIS_P_IS, 0xFFFFFFFF); ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED); if (bootverbose) device_printf(dev, "SIIS reset done: devices=%08x\n", ch->devices); /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); xpt_release_simq(ch->sim, TRUE); } static int siis_setup_fis(device_t dev, struct siis_cmd *ctp, union ccb *ccb, int tag) { struct siis_channel *ch = device_get_softc(dev); u_int8_t *fis = &ctp->fis[0]; bzero(fis, 24); fis[0] = 0x27; /* host to device */ fis[1] = (ccb->ccb_h.target_id & 0x0f); if (ccb->ccb_h.func_code == XPT_SCSI_IO) { fis[1] |= 0x80; fis[2] = ATA_PACKET_CMD; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) fis[3] = ATA_F_DMA; else { fis[5] = ccb->csio.dxfer_len; fis[6] = ccb->csio.dxfer_len >> 8; } fis[7] = ATA_D_LBA; fis[15] = ATA_A_4BIT; bzero(ctp->u.atapi.ccb, 16); bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, ctp->u.atapi.ccb, ccb->csio.cdb_len); } else if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) == 0) { fis[1] |= 0x80; fis[2] = ccb->ataio.cmd.command; fis[3] = ccb->ataio.cmd.features; fis[4] = ccb->ataio.cmd.lba_low; fis[5] = ccb->ataio.cmd.lba_mid; fis[6] = ccb->ataio.cmd.lba_high; fis[7] = ccb->ataio.cmd.device; fis[8] = ccb->ataio.cmd.lba_low_exp; fis[9] = ccb->ataio.cmd.lba_mid_exp; fis[10] = ccb->ataio.cmd.lba_high_exp; fis[11] = ccb->ataio.cmd.features_exp; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { fis[12] = tag << 3; fis[13] = 0; } else { fis[12] = ccb->ataio.cmd.sector_count; fis[13] = ccb->ataio.cmd.sector_count_exp; } fis[15] = ATA_A_4BIT; } else { /* Soft reset. */ } return (20); } static int siis_sata_connect(struct siis_channel *ch) { u_int32_t status; int timeout; /* Wait up to 100ms for "connect well" */ for (timeout = 0; timeout < 100 ; timeout++) { status = ATA_INL(ch->r_mem, SIIS_P_SSTS); if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) break; DELAY(1000); } if (timeout >= 100) { if (bootverbose) { device_printf(ch->dev, "SATA connect timeout status=%08x\n", status); } return (0); } if (bootverbose) { device_printf(ch->dev, "SATA connect time=%dms status=%08x\n", timeout, status); } /* Clear SATA error register */ ATA_OUTL(ch->r_mem, SIIS_P_SERR, 0xffffffff); return (1); } static int siis_check_ids(device_t dev, union ccb *ccb) { if (ccb->ccb_h.target_id > 15) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void siisaction(struct cam_sim *sim, union ccb *ccb) { device_t dev; struct siis_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("siisaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct siis_channel *)cam_sim_softc(sim); dev = ch->dev; mtx_assert(&ch->mtx, MA_OWNED); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (siis_check_ids(dev, ccb)) return; if (ch->devices == 0 || (ch->pm_present == 0 && ccb->ccb_h.target_id > 0 && ccb->ccb_h.target_id < 15)) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } /* Check for command collision. */ if (siis_check_collision(dev, ccb)) { /* Freeze command. */ ch->frozen = ccb; /* We have only one frozen slot, so freeze simq also. */ xpt_freeze_simq(ch->sim, 1); return; } siis_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct siis_device *d; if (siis_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(SIIS_MAX_SLOTS, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) { ch->pm_present = cts->xport_specific.sata.pm_present; if (ch->pm_present) ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME); else ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct siis_device *d; uint32_t status; if (siis_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->proto_specific.valid = 0; cts->xport_specific.sata.valid = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (ccb->ccb_h.target_id == 15 || (ccb->ccb_h.target_id == 0 && !ch->pm_present))) { status = ATA_INL(ch->r_mem, SIIS_P_SSTS) & ATA_SS_SPD_MASK; if (status & 0x0f0) { cts->xport_specific.sata.revision = (status & 0x0f0) >> 4; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; if (ch->pm_level) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; cts->xport_specific.sata.pm_present = ch->pm_present; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.tags = d->tags; cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS; cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ siis_reset(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; cpi->hba_inquiry |= PI_SATAPM; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; cpi->max_target = 15; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "SIIS", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->ccb_h.status = CAM_REQ_CMP; cpi->maxio = MAXPHYS; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void siispoll(struct cam_sim *sim) { struct siis_channel *ch = (struct siis_channel *)cam_sim_softc(sim); siis_ch_intr(ch->dev); } Index: stable/8/sys/dev/xen/xenpci =================================================================== --- stable/8/sys/dev/xen/xenpci (revision 208447) +++ stable/8/sys/dev/xen/xenpci (revision 208448) Property changes on: stable/8/sys/dev/xen/xenpci ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/dev/xen/xenpci:r208375,208393 Index: stable/8/sys/geom/sched =================================================================== --- stable/8/sys/geom/sched (revision 208447) +++ stable/8/sys/geom/sched (revision 208448) Property changes on: stable/8/sys/geom/sched ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/geom/sched:r208375,208393 Index: stable/8/sys =================================================================== --- stable/8/sys (revision 208447) +++ stable/8/sys (revision 208448) Property changes on: stable/8/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r208375,208393