xref: /dflybsd-src/sys/dev/disk/ahci/ahci_cam.c (revision cec957e929d4fbddf545b1918d45b9eadc8268ce)
1258223a3SMatthew Dillon /*
2fb00c6edSMatthew Dillon  * (MPSAFE)
3fb00c6edSMatthew Dillon  *
4258223a3SMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
5258223a3SMatthew Dillon  *
6258223a3SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
7258223a3SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
8258223a3SMatthew Dillon  *
9258223a3SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
10258223a3SMatthew Dillon  * modification, are permitted provided that the following conditions
11258223a3SMatthew Dillon  * are met:
12258223a3SMatthew Dillon  *
13258223a3SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
14258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
15258223a3SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
16258223a3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
17258223a3SMatthew Dillon  *    the documentation and/or other materials provided with the
18258223a3SMatthew Dillon  *    distribution.
19258223a3SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
20258223a3SMatthew Dillon  *    contributors may be used to endorse or promote products derived
21258223a3SMatthew Dillon  *    from this software without specific, prior written permission.
22258223a3SMatthew Dillon  *
23258223a3SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24258223a3SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25258223a3SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26258223a3SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27258223a3SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28258223a3SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29258223a3SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30258223a3SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31258223a3SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32258223a3SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33258223a3SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34258223a3SMatthew Dillon  * SUCH DAMAGE.
35258223a3SMatthew Dillon  *
36258223a3SMatthew Dillon  *
37258223a3SMatthew Dillon  * Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
38258223a3SMatthew Dillon  *
39258223a3SMatthew Dillon  * Permission to use, copy, modify, and distribute this software for any
40258223a3SMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
41258223a3SMatthew Dillon  * copyright notice and this permission notice appear in all copies.
42258223a3SMatthew Dillon  *
43258223a3SMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
44258223a3SMatthew Dillon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
45258223a3SMatthew Dillon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
46258223a3SMatthew Dillon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
47258223a3SMatthew Dillon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
48258223a3SMatthew Dillon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
49258223a3SMatthew Dillon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
50258223a3SMatthew Dillon  *
51258223a3SMatthew Dillon  * $OpenBSD: atascsi.c,v 1.64 2009/02/16 21:19:06 miod Exp $
52258223a3SMatthew Dillon  */
53258223a3SMatthew Dillon /*
54258223a3SMatthew Dillon  * Implement each SATA port as its own SCSI bus on CAM.  This way we can
55258223a3SMatthew Dillon  * implement future port multiplier features as individual devices on the
56258223a3SMatthew Dillon  * bus.
57258223a3SMatthew Dillon  *
58258223a3SMatthew Dillon  * Much of the cdb<->xa conversion code was taken from OpenBSD, the rest
59258223a3SMatthew Dillon  * was written natively for DragonFly.
60d16d3400SMatthew Dillon  *
61d16d3400SMatthew Dillon  * NOTE-1: I was temporarily unlocking the port while making the CCB
62d16d3400SMatthew Dillon  *	   callback, to reduce the chance of a deadlock and to improve
63d16d3400SMatthew Dillon  *	   performance by allowing new commands to be queued.
64d16d3400SMatthew Dillon  *
65d16d3400SMatthew Dillon  *	   However, this also creates an opening where another AHCI
66d16d3400SMatthew Dillon  *	   interrupt can come in and execute the ahci_port_intr()
67d16d3400SMatthew Dillon  *	   function, creating a huge mess in the sequencing of the
68d16d3400SMatthew Dillon  *	   chipset.
69d16d3400SMatthew Dillon  *
70d16d3400SMatthew Dillon  *	   So for now we don't do this. XXX
71258223a3SMatthew Dillon  */
72258223a3SMatthew Dillon 
73258223a3SMatthew Dillon #include "ahci.h"
74258223a3SMatthew Dillon 
75258223a3SMatthew Dillon static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb);
76258223a3SMatthew Dillon static void ahci_xpt_poll(struct cam_sim *sim);
771980eff3SMatthew Dillon static void ahci_xpt_scsi_disk_io(struct ahci_port *ap,
781980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
791980eff3SMatthew Dillon static void ahci_xpt_scsi_atapi_io(struct ahci_port *ap,
801980eff3SMatthew Dillon 			struct ata_port *at, union ccb *ccb);
816e0003adSMatthew Dillon static void ahci_xpt_page_inquiry(struct ahci_port *ap,
826e0003adSMatthew Dillon 			struct ata_port *at, union ccb *ccb);
83258223a3SMatthew Dillon 
84258223a3SMatthew Dillon static void ahci_ata_complete_disk_rw(struct ata_xfer *xa);
85258223a3SMatthew Dillon static void ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa);
86b4189e5eSMatthew Dillon static void ahci_atapi_complete_cmd(struct ata_xfer *xa);
87b4189e5eSMatthew Dillon static void ahci_ata_dummy_sense(struct scsi_sense_data *sense_data);
887d4fcf34SMatthew Dillon static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
897d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data);
90258223a3SMatthew Dillon 
911980eff3SMatthew Dillon static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
921980eff3SMatthew Dillon static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
9369b3741eSMatthew Dillon static int ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx);
94b4189e5eSMatthew Dillon static void ahci_ata_dummy_done(struct ata_xfer *xa);
95258223a3SMatthew Dillon static void ata_fix_identify(struct ata_identify *id);
96258223a3SMatthew Dillon static void ahci_cam_rescan(struct ahci_port *ap);
976e0003adSMatthew Dillon static void ahci_strip_string(const char **basep, int *lenp);
98258223a3SMatthew Dillon 
99258223a3SMatthew Dillon int
ahci_cam_attach(struct ahci_port * ap)100258223a3SMatthew Dillon ahci_cam_attach(struct ahci_port *ap)
101258223a3SMatthew Dillon {
102258223a3SMatthew Dillon 	struct cam_devq *devq;
103258223a3SMatthew Dillon 	struct cam_sim *sim;
104258223a3SMatthew Dillon 	int error;
105258223a3SMatthew Dillon 	int unit;
106258223a3SMatthew Dillon 
107cec85a37SMatthew Dillon 	/*
108cec85a37SMatthew Dillon 	 * We want at least one ccb to be available for error processing
109cec85a37SMatthew Dillon 	 * so don't let CAM use more then ncmds - 1.
110cec85a37SMatthew Dillon 	 */
111258223a3SMatthew Dillon 	unit = device_get_unit(ap->ap_sc->sc_dev);
112cec85a37SMatthew Dillon 	if (ap->ap_sc->sc_ncmds > 1)
113cec85a37SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
114cec85a37SMatthew Dillon 	else
115258223a3SMatthew Dillon 		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
116258223a3SMatthew Dillon 	if (devq == NULL) {
117258223a3SMatthew Dillon 		return (ENOMEM);
118258223a3SMatthew Dillon 	}
119949597c2SMatthew Dillon 
120949597c2SMatthew Dillon 	/*
121949597c2SMatthew Dillon 	 * Give the devq enough room to run with 32 max_dev_transactions,
122949597c2SMatthew Dillon 	 * but set the overall max tags to 1 until NCQ is negotiated.
123949597c2SMatthew Dillon 	 */
124258223a3SMatthew Dillon 	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
125949597c2SMatthew Dillon 			   (void *)ap, unit, &ap->ap_sim_lock,
126949597c2SMatthew Dillon 			   32, 1, devq);
127258223a3SMatthew Dillon 	cam_simq_release(devq);
128258223a3SMatthew Dillon 	if (sim == NULL) {
129258223a3SMatthew Dillon 		return (ENOMEM);
130258223a3SMatthew Dillon 	}
131258223a3SMatthew Dillon 	ap->ap_sim = sim;
132831bc9e3SMatthew Dillon 	ahci_os_unlock_port(ap);
133fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
134258223a3SMatthew Dillon 	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
135fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_RELEASE);
136831bc9e3SMatthew Dillon 	ahci_os_lock_port(ap);
137258223a3SMatthew Dillon 	if (error != CAM_SUCCESS) {
138258223a3SMatthew Dillon 		ahci_cam_detach(ap);
139258223a3SMatthew Dillon 		return (EINVAL);
140258223a3SMatthew Dillon 	}
141258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_BUS_REGISTERED;
142258223a3SMatthew Dillon 
143c408a8b3SMatthew Dillon 	if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
1441980eff3SMatthew Dillon 		error = ahci_cam_probe(ap, NULL);
145c408a8b3SMatthew Dillon 	else
146c408a8b3SMatthew Dillon 		error = 0;
147258223a3SMatthew Dillon 	if (error) {
148258223a3SMatthew Dillon 		ahci_cam_detach(ap);
149258223a3SMatthew Dillon 		return (EIO);
150258223a3SMatthew Dillon 	}
151258223a3SMatthew Dillon 	ap->ap_flags |= AP_F_CAM_ATTACHED;
152258223a3SMatthew Dillon 
153258223a3SMatthew Dillon 	return(0);
154258223a3SMatthew Dillon }
155258223a3SMatthew Dillon 
1561980eff3SMatthew Dillon /*
1573209f581SMatthew Dillon  * The state of the port has changed.
1583209f581SMatthew Dillon  *
15944a472baSSascha Wildner  * If atx is NULL the physical port has changed state.
16044a472baSSascha Wildner  * If atx is non-NULL a particular target behind a PM has changed state.
1613209f581SMatthew Dillon  *
1623209f581SMatthew Dillon  * If found is -1 the target state must be queued to a non-interrupt context.
1633209f581SMatthew Dillon  * (only works with at == NULL).
1643209f581SMatthew Dillon  *
1653209f581SMatthew Dillon  * If found is 0 the target was removed.
1663209f581SMatthew Dillon  * If found is 1 the target was inserted.
1671980eff3SMatthew Dillon  */
168258223a3SMatthew Dillon void
ahci_cam_changed(struct ahci_port * ap,struct ata_port * atx,int found)1693209f581SMatthew Dillon ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found)
170258223a3SMatthew Dillon {
171fd8bd957SMatthew Dillon 	struct cam_path *tmppath;
1723209f581SMatthew Dillon 	int status;
1733209f581SMatthew Dillon 	int target;
1743209f581SMatthew Dillon 
1753209f581SMatthew Dillon 	target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
176fd8bd957SMatthew Dillon 
177258223a3SMatthew Dillon 	if (ap->ap_sim == NULL)
178258223a3SMatthew Dillon 		return;
1793209f581SMatthew Dillon 	if (found == CAM_TARGET_WILDCARD) {
1803209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1813209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1823209f581SMatthew Dillon 					 target, CAM_LUN_WILDCARD);
1833209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
184fd8bd957SMatthew Dillon 			return;
185258223a3SMatthew Dillon 		ahci_cam_rescan(ap);
186fd8bd957SMatthew Dillon 	} else {
1873209f581SMatthew Dillon 		status = xpt_create_path(&tmppath, NULL,
1883209f581SMatthew Dillon 					 cam_sim_path(ap->ap_sim),
1893209f581SMatthew Dillon 					 target,
1903209f581SMatthew Dillon 					 CAM_LUN_WILDCARD);
1913209f581SMatthew Dillon 		if (status != CAM_REQ_CMP)
1923209f581SMatthew Dillon 			return;
1933209f581SMatthew Dillon #if 0
1943209f581SMatthew Dillon 		/*
1953209f581SMatthew Dillon 		 * This confuses CAM
1963209f581SMatthew Dillon 		 */
1973209f581SMatthew Dillon 		if (found)
1983209f581SMatthew Dillon 			xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
1993209f581SMatthew Dillon 		else
200fd8bd957SMatthew Dillon 			xpt_async(AC_LOST_DEVICE, tmppath, NULL);
2013209f581SMatthew Dillon #endif
202fd8bd957SMatthew Dillon 	}
203fd8bd957SMatthew Dillon 	xpt_free_path(tmppath);
204258223a3SMatthew Dillon }
205258223a3SMatthew Dillon 
206258223a3SMatthew Dillon void
ahci_cam_detach(struct ahci_port * ap)207258223a3SMatthew Dillon ahci_cam_detach(struct ahci_port *ap)
208258223a3SMatthew Dillon {
209a43d9d72SSascha Wildner 	int error __debugvar;
210258223a3SMatthew Dillon 
211258223a3SMatthew Dillon 	if ((ap->ap_flags & AP_F_CAM_ATTACHED) == 0)
212258223a3SMatthew Dillon 		return;
213fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
214258223a3SMatthew Dillon 	if (ap->ap_sim) {
215258223a3SMatthew Dillon 		xpt_freeze_simq(ap->ap_sim, 1);
216258223a3SMatthew Dillon 	}
217258223a3SMatthew Dillon 	if (ap->ap_flags & AP_F_BUS_REGISTERED) {
218258223a3SMatthew Dillon 		error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
219258223a3SMatthew Dillon 		KKASSERT(error == CAM_REQ_CMP);
220258223a3SMatthew Dillon 		ap->ap_flags &= ~AP_F_BUS_REGISTERED;
221258223a3SMatthew Dillon 	}
222258223a3SMatthew Dillon 	if (ap->ap_sim) {
223258223a3SMatthew Dillon 		cam_sim_free(ap->ap_sim);
224258223a3SMatthew Dillon 		ap->ap_sim = NULL;
225258223a3SMatthew Dillon 	}
226fb00c6edSMatthew Dillon 	lockmgr(&ap->ap_sim_lock, LK_RELEASE);
227258223a3SMatthew Dillon 	ap->ap_flags &= ~AP_F_CAM_ATTACHED;
228258223a3SMatthew Dillon }
229258223a3SMatthew Dillon 
230258223a3SMatthew Dillon /*
2311980eff3SMatthew Dillon  * Once the AHCI port has been attached we need to probe for a device or
232258223a3SMatthew Dillon  * devices on the port and setup various options.
2331980eff3SMatthew Dillon  *
2341980eff3SMatthew Dillon  * If at is NULL we are probing the direct-attached device on the port,
2351980eff3SMatthew Dillon  * which may or may not be a port multiplier.
236258223a3SMatthew Dillon  */
2373209f581SMatthew Dillon int
ahci_cam_probe(struct ahci_port * ap,struct ata_port * atx)2381980eff3SMatthew Dillon ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
239258223a3SMatthew Dillon {
2401980eff3SMatthew Dillon 	struct ata_port	*at;
241258223a3SMatthew Dillon 	struct ata_xfer	*xa;
242258223a3SMatthew Dillon 	u_int64_t	capacity;
243258223a3SMatthew Dillon 	u_int64_t	capacity_bytes;
244258223a3SMatthew Dillon 	int		model_len;
2456e0003adSMatthew Dillon 	int		firmware_len;
2466e0003adSMatthew Dillon 	int		serial_len;
247fd8bd957SMatthew Dillon 	int		error;
248258223a3SMatthew Dillon 	int		devncqdepth;
249258223a3SMatthew Dillon 	int		i;
2506e0003adSMatthew Dillon 	const char	*model_id;
2516e0003adSMatthew Dillon 	const char	*firmware_id;
2526e0003adSMatthew Dillon 	const char	*serial_id;
253669fbbf7SMatthew Dillon 	const char	*wcstr;
254669fbbf7SMatthew Dillon 	const char	*rastr;
255fd8bd957SMatthew Dillon 	const char	*scstr;
256fd8bd957SMatthew Dillon 	const char	*type;
257fd8bd957SMatthew Dillon 
2583209f581SMatthew Dillon 	error = EIO;
2591980eff3SMatthew Dillon 
2601980eff3SMatthew Dillon 	/*
261f4553de1SMatthew Dillon 	 * Delayed CAM attachment for initial probe, sim may be NULL
262f4553de1SMatthew Dillon 	 */
263f4553de1SMatthew Dillon 	if (ap->ap_sim == NULL)
264f4553de1SMatthew Dillon 		return(0);
265f4553de1SMatthew Dillon 
266f4553de1SMatthew Dillon 	/*
2671980eff3SMatthew Dillon 	 * A NULL atx indicates a probe of the directly connected device.
2681980eff3SMatthew Dillon 	 * A non-NULL atx indicates a device connected via a port multiplier.
2691980eff3SMatthew Dillon 	 * We need to preserve atx for calls to ahci_ata_get_xfer().
2701980eff3SMatthew Dillon 	 *
2711980eff3SMatthew Dillon 	 * at is always non-NULL.  For directly connected devices we supply
2721980eff3SMatthew Dillon 	 * an (at) pointing to target 0.
2731980eff3SMatthew Dillon 	 */
2741980eff3SMatthew Dillon 	if (atx == NULL) {
275b012a2caSMatthew Dillon 		at = ap->ap_ata[0];	/* direct attached - device 0 */
2761980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM) {
277831bc9e3SMatthew Dillon 			kprintf("%s: Found Port Multiplier\n",
278831bc9e3SMatthew Dillon 				ATANAME(ap, atx));
2791980eff3SMatthew Dillon 			return (0);
2801980eff3SMatthew Dillon 		}
2811980eff3SMatthew Dillon 		at->at_type = ap->ap_type;
2821980eff3SMatthew Dillon 	} else {
2833209f581SMatthew Dillon 		at = atx;
2841980eff3SMatthew Dillon 		if (atx->at_type == ATA_PORT_T_PM) {
2851980eff3SMatthew Dillon 			kprintf("%s: Bogus device, reducing port count to %d\n",
2861980eff3SMatthew Dillon 				ATANAME(ap, atx), atx->at_target);
2871980eff3SMatthew Dillon 			if (ap->ap_pmcount > atx->at_target)
2881980eff3SMatthew Dillon 				ap->ap_pmcount = atx->at_target;
2893209f581SMatthew Dillon 			goto err;
2901980eff3SMatthew Dillon 		}
2911980eff3SMatthew Dillon 	}
2923209f581SMatthew Dillon 	if (ap->ap_type == ATA_PORT_T_NONE)
2933209f581SMatthew Dillon 		goto err;
2941980eff3SMatthew Dillon 	if (at->at_type == ATA_PORT_T_NONE)
2953209f581SMatthew Dillon 		goto err;
296258223a3SMatthew Dillon 
297258223a3SMatthew Dillon 	/*
298258223a3SMatthew Dillon 	 * Issue identify, saving the result
299258223a3SMatthew Dillon 	 */
3001980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
301258223a3SMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
3021980eff3SMatthew Dillon 	xa->data = &at->at_identify;
3031980eff3SMatthew Dillon 	xa->datalen = sizeof(at->at_identify);
30412feb904SMatthew Dillon 	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
3051980eff3SMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
3061980eff3SMatthew Dillon 
3071980eff3SMatthew Dillon 	switch(at->at_type) {
3081980eff3SMatthew Dillon 	case ATA_PORT_T_DISK:
309258223a3SMatthew Dillon 		xa->fis->command = ATA_C_IDENTIFY;
310fd8bd957SMatthew Dillon 		type = "DISK";
3111980eff3SMatthew Dillon 		break;
3121980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
3131980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
31412feb904SMatthew Dillon 		xa->flags |= ATA_F_AUTOSENSE;
3151980eff3SMatthew Dillon 		type = "ATAPI";
3161980eff3SMatthew Dillon 		break;
3171980eff3SMatthew Dillon 	default:
3181980eff3SMatthew Dillon 		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
3191980eff3SMatthew Dillon 		type = "UNKNOWN(ATAPI?)";
3201980eff3SMatthew Dillon 		break;
321fd8bd957SMatthew Dillon 	}
322258223a3SMatthew Dillon 	xa->fis->features = 0;
323258223a3SMatthew Dillon 	xa->fis->device = 0;
3243209f581SMatthew Dillon 	xa->timeout = 1000;
325258223a3SMatthew Dillon 
326831bc9e3SMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
327fd8bd957SMatthew Dillon 		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
3281980eff3SMatthew Dillon 			ATANAME(ap, atx), type);
329258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
3303209f581SMatthew Dillon 		goto err;
331258223a3SMatthew Dillon 	}
332258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
333258223a3SMatthew Dillon 
3341980eff3SMatthew Dillon 	ata_fix_identify(&at->at_identify);
335258223a3SMatthew Dillon 
3366a790b6aSSepherosa Ziehau 	if (at->at_type == ATA_PORT_T_DISK && at->at_identify.nomrota_rate == 1)
3376a790b6aSSepherosa Ziehau 		type = "SSD";
3386a790b6aSSepherosa Ziehau 
339258223a3SMatthew Dillon 	/*
340258223a3SMatthew Dillon 	 * Read capacity using SATA probe info.
341258223a3SMatthew Dillon 	 */
3421980eff3SMatthew Dillon 	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
343258223a3SMatthew Dillon 		/* LBA48 feature set supported */
344258223a3SMatthew Dillon 		capacity = 0;
345258223a3SMatthew Dillon 		for (i = 3; i >= 0; --i) {
346258223a3SMatthew Dillon 			capacity <<= 16;
347258223a3SMatthew Dillon 			capacity +=
3481980eff3SMatthew Dillon 			    le16toh(at->at_identify.addrsecxt[i]);
349258223a3SMatthew Dillon 		}
350258223a3SMatthew Dillon 	} else {
3511980eff3SMatthew Dillon 		capacity = le16toh(at->at_identify.addrsec[1]);
352258223a3SMatthew Dillon 		capacity <<= 16;
3531980eff3SMatthew Dillon 		capacity += le16toh(at->at_identify.addrsec[0]);
354258223a3SMatthew Dillon 	}
35512feb904SMatthew Dillon 	if (capacity == 0)
35612feb904SMatthew Dillon 		capacity = 1024 * 1024 / 512;
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) {
394949597c2SMatthew Dillon 				cam_sim_set_max_tags(ap->ap_sim,
3951980eff3SMatthew Dillon 						     at->at_ncqdepth - 1);
396258223a3SMatthew Dillon 			}
397cec85a37SMatthew Dillon 		}
398258223a3SMatthew Dillon 	} else {
399258223a3SMatthew Dillon 		devncqdepth = 0;
400258223a3SMatthew Dillon 	}
401258223a3SMatthew Dillon 
4026e0003adSMatthew Dillon 	model_len = sizeof(at->at_identify.model);
4036e0003adSMatthew Dillon 	model_id = at->at_identify.model;
4046e0003adSMatthew Dillon 	ahci_strip_string(&model_id, &model_len);
4056e0003adSMatthew Dillon 
4066e0003adSMatthew Dillon 	firmware_len = sizeof(at->at_identify.firmware);
4076e0003adSMatthew Dillon 	firmware_id = at->at_identify.firmware;
4086e0003adSMatthew Dillon 	ahci_strip_string(&firmware_id, &firmware_len);
4096e0003adSMatthew Dillon 
4106e0003adSMatthew Dillon 	serial_len = sizeof(at->at_identify.serial);
4116e0003adSMatthew Dillon 	serial_id = at->at_identify.serial;
4126e0003adSMatthew Dillon 	ahci_strip_string(&serial_id, &serial_len);
413669fbbf7SMatthew Dillon 
414fd8bd957SMatthew Dillon 	/*
415fd8bd957SMatthew Dillon 	 * Generate informatiive strings.
416fd8bd957SMatthew Dillon 	 *
417fd8bd957SMatthew Dillon 	 * NOTE: We do not automatically set write caching, lookahead,
418fd8bd957SMatthew Dillon 	 *	 or the security state for ATAPI devices.
419fd8bd957SMatthew Dillon 	 */
4201980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
4211980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
422669fbbf7SMatthew Dillon 			wcstr = "enabled";
4231980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
424fd8bd957SMatthew Dillon 			wcstr = "disabled";
425669fbbf7SMatthew Dillon 		else
426669fbbf7SMatthew Dillon 			wcstr = "enabling";
427669fbbf7SMatthew Dillon 	} else {
428669fbbf7SMatthew Dillon 		    wcstr = "notsupp";
429669fbbf7SMatthew Dillon 	}
430669fbbf7SMatthew Dillon 
4311980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
4321980eff3SMatthew Dillon 		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
433669fbbf7SMatthew Dillon 			rastr = "enabled";
4341980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
435fd8bd957SMatthew Dillon 			rastr = "disabled";
436669fbbf7SMatthew Dillon 		else
437669fbbf7SMatthew Dillon 			rastr = "enabling";
438669fbbf7SMatthew Dillon 	} else {
439669fbbf7SMatthew Dillon 		    rastr = "notsupp";
440669fbbf7SMatthew Dillon 	}
441669fbbf7SMatthew Dillon 
4421980eff3SMatthew Dillon 	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
4431980eff3SMatthew Dillon 		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
444fd8bd957SMatthew Dillon 			scstr = "frozen";
4451980eff3SMatthew Dillon 		else if (at->at_type == ATA_PORT_T_ATAPI)
446fd8bd957SMatthew Dillon 			scstr = "unfrozen";
447afa796d2SMatthew Dillon 		else if (AhciNoFeatures & (1 << ap->ap_num))
448afa796d2SMatthew Dillon 			scstr = "<disabled>";
449fd8bd957SMatthew Dillon 		else
450fd8bd957SMatthew Dillon 			scstr = "freezing";
451fd8bd957SMatthew Dillon 	} else {
452fd8bd957SMatthew Dillon 		    scstr = "notsupp";
453fd8bd957SMatthew Dillon 	}
454fd8bd957SMatthew Dillon 
4556e0003adSMatthew Dillon 	kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
456b989944dSSepherosa Ziehau 		"%s: tags=%d/%d satacap=%04x satacap2=%04x satafea=%04x NCQ=%s "
457074579dfSMatthew Dillon 		"capacity=%lld.%02dMB\n",
458074579dfSMatthew Dillon 
4591980eff3SMatthew Dillon 		ATANAME(ap, atx),
460fd8bd957SMatthew Dillon 		type,
4616e0003adSMatthew Dillon 		model_len, model_len, model_id,
4626e0003adSMatthew Dillon 		firmware_len, firmware_len, firmware_id,
4636e0003adSMatthew Dillon 		serial_len, serial_len, serial_id,
464258223a3SMatthew Dillon 
4651980eff3SMatthew Dillon 		ATANAME(ap, atx),
466258223a3SMatthew Dillon 		devncqdepth, ap->ap_sc->sc_ncmds,
4671980eff3SMatthew Dillon 		at->at_identify.satacap,
468b989944dSSepherosa Ziehau 		at->at_identify.satacap2,
4691980eff3SMatthew Dillon 		at->at_identify.satafsup,
470074579dfSMatthew Dillon 		(at->at_ncqdepth > 1 ? "YES" : "NO"),
471258223a3SMatthew Dillon 		(long long)capacity_bytes / (1024 * 1024),
472074579dfSMatthew Dillon 		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
473074579dfSMatthew Dillon 	);
474074579dfSMatthew Dillon 	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
4751980eff3SMatthew Dillon 		ATANAME(ap, atx),
4761980eff3SMatthew Dillon 		at->at_identify.features85,
4771980eff3SMatthew Dillon 		at->at_identify.features86,
4781980eff3SMatthew Dillon 		at->at_identify.features87,
479669fbbf7SMatthew Dillon 		wcstr,
480fd8bd957SMatthew Dillon 		rastr,
481fd8bd957SMatthew Dillon 		scstr
482258223a3SMatthew Dillon 	);
483258223a3SMatthew Dillon 
484258223a3SMatthew Dillon 	/*
485fd8bd957SMatthew Dillon 	 * Additional type-specific probing
486fd8bd957SMatthew Dillon 	 */
4871980eff3SMatthew Dillon 	switch(at->at_type) {
488fd8bd957SMatthew Dillon 	case ATA_PORT_T_DISK:
4891980eff3SMatthew Dillon 		error = ahci_cam_probe_disk(ap, atx);
4901980eff3SMatthew Dillon 		break;
4911980eff3SMatthew Dillon 	case ATA_PORT_T_ATAPI:
4921980eff3SMatthew Dillon 		error = ahci_cam_probe_atapi(ap, atx);
493fd8bd957SMatthew Dillon 		break;
494fd8bd957SMatthew Dillon 	default:
4951980eff3SMatthew Dillon 		error = EIO;
496fd8bd957SMatthew Dillon 		break;
497fd8bd957SMatthew Dillon 	}
4983209f581SMatthew Dillon err:
4993209f581SMatthew Dillon 	if (error) {
5003209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_FAILED;
5013209f581SMatthew Dillon 		if (atx == NULL)
5023209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5033209f581SMatthew Dillon 	} else {
5043209f581SMatthew Dillon 		at->at_probe = ATA_PROBE_GOOD;
5053209f581SMatthew Dillon 		if (atx == NULL)
5063209f581SMatthew Dillon 			ap->ap_probe = at->at_probe;
5073209f581SMatthew Dillon 	}
5083209f581SMatthew Dillon 	return (error);
509fd8bd957SMatthew Dillon }
510fd8bd957SMatthew Dillon 
511fd8bd957SMatthew Dillon /*
512fd8bd957SMatthew Dillon  * DISK-specific probe after initial ident
513fd8bd957SMatthew Dillon  */
514fd8bd957SMatthew Dillon static int
ahci_cam_probe_disk(struct ahci_port * ap,struct ata_port * atx)5151980eff3SMatthew Dillon ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
516fd8bd957SMatthew Dillon {
5171980eff3SMatthew Dillon 	struct ata_port *at;
518fd8bd957SMatthew Dillon 	struct ata_xfer	*xa;
519fd8bd957SMatthew Dillon 
520b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
5211980eff3SMatthew Dillon 
522fd8bd957SMatthew Dillon 	/*
52369b3741eSMatthew Dillon 	 * Set dummy xfer mode
52469b3741eSMatthew Dillon 	 */
52569b3741eSMatthew Dillon 	ahci_set_xfer(ap, atx);
52669b3741eSMatthew Dillon 
52769b3741eSMatthew Dillon 	/*
528258223a3SMatthew Dillon 	 * Enable write cache if supported
529fd8bd957SMatthew Dillon 	 *
530fd8bd957SMatthew Dillon 	 * NOTE: "WD My Book" external disk devices have a very poor
531fd8bd957SMatthew Dillon 	 *	 daughter board between the the ESATA and the HD.  Sending
532fd8bd957SMatthew Dillon 	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
533fd8bd957SMatthew Dillon 	 *	 with a fatal protocol error.  However, this device also
534fd8bd957SMatthew Dillon 	 *	 indicates that WRITECACHE is already on and READAHEAD is
535fd8bd957SMatthew Dillon 	 *	 not supported so we avoid the issue.
536258223a3SMatthew Dillon 	 */
5371980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
5381980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
5391980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
540258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
541258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
542b012a2caSMatthew Dillon 		xa->fis->features = ATA_SF_WRITECACHE_EN;
543b012a2caSMatthew Dillon 		/* xa->fis->features = ATA_SF_LOOKAHEAD_EN; */
5441980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
545669fbbf7SMatthew Dillon 		xa->fis->device = 0;
546b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5473209f581SMatthew Dillon 		xa->timeout = 1000;
548669fbbf7SMatthew Dillon 		xa->datalen = 0;
549831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5501980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_WCACHE;
551afa796d2SMatthew Dillon 		else
552afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable write-caching\n",
553afa796d2SMatthew Dillon 				ATANAME(ap, atx));
554258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
555258223a3SMatthew Dillon 	}
556258223a3SMatthew Dillon 
557258223a3SMatthew Dillon 	/*
558258223a3SMatthew Dillon 	 * Enable readahead if supported
559258223a3SMatthew Dillon 	 */
5601980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
5611980eff3SMatthew Dillon 	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
5621980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
563258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
564258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SET_FEATURES;
565258223a3SMatthew Dillon 		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
5661980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
567669fbbf7SMatthew Dillon 		xa->fis->device = 0;
568b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5693209f581SMatthew Dillon 		xa->timeout = 1000;
570669fbbf7SMatthew Dillon 		xa->datalen = 0;
571831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5721980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_RAHEAD;
573afa796d2SMatthew Dillon 		else
574afa796d2SMatthew Dillon 			kprintf("%s: Unable to enable read-ahead\n",
575afa796d2SMatthew Dillon 				ATANAME(ap, atx));
576258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
577258223a3SMatthew Dillon 	}
578258223a3SMatthew Dillon 
579258223a3SMatthew Dillon 	/*
580258223a3SMatthew Dillon 	 * FREEZE LOCK the device so malicious users can't lock it on us.
581258223a3SMatthew Dillon 	 * As there is no harm in issuing this to devices that don't
582258223a3SMatthew Dillon 	 * support the security feature set we just send it, and don't bother
583258223a3SMatthew Dillon 	 * checking if the device sends a command abort to tell us it doesn't
584258223a3SMatthew Dillon 	 * support it
585258223a3SMatthew Dillon 	 */
5861980eff3SMatthew Dillon 	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
587afa796d2SMatthew Dillon 	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
588afa796d2SMatthew Dillon 	    (AhciNoFeatures & (1 << ap->ap_num)) == 0) {
5891980eff3SMatthew Dillon 		xa = ahci_ata_get_xfer(ap, atx);
590258223a3SMatthew Dillon 		xa->complete = ahci_ata_dummy_done;
591258223a3SMatthew Dillon 		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
5921980eff3SMatthew Dillon 		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
593b012a2caSMatthew Dillon 		xa->flags = ATA_F_PIO | ATA_F_POLL;
5943209f581SMatthew Dillon 		xa->timeout = 1000;
595669fbbf7SMatthew Dillon 		xa->datalen = 0;
596831bc9e3SMatthew Dillon 		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
5971980eff3SMatthew Dillon 			at->at_features |= ATA_PORT_F_FRZLCK;
598afa796d2SMatthew Dillon 		else
599afa796d2SMatthew Dillon 			kprintf("%s: Unable to set security freeze\n",
600afa796d2SMatthew Dillon 				ATANAME(ap, atx));
601258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
602669fbbf7SMatthew Dillon 	}
603258223a3SMatthew Dillon 
604b4189e5eSMatthew Dillon 	return (0);
605b4189e5eSMatthew Dillon }
606b4189e5eSMatthew Dillon 
607fd8bd957SMatthew Dillon /*
608fd8bd957SMatthew Dillon  * ATAPI-specific probe after initial ident
609fd8bd957SMatthew Dillon  */
610b4189e5eSMatthew Dillon static int
ahci_cam_probe_atapi(struct ahci_port * ap,struct ata_port * atx)6111980eff3SMatthew Dillon ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *atx)
612b4189e5eSMatthew Dillon {
61369b3741eSMatthew Dillon 	ahci_set_xfer(ap, atx);
61469b3741eSMatthew Dillon 	return(0);
61569b3741eSMatthew Dillon }
61669b3741eSMatthew Dillon 
61769b3741eSMatthew Dillon /*
61869b3741eSMatthew Dillon  * Setting the transfer mode is irrelevant for the SATA transport
61969b3741eSMatthew Dillon  * but some (atapi) devices seem to need it anyway.  In addition
62069b3741eSMatthew Dillon  * if we are running through a SATA->PATA converter for some reason
62169b3741eSMatthew Dillon  * beyond my comprehension we might have to set the mode.
622d15a49f2SMatthew Dillon  *
623d15a49f2SMatthew Dillon  * We only support DMA modes for SATA attached devices, so don't bother
624d15a49f2SMatthew Dillon  * with legacy modes.
62569b3741eSMatthew Dillon  */
62669b3741eSMatthew Dillon static int
ahci_set_xfer(struct ahci_port * ap,struct ata_port * atx)62769b3741eSMatthew Dillon ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx)
62869b3741eSMatthew Dillon {
62969b3741eSMatthew Dillon 	struct ata_port *at;
63069b3741eSMatthew Dillon 	struct ata_xfer	*xa;
63169b3741eSMatthew Dillon 	u_int16_t mode;
632d15a49f2SMatthew Dillon 	u_int16_t mask;
63369b3741eSMatthew Dillon 
63469b3741eSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
63569b3741eSMatthew Dillon 
63669b3741eSMatthew Dillon 	/*
637d15a49f2SMatthew Dillon 	 * Figure out the supported UDMA mode.  Ignore other legacy modes.
63869b3741eSMatthew Dillon 	 */
639d15a49f2SMatthew Dillon 	mask = le16toh(at->at_identify.ultradma);
640d15a49f2SMatthew Dillon 	if ((mask & 0xFF) == 0 || mask == 0xFFFF)
64169b3741eSMatthew Dillon 		return(0);
642d15a49f2SMatthew Dillon 	mask &= 0xFF;
643d15a49f2SMatthew Dillon 	mode = 0x4F;
644d15a49f2SMatthew Dillon 	while ((mask & 0x8000) == 0) {
645d15a49f2SMatthew Dillon 		mask <<= 1;
646d15a49f2SMatthew Dillon 		--mode;
647d15a49f2SMatthew Dillon 	}
64869b3741eSMatthew Dillon 
64969b3741eSMatthew Dillon 	/*
65069b3741eSMatthew Dillon 	 * SATA atapi devices often still report a dma mode, even though
65169b3741eSMatthew Dillon 	 * it is irrelevant for SATA transport.  It is also possible that
65269b3741eSMatthew Dillon 	 * we are running through a SATA->PATA converter and seeing the
65369b3741eSMatthew Dillon 	 * PATA dma mode.
65469b3741eSMatthew Dillon 	 *
65569b3741eSMatthew Dillon 	 * In this case the device may require a (dummy) SETXFER to be
65669b3741eSMatthew Dillon 	 * sent before it will work properly.
65769b3741eSMatthew Dillon 	 */
65869b3741eSMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
65969b3741eSMatthew Dillon 	xa->complete = ahci_ata_dummy_done;
66069b3741eSMatthew Dillon 	xa->fis->command = ATA_C_SET_FEATURES;
66169b3741eSMatthew Dillon 	xa->fis->features = ATA_SF_SETXFER;
66269b3741eSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
663d15a49f2SMatthew Dillon 	xa->fis->sector_count = mode;
66469b3741eSMatthew Dillon 	xa->flags = ATA_F_PIO | ATA_F_POLL;
66569b3741eSMatthew Dillon 	xa->timeout = 1000;
66669b3741eSMatthew Dillon 	xa->datalen = 0;
66769b3741eSMatthew Dillon 	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
66869b3741eSMatthew Dillon 		kprintf("%s: Unable to set dummy xfer mode \n",
66969b3741eSMatthew Dillon 			ATANAME(ap, atx));
67069b3741eSMatthew Dillon 	} else if (bootverbose) {
67169b3741eSMatthew Dillon 		kprintf("%s: Set dummy xfer mode to %02x\n",
672d15a49f2SMatthew Dillon 			ATANAME(ap, atx), mode);
67369b3741eSMatthew Dillon 	}
67469b3741eSMatthew Dillon 	ahci_ata_put_xfer(xa);
675fd8bd957SMatthew Dillon 	return(0);
676fd8bd957SMatthew Dillon }
677fd8bd957SMatthew Dillon 
678b4189e5eSMatthew Dillon /*
679b4189e5eSMatthew Dillon  * Fix byte ordering so buffers can be accessed as
680b4189e5eSMatthew Dillon  * strings.
681b4189e5eSMatthew Dillon  */
682258223a3SMatthew Dillon static void
ata_fix_identify(struct ata_identify * id)683258223a3SMatthew Dillon ata_fix_identify(struct ata_identify *id)
684258223a3SMatthew Dillon {
685258223a3SMatthew Dillon 	u_int16_t	*swap;
686258223a3SMatthew Dillon 	int		i;
687258223a3SMatthew Dillon 
688258223a3SMatthew Dillon 	swap = (u_int16_t *)id->serial;
689258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->serial) / sizeof(u_int16_t); i++)
690258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
691258223a3SMatthew Dillon 
692258223a3SMatthew Dillon 	swap = (u_int16_t *)id->firmware;
693258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->firmware) / sizeof(u_int16_t); i++)
694258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
695258223a3SMatthew Dillon 
696258223a3SMatthew Dillon 	swap = (u_int16_t *)id->model;
697258223a3SMatthew Dillon 	for (i = 0; i < sizeof(id->model) / sizeof(u_int16_t); i++)
698258223a3SMatthew Dillon 		swap[i] = bswap16(swap[i]);
699258223a3SMatthew Dillon }
700258223a3SMatthew Dillon 
701258223a3SMatthew Dillon /*
702b4189e5eSMatthew Dillon  * Dummy done callback for xa.
703b4189e5eSMatthew Dillon  */
704b4189e5eSMatthew Dillon static void
ahci_ata_dummy_done(struct ata_xfer * xa)705b4189e5eSMatthew Dillon ahci_ata_dummy_done(struct ata_xfer *xa)
706b4189e5eSMatthew Dillon {
707b4189e5eSMatthew Dillon }
708b4189e5eSMatthew Dillon 
709b4189e5eSMatthew Dillon /*
7103209f581SMatthew Dillon  * Use an engineering request to initiate a target scan for devices
7113209f581SMatthew Dillon  * behind a port multiplier.
712fd8bd957SMatthew Dillon  *
7133209f581SMatthew Dillon  * An asynchronous bus scan is used to avoid reentrancy issues.
714258223a3SMatthew Dillon  */
715258223a3SMatthew Dillon static void
ahci_cam_rescan_callback(struct cam_periph * periph,union ccb * ccb)716258223a3SMatthew Dillon ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
717258223a3SMatthew Dillon {
7183209f581SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
7193209f581SMatthew Dillon 
720f4553de1SMatthew Dillon 	if (ccb->ccb_h.func_code == XPT_SCAN_BUS) {
7213209f581SMatthew Dillon 		ap->ap_flags &= ~AP_F_SCAN_RUNNING;
7223209f581SMatthew Dillon 		if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
7233209f581SMatthew Dillon 			ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
7243209f581SMatthew Dillon 			ahci_cam_rescan(ap);
7253209f581SMatthew Dillon 		}
726f4553de1SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_COMPLETED;
727f4553de1SMatthew Dillon 		wakeup(&ap->ap_flags);
728f4553de1SMatthew Dillon 	}
729*cec957e9SMatthew Dillon 	xpt_free_ccb(&ccb->ccb_h);
730258223a3SMatthew Dillon }
731258223a3SMatthew Dillon 
732258223a3SMatthew Dillon static void
ahci_cam_rescan(struct ahci_port * ap)733258223a3SMatthew Dillon ahci_cam_rescan(struct ahci_port *ap)
734258223a3SMatthew Dillon {
735258223a3SMatthew Dillon 	struct cam_path *path;
736258223a3SMatthew Dillon 	union ccb *ccb;
737258223a3SMatthew Dillon 	int status;
7383209f581SMatthew Dillon 	int i;
7393209f581SMatthew Dillon 
7403209f581SMatthew Dillon 	if (ap->ap_flags & AP_F_SCAN_RUNNING) {
7413209f581SMatthew Dillon 		ap->ap_flags |= AP_F_SCAN_REQUESTED;
7423209f581SMatthew Dillon 		return;
7433209f581SMatthew Dillon 	}
7443209f581SMatthew Dillon 	ap->ap_flags |= AP_F_SCAN_RUNNING;
7453209f581SMatthew Dillon 	for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
746b012a2caSMatthew Dillon 		ap->ap_ata[i]->at_features |= ATA_PORT_F_RESCAN;
7473209f581SMatthew Dillon 	}
748258223a3SMatthew Dillon 
749258223a3SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
750258223a3SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
751258223a3SMatthew Dillon 	if (status != CAM_REQ_CMP)
752258223a3SMatthew Dillon 		return;
753258223a3SMatthew Dillon 
754f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
755258223a3SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
7562de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_ENG_EXEC;
757258223a3SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7583209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
759258223a3SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
7602de5e9baSMatthew Dillon 	xpt_action_async(ccb);
761258223a3SMatthew Dillon }
762258223a3SMatthew Dillon 
7633209f581SMatthew Dillon static void
ahci_xpt_rescan(struct ahci_port * ap)7643209f581SMatthew Dillon ahci_xpt_rescan(struct ahci_port *ap)
7653209f581SMatthew Dillon {
7663209f581SMatthew Dillon 	struct cam_path *path;
7673209f581SMatthew Dillon 	union ccb *ccb;
7683209f581SMatthew Dillon 	int status;
7693209f581SMatthew Dillon 
7703209f581SMatthew Dillon 	status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
7713209f581SMatthew Dillon 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
7723209f581SMatthew Dillon 	if (status != CAM_REQ_CMP)
7733209f581SMatthew Dillon 		return;
774f4553de1SMatthew Dillon 
775f4553de1SMatthew Dillon 	ccb = xpt_alloc_ccb();
7763209f581SMatthew Dillon 	xpt_setup_ccb(&ccb->ccb_h, path, 5);	/* 5 = low priority */
7772de5e9baSMatthew Dillon 	ccb->ccb_h.func_code = XPT_SCAN_BUS;
7783209f581SMatthew Dillon 	ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
7793209f581SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
7803209f581SMatthew Dillon 	ccb->crcn.flags = CAM_FLAG_NONE;
781baef7501SMatthew Dillon 	xpt_action_async(ccb);
7823209f581SMatthew Dillon }
7833209f581SMatthew Dillon 
784258223a3SMatthew Dillon /*
785258223a3SMatthew Dillon  * Action function - dispatch command
786258223a3SMatthew Dillon  */
787258223a3SMatthew Dillon static
788258223a3SMatthew Dillon void
ahci_xpt_action(struct cam_sim * sim,union ccb * ccb)789258223a3SMatthew Dillon ahci_xpt_action(struct cam_sim *sim, union ccb *ccb)
790258223a3SMatthew Dillon {
791258223a3SMatthew Dillon 	struct ahci_port *ap;
7921980eff3SMatthew Dillon 	struct ata_port	 *at, *atx;
793258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
794258223a3SMatthew Dillon 
795258223a3SMatthew Dillon 	/* XXX lock */
796258223a3SMatthew Dillon 	ap = cam_sim_softc(sim);
7971980eff3SMatthew Dillon 	atx = NULL;
798258223a3SMatthew Dillon 	KKASSERT(ap != NULL);
799258223a3SMatthew Dillon 	ccbh = &ccb->ccb_h;
800258223a3SMatthew Dillon 
801258223a3SMatthew Dillon 	/*
8023209f581SMatthew Dillon 	 * Early failure checks.  These checks do not apply to XPT_PATH_INQ,
8033209f581SMatthew Dillon 	 * otherwise the bus rescan will not remove the dead devices when
8043209f581SMatthew Dillon 	 * unplugging a PM.
8053209f581SMatthew Dillon 	 *
8061980eff3SMatthew Dillon 	 * For non-wildcards we have one target (0) and one lun (0),
8071980eff3SMatthew Dillon 	 * unless we have a port multiplier.
8081980eff3SMatthew Dillon 	 *
8091980eff3SMatthew Dillon 	 * A wildcard target indicates only the general bus is being
8101980eff3SMatthew Dillon 	 * probed.
8111980eff3SMatthew Dillon 	 *
8121980eff3SMatthew Dillon 	 * Calculate at and atx.  at is always non-NULL.  atx is only
81344a472baSSascha Wildner 	 * NULL for direct-attached devices.  It will be non-NULL for
8141980eff3SMatthew Dillon 	 * devices behind a port multiplier.
815258223a3SMatthew Dillon 	 *
816258223a3SMatthew Dillon 	 * XXX What do we do with a LUN wildcard?
817258223a3SMatthew Dillon 	 */
8183209f581SMatthew Dillon 	if (ccbh->target_id != CAM_TARGET_WILDCARD &&
8193209f581SMatthew Dillon 	    ccbh->func_code != XPT_PATH_INQ) {
8201980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
8213209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
822258223a3SMatthew Dillon 			xpt_done(ccb);
823258223a3SMatthew Dillon 			return;
824258223a3SMatthew Dillon 		}
8251980eff3SMatthew Dillon 		if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) {
826258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
827258223a3SMatthew Dillon 			xpt_done(ccb);
828258223a3SMatthew Dillon 			return;
829258223a3SMatthew Dillon 		}
830b012a2caSMatthew Dillon 		at = ap->ap_ata[ccbh->target_id];
8311980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_PM)
8321980eff3SMatthew Dillon 			atx = at;
8331980eff3SMatthew Dillon 
834258223a3SMatthew Dillon 		if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) {
835258223a3SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
836258223a3SMatthew Dillon 			xpt_done(ccb);
837258223a3SMatthew Dillon 			return;
838258223a3SMatthew Dillon 		}
839b012a2caSMatthew Dillon 	} else {
840b012a2caSMatthew Dillon 		at = ap->ap_ata[0];
841258223a3SMatthew Dillon 	}
842258223a3SMatthew Dillon 
843258223a3SMatthew Dillon 	/*
844258223a3SMatthew Dillon 	 * Switch on the meta XPT command
845258223a3SMatthew Dillon 	 */
846258223a3SMatthew Dillon 	switch(ccbh->func_code) {
8473209f581SMatthew Dillon 	case XPT_ENG_EXEC:
8483209f581SMatthew Dillon 		/*
8493209f581SMatthew Dillon 		 * This routine is called after a port multiplier has been
8503209f581SMatthew Dillon 		 * probed.
8513209f581SMatthew Dillon 		 */
8523209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
853f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
854831bc9e3SMatthew Dillon 		ahci_port_state_machine(ap, 0);
855f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
8563209f581SMatthew Dillon 		xpt_done(ccb);
8573209f581SMatthew Dillon 		ahci_xpt_rescan(ap);
8583209f581SMatthew Dillon 		break;
859258223a3SMatthew Dillon 	case XPT_PATH_INQ:
8603209f581SMatthew Dillon 		/*
8613209f581SMatthew Dillon 		 * This command always succeeds, otherwise the bus scan
8623209f581SMatthew Dillon 		 * will not detach dead devices.
8633209f581SMatthew Dillon 		 */
864258223a3SMatthew Dillon 		ccb->cpi.version_num = 1;
865258223a3SMatthew Dillon 		ccb->cpi.hba_inquiry = 0;
866258223a3SMatthew Dillon 		ccb->cpi.target_sprt = 0;
8671980eff3SMatthew Dillon 		ccb->cpi.hba_misc = PIM_SEQSCAN;
868258223a3SMatthew Dillon 		ccb->cpi.hba_eng_cnt = 0;
869258223a3SMatthew Dillon 		bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags));
87076497a9cSMatthew Dillon 		ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1;
871258223a3SMatthew Dillon 		ccb->cpi.max_lun = 0;
872258223a3SMatthew Dillon 		ccb->cpi.async_flags = 0;
873258223a3SMatthew Dillon 		ccb->cpi.hpath_id = 0;
8741980eff3SMatthew Dillon 		ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1;
875258223a3SMatthew Dillon 		ccb->cpi.unit_number = cam_sim_unit(sim);
876258223a3SMatthew Dillon 		ccb->cpi.bus_id = cam_sim_bus(sim);
877258223a3SMatthew Dillon 		ccb->cpi.base_transfer_speed = 150000;
8782cc2e845SMatthew Dillon 		ccb->cpi.transport = XPORT_SATA;
879258223a3SMatthew Dillon 		ccb->cpi.transport_version = 1;
880258223a3SMatthew Dillon 		ccb->cpi.protocol = PROTO_SCSI;
881258223a3SMatthew Dillon 		ccb->cpi.protocol_version = SCSI_REV_2;
882b4faa036SFrançois Tigeot 		ccb->cpi.maxio = AHCI_MAXPHYS;
883258223a3SMatthew Dillon 
8843209f581SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
885831bc9e3SMatthew Dillon 		if (ccbh->target_id == CAM_TARGET_WILDCARD) {
886831bc9e3SMatthew Dillon 			ahci_os_lock_port(ap);
887831bc9e3SMatthew Dillon 			ahci_port_state_machine(ap, 0);
888831bc9e3SMatthew Dillon 			ahci_os_unlock_port(ap);
889831bc9e3SMatthew Dillon 		} else {
890258223a3SMatthew Dillon 			switch(ahci_pread(ap, AHCI_PREG_SSTS) &
891258223a3SMatthew Dillon 			       AHCI_PREG_SSTS_SPD) {
892258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN1:
893258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 150000;
894258223a3SMatthew Dillon 				break;
895258223a3SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN2:
896258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 300000;
897258223a3SMatthew Dillon 				break;
8988986d351SMatthew Dillon 			case AHCI_PREG_SSTS_SPD_GEN3:
8998986d351SMatthew Dillon 				ccb->cpi.base_transfer_speed = 600000;
9008986d351SMatthew Dillon 				break;
901258223a3SMatthew Dillon 			default:
902258223a3SMatthew Dillon 				/* unknown */
903258223a3SMatthew Dillon 				ccb->cpi.base_transfer_speed = 1000;
904258223a3SMatthew Dillon 				break;
905258223a3SMatthew Dillon 			}
9063209f581SMatthew Dillon #if 0
9071980eff3SMatthew Dillon 			if (ap->ap_type == ATA_PORT_T_NONE)
9081980eff3SMatthew Dillon 				ccbh->status = CAM_DEV_NOT_THERE;
9093209f581SMatthew Dillon #endif
9101980eff3SMatthew Dillon 		}
911258223a3SMatthew Dillon 		xpt_done(ccb);
912258223a3SMatthew Dillon 		break;
913258223a3SMatthew Dillon 	case XPT_RESET_DEV:
914f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
9151980eff3SMatthew Dillon 		if (ap->ap_type == ATA_PORT_T_NONE) {
9163209f581SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
9171980eff3SMatthew Dillon 		} else {
9181980eff3SMatthew Dillon 			ahci_port_reset(ap, atx, 0);
919fd8bd957SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
9201980eff3SMatthew Dillon 		}
921f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
922258223a3SMatthew Dillon 		xpt_done(ccb);
923258223a3SMatthew Dillon 		break;
924258223a3SMatthew Dillon 	case XPT_RESET_BUS:
925f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
9261980eff3SMatthew Dillon 		ahci_port_reset(ap, NULL, 1);
927f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
928fd8bd957SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
929258223a3SMatthew Dillon 		xpt_done(ccb);
930258223a3SMatthew Dillon 		break;
931258223a3SMatthew Dillon 	case XPT_SET_TRAN_SETTINGS:
932258223a3SMatthew Dillon 		ccbh->status = CAM_FUNC_NOTAVAIL;
933258223a3SMatthew Dillon 		xpt_done(ccb);
934258223a3SMatthew Dillon 		break;
935258223a3SMatthew Dillon 	case XPT_GET_TRAN_SETTINGS:
936258223a3SMatthew Dillon 		ccb->cts.protocol = PROTO_SCSI;
937258223a3SMatthew Dillon 		ccb->cts.protocol_version = SCSI_REV_2;
9382cc2e845SMatthew Dillon 		ccb->cts.transport = XPORT_SATA;
939258223a3SMatthew Dillon 		ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED;
940258223a3SMatthew Dillon 		ccb->cts.proto_specific.valid = 0;
941258223a3SMatthew Dillon 		ccb->cts.xport_specific.valid = 0;
942258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
943258223a3SMatthew Dillon 		xpt_done(ccb);
944258223a3SMatthew Dillon 		break;
945258223a3SMatthew Dillon 	case XPT_CALC_GEOMETRY:
946258223a3SMatthew Dillon 		cam_calc_geometry(&ccb->ccg, 1);
947258223a3SMatthew Dillon 		xpt_done(ccb);
948258223a3SMatthew Dillon 		break;
949258223a3SMatthew Dillon 	case XPT_SCSI_IO:
950f4553de1SMatthew Dillon 		/*
951f4553de1SMatthew Dillon 		 * Our parallel startup code might have only probed through
952f4553de1SMatthew Dillon 		 * to the IDENT, so do the last step if necessary.
953f4553de1SMatthew Dillon 		 */
954f4553de1SMatthew Dillon 		if (at->at_probe == ATA_PROBE_NEED_IDENT)
955f4553de1SMatthew Dillon 			ahci_cam_probe(ap, atx);
956f4553de1SMatthew Dillon 		if (at->at_probe != ATA_PROBE_GOOD) {
957f4553de1SMatthew Dillon 			ccbh->status = CAM_DEV_NOT_THERE;
958f4553de1SMatthew Dillon 			xpt_done(ccb);
959f4553de1SMatthew Dillon 			break;
960f4553de1SMatthew Dillon 		}
9611980eff3SMatthew Dillon 		switch(at->at_type) {
962258223a3SMatthew Dillon 		case ATA_PORT_T_DISK:
9631980eff3SMatthew Dillon 			ahci_xpt_scsi_disk_io(ap, atx, ccb);
964258223a3SMatthew Dillon 			break;
965258223a3SMatthew Dillon 		case ATA_PORT_T_ATAPI:
9661980eff3SMatthew Dillon 			ahci_xpt_scsi_atapi_io(ap, atx, ccb);
967258223a3SMatthew Dillon 			break;
968258223a3SMatthew Dillon 		default:
969258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
970258223a3SMatthew Dillon 			xpt_done(ccb);
971258223a3SMatthew Dillon 			break;
972258223a3SMatthew Dillon 		}
973258223a3SMatthew Dillon 		break;
974e0fb398bSTim 	case XPT_TRIM:
975e0fb398bSTim 	{
976e0fb398bSTim 		scsi_cdb_t cdb;
977e0fb398bSTim 		struct ccb_scsiio *csio;
978e0fb398bSTim 		csio = &ccb->csio;
979e0fb398bSTim 		cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
980e0fb398bSTim 		    csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
981e0fb398bSTim 		cdb->generic.opcode = TRIM;
982e0fb398bSTim 		ahci_xpt_scsi_disk_io(ap, atx, ccb);
983e0fb398bSTim 		break;
984e0fb398bSTim 	}
985258223a3SMatthew Dillon 	default:
986258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
987258223a3SMatthew Dillon 		xpt_done(ccb);
988258223a3SMatthew Dillon 		break;
989258223a3SMatthew Dillon 	}
990258223a3SMatthew Dillon }
991258223a3SMatthew Dillon 
992258223a3SMatthew Dillon /*
993de68d532SMatthew Dillon  * Poll function.
994de68d532SMatthew Dillon  *
995de68d532SMatthew Dillon  * Generally this function gets called heavily when interrupts might be
996de68d532SMatthew Dillon  * non-operational, during a halt/reboot or panic.
997258223a3SMatthew Dillon  */
998258223a3SMatthew Dillon static
999258223a3SMatthew Dillon void
ahci_xpt_poll(struct cam_sim * sim)1000258223a3SMatthew Dillon ahci_xpt_poll(struct cam_sim *sim)
1001258223a3SMatthew Dillon {
1002de68d532SMatthew Dillon 	struct ahci_port *ap;
1003258223a3SMatthew Dillon 
1004de68d532SMatthew Dillon 	ap = cam_sim_softc(sim);
1005de68d532SMatthew Dillon 	crit_enter();
1006f4553de1SMatthew Dillon 	ahci_os_lock_port(ap);
1007f4553de1SMatthew Dillon 	ahci_port_intr(ap, 1);
1008f4553de1SMatthew Dillon 	ahci_os_unlock_port(ap);
1009de68d532SMatthew Dillon 	crit_exit();
1010258223a3SMatthew Dillon }
1011258223a3SMatthew Dillon 
1012258223a3SMatthew Dillon /*
1013b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1014b4189e5eSMatthew Dillon  * for ATA_PORT_T_DISK operations.  Set the completion function
1015b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1016b4189e5eSMatthew Dillon  * layer.
1017258223a3SMatthew Dillon  *
1018b4189e5eSMatthew Dillon  * AHCI DISK commands only support a limited command set, and we
1019b4189e5eSMatthew Dillon  * fake additional commands to make it play nice with the CAM subsystem.
1020258223a3SMatthew Dillon  */
1021258223a3SMatthew Dillon static
1022258223a3SMatthew Dillon void
ahci_xpt_scsi_disk_io(struct ahci_port * ap,struct ata_port * atx,union ccb * ccb)10231980eff3SMatthew Dillon ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
10241980eff3SMatthew Dillon 		      union ccb *ccb)
1025258223a3SMatthew Dillon {
1026258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1027258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1028258223a3SMatthew Dillon 	struct ata_xfer *xa;
10291980eff3SMatthew Dillon 	struct ata_port	*at;
1030258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1031bedbe7f8SAlex Hornung 	struct ata_pass_12 *atp12;
1032bedbe7f8SAlex Hornung 	struct ata_pass_16 *atp16;
1033258223a3SMatthew Dillon 	scsi_cdb_t cdb;
1034258223a3SMatthew Dillon 	union scsi_data *rdata;
1035258223a3SMatthew Dillon 	int rdata_len;
1036258223a3SMatthew Dillon 	u_int64_t capacity;
1037258223a3SMatthew Dillon 	u_int64_t lba;
1038258223a3SMatthew Dillon 	u_int32_t count;
1039258223a3SMatthew Dillon 
1040258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1041258223a3SMatthew Dillon 	csio = &ccb->csio;
1042b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
10431980eff3SMatthew Dillon 
10441980eff3SMatthew Dillon 	/*
10451980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
10461980eff3SMatthew Dillon 	 */
10471980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1048258223a3SMatthew Dillon 	rdata = (void *)csio->data_ptr;
1049258223a3SMatthew Dillon 	rdata_len = csio->dxfer_len;
1050258223a3SMatthew Dillon 
1051258223a3SMatthew Dillon 	/*
1052258223a3SMatthew Dillon 	 * Build the FIS or process the csio to completion.
1053258223a3SMatthew Dillon 	 */
1054258223a3SMatthew Dillon 	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1055258223a3SMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1056258223a3SMatthew Dillon 
1057258223a3SMatthew Dillon 	switch(cdb->generic.opcode) {
1058258223a3SMatthew Dillon 	case REQUEST_SENSE:
1059258223a3SMatthew Dillon 		/*
1060258223a3SMatthew Dillon 		 * Auto-sense everything, so explicit sense requests
1061258223a3SMatthew Dillon 		 * return no-sense.
1062258223a3SMatthew Dillon 		 */
1063258223a3SMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1064258223a3SMatthew Dillon 		break;
1065258223a3SMatthew Dillon 	case INQUIRY:
1066258223a3SMatthew Dillon 		/*
1067258223a3SMatthew Dillon 		 * Inquiry supported features
1068258223a3SMatthew Dillon 		 *
1069258223a3SMatthew Dillon 		 * [opcode, byte2, page_code, length, control]
1070258223a3SMatthew Dillon 		 */
1071258223a3SMatthew Dillon 		if (cdb->inquiry.byte2 & SI_EVPD) {
10726e0003adSMatthew Dillon 			ahci_xpt_page_inquiry(ap, at, ccb);
1073258223a3SMatthew Dillon 		} else {
1074258223a3SMatthew Dillon 			bzero(rdata, rdata_len);
1075258223a3SMatthew Dillon 			if (rdata_len < SHORT_INQUIRY_LENGTH) {
1076258223a3SMatthew Dillon 				ccbh->status = CAM_CCB_LEN_ERR;
1077258223a3SMatthew Dillon 				break;
1078258223a3SMatthew Dillon 			}
1079258223a3SMatthew Dillon 			if (rdata_len > sizeof(rdata->inquiry_data))
1080258223a3SMatthew Dillon 				rdata_len = sizeof(rdata->inquiry_data);
1081258223a3SMatthew Dillon 			rdata->inquiry_data.device = T_DIRECT;
1082258223a3SMatthew Dillon 			rdata->inquiry_data.version = SCSI_REV_SPC2;
1083258223a3SMatthew Dillon 			rdata->inquiry_data.response_format = 2;
1084258223a3SMatthew Dillon 			rdata->inquiry_data.additional_length = 32;
1085258223a3SMatthew Dillon 			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
10861980eff3SMatthew Dillon 			bcopy(at->at_identify.model,
1087258223a3SMatthew Dillon 			      rdata->inquiry_data.product,
1088258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.product));
10891980eff3SMatthew Dillon 			bcopy(at->at_identify.firmware,
1090258223a3SMatthew Dillon 			      rdata->inquiry_data.revision,
1091258223a3SMatthew Dillon 			      sizeof(rdata->inquiry_data.revision));
1092258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_CMP;
1093258223a3SMatthew Dillon 		}
1094e0fb398bSTim 
1095e0fb398bSTim 		/*
1096e0fb398bSTim 		 * Use the vendor specific area to set the TRIM status
1097e0fb398bSTim 		 * for scsi_da
1098e0fb398bSTim 		 */
1099e0fb398bSTim 		if (at->at_identify.support_dsm) {
1100e0fb398bSTim 			rdata->inquiry_data.vendor_specific1[0] =
1101e0fb398bSTim 			    at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM;
1102e0fb398bSTim 			rdata->inquiry_data.vendor_specific1[1] =
1103e0fb398bSTim 			    at->at_identify.max_dsm_blocks;
1104e0fb398bSTim 		}
1105258223a3SMatthew Dillon 		break;
1106258223a3SMatthew Dillon 	case READ_CAPACITY_16:
1107258223a3SMatthew Dillon 		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
1108258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1109258223a3SMatthew Dillon 			break;
1110258223a3SMatthew Dillon 		}
1111258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
1112258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1113258223a3SMatthew Dillon 			break;
1114258223a3SMatthew Dillon 		}
1115258223a3SMatthew Dillon 		/* fall through */
1116258223a3SMatthew Dillon 	case READ_CAPACITY:
1117258223a3SMatthew Dillon 		if (rdata_len < sizeof(rdata->read_capacity_data)) {
1118258223a3SMatthew Dillon 			ccbh->status = CAM_CCB_LEN_ERR;
1119258223a3SMatthew Dillon 			break;
1120258223a3SMatthew Dillon 		}
1121258223a3SMatthew Dillon 
11221980eff3SMatthew Dillon 		capacity = at->at_capacity;
1123258223a3SMatthew Dillon 
1124258223a3SMatthew Dillon 		bzero(rdata, rdata_len);
1125258223a3SMatthew Dillon 		if (cdb->generic.opcode == READ_CAPACITY) {
1126258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data);
112709e5fa07SSascha Wildner 			if (capacity > 0xFFFFFFFFU) {
112809e5fa07SSascha Wildner 				/*
112909e5fa07SSascha Wildner 				 * Set capacity to 0 so maxsector winds up
113009e5fa07SSascha Wildner 				 * being 0xffffffff in CAM in order to trigger
113109e5fa07SSascha Wildner 				 * DA_STATE_PROBE2.
113209e5fa07SSascha Wildner 				 */
113309e5fa07SSascha Wildner 				capacity = 0;
113409e5fa07SSascha Wildner 			}
1135258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data, rdata_len);
1136258223a3SMatthew Dillon 			scsi_ulto4b((u_int32_t)capacity - 1,
1137258223a3SMatthew Dillon 				    rdata->read_capacity_data.addr);
1138258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data.length);
1139258223a3SMatthew Dillon 		} else {
1140258223a3SMatthew Dillon 			rdata_len = sizeof(rdata->read_capacity_data_16);
1141258223a3SMatthew Dillon 			bzero(&rdata->read_capacity_data_16, rdata_len);
1142258223a3SMatthew Dillon 			scsi_u64to8b(capacity - 1,
1143258223a3SMatthew Dillon 				     rdata->read_capacity_data_16.addr);
1144258223a3SMatthew Dillon 			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
1145258223a3SMatthew Dillon 		}
1146258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1147258223a3SMatthew Dillon 		break;
1148258223a3SMatthew Dillon 	case SYNCHRONIZE_CACHE:
1149258223a3SMatthew Dillon 		/*
1150258223a3SMatthew Dillon 		 * Synchronize cache.  Specification says this can take
1151258223a3SMatthew Dillon 		 * greater then 30 seconds so give it at least 45.
1152258223a3SMatthew Dillon 		 */
1153258223a3SMatthew Dillon 		fis = xa->fis;
1154258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1155258223a3SMatthew Dillon 		fis->command = ATA_C_FLUSH_CACHE;
1156258223a3SMatthew Dillon 		fis->device = 0;
11573209f581SMatthew Dillon 		if (xa->timeout < 45000)
11583209f581SMatthew Dillon 			xa->timeout = 45000;
11591980eff3SMatthew Dillon 		xa->datalen = 0;
1160b012a2caSMatthew Dillon 		xa->flags = 0;
11611980eff3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_synchronize_cache;
1162258223a3SMatthew Dillon 		break;
1163e0fb398bSTim 	case TRIM:
1164e0fb398bSTim 		fis = xa->fis;
1165e0fb398bSTim 		fis->command = ATA_C_DATA_SET_MANAGEMENT;
1166e0fb398bSTim 		fis->features = (u_int8_t)ATA_SF_DSM_TRIM;
1167e0fb398bSTim 		fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8);
1168e0fb398bSTim 
1169e0fb398bSTim 		xa->flags = ATA_F_WRITE;
1170e0fb398bSTim 		fis->flags = ATA_H2D_FLAGS_CMD;
1171e0fb398bSTim 
1172e0fb398bSTim 		xa->data = csio->data_ptr;
1173e0fb398bSTim 		xa->datalen = csio->dxfer_len;
1174e0fb398bSTim 		xa->timeout = ccbh->timeout*50;	/* milliseconds */
1175e0fb398bSTim 
1176e0fb398bSTim 		fis->sector_count = (u_int8_t)(xa->datalen/512);
1177e0fb398bSTim 		fis->sector_count_exp = (u_int8_t)((xa->datalen/512)>>8);
1178e0fb398bSTim 
11790e589b85SMatthew Dillon 		/*
11800e589b85SMatthew Dillon 		 * lba field is reserved and must be 0.  LBAs are encoded
11810e589b85SMatthew Dillon 		 * in the range/length array passed as data.
11820e589b85SMatthew Dillon 		 */
1183e0fb398bSTim 		lba = 0;
1184e0fb398bSTim 		fis->lba_low = (u_int8_t)lba;
1185e0fb398bSTim 		fis->lba_mid = (u_int8_t)(lba >> 8);
1186e0fb398bSTim 		fis->lba_high = (u_int8_t)(lba >> 16);
1187e0fb398bSTim 		fis->lba_low_exp = (u_int8_t)(lba >> 24);
1188e0fb398bSTim 		fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1189e0fb398bSTim 		fis->lba_high_exp = (u_int8_t)(lba >> 40);
1190e0fb398bSTim 
1191e0fb398bSTim 		fis->device = ATA_H2D_DEVICE_LBA;
1192e0fb398bSTim 		xa->data = csio->data_ptr;
1193e0fb398bSTim 
1194e0fb398bSTim 		xa->complete = ahci_ata_complete_disk_rw;
1195e0fb398bSTim 		ccbh->status = CAM_REQ_INPROG;
1196e0fb398bSTim 		break;
1197258223a3SMatthew Dillon 	case TEST_UNIT_READY:
1198258223a3SMatthew Dillon 	case START_STOP_UNIT:
1199258223a3SMatthew Dillon 	case PREVENT_ALLOW:
1200258223a3SMatthew Dillon 		/*
1201258223a3SMatthew Dillon 		 * Just silently return success
1202258223a3SMatthew Dillon 		 */
1203258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1204258223a3SMatthew Dillon 		rdata_len = 0;
1205258223a3SMatthew Dillon 		break;
1206258223a3SMatthew Dillon 	case ATA_PASS_12:
1207bedbe7f8SAlex Hornung 		atp12 = &cdb->ata_pass_12;
1208bedbe7f8SAlex Hornung 		fis = xa->fis;
1209258223a3SMatthew Dillon 		/*
1210bedbe7f8SAlex Hornung 		 * Figure out the flags to be used, depending on the direction of the
1211bedbe7f8SAlex Hornung 		 * CAM request.
1212258223a3SMatthew Dillon 		 */
1213bedbe7f8SAlex Hornung 		switch (ccbh->flags & CAM_DIR_MASK) {
1214bedbe7f8SAlex Hornung 		case CAM_DIR_IN:
1215bedbe7f8SAlex Hornung 			xa->flags = ATA_F_READ;
1216bedbe7f8SAlex Hornung 			break;
1217bedbe7f8SAlex Hornung 		case CAM_DIR_OUT:
1218bedbe7f8SAlex Hornung 			xa->flags = ATA_F_WRITE;
1219bedbe7f8SAlex Hornung 			break;
1220bedbe7f8SAlex Hornung 		default:
1221bedbe7f8SAlex Hornung 			xa->flags = 0;
1222bedbe7f8SAlex Hornung 		}
122322726f69SMatthew Dillon 		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
1224bedbe7f8SAlex Hornung 		xa->data = csio->data_ptr;
1225bedbe7f8SAlex Hornung 		xa->datalen = csio->dxfer_len;
1226bedbe7f8SAlex Hornung 		xa->complete = ahci_ata_complete_disk_rw;
1227bedbe7f8SAlex Hornung 		xa->timeout = ccbh->timeout;
1228bedbe7f8SAlex Hornung 
1229bedbe7f8SAlex Hornung 		/*
1230bedbe7f8SAlex Hornung 		 * Populate the fis from the information we received through CAM
1231bedbe7f8SAlex Hornung 		 * ATA passthrough.
1232bedbe7f8SAlex Hornung 		 */
1233bedbe7f8SAlex Hornung 		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp12->flags ? */
1234bedbe7f8SAlex Hornung 		fis->features = atp12->features;
1235bedbe7f8SAlex Hornung 		fis->sector_count = atp12->sector_count;
1236bedbe7f8SAlex Hornung 		fis->lba_low = atp12->lba_low;
1237bedbe7f8SAlex Hornung 		fis->lba_mid = atp12->lba_mid;
1238bedbe7f8SAlex Hornung 		fis->lba_high = atp12->lba_high;
1239bedbe7f8SAlex Hornung 		fis->device = atp12->device;	/* maybe always 0? */
1240bedbe7f8SAlex Hornung 		fis->command = atp12->command;
1241bedbe7f8SAlex Hornung 		fis->control = atp12->control;
1242bedbe7f8SAlex Hornung 
1243bedbe7f8SAlex Hornung 		/*
1244bedbe7f8SAlex Hornung 		 * Mark as in progress so it is sent to the device.
1245bedbe7f8SAlex Hornung 		 */
1246bedbe7f8SAlex Hornung 		ccbh->status = CAM_REQ_INPROG;
1247bedbe7f8SAlex Hornung 		break;
1248bedbe7f8SAlex Hornung 	case ATA_PASS_16:
1249bedbe7f8SAlex Hornung 		atp16 = &cdb->ata_pass_16;
1250bedbe7f8SAlex Hornung 		fis = xa->fis;
1251bedbe7f8SAlex Hornung 		/*
1252bedbe7f8SAlex Hornung 		 * Figure out the flags to be used, depending on the direction of the
1253bedbe7f8SAlex Hornung 		 * CAM request.
1254bedbe7f8SAlex Hornung 		 */
1255bedbe7f8SAlex Hornung 		switch (ccbh->flags & CAM_DIR_MASK) {
1256bedbe7f8SAlex Hornung 		case CAM_DIR_IN:
1257bedbe7f8SAlex Hornung 			xa->flags = ATA_F_READ;
1258bedbe7f8SAlex Hornung 			break;
1259bedbe7f8SAlex Hornung 		case CAM_DIR_OUT:
1260bedbe7f8SAlex Hornung 			xa->flags = ATA_F_WRITE;
1261bedbe7f8SAlex Hornung 			break;
1262bedbe7f8SAlex Hornung 		default:
1263bedbe7f8SAlex Hornung 			xa->flags = 0;
1264bedbe7f8SAlex Hornung 		}
126522726f69SMatthew Dillon 		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
1266bedbe7f8SAlex Hornung 		xa->data = csio->data_ptr;
1267bedbe7f8SAlex Hornung 		xa->datalen = csio->dxfer_len;
1268bedbe7f8SAlex Hornung 		xa->complete = ahci_ata_complete_disk_rw;
1269bedbe7f8SAlex Hornung 		xa->timeout = ccbh->timeout;
1270bedbe7f8SAlex Hornung 
1271bedbe7f8SAlex Hornung 		/*
1272bedbe7f8SAlex Hornung 		 * Populate the fis from the information we received through CAM
1273bedbe7f8SAlex Hornung 		 * ATA passthrough.
1274bedbe7f8SAlex Hornung 		 */
1275bedbe7f8SAlex Hornung 		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp16->flags ? */
1276bedbe7f8SAlex Hornung 		fis->features = atp16->features;
1277bedbe7f8SAlex Hornung 		fis->features_exp = atp16->features_ext;
1278bedbe7f8SAlex Hornung 		fis->sector_count = atp16->sector_count;
1279bedbe7f8SAlex Hornung 		fis->sector_count_exp = atp16->sector_count_ext;
1280bedbe7f8SAlex Hornung 		fis->lba_low = atp16->lba_low;
1281bedbe7f8SAlex Hornung 		fis->lba_low_exp = atp16->lba_low_ext;
1282bedbe7f8SAlex Hornung 		fis->lba_mid = atp16->lba_mid;
1283bedbe7f8SAlex Hornung 		fis->lba_mid_exp = atp16->lba_mid_ext;
1284bedbe7f8SAlex Hornung 		fis->lba_high = atp16->lba_high;
1285bedbe7f8SAlex Hornung 		fis->lba_mid_exp = atp16->lba_mid_ext;
1286bedbe7f8SAlex Hornung 		fis->device = atp16->device;	/* maybe always 0? */
1287bedbe7f8SAlex Hornung 		fis->command = atp16->command;
1288bedbe7f8SAlex Hornung 
1289bedbe7f8SAlex Hornung 		/*
1290bedbe7f8SAlex Hornung 		 * Mark as in progress so it is sent to the device.
1291bedbe7f8SAlex Hornung 		 */
1292bedbe7f8SAlex Hornung 		ccbh->status = CAM_REQ_INPROG;
1293258223a3SMatthew Dillon 		break;
1294258223a3SMatthew Dillon 	default:
1295258223a3SMatthew Dillon 		switch(cdb->generic.opcode) {
1296258223a3SMatthew Dillon 		case READ_6:
1297258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1298258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1299258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1300258223a3SMatthew Dillon 			break;
1301258223a3SMatthew Dillon 		case READ_10:
1302258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1303258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1304258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1305258223a3SMatthew Dillon 			break;
1306258223a3SMatthew Dillon 		case READ_12:
1307258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1308258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1309258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1310258223a3SMatthew Dillon 			break;
1311258223a3SMatthew Dillon 		case READ_16:
1312258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1313258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1314258223a3SMatthew Dillon 			xa->flags = ATA_F_READ;
1315258223a3SMatthew Dillon 			break;
1316258223a3SMatthew Dillon 		case WRITE_6:
1317258223a3SMatthew Dillon 			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
1318258223a3SMatthew Dillon 			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
1319258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1320258223a3SMatthew Dillon 			break;
1321258223a3SMatthew Dillon 		case WRITE_10:
1322258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_10.addr);
1323258223a3SMatthew Dillon 			count = scsi_2btoul(cdb->rw_10.length);
1324258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1325258223a3SMatthew Dillon 			break;
1326258223a3SMatthew Dillon 		case WRITE_12:
1327258223a3SMatthew Dillon 			lba = scsi_4btoul(cdb->rw_12.addr);
1328258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_12.length);
1329258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1330258223a3SMatthew Dillon 			break;
1331258223a3SMatthew Dillon 		case WRITE_16:
1332258223a3SMatthew Dillon 			lba = scsi_8btou64(cdb->rw_16.addr);
1333258223a3SMatthew Dillon 			count = scsi_4btoul(cdb->rw_16.length);
1334258223a3SMatthew Dillon 			xa->flags = ATA_F_WRITE;
1335258223a3SMatthew Dillon 			break;
1336258223a3SMatthew Dillon 		default:
1337258223a3SMatthew Dillon 			ccbh->status = CAM_REQ_INVALID;
1338258223a3SMatthew Dillon 			break;
1339258223a3SMatthew Dillon 		}
1340258223a3SMatthew Dillon 		if (ccbh->status != CAM_REQ_INPROG)
1341258223a3SMatthew Dillon 			break;
1342258223a3SMatthew Dillon 
1343258223a3SMatthew Dillon 		fis = xa->fis;
1344258223a3SMatthew Dillon 		fis->flags = ATA_H2D_FLAGS_CMD;
1345258223a3SMatthew Dillon 		fis->lba_low = (u_int8_t)lba;
1346258223a3SMatthew Dillon 		fis->lba_mid = (u_int8_t)(lba >> 8);
1347258223a3SMatthew Dillon 		fis->lba_high = (u_int8_t)(lba >> 16);
1348258223a3SMatthew Dillon 		fis->device = ATA_H2D_DEVICE_LBA;
1349258223a3SMatthew Dillon 
13501980eff3SMatthew Dillon 		/*
13511980eff3SMatthew Dillon 		 * NCQ only for direct-attached disks, do not currently
13521980eff3SMatthew Dillon 		 * try to use NCQ with port multipliers.
13531980eff3SMatthew Dillon 		 */
13541980eff3SMatthew Dillon 		if (at->at_ncqdepth > 1 &&
13551980eff3SMatthew Dillon 		    ap->ap_type == ATA_PORT_T_DISK &&
1356258223a3SMatthew Dillon 		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
1357258223a3SMatthew Dillon 		    (ccbh->flags & CAM_POLLED) == 0) {
1358258223a3SMatthew Dillon 			/*
1359258223a3SMatthew Dillon 			 * Use NCQ - always uses 48 bit addressing
1360258223a3SMatthew Dillon 			 */
1361258223a3SMatthew Dillon 			xa->flags |= ATA_F_NCQ;
1362258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1363258223a3SMatthew Dillon 					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
1364258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1365258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1366258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1367258223a3SMatthew Dillon 			fis->sector_count = xa->tag << 3;
1368258223a3SMatthew Dillon 			fis->features = (u_int8_t)count;
1369258223a3SMatthew Dillon 			fis->features_exp = (u_int8_t)(count >> 8);
1370b2772aeeSMatthew Dillon 		} else if (count > 0x100 || lba > 0x0FFFFFFFU) {
1371258223a3SMatthew Dillon 			/*
1372258223a3SMatthew Dillon 			 * Use LBA48
1373258223a3SMatthew Dillon 			 */
1374258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1375258223a3SMatthew Dillon 					ATA_C_WRITEDMA_EXT : ATA_C_READDMA_EXT;
1376258223a3SMatthew Dillon 			fis->lba_low_exp = (u_int8_t)(lba >> 24);
1377258223a3SMatthew Dillon 			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
1378258223a3SMatthew Dillon 			fis->lba_high_exp = (u_int8_t)(lba >> 40);
1379258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1380258223a3SMatthew Dillon 			fis->sector_count_exp = (u_int8_t)(count >> 8);
1381258223a3SMatthew Dillon 		} else {
1382258223a3SMatthew Dillon 			/*
1383258223a3SMatthew Dillon 			 * Use LBA
1384258223a3SMatthew Dillon 			 *
1385258223a3SMatthew Dillon 			 * NOTE: 256 sectors is supported, stored as 0.
1386258223a3SMatthew Dillon 			 */
1387258223a3SMatthew Dillon 			fis->command = (xa->flags & ATA_F_WRITE) ?
1388258223a3SMatthew Dillon 					ATA_C_WRITEDMA : ATA_C_READDMA;
1389258223a3SMatthew Dillon 			fis->device |= (u_int8_t)(lba >> 24) & 0x0F;
1390258223a3SMatthew Dillon 			fis->sector_count = (u_int8_t)count;
1391258223a3SMatthew Dillon 		}
1392258223a3SMatthew Dillon 
13930e589b85SMatthew Dillon 		xa->lba = lba;
1394258223a3SMatthew Dillon 		xa->data = csio->data_ptr;
1395258223a3SMatthew Dillon 		xa->datalen = csio->dxfer_len;
1396258223a3SMatthew Dillon 		xa->complete = ahci_ata_complete_disk_rw;
13973209f581SMatthew Dillon 		xa->timeout = ccbh->timeout;	/* milliseconds */
139812feb904SMatthew Dillon #if 0
139912feb904SMatthew Dillon 		if (xa->timeout > 10000)	/* XXX - debug */
140012feb904SMatthew Dillon 			xa->timeout = 10000;
140112feb904SMatthew Dillon #endif
1402258223a3SMatthew Dillon 		if (ccbh->flags & CAM_POLLED)
1403258223a3SMatthew Dillon 			xa->flags |= ATA_F_POLL;
1404258223a3SMatthew Dillon 		break;
1405258223a3SMatthew Dillon 	}
1406258223a3SMatthew Dillon 
1407258223a3SMatthew Dillon 	/*
1408258223a3SMatthew Dillon 	 * If the request is still in progress the xa and FIS have
14096e0003adSMatthew Dillon 	 * been set up (except for the PM target), and must be dispatched.
14106e0003adSMatthew Dillon 	 * Otherwise the request was completed.
1411258223a3SMatthew Dillon 	 */
1412258223a3SMatthew Dillon 	if (ccbh->status == CAM_REQ_INPROG) {
1413258223a3SMatthew Dillon 		KKASSERT(xa->complete != NULL);
1414258223a3SMatthew Dillon 		xa->atascsi_private = ccb;
1415258223a3SMatthew Dillon 		ccb->ccb_h.sim_priv.entries[0].ptr = ap;
1416f4553de1SMatthew Dillon 		ahci_os_lock_port(ap);
14176e0003adSMatthew Dillon 		xa->fis->flags |= at->at_target;
1418258223a3SMatthew Dillon 		ahci_ata_cmd(xa);
1419f4553de1SMatthew Dillon 		ahci_os_unlock_port(ap);
1420258223a3SMatthew Dillon 	} else {
1421258223a3SMatthew Dillon 		ahci_ata_put_xfer(xa);
1422258223a3SMatthew Dillon 		xpt_done(ccb);
1423258223a3SMatthew Dillon 	}
1424258223a3SMatthew Dillon }
1425258223a3SMatthew Dillon 
1426b4189e5eSMatthew Dillon /*
1427b4189e5eSMatthew Dillon  * Convert the SCSI command in ccb to an ata_xfer command in xa
1428b4189e5eSMatthew Dillon  * for ATA_PORT_T_ATAPI operations.  Set the completion function
1429b4189e5eSMatthew Dillon  * to convert the response back, then dispatch to the OpenBSD AHCI
1430b4189e5eSMatthew Dillon  * layer.
1431b4189e5eSMatthew Dillon  */
1432258223a3SMatthew Dillon static
1433258223a3SMatthew Dillon void
ahci_xpt_scsi_atapi_io(struct ahci_port * ap,struct ata_port * atx,union ccb * ccb)14341980eff3SMatthew Dillon ahci_xpt_scsi_atapi_io(struct ahci_port *ap, struct ata_port *atx,
14351980eff3SMatthew Dillon 			union ccb *ccb)
1436258223a3SMatthew Dillon {
1437258223a3SMatthew Dillon 	struct ccb_hdr *ccbh;
1438258223a3SMatthew Dillon 	struct ccb_scsiio *csio;
1439258223a3SMatthew Dillon 	struct ata_xfer *xa;
1440258223a3SMatthew Dillon 	struct ata_fis_h2d *fis;
1441b4189e5eSMatthew Dillon 	scsi_cdb_t cdbs;
1442b4189e5eSMatthew Dillon 	scsi_cdb_t cdbd;
1443b4189e5eSMatthew Dillon 	int flags;
14441980eff3SMatthew Dillon 	struct ata_port	*at;
1445258223a3SMatthew Dillon 
1446258223a3SMatthew Dillon 	ccbh = &ccb->csio.ccb_h;
1447258223a3SMatthew Dillon 	csio = &ccb->csio;
1448b012a2caSMatthew Dillon 	at = atx ? atx : ap->ap_ata[0];
1449b4189e5eSMatthew Dillon 
1450b4189e5eSMatthew Dillon 	switch (ccbh->flags & CAM_DIR_MASK) {
1451b4189e5eSMatthew Dillon 	case CAM_DIR_IN:
1452b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_READ;
1453b4189e5eSMatthew Dillon 		break;
1454b4189e5eSMatthew Dillon 	case CAM_DIR_OUT:
1455b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET | ATA_F_WRITE;
1456b4189e5eSMatthew Dillon 		break;
1457b4189e5eSMatthew Dillon 	case CAM_DIR_NONE:
1458b4189e5eSMatthew Dillon 		flags = ATA_F_PACKET;
1459b4189e5eSMatthew Dillon 		break;
1460b4189e5eSMatthew Dillon 	default:
1461b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_INVALID;
1462b4189e5eSMatthew Dillon 		xpt_done(ccb);
1463b4189e5eSMatthew Dillon 		return;
1464b4189e5eSMatthew Dillon 		/* NOT REACHED */
1465b4189e5eSMatthew Dillon 	}
1466b4189e5eSMatthew Dillon 
1467b4189e5eSMatthew Dillon 	/*
146812feb904SMatthew Dillon 	 * Special handling to get the rfis back into host memory while
1469192ee1d0SMatthew Dillon 	 * still allowing the chip to run commands in parallel to
147012feb904SMatthew Dillon 	 * ATAPI devices behind a PM.
147112feb904SMatthew Dillon 	 */
147212feb904SMatthew Dillon 	flags |= ATA_F_AUTOSENSE;
147312feb904SMatthew Dillon 
147412feb904SMatthew Dillon 	/*
1475b4189e5eSMatthew Dillon 	 * The command has to fit in the packet command buffer.
1476b4189e5eSMatthew Dillon 	 */
1477b4189e5eSMatthew Dillon 	if (csio->cdb_len < 6 || csio->cdb_len > 16) {
1478b4189e5eSMatthew Dillon 		ccbh->status = CAM_CCB_LEN_ERR;
1479b4189e5eSMatthew Dillon 		xpt_done(ccb);
1480b4189e5eSMatthew Dillon 		return;
1481b4189e5eSMatthew Dillon 	}
1482b4189e5eSMatthew Dillon 
1483b4189e5eSMatthew Dillon 	/*
1484192ee1d0SMatthew Dillon 	 * Initialize the XA and FIS.  It is unclear how much of
1485192ee1d0SMatthew Dillon 	 * this has to mimic the equivalent ATA command.
14861980eff3SMatthew Dillon 	 *
14871980eff3SMatthew Dillon 	 * XXX not passing NULL at for direct attach!
1488b4189e5eSMatthew Dillon 	 */
14891980eff3SMatthew Dillon 	xa = ahci_ata_get_xfer(ap, atx);
1490258223a3SMatthew Dillon 	fis = xa->fis;
1491258223a3SMatthew Dillon 
14921980eff3SMatthew Dillon 	fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
14931980eff3SMatthew Dillon 	fis->command = ATA_C_PACKET;
1494192ee1d0SMatthew Dillon 	fis->device = ATA_H2D_DEVICE_LBA;
14951980eff3SMatthew Dillon 	fis->sector_count = xa->tag << 3;
1496192ee1d0SMatthew Dillon 	if (flags & (ATA_F_READ | ATA_F_WRITE)) {
1497192ee1d0SMatthew Dillon 		if (flags & ATA_F_WRITE) {
14981980eff3SMatthew Dillon 			fis->features = ATA_H2D_FEATURES_DMA |
1499192ee1d0SMatthew Dillon 				       ATA_H2D_FEATURES_DIR_WRITE;
1500192ee1d0SMatthew Dillon 		} else {
1501192ee1d0SMatthew Dillon 			fis->features = ATA_H2D_FEATURES_DMA |
1502192ee1d0SMatthew Dillon 				       ATA_H2D_FEATURES_DIR_READ;
1503192ee1d0SMatthew Dillon 		}
1504192ee1d0SMatthew Dillon 	} else {
1505192ee1d0SMatthew Dillon 		fis->lba_mid = 0;
1506192ee1d0SMatthew Dillon 		fis->lba_high = 0;
1507192ee1d0SMatthew Dillon 	}
1508192ee1d0SMatthew Dillon 	fis->control = ATA_FIS_CONTROL_4BIT;
15091980eff3SMatthew Dillon 
1510b4189e5eSMatthew Dillon 	xa->flags = flags;
1511b4189e5eSMatthew Dillon 	xa->data = csio->data_ptr;
1512b4189e5eSMatthew Dillon 	xa->datalen = csio->dxfer_len;
15133209f581SMatthew Dillon 	xa->timeout = ccbh->timeout;	/* milliseconds */
15141980eff3SMatthew Dillon 
1515b4189e5eSMatthew Dillon 	if (ccbh->flags & CAM_POLLED)
1516b4189e5eSMatthew Dillon 		xa->flags |= ATA_F_POLL;
1517258223a3SMatthew Dillon 
1518258223a3SMatthew Dillon 	/*
1519b4189e5eSMatthew Dillon 	 * Copy the cdb to the packetcmd buffer in the FIS using a
1520b4189e5eSMatthew Dillon 	 * convenient pointer in the xa.
1521a7d90c87SMatthew Dillon 	 *
1522a7d90c87SMatthew Dillon 	 * Zero-out any trailing bytes in case the ATAPI device cares.
1523258223a3SMatthew Dillon 	 */
1524b4189e5eSMatthew Dillon 	cdbs = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
1525b4189e5eSMatthew Dillon 			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);
1526b4189e5eSMatthew Dillon 	bcopy(cdbs, xa->packetcmd, csio->cdb_len);
1527a7d90c87SMatthew Dillon 	if (csio->cdb_len < 16)
1528a7d90c87SMatthew Dillon 		bzero(xa->packetcmd + csio->cdb_len, 16 - csio->cdb_len);
1529b4189e5eSMatthew Dillon 
1530669fbbf7SMatthew Dillon #if 0
1531b4189e5eSMatthew Dillon 	kprintf("opcode %d cdb_len %d dxfer_len %d\n",
1532b4189e5eSMatthew Dillon 		cdbs->generic.opcode,
1533b4189e5eSMatthew Dillon 		csio->cdb_len, csio->dxfer_len);
1534669fbbf7SMatthew Dillon #endif
1535b4189e5eSMatthew Dillon 
1536b4189e5eSMatthew Dillon 	/*
1537b4189e5eSMatthew Dillon 	 * Some ATAPI commands do not actually follow the SCSI standard.
1538b4189e5eSMatthew Dillon 	 */
1539b4189e5eSMatthew Dillon 	cdbd = (void *)xa->packetcmd;
1540b4189e5eSMatthew Dillon 
1541b4189e5eSMatthew Dillon 	switch(cdbd->generic.opcode) {
1542192ee1d0SMatthew Dillon 	case REQUEST_SENSE:
1543192ee1d0SMatthew Dillon 		/*
1544192ee1d0SMatthew Dillon 		 * Force SENSE requests to the ATAPI sense length.
1545192ee1d0SMatthew Dillon 		 *
1546192ee1d0SMatthew Dillon 		 * It is unclear if this is needed or not.
1547192ee1d0SMatthew Dillon 		 */
1548192ee1d0SMatthew Dillon 		if (cdbd->sense.length == SSD_FULL_SIZE) {
1549b012a2caSMatthew Dillon 			if (bootverbose) {
1550192ee1d0SMatthew Dillon 				kprintf("%s: Shortening sense request\n",
1551192ee1d0SMatthew Dillon 					PORTNAME(ap));
1552b012a2caSMatthew Dillon 			}
1553192ee1d0SMatthew Dillon 			cdbd->sense.length = offsetof(struct scsi_sense_data,
1554192ee1d0SMatthew Dillon 						      extra_bytes[0]);
1555192ee1d0SMatthew Dillon 		}
1556192ee1d0SMatthew Dillon 		break;
1557258223a3SMatthew Dillon 	case INQUIRY:
1558b4189e5eSMatthew Dillon 		/*
1559b4189e5eSMatthew Dillon 		 * Some ATAPI devices can't handle long inquiry lengths,
1560b4189e5eSMatthew Dillon 		 * don't ask me why.  Truncate the inquiry length.
1561b4189e5eSMatthew Dillon 		 */
1562b4189e5eSMatthew Dillon 		if (cdbd->inquiry.page_code == 0 &&
1563b4189e5eSMatthew Dillon 		    cdbd->inquiry.length > SHORT_INQUIRY_LENGTH) {
1564b4189e5eSMatthew Dillon 			cdbd->inquiry.length = SHORT_INQUIRY_LENGTH;
1565b4189e5eSMatthew Dillon 		}
1566b4189e5eSMatthew Dillon 		break;
1567258223a3SMatthew Dillon 	case READ_6:
1568258223a3SMatthew Dillon 	case WRITE_6:
1569b4189e5eSMatthew Dillon 		/*
1570b4189e5eSMatthew Dillon 		 * Convert *_6 to *_10 commands.  Most ATAPI devices
1571b4189e5eSMatthew Dillon 		 * cannot handle the SCSI READ_6 and WRITE_6 commands.
1572b4189e5eSMatthew Dillon 		 */
1573b4189e5eSMatthew Dillon 		cdbd->rw_10.opcode |= 0x20;
1574b4189e5eSMatthew Dillon 		cdbd->rw_10.byte2 = 0;
1575b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[0] = cdbs->rw_6.addr[0] & 0x1F;
1576b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[1] = cdbs->rw_6.addr[1];
1577b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[2] = cdbs->rw_6.addr[2];
1578b4189e5eSMatthew Dillon 		cdbd->rw_10.addr[3] = 0;
1579b4189e5eSMatthew Dillon 		cdbd->rw_10.reserved = 0;
1580b4189e5eSMatthew Dillon 		cdbd->rw_10.length[0] = 0;
1581b4189e5eSMatthew Dillon 		cdbd->rw_10.length[1] = cdbs->rw_6.length;
1582b4189e5eSMatthew Dillon 		cdbd->rw_10.control = cdbs->rw_6.control;
1583b4189e5eSMatthew Dillon 		break;
1584258223a3SMatthew Dillon 	default:
1585258223a3SMatthew Dillon 		break;
1586258223a3SMatthew Dillon 	}
1587258223a3SMatthew Dillon 
1588b4189e5eSMatthew Dillon 	/*
1589b4189e5eSMatthew Dillon 	 * And dispatch
1590b4189e5eSMatthew Dillon 	 */
1591b4189e5eSMatthew Dillon 	xa->complete = ahci_atapi_complete_cmd;
1592258223a3SMatthew Dillon 	xa->atascsi_private = ccb;
1593258223a3SMatthew Dillon 	ccb->ccb_h.sim_priv.entries[0].ptr = ap;
159476497a9cSMatthew Dillon 	ahci_os_lock_port(ap);
1595258223a3SMatthew Dillon 	ahci_ata_cmd(xa);
159676497a9cSMatthew Dillon 	ahci_os_unlock_port(ap);
1597258223a3SMatthew Dillon }
1598258223a3SMatthew Dillon 
1599b4189e5eSMatthew Dillon /*
16006e0003adSMatthew Dillon  * Simulate page inquiries for disk attachments.
16016e0003adSMatthew Dillon  */
16026e0003adSMatthew Dillon static
16036e0003adSMatthew Dillon void
ahci_xpt_page_inquiry(struct ahci_port * ap,struct ata_port * at,union ccb * ccb)16046e0003adSMatthew Dillon ahci_xpt_page_inquiry(struct ahci_port *ap, struct ata_port *at, union ccb *ccb)
16056e0003adSMatthew Dillon {
16066e0003adSMatthew Dillon 	union {
16076e0003adSMatthew Dillon 		struct scsi_vpd_supported_page_list	list;
16086e0003adSMatthew Dillon 		struct scsi_vpd_unit_serial_number	serno;
16096e0003adSMatthew Dillon 		struct scsi_vpd_unit_devid		devid;
16106e0003adSMatthew Dillon 		char					buf[256];
16116e0003adSMatthew Dillon 	} *page;
16126e0003adSMatthew Dillon 	scsi_cdb_t cdb;
16136e0003adSMatthew Dillon 	int i;
16146e0003adSMatthew Dillon 	int j;
16156e0003adSMatthew Dillon 	int len;
16166e0003adSMatthew Dillon 
16176e0003adSMatthew Dillon 	page = kmalloc(sizeof(*page), M_DEVBUF, M_WAITOK | M_ZERO);
16186e0003adSMatthew Dillon 
16196e0003adSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
16206e0003adSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
16216e0003adSMatthew Dillon 
16226e0003adSMatthew Dillon 	switch(cdb->inquiry.page_code) {
16236e0003adSMatthew Dillon 	case SVPD_SUPPORTED_PAGE_LIST:
16246e0003adSMatthew Dillon 		i = 0;
16256e0003adSMatthew Dillon 		page->list.device = T_DIRECT;
16266e0003adSMatthew Dillon 		page->list.page_code = SVPD_SUPPORTED_PAGE_LIST;
16276e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_SUPPORTED_PAGE_LIST;
16286e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_SERIAL_NUMBER;
16296e0003adSMatthew Dillon 		page->list.list[i++] = SVPD_UNIT_DEVID;
16306e0003adSMatthew Dillon 		page->list.length = i;
16316e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_supported_page_list, list[3]);
16326e0003adSMatthew Dillon 		break;
16336e0003adSMatthew Dillon 	case SVPD_UNIT_SERIAL_NUMBER:
16346e0003adSMatthew Dillon 		i = 0;
16356e0003adSMatthew Dillon 		j = sizeof(at->at_identify.serial);
16366e0003adSMatthew Dillon 		for (i = 0; i < j && at->at_identify.serial[i] == ' '; ++i)
16376e0003adSMatthew Dillon 			;
16386e0003adSMatthew Dillon 		while (j > i && at->at_identify.serial[j-1] == ' ')
16396e0003adSMatthew Dillon 			--j;
16406e0003adSMatthew Dillon 		page->serno.device = T_DIRECT;
16416e0003adSMatthew Dillon 		page->serno.page_code = SVPD_UNIT_SERIAL_NUMBER;
16426e0003adSMatthew Dillon 		page->serno.length = j - i;
16436e0003adSMatthew Dillon 		bcopy(at->at_identify.serial + i,
16446e0003adSMatthew Dillon 		      page->serno.serial_num, j - i);
16456e0003adSMatthew Dillon 		len = offsetof(struct scsi_vpd_unit_serial_number,
16466e0003adSMatthew Dillon 			       serial_num[j-i]);
16476e0003adSMatthew Dillon 		break;
16486e0003adSMatthew Dillon 	case SVPD_UNIT_DEVID:
16496e0003adSMatthew Dillon 		/* fall through for now */
16506e0003adSMatthew Dillon 	default:
16516e0003adSMatthew Dillon 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
16526e0003adSMatthew Dillon 		len = 0;
16536e0003adSMatthew Dillon 		break;
16546e0003adSMatthew Dillon 	}
16556e0003adSMatthew Dillon 	if (ccb->ccb_h.status == CAM_REQ_INPROG) {
16566e0003adSMatthew Dillon 		if (len <= ccb->csio.dxfer_len) {
16576e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_REQ_CMP;
16586e0003adSMatthew Dillon 			bzero(ccb->csio.data_ptr, ccb->csio.dxfer_len);
16596e0003adSMatthew Dillon 			bcopy(page, ccb->csio.data_ptr, len);
16606e0003adSMatthew Dillon 			ccb->csio.resid = ccb->csio.dxfer_len - len;
16616e0003adSMatthew Dillon 		} else {
16626e0003adSMatthew Dillon 			ccb->ccb_h.status = CAM_CCB_LEN_ERR;
16636e0003adSMatthew Dillon 		}
16646e0003adSMatthew Dillon 	}
16656e0003adSMatthew Dillon 	kfree(page, M_DEVBUF);
16666e0003adSMatthew Dillon }
16676e0003adSMatthew Dillon 
16686e0003adSMatthew Dillon /*
1669b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK cache synchronization.
1670b4189e5eSMatthew Dillon  */
1671258223a3SMatthew Dillon static
1672258223a3SMatthew Dillon void
ahci_ata_complete_disk_synchronize_cache(struct ata_xfer * xa)1673258223a3SMatthew Dillon ahci_ata_complete_disk_synchronize_cache(struct ata_xfer *xa)
1674258223a3SMatthew Dillon {
1675258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1676258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1677258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1678258223a3SMatthew Dillon 
1679258223a3SMatthew Dillon 	switch(xa->state) {
1680258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1681258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1682b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1683258223a3SMatthew Dillon 		break;
1684258223a3SMatthew Dillon 	case ATA_S_ERROR:
16851980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: error\n",
16861980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1687b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1688b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1689b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1690258223a3SMatthew Dillon 		break;
1691258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
16921980eff3SMatthew Dillon 		kprintf("%s: synchronize_cache: timeout\n",
16931980eff3SMatthew Dillon 			ATANAME(ap, xa->at));
1694258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
1695258223a3SMatthew Dillon 		break;
1696258223a3SMatthew Dillon 	default:
1697258223a3SMatthew Dillon 		kprintf("%s: synchronize_cache: unknown state %d\n",
16981980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
169946528d33SMatthew Dillon 		panic("%s: Unknown state", ATANAME(ap, xa->at));
1700258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1701258223a3SMatthew Dillon 		break;
1702258223a3SMatthew Dillon 	}
1703258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1704d16d3400SMatthew Dillon 	/*ahci_os_unlock_port(ap); ILLEGAL SEE NOTE-1 AT TOP */
1705258223a3SMatthew Dillon 	xpt_done(ccb);
1706d16d3400SMatthew Dillon 	/*ahci_os_lock_port(ap);*/
1707258223a3SMatthew Dillon }
1708258223a3SMatthew Dillon 
1709b4189e5eSMatthew Dillon /*
1710b4189e5eSMatthew Dillon  * Completion function for ATA_PORT_T_DISK I/O
1711b4189e5eSMatthew Dillon  */
1712258223a3SMatthew Dillon static
1713258223a3SMatthew Dillon void
ahci_ata_complete_disk_rw(struct ata_xfer * xa)1714258223a3SMatthew Dillon ahci_ata_complete_disk_rw(struct ata_xfer *xa)
1715258223a3SMatthew Dillon {
1716258223a3SMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1717258223a3SMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1718258223a3SMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
17190e589b85SMatthew Dillon 	struct ata_fis_h2d *fis;
1720258223a3SMatthew Dillon 
1721258223a3SMatthew Dillon 	switch(xa->state) {
1722258223a3SMatthew Dillon 	case ATA_S_COMPLETE:
1723258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1724b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1725258223a3SMatthew Dillon 		break;
1726258223a3SMatthew Dillon 	case ATA_S_ERROR:
17270e589b85SMatthew Dillon 		fis = xa->fis;
17280e589b85SMatthew Dillon 		kprintf("%s: disk_rw: error fiscmd=0x%02x @off=0x%016jx, %zu\n",
17290e589b85SMatthew Dillon 			ATANAME(ap, xa->at),
17300e589b85SMatthew Dillon 			fis->command,
17310e589b85SMatthew Dillon 			(intmax_t)xa->lba * 512,
17320e589b85SMatthew Dillon 			xa->datalen);
1733b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
1734b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
1735b4189e5eSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1736258223a3SMatthew Dillon 		break;
1737258223a3SMatthew Dillon 	case ATA_S_TIMEOUT:
17381980eff3SMatthew Dillon 		kprintf("%s: disk_rw: timeout\n", ATANAME(ap, xa->at));
1739258223a3SMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
17404c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17414c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1742258223a3SMatthew Dillon 		break;
1743258223a3SMatthew Dillon 	default:
1744258223a3SMatthew Dillon 		kprintf("%s: disk_rw: unknown state %d\n",
17451980eff3SMatthew Dillon 			ATANAME(ap, xa->at), xa->state);
174646528d33SMatthew Dillon 		panic("%s: Unknown state", ATANAME(ap, xa->at));
1747258223a3SMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1748258223a3SMatthew Dillon 		break;
1749258223a3SMatthew Dillon 	}
1750258223a3SMatthew Dillon 	ccb->csio.resid = xa->resid;
1751258223a3SMatthew Dillon 	ahci_ata_put_xfer(xa);
1752d16d3400SMatthew Dillon 	/*ahci_os_unlock_port(ap); ILLEGAL SEE NOTE-1 AT TOP */
1753258223a3SMatthew Dillon 	xpt_done(ccb);
1754d16d3400SMatthew Dillon 	/*ahci_os_lock_port(ap);*/
1755258223a3SMatthew Dillon }
1756b4189e5eSMatthew Dillon 
17577d4fcf34SMatthew Dillon /*
17587d4fcf34SMatthew Dillon  * Completion function for ATA_PORT_T_ATAPI I/O
17597d4fcf34SMatthew Dillon  *
17607d4fcf34SMatthew Dillon  * Sense data is returned in the rfis.
17617d4fcf34SMatthew Dillon  */
1762b4189e5eSMatthew Dillon static
1763b4189e5eSMatthew Dillon void
ahci_atapi_complete_cmd(struct ata_xfer * xa)1764b4189e5eSMatthew Dillon ahci_atapi_complete_cmd(struct ata_xfer *xa)
1765b4189e5eSMatthew Dillon {
1766b4189e5eSMatthew Dillon 	union ccb *ccb = xa->atascsi_private;
1767b4189e5eSMatthew Dillon 	struct ccb_hdr *ccbh = &ccb->ccb_h;
1768b4189e5eSMatthew Dillon 	struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
1769b4189e5eSMatthew Dillon 	scsi_cdb_t cdb;
1770b4189e5eSMatthew Dillon 
1771b4189e5eSMatthew Dillon 	cdb = (void *)((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
1772b4189e5eSMatthew Dillon 			ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes);
1773b4189e5eSMatthew Dillon 
1774b4189e5eSMatthew Dillon 	switch(xa->state) {
1775b4189e5eSMatthew Dillon 	case ATA_S_COMPLETE:
1776b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP;
1777b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_OK;
1778b4189e5eSMatthew Dillon 		break;
1779b4189e5eSMatthew Dillon 	case ATA_S_ERROR:
1780b4189e5eSMatthew Dillon 		ccbh->status = CAM_SCSI_STATUS_ERROR;
1781b4189e5eSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17827d4fcf34SMatthew Dillon 		ahci_ata_atapi_sense(&xa->rfis, &ccb->csio.sense_data);
1783b4189e5eSMatthew Dillon 		break;
1784b4189e5eSMatthew Dillon 	case ATA_S_TIMEOUT:
1785b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: timeout\n",
1786b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode);
1787b4189e5eSMatthew Dillon 		ccbh->status = CAM_CMD_TIMEOUT;
17884c339a5fSMatthew Dillon 		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
17894c339a5fSMatthew Dillon 		ahci_ata_dummy_sense(&ccb->csio.sense_data);
1790b4189e5eSMatthew Dillon 		break;
1791b4189e5eSMatthew Dillon 	default:
1792b4189e5eSMatthew Dillon 		kprintf("%s: cmd %d: unknown state %d\n",
1793b4189e5eSMatthew Dillon 			PORTNAME(ap), cdb->generic.opcode, xa->state);
179446528d33SMatthew Dillon 		panic("%s: Unknown state", PORTNAME(ap));
1795b4189e5eSMatthew Dillon 		ccbh->status = CAM_REQ_CMP_ERR;
1796b4189e5eSMatthew Dillon 		break;
1797b4189e5eSMatthew Dillon 	}
1798b4189e5eSMatthew Dillon 	ccb->csio.resid = xa->resid;
1799d16d3400SMatthew Dillon 	xa->atascsi_private = NULL;
1800b4189e5eSMatthew Dillon 	ahci_ata_put_xfer(xa);
1801d16d3400SMatthew Dillon 	/*ahci_os_unlock_port(ap); ILLEGAL SEE NOTE-1 AT TOP */
1802b4189e5eSMatthew Dillon 	xpt_done(ccb);
1803d16d3400SMatthew Dillon 	/*ahci_os_lock_port(ap);*/
1804b4189e5eSMatthew Dillon }
1805b4189e5eSMatthew Dillon 
18067d4fcf34SMatthew Dillon /*
18077d4fcf34SMatthew Dillon  * Construct dummy sense data for errors on DISKs
18087d4fcf34SMatthew Dillon  */
1809b4189e5eSMatthew Dillon static
1810b4189e5eSMatthew Dillon void
ahci_ata_dummy_sense(struct scsi_sense_data * sense_data)1811b4189e5eSMatthew Dillon ahci_ata_dummy_sense(struct scsi_sense_data *sense_data)
1812b4189e5eSMatthew Dillon {
1813b4189e5eSMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
1814b4189e5eSMatthew Dillon 	sense_data->segment = 0;
1815b4189e5eSMatthew Dillon 	sense_data->flags = SSD_KEY_MEDIUM_ERROR;
1816b4189e5eSMatthew Dillon 	sense_data->info[0] = 0;
1817b4189e5eSMatthew Dillon 	sense_data->info[1] = 0;
1818b4189e5eSMatthew Dillon 	sense_data->info[2] = 0;
1819b4189e5eSMatthew Dillon 	sense_data->info[3] = 0;
1820b4189e5eSMatthew Dillon 	sense_data->extra_len = 0;
1821b4189e5eSMatthew Dillon }
18227d4fcf34SMatthew Dillon 
18237d4fcf34SMatthew Dillon /*
18247d4fcf34SMatthew Dillon  * Construct atapi sense data for errors on ATAPI
18257d4fcf34SMatthew Dillon  *
18267d4fcf34SMatthew Dillon  * The ATAPI sense data is stored in the passed rfis and must be converted
18277d4fcf34SMatthew Dillon  * to SCSI sense data.
18287d4fcf34SMatthew Dillon  */
18297d4fcf34SMatthew Dillon static
18307d4fcf34SMatthew Dillon void
ahci_ata_atapi_sense(struct ata_fis_d2h * rfis,struct scsi_sense_data * sense_data)18317d4fcf34SMatthew Dillon ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
18327d4fcf34SMatthew Dillon 		     struct scsi_sense_data *sense_data)
18337d4fcf34SMatthew Dillon {
18347d4fcf34SMatthew Dillon 	sense_data->error_code = SSD_ERRCODE_VALID | SSD_CURRENT_ERROR;
18357d4fcf34SMatthew Dillon 	sense_data->segment = 0;
18367d4fcf34SMatthew Dillon 	sense_data->flags = (rfis->error & 0xF0) >> 4;
18377d4fcf34SMatthew Dillon 	if (rfis->error & 0x04)
18387d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_KEY_ILLEGAL_REQUEST;
18397d4fcf34SMatthew Dillon 	if (rfis->error & 0x02)
18407d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_EOM;
18417d4fcf34SMatthew Dillon 	if (rfis->error & 0x01)
18427d4fcf34SMatthew Dillon 		sense_data->flags |= SSD_ILI;
18437d4fcf34SMatthew Dillon 	sense_data->info[0] = 0;
18447d4fcf34SMatthew Dillon 	sense_data->info[1] = 0;
18457d4fcf34SMatthew Dillon 	sense_data->info[2] = 0;
18467d4fcf34SMatthew Dillon 	sense_data->info[3] = 0;
18477d4fcf34SMatthew Dillon 	sense_data->extra_len = 0;
18487d4fcf34SMatthew Dillon }
18496e0003adSMatthew Dillon 
18506e0003adSMatthew Dillon static
18516e0003adSMatthew Dillon void
ahci_strip_string(const char ** basep,int * lenp)18526e0003adSMatthew Dillon ahci_strip_string(const char **basep, int *lenp)
18536e0003adSMatthew Dillon {
18546e0003adSMatthew Dillon 	const char *base = *basep;
18556e0003adSMatthew Dillon 	int len = *lenp;
18566e0003adSMatthew Dillon 
18576e0003adSMatthew Dillon 	while (len && (*base == 0 || *base == ' ')) {
18586e0003adSMatthew Dillon 		--len;
18596e0003adSMatthew Dillon 		++base;
18606e0003adSMatthew Dillon 	}
18616e0003adSMatthew Dillon 	while (len && (base[len-1] == 0 || base[len-1] == ' '))
18626e0003adSMatthew Dillon 		--len;
18636e0003adSMatthew Dillon 	*basep = base;
18646e0003adSMatthew Dillon 	*lenp = len;
18656e0003adSMatthew Dillon }
1866