1258223a3SMatthew Dillon /* 2258223a3SMatthew Dillon * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3258223a3SMatthew Dillon * 4258223a3SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 5258223a3SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 6258223a3SMatthew Dillon * 7258223a3SMatthew Dillon * Redistribution and use in source and binary forms, with or without 8258223a3SMatthew Dillon * modification, are permitted provided that the following conditions 9258223a3SMatthew Dillon * are met: 10258223a3SMatthew Dillon * 11258223a3SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 12258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer. 13258223a3SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 14258223a3SMatthew Dillon * notice, this list of conditions and the following disclaimer in 15258223a3SMatthew Dillon * the documentation and/or other materials provided with the 16258223a3SMatthew Dillon * distribution. 17258223a3SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 18258223a3SMatthew Dillon * contributors may be used to endorse or promote products derived 19258223a3SMatthew Dillon * from this software without specific, prior written permission. 20258223a3SMatthew Dillon * 21258223a3SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22258223a3SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23258223a3SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24258223a3SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25258223a3SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26258223a3SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27258223a3SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28258223a3SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29258223a3SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30258223a3SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31258223a3SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32258223a3SMatthew Dillon * SUCH DAMAGE. 33258223a3SMatthew Dillon * 34258223a3SMatthew Dillon * 35258223a3SMatthew Dillon * Copyright (c) 2007 David Gwynne <dlg@openbsd.org> 36258223a3SMatthew Dillon * 37258223a3SMatthew Dillon * Permission to use, copy, modify, and distribute this software for any 38258223a3SMatthew Dillon * purpose with or without fee is hereby granted, provided that the above 39258223a3SMatthew Dillon * copyright notice and this permission notice appear in all copies. 40258223a3SMatthew Dillon * 41258223a3SMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 42258223a3SMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 43258223a3SMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 44258223a3SMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 45258223a3SMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 46258223a3SMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 47258223a3SMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 48258223a3SMatthew Dillon * 49258223a3SMatthew Dillon * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $ 50258223a3SMatthew Dillon * $DragonFly$ 51258223a3SMatthew Dillon */ 52258223a3SMatthew Dillon /* 53258223a3SMatthew Dillon * Implement each SATA port as its own SCSI bus on CAM. This way we can 54258223a3SMatthew Dillon * implement future port multiplier features as individual devices on the 55258223a3SMatthew Dillon * bus. 56258223a3SMatthew Dillon * 57258223a3SMatthew Dillon * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest 58258223a3SMatthew Dillon * was written natively for DragonFly. 59258223a3SMatthew Dillon */ 60258223a3SMatthew Dillon 61258223a3SMatthew Dillon #include "ahci.h" 62258223a3SMatthew Dillon 63b4189e5eSMatthew Dillon const char *ScsiTypeArray[32] = { 64b4189e5eSMatthew Dillon "DIRECT", 65b4189e5eSMatthew Dillon "SEQUENTIAL", 66b4189e5eSMatthew Dillon "PRINTER", 67b4189e5eSMatthew Dillon "PROCESSOR", 68b4189e5eSMatthew Dillon "WORM", 69b4189e5eSMatthew Dillon "CDROM", 70b4189e5eSMatthew Dillon "SCANNER", 71b4189e5eSMatthew Dillon "OPTICAL", 72b4189e5eSMatthew Dillon "CHANGER", 73b4189e5eSMatthew Dillon "COMM", 74b4189e5eSMatthew Dillon "ASC0", 75b4189e5eSMatthew Dillon "ASC1", 76b4189e5eSMatthew Dillon "STORARRAY", 77b4189e5eSMatthew Dillon "ENCLOSURE", 78b4189e5eSMatthew Dillon "RBC", 79b4189e5eSMatthew Dillon "OCRW", 80b4189e5eSMatthew Dillon "0x10", 81b4189e5eSMatthew Dillon "OSD", 82b4189e5eSMatthew Dillon "ADC", 83b4189e5eSMatthew Dillon "0x13", 84b4189e5eSMatthew Dillon "0x14", 85b4189e5eSMatthew Dillon "0x15", 86b4189e5eSMatthew Dillon "0x16", 87b4189e5eSMatthew Dillon "0x17", 88b4189e5eSMatthew Dillon "0x18", 89b4189e5eSMatthew Dillon "0x19", 90b4189e5eSMatthew Dillon "0x1A", 91b4189e5eSMatthew Dillon "0x1B", 92b4189e5eSMatthew Dillon "0x1C", 93b4189e5eSMatthew Dillon "0x1D", 94b4189e5eSMatthew Dillon "0x1E", 95b4189e5eSMatthew Dillon "NODEVICE" 96b4189e5eSMatthew Dillon }; 97b4189e5eSMatthew Dillon 98258223a3SMatthew Dillon static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb); 99258223a3SMatthew Dillon static void ahci_xpt_poll(struct cam_sim *sim); 1001980eff3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct ahci_port *ap, 1011980eff3SMatthew Dillon struct ata_port *at, union ccb *ccb); 1021980eff3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap, 1031980eff3SMatthew Dillon struct ata_port *at, union ccb *ccb); 104258223a3SMatthew Dillon 105258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa); 106258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa); 107b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa); 108b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data); 1097d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis, 1107d4fcf34SMatthew Dillon struct scsi_sense_data *sense_data); 111258223a3SMatthew Dillon 1121980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at); 1131980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at); 114b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa); 115258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id); 116258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap); 117258223a3SMatthew Dillon 118258223a3SMatthew Dillon int 119258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap) 120258223a3SMatthew Dillon { 121258223a3SMatthew Dillon struct cam_devq *devq; 122258223a3SMatthew Dillon struct cam_sim *sim; 123258223a3SMatthew Dillon int error; 124258223a3SMatthew Dillon int unit; 125258223a3SMatthew Dillon 126cec85a37SMatthew Dillon /* 127cec85a37SMatthew Dillon * We want at least one ccb to be available for error processing 128cec85a37SMatthew Dillon * so don't let CAM use more then ncmds - 1. 129cec85a37SMatthew Dillon */ 130258223a3SMatthew Dillon unit = device_get_unit(ap->ap_sc->sc_dev); 131cec85a37SMatthew Dillon if (ap->ap_sc->sc_ncmds > 1) 132cec85a37SMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1); 133cec85a37SMatthew Dillon else 134258223a3SMatthew Dillon devq = cam_simq_alloc(ap->ap_sc->sc_ncmds); 135258223a3SMatthew Dillon if (devq == NULL) { 136258223a3SMatthew Dillon return (ENOMEM); 137258223a3SMatthew Dillon } 138258223a3SMatthew Dillon sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci", 139258223a3SMatthew Dillon (void *)ap, unit, &sim_mplock, 1, 1, devq); 140258223a3SMatthew Dillon cam_simq_release(devq); 141258223a3SMatthew Dillon if (sim == NULL) { 142258223a3SMatthew Dillon return (ENOMEM); 143258223a3SMatthew Dillon } 144258223a3SMatthew Dillon ap->ap_sim = sim; 145831bc9e3SMatthew Dillon ahci_os_unlock_port(ap); 146258223a3SMatthew Dillon error = xpt_bus_register(ap->ap_sim, ap->ap_num); 147831bc9e3SMatthew Dillon ahci_os_lock_port(ap); 148258223a3SMatthew Dillon if (error != CAM_SUCCESS) { 149258223a3SMatthew Dillon ahci_cam_detach(ap); 150258223a3SMatthew Dillon return (EINVAL); 151258223a3SMatthew Dillon } 152258223a3SMatthew Dillon ap->ap_flags |= AP_F_BUS_REGISTERED; 153258223a3SMatthew Dillon 154c408a8b3SMatthew Dillon if (ap->ap_probe == ATA_PROBE_NEED_IDENT) 1551980eff3SMatthew Dillon error = ahci_cam_probe(ap, NULL); 156c408a8b3SMatthew Dillon else 157c408a8b3SMatthew Dillon error = 0; 158258223a3SMatthew Dillon if (error) { 159258223a3SMatthew Dillon ahci_cam_detach(ap); 160258223a3SMatthew Dillon return (EIO); 161258223a3SMatthew Dillon } 162258223a3SMatthew Dillon ap->ap_flags |= AP_F_CAM_ATTACHED; 163258223a3SMatthew Dillon 164258223a3SMatthew Dillon return(0); 165258223a3SMatthew Dillon } 166258223a3SMatthew Dillon 1671980eff3SMatthew Dillon /* 1683209f581SMatthew Dillon * The state of the port has changed. 1693209f581SMatthew Dillon * 1703209f581SMatthew Dillon * If at is NULL the physical port has changed state. 1713209f581SMatthew Dillon * If at is non-NULL a particular target behind a PM has changed state. 1723209f581SMatthew Dillon * 1733209f581SMatthew Dillon * If found is -1 the target state must be queued to a non-interrupt context. 1743209f581SMatthew Dillon * (only works with at == NULL). 1753209f581SMatthew Dillon * 1763209f581SMatthew Dillon * If found is 0 the target was removed. 1773209f581SMatthew Dillon * If found is 1 the target was inserted. 1781980eff3SMatthew Dillon */ 179258223a3SMatthew Dillon void 1803209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found) 181258223a3SMatthew Dillon { 182fd8bd957SMatthew Dillon struct cam_path *tmppath; 1833209f581SMatthew Dillon int status; 1843209f581SMatthew Dillon int target; 1853209f581SMatthew Dillon 1863209f581SMatthew Dillon target = atx ? atx->at_target : CAM_TARGET_WILDCARD; 187fd8bd957SMatthew Dillon 188258223a3SMatthew Dillon if (ap->ap_sim == NULL) 189258223a3SMatthew Dillon return; 1903209f581SMatthew Dillon if (found == CAM_TARGET_WILDCARD) { 1913209f581SMatthew Dillon status = xpt_create_path(&tmppath, NULL, 1923209f581SMatthew Dillon cam_sim_path(ap->ap_sim), 1933209f581SMatthew Dillon target, CAM_LUN_WILDCARD); 1943209f581SMatthew Dillon if (status != CAM_REQ_CMP) 195fd8bd957SMatthew Dillon return; 196258223a3SMatthew Dillon ahci_cam_rescan(ap); 197fd8bd957SMatthew Dillon } else { 1983209f581SMatthew Dillon status = xpt_create_path(&tmppath, NULL, 1993209f581SMatthew Dillon cam_sim_path(ap->ap_sim), 2003209f581SMatthew Dillon target, 2013209f581SMatthew Dillon CAM_LUN_WILDCARD); 2023209f581SMatthew Dillon if (status != CAM_REQ_CMP) 2033209f581SMatthew Dillon return; 2043209f581SMatthew Dillon #if 0 2053209f581SMatthew Dillon /* 2063209f581SMatthew Dillon * This confuses CAM 2073209f581SMatthew Dillon */ 2083209f581SMatthew Dillon if (found) 2093209f581SMatthew Dillon xpt_async(AC_FOUND_DEVICE, tmppath, NULL); 2103209f581SMatthew Dillon else 211fd8bd957SMatthew Dillon xpt_async(AC_LOST_DEVICE, tmppath, NULL); 2123209f581SMatthew Dillon #endif 213fd8bd957SMatthew Dillon } 214fd8bd957SMatthew Dillon xpt_free_path(tmppath); 215258223a3SMatthew Dillon } 216258223a3SMatthew Dillon 217258223a3SMatthew Dillon void 218258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap) 219258223a3SMatthew Dillon { 220258223a3SMatthew Dillon int error; 221258223a3SMatthew Dillon 222258223a3SMatthew Dillon if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0) 223258223a3SMatthew Dillon return; 224258223a3SMatthew Dillon get_mplock(); 225258223a3SMatthew Dillon if (ap->ap_sim) { 226258223a3SMatthew Dillon xpt_freeze_simq(ap->ap_sim, 1); 227258223a3SMatthew Dillon } 228258223a3SMatthew Dillon if (ap->ap_flags & AP_F_BUS_REGISTERED) { 229258223a3SMatthew Dillon error = xpt_bus_deregister(cam_sim_path(ap->ap_sim)); 230258223a3SMatthew Dillon KKASSERT(error == CAM_REQ_CMP); 231258223a3SMatthew Dillon ap->ap_flags &= ~AP_F_BUS_REGISTERED; 232258223a3SMatthew Dillon } 233258223a3SMatthew Dillon if (ap->ap_sim) { 234258223a3SMatthew Dillon cam_sim_free(ap->ap_sim); 235258223a3SMatthew Dillon ap->ap_sim = NULL; 236258223a3SMatthew Dillon } 237258223a3SMatthew Dillon rel_mplock(); 238258223a3SMatthew Dillon ap->ap_flags &= ~AP_F_CAM_ATTACHED; 239258223a3SMatthew Dillon } 240258223a3SMatthew Dillon 241258223a3SMatthew Dillon /* 2421980eff3SMatthew Dillon * Once the AHCI port has been attached we need to probe for a device or 243258223a3SMatthew Dillon * devices on the port and setup various options. 2441980eff3SMatthew Dillon * 2451980eff3SMatthew Dillon * If at is NULL we are probing the direct-attached device on the port, 2461980eff3SMatthew Dillon * which may or may not be a port multiplier. 247258223a3SMatthew Dillon */ 2483209f581SMatthew Dillon int 2491980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx) 250258223a3SMatthew Dillon { 2511980eff3SMatthew Dillon struct ata_port *at; 252258223a3SMatthew Dillon struct ata_xfer *xa; 253258223a3SMatthew Dillon u_int64_t capacity; 254258223a3SMatthew Dillon u_int64_t capacity_bytes; 255258223a3SMatthew Dillon int model_len; 256fd8bd957SMatthew Dillon int error; 257258223a3SMatthew Dillon int devncqdepth; 258258223a3SMatthew Dillon int i; 259669fbbf7SMatthew Dillon const char *wcstr; 260669fbbf7SMatthew Dillon const char *rastr; 261fd8bd957SMatthew Dillon const char *scstr; 262fd8bd957SMatthew Dillon const char *type; 263fd8bd957SMatthew Dillon 2643209f581SMatthew Dillon error = EIO; 2651980eff3SMatthew Dillon 2661980eff3SMatthew Dillon /* 267f4553de1SMatthew Dillon * Delayed CAM attachment for initial probe, sim may be NULL 268f4553de1SMatthew Dillon */ 269f4553de1SMatthew Dillon if (ap->ap_sim == NULL) 270f4553de1SMatthew Dillon return(0); 271f4553de1SMatthew Dillon 272f4553de1SMatthew Dillon /* 2731980eff3SMatthew Dillon * A NULL atx indicates a probe of the directly connected device. 2741980eff3SMatthew Dillon * A non-NULL atx indicates a device connected via a port multiplier. 2751980eff3SMatthew Dillon * We need to preserve atx for calls to ahci_ata_get_xfer(). 2761980eff3SMatthew Dillon * 2771980eff3SMatthew Dillon * at is always non-NULL. For directly connected devices we supply 2781980eff3SMatthew Dillon * an (at) pointing to target 0. 2791980eff3SMatthew Dillon */ 2801980eff3SMatthew Dillon if (atx == NULL) { 2813209f581SMatthew Dillon at = ap->ap_ata; /* direct attached - device 0 */ 2821980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) { 283831bc9e3SMatthew Dillon kprintf("%s: Found Port Multiplier\n", 284831bc9e3SMatthew Dillon ATANAME(ap, atx)); 2851980eff3SMatthew Dillon return (0); 2861980eff3SMatthew Dillon } 2871980eff3SMatthew Dillon at->at_type = ap->ap_type; 2881980eff3SMatthew Dillon } else { 2893209f581SMatthew Dillon at = atx; 2901980eff3SMatthew Dillon if (atx->at_type == ATA_PORT_T_PM) { 2911980eff3SMatthew Dillon kprintf("%s: Bogus device, reducing port count to %d\n", 2921980eff3SMatthew Dillon ATANAME(ap, atx), atx->at_target); 2931980eff3SMatthew Dillon if (ap->ap_pmcount > atx->at_target) 2941980eff3SMatthew Dillon ap->ap_pmcount = atx->at_target; 2953209f581SMatthew Dillon goto err; 2961980eff3SMatthew Dillon } 2971980eff3SMatthew Dillon } 2983209f581SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 2993209f581SMatthew Dillon goto err; 3001980eff3SMatthew Dillon if (at->at_type == ATA_PORT_T_NONE) 3013209f581SMatthew Dillon goto err; 302258223a3SMatthew Dillon 303258223a3SMatthew Dillon /* 304258223a3SMatthew Dillon * Issue identify, saving the result 305258223a3SMatthew Dillon */ 3061980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 307258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 3081980eff3SMatthew Dillon xa->data = &at->at_identify; 3091980eff3SMatthew Dillon xa->datalen = sizeof(at->at_identify); 3101980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 3111980eff3SMatthew Dillon 3121980eff3SMatthew Dillon switch(at->at_type) { 3131980eff3SMatthew Dillon case ATA_PORT_T_DISK: 314258223a3SMatthew Dillon xa->fis->command = ATA_C_IDENTIFY; 315fd8bd957SMatthew Dillon type = "DISK"; 3161980eff3SMatthew Dillon break; 3171980eff3SMatthew Dillon case ATA_PORT_T_ATAPI: 3181980eff3SMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 3191980eff3SMatthew Dillon type = "ATAPI"; 3201980eff3SMatthew Dillon break; 3211980eff3SMatthew Dillon default: 3221980eff3SMatthew Dillon xa->fis->command = ATA_C_ATAPI_IDENTIFY; 3231980eff3SMatthew Dillon type = "UNKNOWN(ATAPI?)"; 3241980eff3SMatthew Dillon break; 325fd8bd957SMatthew Dillon } 326258223a3SMatthew Dillon xa->fis->features = 0; 327258223a3SMatthew Dillon xa->fis->device = 0; 328258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 3293209f581SMatthew Dillon xa->timeout = 1000; 330258223a3SMatthew Dillon 331831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) { 332fd8bd957SMatthew Dillon kprintf("%s: Detected %s device but unable to IDENTIFY\n", 3331980eff3SMatthew Dillon ATANAME(ap, atx), type); 334258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 3353209f581SMatthew Dillon goto err; 336258223a3SMatthew Dillon } 337258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 338258223a3SMatthew Dillon 3391980eff3SMatthew Dillon ata_fix_identify(&at->at_identify); 340258223a3SMatthew Dillon 341258223a3SMatthew Dillon /* 342258223a3SMatthew Dillon * Read capacity using SATA probe info. 343258223a3SMatthew Dillon */ 3441980eff3SMatthew Dillon if (le16toh(at->at_identify.cmdset83) & 0x0400) { 345258223a3SMatthew Dillon /* LBA48 feature set supported */ 346258223a3SMatthew Dillon capacity = 0; 347258223a3SMatthew Dillon for (i = 3; i >= 0; --i) { 348258223a3SMatthew Dillon capacity <<= 16; 349258223a3SMatthew Dillon capacity += 3501980eff3SMatthew Dillon le16toh(at->at_identify.addrsecxt[i]); 351258223a3SMatthew Dillon } 352258223a3SMatthew Dillon } else { 3531980eff3SMatthew Dillon capacity = le16toh(at->at_identify.addrsec[1]); 354258223a3SMatthew Dillon capacity <<= 16; 3551980eff3SMatthew Dillon capacity += le16toh(at->at_identify.addrsec[0]); 356258223a3SMatthew Dillon } 3571980eff3SMatthew Dillon at->at_capacity = capacity; 3581980eff3SMatthew Dillon if (atx == NULL) 3591980eff3SMatthew Dillon ap->ap_probe = ATA_PROBE_GOOD; 360258223a3SMatthew Dillon 361258223a3SMatthew Dillon capacity_bytes = capacity * 512; 362258223a3SMatthew Dillon 363258223a3SMatthew Dillon /* 364258223a3SMatthew Dillon * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated 365258223a3SMatthew Dillon * number of slots and limit the number of CAM ccb's to one less 366258223a3SMatthew Dillon * so we always have a slot available for recovery. 367258223a3SMatthew Dillon * 368258223a3SMatthew Dillon * NCQ is not used if ap_ncqdepth is 1 or the host controller does 369258223a3SMatthew Dillon * not support it, and in that case the driver can handle extra 370258223a3SMatthew Dillon * ccb's. 371cec85a37SMatthew Dillon * 3721980eff3SMatthew Dillon * NCQ is currently used only with direct-attached disks. It is 3731980eff3SMatthew Dillon * not used with port multipliers or direct-attached ATAPI devices. 3741980eff3SMatthew Dillon * 375cec85a37SMatthew Dillon * Remember at least one extra CCB needs to be reserved for the 376cec85a37SMatthew Dillon * error ccb. 377258223a3SMatthew Dillon */ 378258223a3SMatthew Dillon if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) && 3791980eff3SMatthew Dillon ap->ap_type == ATA_PORT_T_DISK && 3801980eff3SMatthew Dillon (le16toh(at->at_identify.satacap) & (1 << 8))) { 3811980eff3SMatthew Dillon at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1; 3821980eff3SMatthew Dillon devncqdepth = at->at_ncqdepth; 3831980eff3SMatthew Dillon if (at->at_ncqdepth > ap->ap_sc->sc_ncmds) 3841980eff3SMatthew Dillon at->at_ncqdepth = ap->ap_sc->sc_ncmds; 3851980eff3SMatthew Dillon if (at->at_ncqdepth > 1) { 386258223a3SMatthew Dillon for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) { 3871980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 3881980eff3SMatthew Dillon if (xa->tag < at->at_ncqdepth) { 389258223a3SMatthew Dillon xa->state = ATA_S_COMPLETE; 390258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 391258223a3SMatthew Dillon } 392258223a3SMatthew Dillon } 3931980eff3SMatthew Dillon if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) { 394258223a3SMatthew Dillon cam_devq_resize(ap->ap_sim->devq, 3951980eff3SMatthew Dillon at->at_ncqdepth - 1); 396258223a3SMatthew Dillon } 397cec85a37SMatthew Dillon } 398258223a3SMatthew Dillon } else { 399258223a3SMatthew Dillon devncqdepth = 0; 400258223a3SMatthew Dillon } 401258223a3SMatthew Dillon 402fd8bd957SMatthew Dillon /* 403fd8bd957SMatthew Dillon * Make the model string a bit more presentable 404fd8bd957SMatthew Dillon */ 405258223a3SMatthew Dillon for (model_len = 40; model_len; --model_len) { 4061980eff3SMatthew Dillon if (at->at_identify.model[model_len-1] == ' ') 407258223a3SMatthew Dillon continue; 4081980eff3SMatthew Dillon if (at->at_identify.model[model_len-1] == 0) 409258223a3SMatthew Dillon continue; 410258223a3SMatthew Dillon break; 411258223a3SMatthew Dillon } 412669fbbf7SMatthew Dillon 413fd8bd957SMatthew Dillon /* 414fd8bd957SMatthew Dillon * Generate informatiive strings. 415fd8bd957SMatthew Dillon * 416fd8bd957SMatthew Dillon * NOTE: We do not automatically set write caching, lookahead, 417fd8bd957SMatthew Dillon * or the security state for ATAPI devices. 418fd8bd957SMatthew Dillon */ 4191980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) { 4201980eff3SMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) 421669fbbf7SMatthew Dillon wcstr = "enabled"; 4221980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 423fd8bd957SMatthew Dillon wcstr = "disabled"; 424669fbbf7SMatthew Dillon else 425669fbbf7SMatthew Dillon wcstr = "enabling"; 426669fbbf7SMatthew Dillon } else { 427669fbbf7SMatthew Dillon wcstr = "notsupp"; 428669fbbf7SMatthew Dillon } 429669fbbf7SMatthew Dillon 4301980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) { 4311980eff3SMatthew Dillon if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) 432669fbbf7SMatthew Dillon rastr = "enabled"; 4331980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 434fd8bd957SMatthew Dillon rastr = "disabled"; 435669fbbf7SMatthew Dillon else 436669fbbf7SMatthew Dillon rastr = "enabling"; 437669fbbf7SMatthew Dillon } else { 438669fbbf7SMatthew Dillon rastr = "notsupp"; 439669fbbf7SMatthew Dillon } 440669fbbf7SMatthew Dillon 4411980eff3SMatthew Dillon if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) { 4421980eff3SMatthew Dillon if (at->at_identify.securestatus & ATA_SECURE_FROZEN) 443fd8bd957SMatthew Dillon scstr = "frozen"; 4441980eff3SMatthew Dillon else if (at->at_type == ATA_PORT_T_ATAPI) 445fd8bd957SMatthew Dillon scstr = "unfrozen"; 446fd8bd957SMatthew Dillon else 447fd8bd957SMatthew Dillon scstr = "freezing"; 448fd8bd957SMatthew Dillon } else { 449fd8bd957SMatthew Dillon scstr = "notsupp"; 450fd8bd957SMatthew Dillon } 451fd8bd957SMatthew Dillon 452fd8bd957SMatthew Dillon kprintf("%s: Found %s \"%*.*s %8.8s\" serial=\"%20.20s\"\n" 453*074579dfSMatthew Dillon "%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s " 454*074579dfSMatthew Dillon "capacity=%lld.%02dMB\n", 455*074579dfSMatthew Dillon 4561980eff3SMatthew Dillon ATANAME(ap, atx), 457fd8bd957SMatthew Dillon type, 458258223a3SMatthew Dillon model_len, model_len, 4591980eff3SMatthew Dillon at->at_identify.model, 4601980eff3SMatthew Dillon at->at_identify.firmware, 4611980eff3SMatthew Dillon at->at_identify.serial, 462258223a3SMatthew Dillon 4631980eff3SMatthew Dillon ATANAME(ap, atx), 464258223a3SMatthew Dillon devncqdepth, ap->ap_sc->sc_ncmds, 4651980eff3SMatthew Dillon at->at_identify.satacap, 4661980eff3SMatthew Dillon at->at_identify.satafsup, 467*074579dfSMatthew Dillon (at->at_ncqdepth > 1 ? "YES" : "NO"), 468258223a3SMatthew Dillon (long long)capacity_bytes / (1024 * 1024), 469*074579dfSMatthew Dillon (int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024) 470*074579dfSMatthew Dillon ); 471*074579dfSMatthew Dillon if (bootverbose) 472*074579dfSMatthew Dillon kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n", 4731980eff3SMatthew Dillon ATANAME(ap, atx), 4741980eff3SMatthew Dillon at->at_identify.features85, 4751980eff3SMatthew Dillon at->at_identify.features86, 4761980eff3SMatthew Dillon at->at_identify.features87, 477669fbbf7SMatthew Dillon wcstr, 478fd8bd957SMatthew Dillon rastr, 479fd8bd957SMatthew Dillon scstr 480258223a3SMatthew Dillon ); 481258223a3SMatthew Dillon 482258223a3SMatthew Dillon /* 483fd8bd957SMatthew Dillon * Additional type-specific probing 484fd8bd957SMatthew Dillon */ 4851980eff3SMatthew Dillon switch(at->at_type) { 486fd8bd957SMatthew Dillon case ATA_PORT_T_DISK: 4871980eff3SMatthew Dillon error = ahci_cam_probe_disk(ap, atx); 4881980eff3SMatthew Dillon break; 4891980eff3SMatthew Dillon case ATA_PORT_T_ATAPI: 4901980eff3SMatthew Dillon error = ahci_cam_probe_atapi(ap, atx); 491fd8bd957SMatthew Dillon break; 492fd8bd957SMatthew Dillon default: 4931980eff3SMatthew Dillon error = EIO; 494fd8bd957SMatthew Dillon break; 495fd8bd957SMatthew Dillon } 4963209f581SMatthew Dillon err: 4973209f581SMatthew Dillon if (error) { 4983209f581SMatthew Dillon at->at_probe = ATA_PROBE_FAILED; 4993209f581SMatthew Dillon if (atx == NULL) 5003209f581SMatthew Dillon ap->ap_probe = at->at_probe; 5013209f581SMatthew Dillon } else { 5023209f581SMatthew Dillon at->at_probe = ATA_PROBE_GOOD; 5033209f581SMatthew Dillon if (atx == NULL) 5043209f581SMatthew Dillon ap->ap_probe = at->at_probe; 5053209f581SMatthew Dillon } 5063209f581SMatthew Dillon return (error); 507fd8bd957SMatthew Dillon } 508fd8bd957SMatthew Dillon 509fd8bd957SMatthew Dillon /* 510fd8bd957SMatthew Dillon * DISK-specific probe after initial ident 511fd8bd957SMatthew Dillon */ 512fd8bd957SMatthew Dillon static int 5131980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx) 514fd8bd957SMatthew Dillon { 5151980eff3SMatthew Dillon struct ata_port *at; 516fd8bd957SMatthew Dillon struct ata_xfer *xa; 517fd8bd957SMatthew Dillon 5181980eff3SMatthew Dillon at = atx ? atx : ap->ap_ata; 5191980eff3SMatthew Dillon 520fd8bd957SMatthew Dillon /* 521258223a3SMatthew Dillon * Enable write cache if supported 522fd8bd957SMatthew Dillon * 523fd8bd957SMatthew Dillon * NOTE: "WD My Book" external disk devices have a very poor 524fd8bd957SMatthew Dillon * daughter board between the the ESATA and the HD. Sending 525fd8bd957SMatthew Dillon * any ATA_C_SET_FEATURES commands will break the hardware port 526fd8bd957SMatthew Dillon * with a fatal protocol error. However, this device also 527fd8bd957SMatthew Dillon * indicates that WRITECACHE is already on and READAHEAD is 528fd8bd957SMatthew Dillon * not supported so we avoid the issue. 529258223a3SMatthew Dillon */ 5301980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) && 5311980eff3SMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) { 5321980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 533258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 534258223a3SMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 535669fbbf7SMatthew Dillon /*xa->fis->features = ATA_SF_WRITECACHE_EN;*/ 536669fbbf7SMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5371980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 538669fbbf7SMatthew Dillon xa->fis->device = 0; 539258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5403209f581SMatthew Dillon xa->timeout = 1000; 541669fbbf7SMatthew Dillon xa->datalen = 0; 542831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 5431980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_WCACHE; 544258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 545258223a3SMatthew Dillon } 546258223a3SMatthew Dillon 547258223a3SMatthew Dillon /* 548258223a3SMatthew Dillon * Enable readahead if supported 549258223a3SMatthew Dillon */ 5501980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) && 5511980eff3SMatthew Dillon (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) { 5521980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 553258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 554258223a3SMatthew Dillon xa->fis->command = ATA_C_SET_FEATURES; 555258223a3SMatthew Dillon xa->fis->features = ATA_SF_LOOKAHEAD_EN; 5561980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 557669fbbf7SMatthew Dillon xa->fis->device = 0; 558258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5593209f581SMatthew Dillon xa->timeout = 1000; 560669fbbf7SMatthew Dillon xa->datalen = 0; 561831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 5621980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_RAHEAD; 563258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 564258223a3SMatthew Dillon } 565258223a3SMatthew Dillon 566258223a3SMatthew Dillon /* 567258223a3SMatthew Dillon * FREEZE LOCK the device so malicious users can't lock it on us. 568258223a3SMatthew Dillon * As there is no harm in issuing this to devices that don't 569258223a3SMatthew Dillon * support the security feature set we just send it, and don't bother 570258223a3SMatthew Dillon * checking if the device sends a command abort to tell us it doesn't 571258223a3SMatthew Dillon * support it 572258223a3SMatthew Dillon */ 5731980eff3SMatthew Dillon if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) && 5741980eff3SMatthew Dillon (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0) { 5751980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 576258223a3SMatthew Dillon xa->complete = ahci_ata_dummy_done; 577258223a3SMatthew Dillon xa->fis->command = ATA_C_SEC_FREEZE_LOCK; 5781980eff3SMatthew Dillon xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 579258223a3SMatthew Dillon xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL; 5803209f581SMatthew Dillon xa->timeout = 1000; 581669fbbf7SMatthew Dillon xa->datalen = 0; 582831bc9e3SMatthew Dillon if (ahci_ata_cmd(xa) == ATA_S_COMPLETE) 5831980eff3SMatthew Dillon at->at_features |= ATA_PORT_F_FRZLCK; 584258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 585669fbbf7SMatthew Dillon } 586258223a3SMatthew Dillon 587b4189e5eSMatthew Dillon return (0); 588b4189e5eSMatthew Dillon } 589b4189e5eSMatthew Dillon 590fd8bd957SMatthew Dillon /* 591fd8bd957SMatthew Dillon * ATAPI-specific probe after initial ident 592fd8bd957SMatthew Dillon */ 593b4189e5eSMatthew Dillon static int 5941980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx) 595b4189e5eSMatthew Dillon { 596fd8bd957SMatthew Dillon return(0); 597fd8bd957SMatthew Dillon } 598fd8bd957SMatthew Dillon 599b4189e5eSMatthew Dillon /* 600b4189e5eSMatthew Dillon * Fix byte ordering so buffers can be accessed as 601b4189e5eSMatthew Dillon * strings. 602b4189e5eSMatthew Dillon */ 603258223a3SMatthew Dillon static void 604258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id) 605258223a3SMatthew Dillon { 606258223a3SMatthew Dillon u_int16_t *swap; 607258223a3SMatthew Dillon int i; 608258223a3SMatthew Dillon 609258223a3SMatthew Dillon swap = (u_int16_t *)id->serial; 610258223a3SMatthew Dillon for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++) 611258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 612258223a3SMatthew Dillon 613258223a3SMatthew Dillon swap = (u_int16_t *)id->firmware; 614258223a3SMatthew Dillon for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++) 615258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 616258223a3SMatthew Dillon 617258223a3SMatthew Dillon swap = (u_int16_t *)id->model; 618258223a3SMatthew Dillon for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++) 619258223a3SMatthew Dillon swap[i] = bswap16(swap[i]); 620258223a3SMatthew Dillon } 621258223a3SMatthew Dillon 622258223a3SMatthew Dillon /* 623b4189e5eSMatthew Dillon * Dummy done callback for xa. 624b4189e5eSMatthew Dillon */ 625b4189e5eSMatthew Dillon static void 626b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa) 627b4189e5eSMatthew Dillon { 628b4189e5eSMatthew Dillon } 629b4189e5eSMatthew Dillon 630b4189e5eSMatthew Dillon /* 6313209f581SMatthew Dillon * Use an engineering request to initiate a target scan for devices 6323209f581SMatthew Dillon * behind a port multiplier. 633fd8bd957SMatthew Dillon * 6343209f581SMatthew Dillon * An asynchronous bus scan is used to avoid reentrancy issues. 635258223a3SMatthew Dillon */ 636258223a3SMatthew Dillon static void 637258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 638258223a3SMatthew Dillon { 6393209f581SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 6403209f581SMatthew Dillon 641f4553de1SMatthew Dillon if (ccb->ccb_h.func_code == XPT_SCAN_BUS) { 6423209f581SMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_RUNNING; 6433209f581SMatthew Dillon if (ap->ap_flags & AP_F_SCAN_REQUESTED) { 6443209f581SMatthew Dillon ap->ap_flags &= ~AP_F_SCAN_REQUESTED; 6453209f581SMatthew Dillon ahci_cam_rescan(ap); 6463209f581SMatthew Dillon } 647f4553de1SMatthew Dillon ap->ap_flags |= AP_F_SCAN_COMPLETED; 648f4553de1SMatthew Dillon wakeup(&ap->ap_flags); 649f4553de1SMatthew Dillon } 650f4553de1SMatthew Dillon xpt_free_ccb(ccb); 651258223a3SMatthew Dillon } 652258223a3SMatthew Dillon 653258223a3SMatthew Dillon static void 654258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap) 655258223a3SMatthew Dillon { 656258223a3SMatthew Dillon struct cam_path *path; 657258223a3SMatthew Dillon union ccb *ccb; 658258223a3SMatthew Dillon int status; 6593209f581SMatthew Dillon int i; 6603209f581SMatthew Dillon 6613209f581SMatthew Dillon if (ap->ap_flags & AP_F_SCAN_RUNNING) { 6623209f581SMatthew Dillon ap->ap_flags |= AP_F_SCAN_REQUESTED; 6633209f581SMatthew Dillon return; 6643209f581SMatthew Dillon } 6653209f581SMatthew Dillon ap->ap_flags |= AP_F_SCAN_RUNNING; 6663209f581SMatthew Dillon for (i = 0; i < AHCI_MAX_PMPORTS; ++i) { 6673209f581SMatthew Dillon ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN; 6683209f581SMatthew Dillon } 669258223a3SMatthew Dillon 670258223a3SMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 671258223a3SMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 672258223a3SMatthew Dillon if (status != CAM_REQ_CMP) 673258223a3SMatthew Dillon return; 674258223a3SMatthew Dillon 675f4553de1SMatthew Dillon ccb = xpt_alloc_ccb(); 676258223a3SMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 6772de5e9baSMatthew Dillon ccb->ccb_h.func_code = XPT_ENG_EXEC; 678258223a3SMatthew Dillon ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback; 6793209f581SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 680258223a3SMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 6812de5e9baSMatthew Dillon xpt_action_async(ccb); 682258223a3SMatthew Dillon } 683258223a3SMatthew Dillon 6843209f581SMatthew Dillon static void 6853209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap) 6863209f581SMatthew Dillon { 6873209f581SMatthew Dillon struct cam_path *path; 6883209f581SMatthew Dillon union ccb *ccb; 6893209f581SMatthew Dillon int status; 6903209f581SMatthew Dillon 6913209f581SMatthew Dillon status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim), 6923209f581SMatthew Dillon CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 6933209f581SMatthew Dillon if (status != CAM_REQ_CMP) 6943209f581SMatthew Dillon return; 695f4553de1SMatthew Dillon 696f4553de1SMatthew Dillon ccb = xpt_alloc_ccb(); 6973209f581SMatthew Dillon xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */ 6982de5e9baSMatthew Dillon ccb->ccb_h.func_code = XPT_SCAN_BUS; 6993209f581SMatthew Dillon ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback; 7003209f581SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 7013209f581SMatthew Dillon ccb->crcn.flags = CAM_FLAG_NONE; 702baef7501SMatthew Dillon xpt_action_async(ccb); 7033209f581SMatthew Dillon } 7043209f581SMatthew Dillon 705258223a3SMatthew Dillon /* 706258223a3SMatthew Dillon * Action function - dispatch command 707258223a3SMatthew Dillon */ 708258223a3SMatthew Dillon static 709258223a3SMatthew Dillon void 710258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb) 711258223a3SMatthew Dillon { 712258223a3SMatthew Dillon struct ahci_port *ap; 7131980eff3SMatthew Dillon struct ata_port *at, *atx; 714258223a3SMatthew Dillon struct ccb_hdr *ccbh; 715258223a3SMatthew Dillon int unit; 716258223a3SMatthew Dillon 717258223a3SMatthew Dillon /* XXX lock */ 718258223a3SMatthew Dillon ap = cam_sim_softc(sim); 7191980eff3SMatthew Dillon at = ap->ap_ata; 7201980eff3SMatthew Dillon atx = NULL; 721258223a3SMatthew Dillon KKASSERT(ap != NULL); 722258223a3SMatthew Dillon ccbh = &ccb->ccb_h; 723258223a3SMatthew Dillon unit = cam_sim_unit(sim); 724258223a3SMatthew Dillon 725258223a3SMatthew Dillon /* 7263209f581SMatthew Dillon * Early failure checks. These checks do not apply to XPT_PATH_INQ, 7273209f581SMatthew Dillon * otherwise the bus rescan will not remove the dead devices when 7283209f581SMatthew Dillon * unplugging a PM. 7293209f581SMatthew Dillon * 7301980eff3SMatthew Dillon * For non-wildcards we have one target (0) and one lun (0), 7311980eff3SMatthew Dillon * unless we have a port multiplier. 7321980eff3SMatthew Dillon * 7331980eff3SMatthew Dillon * A wildcard target indicates only the general bus is being 7341980eff3SMatthew Dillon * probed. 7351980eff3SMatthew Dillon * 7361980eff3SMatthew Dillon * Calculate at and atx. at is always non-NULL. atx is only 7371980eff3SMatthew Dillon * non-NULL for direct-attached devices. It will be NULL for 7381980eff3SMatthew Dillon * devices behind a port multiplier. 739258223a3SMatthew Dillon * 740258223a3SMatthew Dillon * XXX What do we do with a LUN wildcard? 741258223a3SMatthew Dillon */ 7423209f581SMatthew Dillon if (ccbh->target_id != CAM_TARGET_WILDCARD && 7433209f581SMatthew Dillon ccbh->func_code != XPT_PATH_INQ) { 7441980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 7453209f581SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 746258223a3SMatthew Dillon xpt_done(ccb); 747258223a3SMatthew Dillon return; 748258223a3SMatthew Dillon } 7491980eff3SMatthew Dillon if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) { 750258223a3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 751258223a3SMatthew Dillon xpt_done(ccb); 752258223a3SMatthew Dillon return; 753258223a3SMatthew Dillon } 7541980eff3SMatthew Dillon at += ccbh->target_id; 7551980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_PM) 7561980eff3SMatthew Dillon atx = at; 7571980eff3SMatthew Dillon 758258223a3SMatthew Dillon if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) { 759258223a3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 760258223a3SMatthew Dillon xpt_done(ccb); 761258223a3SMatthew Dillon return; 762258223a3SMatthew Dillon } 763258223a3SMatthew Dillon } 764258223a3SMatthew Dillon 765258223a3SMatthew Dillon /* 766258223a3SMatthew Dillon * Switch on the meta XPT command 767258223a3SMatthew Dillon */ 768258223a3SMatthew Dillon switch(ccbh->func_code) { 7693209f581SMatthew Dillon case XPT_ENG_EXEC: 7703209f581SMatthew Dillon /* 7713209f581SMatthew Dillon * This routine is called after a port multiplier has been 7723209f581SMatthew Dillon * probed. 7733209f581SMatthew Dillon */ 7743209f581SMatthew Dillon ccbh->status = CAM_REQ_CMP; 775f4553de1SMatthew Dillon ahci_os_lock_port(ap); 776831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 0); 777f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 7783209f581SMatthew Dillon xpt_done(ccb); 7793209f581SMatthew Dillon ahci_xpt_rescan(ap); 7803209f581SMatthew Dillon break; 781258223a3SMatthew Dillon case XPT_PATH_INQ: 7823209f581SMatthew Dillon /* 7833209f581SMatthew Dillon * This command always succeeds, otherwise the bus scan 7843209f581SMatthew Dillon * will not detach dead devices. 7853209f581SMatthew Dillon */ 786258223a3SMatthew Dillon ccb->cpi.version_num = 1; 787258223a3SMatthew Dillon ccb->cpi.hba_inquiry = 0; 788258223a3SMatthew Dillon ccb->cpi.target_sprt = 0; 7891980eff3SMatthew Dillon ccb->cpi.hba_misc = PIM_SEQSCAN; 790258223a3SMatthew Dillon ccb->cpi.hba_eng_cnt = 0; 791258223a3SMatthew Dillon bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags)); 7921980eff3SMatthew Dillon ccb->cpi.max_target = AHCI_MAX_PMPORTS; 793258223a3SMatthew Dillon ccb->cpi.max_lun = 0; 794258223a3SMatthew Dillon ccb->cpi.async_flags = 0; 795258223a3SMatthew Dillon ccb->cpi.hpath_id = 0; 7961980eff3SMatthew Dillon ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1; 797258223a3SMatthew Dillon ccb->cpi.unit_number = cam_sim_unit(sim); 798258223a3SMatthew Dillon ccb->cpi.bus_id = cam_sim_bus(sim); 799258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 800258223a3SMatthew Dillon ccb->cpi.transport = XPORT_AHCI; 801258223a3SMatthew Dillon ccb->cpi.transport_version = 1; 802258223a3SMatthew Dillon ccb->cpi.protocol = PROTO_SCSI; 803258223a3SMatthew Dillon ccb->cpi.protocol_version = SCSI_REV_2; 804258223a3SMatthew Dillon 8053209f581SMatthew Dillon ccbh->status = CAM_REQ_CMP; 806831bc9e3SMatthew Dillon if (ccbh->target_id == CAM_TARGET_WILDCARD) { 807831bc9e3SMatthew Dillon ahci_os_lock_port(ap); 808831bc9e3SMatthew Dillon ahci_port_state_machine(ap, 0); 809831bc9e3SMatthew Dillon ahci_os_unlock_port(ap); 810831bc9e3SMatthew Dillon } else { 811258223a3SMatthew Dillon switch(ahci_pread(ap, AHCI_PREG_SSTS) & 812258223a3SMatthew Dillon AHCI_PREG_SSTS_SPD) { 813258223a3SMatthew Dillon case AHCI_PREG_SSTS_SPD_GEN1: 814258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 150000; 815258223a3SMatthew Dillon break; 816258223a3SMatthew Dillon case AHCI_PREG_SSTS_SPD_GEN2: 817258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 300000; 818258223a3SMatthew Dillon break; 819258223a3SMatthew Dillon default: 820258223a3SMatthew Dillon /* unknown */ 821258223a3SMatthew Dillon ccb->cpi.base_transfer_speed = 1000; 822258223a3SMatthew Dillon break; 823258223a3SMatthew Dillon } 8243209f581SMatthew Dillon #if 0 8251980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) 8261980eff3SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8273209f581SMatthew Dillon #endif 8281980eff3SMatthew Dillon } 829258223a3SMatthew Dillon xpt_done(ccb); 830258223a3SMatthew Dillon break; 831258223a3SMatthew Dillon case XPT_RESET_DEV: 832f4553de1SMatthew Dillon ahci_os_lock_port(ap); 8331980eff3SMatthew Dillon if (ap->ap_type == ATA_PORT_T_NONE) { 8343209f581SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 8351980eff3SMatthew Dillon } else { 8361980eff3SMatthew Dillon ahci_port_reset(ap, atx, 0); 837fd8bd957SMatthew Dillon ccbh->status = CAM_REQ_CMP; 8381980eff3SMatthew Dillon } 839f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 840258223a3SMatthew Dillon xpt_done(ccb); 841258223a3SMatthew Dillon break; 842258223a3SMatthew Dillon case XPT_RESET_BUS: 843f4553de1SMatthew Dillon ahci_os_lock_port(ap); 8441980eff3SMatthew Dillon ahci_port_reset(ap, NULL, 1); 845f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 846fd8bd957SMatthew Dillon ccbh->status = CAM_REQ_CMP; 847258223a3SMatthew Dillon xpt_done(ccb); 848258223a3SMatthew Dillon break; 849258223a3SMatthew Dillon case XPT_SET_TRAN_SETTINGS: 850258223a3SMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 851258223a3SMatthew Dillon xpt_done(ccb); 852258223a3SMatthew Dillon break; 853258223a3SMatthew Dillon case XPT_GET_TRAN_SETTINGS: 854258223a3SMatthew Dillon ccb->cts.protocol = PROTO_SCSI; 855258223a3SMatthew Dillon ccb->cts.protocol_version = SCSI_REV_2; 856258223a3SMatthew Dillon ccb->cts.transport = XPORT_AHCI; 857258223a3SMatthew Dillon ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED; 858258223a3SMatthew Dillon ccb->cts.proto_specific.valid = 0; 859258223a3SMatthew Dillon ccb->cts.xport_specific.valid = 0; 860258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 861258223a3SMatthew Dillon xpt_done(ccb); 862258223a3SMatthew Dillon break; 863258223a3SMatthew Dillon case XPT_CALC_GEOMETRY: 864258223a3SMatthew Dillon cam_calc_geometry(&ccb->ccg, 1); 865258223a3SMatthew Dillon xpt_done(ccb); 866258223a3SMatthew Dillon break; 867258223a3SMatthew Dillon case XPT_SCSI_IO: 868f4553de1SMatthew Dillon /* 869f4553de1SMatthew Dillon * Our parallel startup code might have only probed through 870f4553de1SMatthew Dillon * to the IDENT, so do the last step if necessary. 871f4553de1SMatthew Dillon */ 872f4553de1SMatthew Dillon if (at->at_probe == ATA_PROBE_NEED_IDENT) 873f4553de1SMatthew Dillon ahci_cam_probe(ap, atx); 874f4553de1SMatthew Dillon if (at->at_probe != ATA_PROBE_GOOD) { 875f4553de1SMatthew Dillon ccbh->status = CAM_DEV_NOT_THERE; 876f4553de1SMatthew Dillon xpt_done(ccb); 877f4553de1SMatthew Dillon break; 878f4553de1SMatthew Dillon } 8791980eff3SMatthew Dillon switch(at->at_type) { 880258223a3SMatthew Dillon case ATA_PORT_T_DISK: 8811980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(ap, atx, ccb); 882258223a3SMatthew Dillon break; 883258223a3SMatthew Dillon case ATA_PORT_T_ATAPI: 8841980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(ap, atx, ccb); 885258223a3SMatthew Dillon break; 886258223a3SMatthew Dillon default: 887258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 888258223a3SMatthew Dillon xpt_done(ccb); 889258223a3SMatthew Dillon break; 890258223a3SMatthew Dillon } 891258223a3SMatthew Dillon break; 892258223a3SMatthew Dillon default: 893258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 894258223a3SMatthew Dillon xpt_done(ccb); 895258223a3SMatthew Dillon break; 896258223a3SMatthew Dillon } 897258223a3SMatthew Dillon } 898258223a3SMatthew Dillon 899258223a3SMatthew Dillon /* 900de68d532SMatthew Dillon * Poll function. 901de68d532SMatthew Dillon * 902de68d532SMatthew Dillon * Generally this function gets called heavily when interrupts might be 903de68d532SMatthew Dillon * non-operational, during a halt/reboot or panic. 904258223a3SMatthew Dillon */ 905258223a3SMatthew Dillon static 906258223a3SMatthew Dillon void 907258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim) 908258223a3SMatthew Dillon { 909de68d532SMatthew Dillon struct ahci_port *ap; 910258223a3SMatthew Dillon 911de68d532SMatthew Dillon ap = cam_sim_softc(sim); 912de68d532SMatthew Dillon crit_enter(); 913f4553de1SMatthew Dillon ahci_os_lock_port(ap); 914f4553de1SMatthew Dillon ahci_port_intr(ap, 1); 915f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 916de68d532SMatthew Dillon crit_exit(); 917258223a3SMatthew Dillon } 918258223a3SMatthew Dillon 919258223a3SMatthew Dillon /* 920b4189e5eSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 921b4189e5eSMatthew Dillon * for ATA_PORT_T_DISK operations. Set the completion function 922b4189e5eSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD AHCI 923b4189e5eSMatthew Dillon * layer. 924258223a3SMatthew Dillon * 925b4189e5eSMatthew Dillon * AHCI DISK commands only support a limited command set, and we 926b4189e5eSMatthew Dillon * fake additional commands to make it play nice with the CAM subsystem. 927258223a3SMatthew Dillon */ 928258223a3SMatthew Dillon static 929258223a3SMatthew Dillon void 9301980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx, 9311980eff3SMatthew Dillon union ccb *ccb) 932258223a3SMatthew Dillon { 933258223a3SMatthew Dillon struct ccb_hdr *ccbh; 934258223a3SMatthew Dillon struct ccb_scsiio *csio; 935258223a3SMatthew Dillon struct ata_xfer *xa; 9361980eff3SMatthew Dillon struct ata_port *at; 937258223a3SMatthew Dillon struct ata_fis_h2d *fis; 938258223a3SMatthew Dillon scsi_cdb_t cdb; 939258223a3SMatthew Dillon union scsi_data *rdata; 940258223a3SMatthew Dillon int rdata_len; 941258223a3SMatthew Dillon u_int64_t capacity; 942258223a3SMatthew Dillon u_int64_t lba; 943258223a3SMatthew Dillon u_int32_t count; 944258223a3SMatthew Dillon 945258223a3SMatthew Dillon ccbh = &ccb->csio.ccb_h; 946258223a3SMatthew Dillon csio = &ccb->csio; 9471980eff3SMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 9481980eff3SMatthew Dillon 9491980eff3SMatthew Dillon /* 9501980eff3SMatthew Dillon * XXX not passing NULL at for direct attach! 9511980eff3SMatthew Dillon */ 9521980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 953258223a3SMatthew Dillon rdata = (void *)csio->data_ptr; 954258223a3SMatthew Dillon rdata_len = csio->dxfer_len; 955258223a3SMatthew Dillon 956258223a3SMatthew Dillon /* 957258223a3SMatthew Dillon * Build the FIS or process the csio to completion. 958258223a3SMatthew Dillon */ 959258223a3SMatthew Dillon cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 960258223a3SMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 961258223a3SMatthew Dillon 962258223a3SMatthew Dillon switch(cdb->generic.opcode) { 963258223a3SMatthew Dillon case REQUEST_SENSE: 964258223a3SMatthew Dillon /* 965258223a3SMatthew Dillon * Auto-sense everything, so explicit sense requests 966258223a3SMatthew Dillon * return no-sense. 967258223a3SMatthew Dillon */ 968258223a3SMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 969258223a3SMatthew Dillon break; 970258223a3SMatthew Dillon case INQUIRY: 971258223a3SMatthew Dillon /* 972258223a3SMatthew Dillon * Inquiry supported features 973258223a3SMatthew Dillon * 974258223a3SMatthew Dillon * [opcode, byte2, page_code, length, control] 975258223a3SMatthew Dillon */ 976258223a3SMatthew Dillon if (cdb->inquiry.byte2 & SI_EVPD) { 977258223a3SMatthew Dillon switch(cdb->inquiry.page_code) { 978258223a3SMatthew Dillon case SVPD_SUPPORTED_PAGE_LIST: 979258223a3SMatthew Dillon /* XXX atascsi_disk_vpd_supported */ 980258223a3SMatthew Dillon case SVPD_UNIT_SERIAL_NUMBER: 981258223a3SMatthew Dillon /* XXX atascsi_disk_vpd_serial */ 982258223a3SMatthew Dillon case SVPD_UNIT_DEVID: 983258223a3SMatthew Dillon /* XXX atascsi_disk_vpd_ident */ 984258223a3SMatthew Dillon default: 985258223a3SMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 986258223a3SMatthew Dillon break; 987258223a3SMatthew Dillon } 988258223a3SMatthew Dillon } else { 989258223a3SMatthew Dillon bzero(rdata, rdata_len); 990258223a3SMatthew Dillon if (rdata_len < SHORT_INQUIRY_LENGTH) { 991258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 992258223a3SMatthew Dillon break; 993258223a3SMatthew Dillon } 994258223a3SMatthew Dillon if (rdata_len > sizeof(rdata->inquiry_data)) 995258223a3SMatthew Dillon rdata_len = sizeof(rdata->inquiry_data); 996258223a3SMatthew Dillon rdata->inquiry_data.device = T_DIRECT; 997258223a3SMatthew Dillon rdata->inquiry_data.version = SCSI_REV_SPC2; 998258223a3SMatthew Dillon rdata->inquiry_data.response_format = 2; 999258223a3SMatthew Dillon rdata->inquiry_data.additional_length = 32; 1000258223a3SMatthew Dillon bcopy("SATA ", rdata->inquiry_data.vendor, 8); 10011980eff3SMatthew Dillon bcopy(at->at_identify.model, 1002258223a3SMatthew Dillon rdata->inquiry_data.product, 1003258223a3SMatthew Dillon sizeof(rdata->inquiry_data.product)); 10041980eff3SMatthew Dillon bcopy(at->at_identify.firmware, 1005258223a3SMatthew Dillon rdata->inquiry_data.revision, 1006258223a3SMatthew Dillon sizeof(rdata->inquiry_data.revision)); 1007258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1008258223a3SMatthew Dillon } 1009258223a3SMatthew Dillon break; 1010258223a3SMatthew Dillon case READ_CAPACITY_16: 1011258223a3SMatthew Dillon if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) { 1012258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1013258223a3SMatthew Dillon break; 1014258223a3SMatthew Dillon } 1015258223a3SMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data_16)) { 1016258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1017258223a3SMatthew Dillon break; 1018258223a3SMatthew Dillon } 1019258223a3SMatthew Dillon /* fall through */ 1020258223a3SMatthew Dillon case READ_CAPACITY: 1021258223a3SMatthew Dillon if (rdata_len < sizeof(rdata->read_capacity_data)) { 1022258223a3SMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1023258223a3SMatthew Dillon break; 1024258223a3SMatthew Dillon } 1025258223a3SMatthew Dillon 10261980eff3SMatthew Dillon capacity = at->at_capacity; 1027258223a3SMatthew Dillon 1028258223a3SMatthew Dillon bzero(rdata, rdata_len); 1029258223a3SMatthew Dillon if (cdb->generic.opcode == READ_CAPACITY) { 1030258223a3SMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data); 1031258223a3SMatthew Dillon if (capacity > 0xFFFFFFFFU) 1032258223a3SMatthew Dillon capacity = 0xFFFFFFFFU; 1033258223a3SMatthew Dillon bzero(&rdata->read_capacity_data, rdata_len); 1034258223a3SMatthew Dillon scsi_ulto4b((u_int32_t)capacity - 1, 1035258223a3SMatthew Dillon rdata->read_capacity_data.addr); 1036258223a3SMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data.length); 1037258223a3SMatthew Dillon } else { 1038258223a3SMatthew Dillon rdata_len = sizeof(rdata->read_capacity_data_16); 1039258223a3SMatthew Dillon bzero(&rdata->read_capacity_data_16, rdata_len); 1040258223a3SMatthew Dillon scsi_u64to8b(capacity - 1, 1041258223a3SMatthew Dillon rdata->read_capacity_data_16.addr); 1042258223a3SMatthew Dillon scsi_ulto4b(512, rdata->read_capacity_data_16.length); 1043258223a3SMatthew Dillon } 1044258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1045258223a3SMatthew Dillon break; 1046258223a3SMatthew Dillon case SYNCHRONIZE_CACHE: 1047258223a3SMatthew Dillon /* 1048258223a3SMatthew Dillon * Synchronize cache. Specification says this can take 1049258223a3SMatthew Dillon * greater then 30 seconds so give it at least 45. 1050258223a3SMatthew Dillon */ 1051258223a3SMatthew Dillon fis = xa->fis; 1052258223a3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 1053258223a3SMatthew Dillon fis->command = ATA_C_FLUSH_CACHE; 1054258223a3SMatthew Dillon fis->device = 0; 10553209f581SMatthew Dillon if (xa->timeout < 45000) 10563209f581SMatthew Dillon xa->timeout = 45000; 10571980eff3SMatthew Dillon xa->datalen = 0; 10581980eff3SMatthew Dillon xa->flags = ATA_F_READ; 10591980eff3SMatthew Dillon xa->complete = ahci_ata_complete_disk_synchronize_cache; 1060258223a3SMatthew Dillon break; 1061258223a3SMatthew Dillon case TEST_UNIT_READY: 1062258223a3SMatthew Dillon case START_STOP_UNIT: 1063258223a3SMatthew Dillon case PREVENT_ALLOW: 1064258223a3SMatthew Dillon /* 1065258223a3SMatthew Dillon * Just silently return success 1066258223a3SMatthew Dillon */ 1067258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1068258223a3SMatthew Dillon rdata_len = 0; 1069258223a3SMatthew Dillon break; 1070258223a3SMatthew Dillon case ATA_PASS_12: 1071258223a3SMatthew Dillon case ATA_PASS_16: 1072258223a3SMatthew Dillon /* 1073258223a3SMatthew Dillon * XXX implement pass-through 1074258223a3SMatthew Dillon */ 1075258223a3SMatthew Dillon ccbh->status = CAM_FUNC_NOTAVAIL; 1076258223a3SMatthew Dillon break; 1077258223a3SMatthew Dillon default: 1078258223a3SMatthew Dillon switch(cdb->generic.opcode) { 1079258223a3SMatthew Dillon case READ_6: 1080258223a3SMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 1081258223a3SMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 1082258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1083258223a3SMatthew Dillon break; 1084258223a3SMatthew Dillon case READ_10: 1085258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 1086258223a3SMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 1087258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1088258223a3SMatthew Dillon break; 1089258223a3SMatthew Dillon case READ_12: 1090258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 1091258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 1092258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1093258223a3SMatthew Dillon break; 1094258223a3SMatthew Dillon case READ_16: 1095258223a3SMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 1096258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 1097258223a3SMatthew Dillon xa->flags = ATA_F_READ; 1098258223a3SMatthew Dillon break; 1099258223a3SMatthew Dillon case WRITE_6: 1100258223a3SMatthew Dillon lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF; 1101258223a3SMatthew Dillon count = cdb->rw_6.length ? cdb->rw_6.length : 0x100; 1102258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1103258223a3SMatthew Dillon break; 1104258223a3SMatthew Dillon case WRITE_10: 1105258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_10.addr); 1106258223a3SMatthew Dillon count = scsi_2btoul(cdb->rw_10.length); 1107258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1108258223a3SMatthew Dillon break; 1109258223a3SMatthew Dillon case WRITE_12: 1110258223a3SMatthew Dillon lba = scsi_4btoul(cdb->rw_12.addr); 1111258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_12.length); 1112258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1113258223a3SMatthew Dillon break; 1114258223a3SMatthew Dillon case WRITE_16: 1115258223a3SMatthew Dillon lba = scsi_8btou64(cdb->rw_16.addr); 1116258223a3SMatthew Dillon count = scsi_4btoul(cdb->rw_16.length); 1117258223a3SMatthew Dillon xa->flags = ATA_F_WRITE; 1118258223a3SMatthew Dillon break; 1119258223a3SMatthew Dillon default: 1120258223a3SMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1121258223a3SMatthew Dillon break; 1122258223a3SMatthew Dillon } 1123258223a3SMatthew Dillon if (ccbh->status != CAM_REQ_INPROG) 1124258223a3SMatthew Dillon break; 1125258223a3SMatthew Dillon 1126258223a3SMatthew Dillon fis = xa->fis; 1127258223a3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD; 1128258223a3SMatthew Dillon fis->lba_low = (u_int8_t)lba; 1129258223a3SMatthew Dillon fis->lba_mid = (u_int8_t)(lba >> 8); 1130258223a3SMatthew Dillon fis->lba_high = (u_int8_t)(lba >> 16); 1131258223a3SMatthew Dillon fis->device = ATA_H2D_DEVICE_LBA; 1132258223a3SMatthew Dillon 11331980eff3SMatthew Dillon /* 11341980eff3SMatthew Dillon * NCQ only for direct-attached disks, do not currently 11351980eff3SMatthew Dillon * try to use NCQ with port multipliers. 11361980eff3SMatthew Dillon */ 11371980eff3SMatthew Dillon if (at->at_ncqdepth > 1 && 11381980eff3SMatthew Dillon ap->ap_type == ATA_PORT_T_DISK && 1139258223a3SMatthew Dillon (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) && 1140258223a3SMatthew Dillon (ccbh->flags & CAM_POLLED) == 0) { 1141258223a3SMatthew Dillon /* 1142258223a3SMatthew Dillon * Use NCQ - always uses 48 bit addressing 1143258223a3SMatthew Dillon */ 1144258223a3SMatthew Dillon xa->flags |= ATA_F_NCQ; 1145258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1146258223a3SMatthew Dillon ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA; 1147258223a3SMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 1148258223a3SMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 1149258223a3SMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 1150258223a3SMatthew Dillon fis->sector_count = xa->tag << 3; 1151258223a3SMatthew Dillon fis->features = (u_int8_t)count; 1152258223a3SMatthew Dillon fis->features_exp = (u_int8_t)(count >> 8); 1153258223a3SMatthew Dillon } else if (count > 0x100 || lba > 0xFFFFFFFFU) { 1154258223a3SMatthew Dillon /* 1155258223a3SMatthew Dillon * Use LBA48 1156258223a3SMatthew Dillon */ 1157258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1158258223a3SMatthew Dillon ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT; 1159258223a3SMatthew Dillon fis->lba_low_exp = (u_int8_t)(lba >> 24); 1160258223a3SMatthew Dillon fis->lba_mid_exp = (u_int8_t)(lba >> 32); 1161258223a3SMatthew Dillon fis->lba_high_exp = (u_int8_t)(lba >> 40); 1162258223a3SMatthew Dillon fis->sector_count = (u_int8_t)count; 1163258223a3SMatthew Dillon fis->sector_count_exp = (u_int8_t)(count >> 8); 1164258223a3SMatthew Dillon } else { 1165258223a3SMatthew Dillon /* 1166258223a3SMatthew Dillon * Use LBA 1167258223a3SMatthew Dillon * 1168258223a3SMatthew Dillon * NOTE: 256 sectors is supported, stored as 0. 1169258223a3SMatthew Dillon */ 1170258223a3SMatthew Dillon fis->command = (xa->flags & ATA_F_WRITE) ? 1171258223a3SMatthew Dillon ATA_C_WRITEDMA : ATA_C_READDMA; 1172258223a3SMatthew Dillon fis->device |= (u_int8_t)(lba >> 24) & 0x0F; 1173258223a3SMatthew Dillon fis->sector_count = (u_int8_t)count; 1174258223a3SMatthew Dillon } 1175258223a3SMatthew Dillon 1176258223a3SMatthew Dillon xa->data = csio->data_ptr; 1177258223a3SMatthew Dillon xa->datalen = csio->dxfer_len; 1178258223a3SMatthew Dillon xa->complete = ahci_ata_complete_disk_rw; 11793209f581SMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 1180258223a3SMatthew Dillon if (ccbh->flags & CAM_POLLED) 1181258223a3SMatthew Dillon xa->flags |= ATA_F_POLL; 1182258223a3SMatthew Dillon break; 1183258223a3SMatthew Dillon } 1184258223a3SMatthew Dillon 1185258223a3SMatthew Dillon /* 1186258223a3SMatthew Dillon * If the request is still in progress the xa and FIS have 1187258223a3SMatthew Dillon * been set up and must be dispatched. Otherwise the request 1188258223a3SMatthew Dillon * is complete. 1189258223a3SMatthew Dillon */ 1190258223a3SMatthew Dillon if (ccbh->status == CAM_REQ_INPROG) { 1191258223a3SMatthew Dillon KKASSERT(xa->complete != NULL); 1192258223a3SMatthew Dillon xa->atascsi_private = ccb; 1193258223a3SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 1194f4553de1SMatthew Dillon ahci_os_lock_port(ap); 11951980eff3SMatthew Dillon fis->flags |= at->at_target; 1196258223a3SMatthew Dillon ahci_ata_cmd(xa); 1197f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1198258223a3SMatthew Dillon } else { 1199258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1200258223a3SMatthew Dillon xpt_done(ccb); 1201258223a3SMatthew Dillon } 1202258223a3SMatthew Dillon } 1203258223a3SMatthew Dillon 1204b4189e5eSMatthew Dillon /* 1205b4189e5eSMatthew Dillon * Convert the SCSI command in ccb to an ata_xfer command in xa 1206b4189e5eSMatthew Dillon * for ATA_PORT_T_ATAPI operations. Set the completion function 1207b4189e5eSMatthew Dillon * to convert the response back, then dispatch to the OpenBSD AHCI 1208b4189e5eSMatthew Dillon * layer. 1209b4189e5eSMatthew Dillon */ 1210258223a3SMatthew Dillon static 1211258223a3SMatthew Dillon void 12121980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx, 12131980eff3SMatthew Dillon union ccb *ccb) 1214258223a3SMatthew Dillon { 1215258223a3SMatthew Dillon struct ccb_hdr *ccbh; 1216258223a3SMatthew Dillon struct ccb_scsiio *csio; 1217258223a3SMatthew Dillon struct ata_xfer *xa; 1218258223a3SMatthew Dillon struct ata_fis_h2d *fis; 1219b4189e5eSMatthew Dillon scsi_cdb_t cdbs; 1220b4189e5eSMatthew Dillon scsi_cdb_t cdbd; 1221b4189e5eSMatthew Dillon int flags; 12221980eff3SMatthew Dillon struct ata_port *at; 1223258223a3SMatthew Dillon 1224258223a3SMatthew Dillon ccbh = &ccb->csio.ccb_h; 1225258223a3SMatthew Dillon csio = &ccb->csio; 12261980eff3SMatthew Dillon at = atx ? atx : &ap->ap_ata[0]; 1227b4189e5eSMatthew Dillon 1228b4189e5eSMatthew Dillon switch (ccbh->flags & CAM_DIR_MASK) { 1229b4189e5eSMatthew Dillon case CAM_DIR_IN: 1230b4189e5eSMatthew Dillon flags = ATA_F_PACKET | ATA_F_READ; 1231b4189e5eSMatthew Dillon break; 1232b4189e5eSMatthew Dillon case CAM_DIR_OUT: 1233b4189e5eSMatthew Dillon flags = ATA_F_PACKET | ATA_F_WRITE; 1234b4189e5eSMatthew Dillon break; 1235b4189e5eSMatthew Dillon case CAM_DIR_NONE: 1236b4189e5eSMatthew Dillon flags = ATA_F_PACKET; 1237b4189e5eSMatthew Dillon break; 1238b4189e5eSMatthew Dillon default: 1239b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_INVALID; 1240b4189e5eSMatthew Dillon xpt_done(ccb); 1241b4189e5eSMatthew Dillon return; 1242b4189e5eSMatthew Dillon /* NOT REACHED */ 1243b4189e5eSMatthew Dillon } 1244b4189e5eSMatthew Dillon 1245b4189e5eSMatthew Dillon /* 1246b4189e5eSMatthew Dillon * The command has to fit in the packet command buffer. 1247b4189e5eSMatthew Dillon */ 1248b4189e5eSMatthew Dillon if (csio->cdb_len < 6 || csio->cdb_len > 16) { 1249b4189e5eSMatthew Dillon ccbh->status = CAM_CCB_LEN_ERR; 1250b4189e5eSMatthew Dillon xpt_done(ccb); 1251b4189e5eSMatthew Dillon return; 1252b4189e5eSMatthew Dillon } 1253b4189e5eSMatthew Dillon 1254b4189e5eSMatthew Dillon /* 1255b4189e5eSMatthew Dillon * Initialize the XA and FIS. 12561980eff3SMatthew Dillon * 12571980eff3SMatthew Dillon * XXX not passing NULL at for direct attach! 1258b4189e5eSMatthew Dillon */ 12591980eff3SMatthew Dillon xa = ahci_ata_get_xfer(ap, atx); 1260258223a3SMatthew Dillon fis = xa->fis; 1261258223a3SMatthew Dillon 12621980eff3SMatthew Dillon fis->flags = ATA_H2D_FLAGS_CMD | at->at_target; 12631980eff3SMatthew Dillon fis->command = ATA_C_PACKET; 12641980eff3SMatthew Dillon fis->device = 0; 12651980eff3SMatthew Dillon fis->sector_count = xa->tag << 3; 12661980eff3SMatthew Dillon fis->features = ATA_H2D_FEATURES_DMA | 12671980eff3SMatthew Dillon ((flags & ATA_F_WRITE) ? 12681980eff3SMatthew Dillon ATA_H2D_FEATURES_DIR_WRITE : ATA_H2D_FEATURES_DIR_READ); 12691980eff3SMatthew Dillon fis->lba_mid = 0x00; 12701980eff3SMatthew Dillon fis->lba_high = 0x20; 12711980eff3SMatthew Dillon 1272b4189e5eSMatthew Dillon xa->flags = flags; 1273b4189e5eSMatthew Dillon xa->data = csio->data_ptr; 1274b4189e5eSMatthew Dillon xa->datalen = csio->dxfer_len; 12753209f581SMatthew Dillon xa->timeout = ccbh->timeout; /* milliseconds */ 12761980eff3SMatthew Dillon 1277b4189e5eSMatthew Dillon if (ccbh->flags & CAM_POLLED) 1278b4189e5eSMatthew Dillon xa->flags |= ATA_F_POLL; 1279258223a3SMatthew Dillon 1280258223a3SMatthew Dillon /* 1281b4189e5eSMatthew Dillon * Copy the cdb to the packetcmd buffer in the FIS using a 1282b4189e5eSMatthew Dillon * convenient pointer in the xa. 1283258223a3SMatthew Dillon */ 1284b4189e5eSMatthew Dillon cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ? 1285b4189e5eSMatthew Dillon csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); 1286b4189e5eSMatthew Dillon bcopy(cdbs, xa->packetcmd, csio->cdb_len); 1287b4189e5eSMatthew Dillon 1288669fbbf7SMatthew Dillon #if 0 1289b4189e5eSMatthew Dillon kprintf("opcode %d cdb_len %d dxfer_len %d\n", 1290b4189e5eSMatthew Dillon cdbs->generic.opcode, 1291b4189e5eSMatthew Dillon csio->cdb_len, csio->dxfer_len); 1292669fbbf7SMatthew Dillon #endif 1293b4189e5eSMatthew Dillon 1294b4189e5eSMatthew Dillon /* 1295b4189e5eSMatthew Dillon * Some ATAPI commands do not actually follow the SCSI standard. 1296b4189e5eSMatthew Dillon */ 1297b4189e5eSMatthew Dillon cdbd = (void *)xa->packetcmd; 1298b4189e5eSMatthew Dillon 1299b4189e5eSMatthew Dillon switch(cdbd->generic.opcode) { 1300258223a3SMatthew Dillon case INQUIRY: 1301b4189e5eSMatthew Dillon /* 1302b4189e5eSMatthew Dillon * Some ATAPI devices can't handle SI_EVPD being set 1303b4189e5eSMatthew Dillon * for a basic inquiry (page_code == 0). 1304b4189e5eSMatthew Dillon * 1305b4189e5eSMatthew Dillon * Some ATAPI devices can't handle long inquiry lengths, 1306b4189e5eSMatthew Dillon * don't ask me why. Truncate the inquiry length. 1307b4189e5eSMatthew Dillon */ 1308b4189e5eSMatthew Dillon if ((cdbd->inquiry.byte2 & SI_EVPD) && 1309b4189e5eSMatthew Dillon cdbd->inquiry.page_code == 0) { 1310b4189e5eSMatthew Dillon cdbd->inquiry.byte2 &= ~SI_EVPD; 1311b4189e5eSMatthew Dillon } 1312b4189e5eSMatthew Dillon if (cdbd->inquiry.page_code == 0 && 1313b4189e5eSMatthew Dillon cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) { 1314b4189e5eSMatthew Dillon cdbd->inquiry.length = SHORT_INQUIRY_LENGTH; 1315b4189e5eSMatthew Dillon } 1316b4189e5eSMatthew Dillon break; 1317258223a3SMatthew Dillon case READ_6: 1318258223a3SMatthew Dillon case WRITE_6: 1319b4189e5eSMatthew Dillon /* 1320b4189e5eSMatthew Dillon * Convert *_6 to *_10 commands. Most ATAPI devices 1321b4189e5eSMatthew Dillon * cannot handle the SCSI READ_6 and WRITE_6 commands. 1322b4189e5eSMatthew Dillon */ 1323b4189e5eSMatthew Dillon cdbd->rw_10.opcode |= 0x20; 1324b4189e5eSMatthew Dillon cdbd->rw_10.byte2 = 0; 1325b4189e5eSMatthew Dillon cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F; 1326b4189e5eSMatthew Dillon cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1]; 1327b4189e5eSMatthew Dillon cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2]; 1328b4189e5eSMatthew Dillon cdbd->rw_10.addr[3] = 0; 1329b4189e5eSMatthew Dillon cdbd->rw_10.reserved = 0; 1330b4189e5eSMatthew Dillon cdbd->rw_10.length[0] = 0; 1331b4189e5eSMatthew Dillon cdbd->rw_10.length[1] = cdbs->rw_6.length; 1332b4189e5eSMatthew Dillon cdbd->rw_10.control = cdbs->rw_6.control; 1333b4189e5eSMatthew Dillon break; 1334258223a3SMatthew Dillon default: 1335258223a3SMatthew Dillon break; 1336258223a3SMatthew Dillon } 1337258223a3SMatthew Dillon 1338b4189e5eSMatthew Dillon /* 1339b4189e5eSMatthew Dillon * And dispatch 1340b4189e5eSMatthew Dillon */ 1341b4189e5eSMatthew Dillon xa->complete = ahci_atapi_complete_cmd; 1342258223a3SMatthew Dillon xa->atascsi_private = ccb; 1343258223a3SMatthew Dillon ccb->ccb_h.sim_priv.entries[0].ptr = ap; 1344258223a3SMatthew Dillon ahci_ata_cmd(xa); 1345258223a3SMatthew Dillon } 1346258223a3SMatthew Dillon 1347b4189e5eSMatthew Dillon /* 1348b4189e5eSMatthew Dillon * Completion function for ATA_PORT_T_DISK cache synchronization. 1349b4189e5eSMatthew Dillon */ 1350258223a3SMatthew Dillon static 1351258223a3SMatthew Dillon void 1352258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa) 1353258223a3SMatthew Dillon { 1354258223a3SMatthew Dillon union ccb *ccb = xa->atascsi_private; 1355258223a3SMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1356258223a3SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1357258223a3SMatthew Dillon 1358258223a3SMatthew Dillon switch(xa->state) { 1359258223a3SMatthew Dillon case ATA_S_COMPLETE: 1360258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1361b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1362258223a3SMatthew Dillon break; 1363258223a3SMatthew Dillon case ATA_S_ERROR: 13641980eff3SMatthew Dillon kprintf("%s: synchronize_cache: error\n", 13651980eff3SMatthew Dillon ATANAME(ap, xa->at)); 1366b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 1367b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 1368b4189e5eSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1369258223a3SMatthew Dillon break; 1370258223a3SMatthew Dillon case ATA_S_TIMEOUT: 13711980eff3SMatthew Dillon kprintf("%s: synchronize_cache: timeout\n", 13721980eff3SMatthew Dillon ATANAME(ap, xa->at)); 1373258223a3SMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 1374258223a3SMatthew Dillon break; 1375258223a3SMatthew Dillon default: 1376258223a3SMatthew Dillon kprintf("%s: synchronize_cache: unknown state %d\n", 13771980eff3SMatthew Dillon ATANAME(ap, xa->at), xa->state); 1378258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1379258223a3SMatthew Dillon break; 1380258223a3SMatthew Dillon } 1381258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1382f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1383258223a3SMatthew Dillon xpt_done(ccb); 1384f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1385258223a3SMatthew Dillon } 1386258223a3SMatthew Dillon 1387b4189e5eSMatthew Dillon /* 1388b4189e5eSMatthew Dillon * Completion function for ATA_PORT_T_DISK I/O 1389b4189e5eSMatthew Dillon */ 1390258223a3SMatthew Dillon static 1391258223a3SMatthew Dillon void 1392258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa) 1393258223a3SMatthew Dillon { 1394258223a3SMatthew Dillon union ccb *ccb = xa->atascsi_private; 1395258223a3SMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1396258223a3SMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1397258223a3SMatthew Dillon 1398258223a3SMatthew Dillon switch(xa->state) { 1399258223a3SMatthew Dillon case ATA_S_COMPLETE: 1400258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP; 1401b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1402258223a3SMatthew Dillon break; 1403258223a3SMatthew Dillon case ATA_S_ERROR: 14041980eff3SMatthew Dillon kprintf("%s: disk_rw: error\n", ATANAME(ap, xa->at)); 1405b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; 1406b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 1407b4189e5eSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1408258223a3SMatthew Dillon break; 1409258223a3SMatthew Dillon case ATA_S_TIMEOUT: 14101980eff3SMatthew Dillon kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at)); 1411258223a3SMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 14124c339a5fSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 14134c339a5fSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1414258223a3SMatthew Dillon break; 1415258223a3SMatthew Dillon default: 1416258223a3SMatthew Dillon kprintf("%s: disk_rw: unknown state %d\n", 14171980eff3SMatthew Dillon ATANAME(ap, xa->at), xa->state); 1418258223a3SMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1419258223a3SMatthew Dillon break; 1420258223a3SMatthew Dillon } 1421258223a3SMatthew Dillon ccb->csio.resid = xa->resid; 1422258223a3SMatthew Dillon ahci_ata_put_xfer(xa); 1423f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1424258223a3SMatthew Dillon xpt_done(ccb); 1425f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1426258223a3SMatthew Dillon } 1427b4189e5eSMatthew Dillon 14287d4fcf34SMatthew Dillon /* 14297d4fcf34SMatthew Dillon * Completion function for ATA_PORT_T_ATAPI I/O 14307d4fcf34SMatthew Dillon * 14317d4fcf34SMatthew Dillon * Sense data is returned in the rfis. 14327d4fcf34SMatthew Dillon */ 1433b4189e5eSMatthew Dillon static 1434b4189e5eSMatthew Dillon void 1435b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa) 1436b4189e5eSMatthew Dillon { 1437b4189e5eSMatthew Dillon union ccb *ccb = xa->atascsi_private; 1438b4189e5eSMatthew Dillon struct ccb_hdr *ccbh = &ccb->ccb_h; 1439b4189e5eSMatthew Dillon struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr; 1440b4189e5eSMatthew Dillon scsi_cdb_t cdb; 1441b4189e5eSMatthew Dillon 1442b4189e5eSMatthew Dillon cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ? 1443b4189e5eSMatthew Dillon ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes); 1444b4189e5eSMatthew Dillon 1445b4189e5eSMatthew Dillon switch(xa->state) { 1446b4189e5eSMatthew Dillon case ATA_S_COMPLETE: 1447b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_CMP; 1448b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_OK; 1449b4189e5eSMatthew Dillon break; 1450b4189e5eSMatthew Dillon case ATA_S_ERROR: 1451b4189e5eSMatthew Dillon ccbh->status = CAM_SCSI_STATUS_ERROR; 1452b4189e5eSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 14537d4fcf34SMatthew Dillon ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data); 1454b4189e5eSMatthew Dillon break; 1455b4189e5eSMatthew Dillon case ATA_S_TIMEOUT: 1456b4189e5eSMatthew Dillon kprintf("%s: cmd %d: timeout\n", 1457b4189e5eSMatthew Dillon PORTNAME(ap), cdb->generic.opcode); 1458b4189e5eSMatthew Dillon ccbh->status = CAM_CMD_TIMEOUT; 14594c339a5fSMatthew Dillon ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; 14604c339a5fSMatthew Dillon ahci_ata_dummy_sense(&ccb->csio.sense_data); 1461b4189e5eSMatthew Dillon break; 1462b4189e5eSMatthew Dillon default: 1463b4189e5eSMatthew Dillon kprintf("%s: cmd %d: unknown state %d\n", 1464b4189e5eSMatthew Dillon PORTNAME(ap), cdb->generic.opcode, xa->state); 1465b4189e5eSMatthew Dillon ccbh->status = CAM_REQ_CMP_ERR; 1466b4189e5eSMatthew Dillon break; 1467b4189e5eSMatthew Dillon } 1468b4189e5eSMatthew Dillon ccb->csio.resid = xa->resid; 1469b4189e5eSMatthew Dillon ahci_ata_put_xfer(xa); 1470f4553de1SMatthew Dillon ahci_os_unlock_port(ap); 1471b4189e5eSMatthew Dillon xpt_done(ccb); 1472f4553de1SMatthew Dillon ahci_os_lock_port(ap); 1473b4189e5eSMatthew Dillon } 1474b4189e5eSMatthew Dillon 14757d4fcf34SMatthew Dillon /* 14767d4fcf34SMatthew Dillon * Construct dummy sense data for errors on DISKs 14777d4fcf34SMatthew Dillon */ 1478b4189e5eSMatthew Dillon static 1479b4189e5eSMatthew Dillon void 1480b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data) 1481b4189e5eSMatthew Dillon { 1482b4189e5eSMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 1483b4189e5eSMatthew Dillon sense_data->segment = 0; 1484b4189e5eSMatthew Dillon sense_data->flags = SSD_KEY_MEDIUM_ERROR; 1485b4189e5eSMatthew Dillon sense_data->info[0] = 0; 1486b4189e5eSMatthew Dillon sense_data->info[1] = 0; 1487b4189e5eSMatthew Dillon sense_data->info[2] = 0; 1488b4189e5eSMatthew Dillon sense_data->info[3] = 0; 1489b4189e5eSMatthew Dillon sense_data->extra_len = 0; 1490b4189e5eSMatthew Dillon } 14917d4fcf34SMatthew Dillon 14927d4fcf34SMatthew Dillon /* 14937d4fcf34SMatthew Dillon * Construct atapi sense data for errors on ATAPI 14947d4fcf34SMatthew Dillon * 14957d4fcf34SMatthew Dillon * The ATAPI sense data is stored in the passed rfis and must be converted 14967d4fcf34SMatthew Dillon * to SCSI sense data. 14977d4fcf34SMatthew Dillon */ 14987d4fcf34SMatthew Dillon static 14997d4fcf34SMatthew Dillon void 15007d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis, 15017d4fcf34SMatthew Dillon struct scsi_sense_data *sense_data) 15027d4fcf34SMatthew Dillon { 15037d4fcf34SMatthew Dillon sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR; 15047d4fcf34SMatthew Dillon sense_data->segment = 0; 15057d4fcf34SMatthew Dillon sense_data->flags = (rfis->error & 0xF0) >> 4; 15067d4fcf34SMatthew Dillon if (rfis->error & 0x04) 15077d4fcf34SMatthew Dillon sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST; 15087d4fcf34SMatthew Dillon if (rfis->error & 0x02) 15097d4fcf34SMatthew Dillon sense_data->flags |= SSD_EOM; 15107d4fcf34SMatthew Dillon if (rfis->error & 0x01) 15117d4fcf34SMatthew Dillon sense_data->flags |= SSD_ILI; 15127d4fcf34SMatthew Dillon sense_data->info[0] = 0; 15137d4fcf34SMatthew Dillon sense_data->info[1] = 0; 15147d4fcf34SMatthew Dillon sense_data->info[2] = 0; 15157d4fcf34SMatthew Dillon sense_data->info[3] = 0; 15167d4fcf34SMatthew Dillon sense_data->extra_len = 0; 15177d4fcf34SMatthew Dillon } 1518