ata_pmp.c revision 236765
1/*-
2 * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/9/sys/cam/ata/ata_pmp.c 236765 2012-06-08 20:30:37Z mav $");
29
30#include <sys/param.h>
31
32#ifdef _KERNEL
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/bio.h>
36#include <sys/sysctl.h>
37#include <sys/taskqueue.h>
38#include <sys/lock.h>
39#include <sys/mutex.h>
40#include <sys/conf.h>
41#include <sys/devicestat.h>
42#include <sys/eventhandler.h>
43#include <sys/malloc.h>
44#include <sys/cons.h>
45#include <geom/geom_disk.h>
46#endif /* _KERNEL */
47
48#ifndef _KERNEL
49#include <stdio.h>
50#include <string.h>
51#endif /* _KERNEL */
52
53#include <cam/cam.h>
54#include <cam/cam_ccb.h>
55#include <cam/cam_periph.h>
56#include <cam/cam_xpt_periph.h>
57#include <cam/cam_xpt_internal.h>
58#include <cam/cam_sim.h>
59
60#include <cam/ata/ata_all.h>
61
62#ifdef _KERNEL
63
64typedef enum {
65	PMP_STATE_NORMAL,
66	PMP_STATE_PORTS,
67	PMP_STATE_PRECONFIG,
68	PMP_STATE_RESET,
69	PMP_STATE_CONNECT,
70	PMP_STATE_CHECK,
71	PMP_STATE_CLEAR,
72	PMP_STATE_CONFIG,
73	PMP_STATE_SCAN
74} pmp_state;
75
76typedef enum {
77	PMP_FLAG_SCTX_INIT	= 0x200
78} pmp_flags;
79
80typedef enum {
81	PMP_CCB_PROBE		= 0x01,
82} pmp_ccb_state;
83
84/* Offsets into our private area for storing information */
85#define ccb_state	ppriv_field0
86#define ccb_bp		ppriv_ptr1
87
88struct pmp_softc {
89	SLIST_ENTRY(pmp_softc)	links;
90	pmp_state		state;
91	pmp_flags		flags;
92	uint32_t		pm_pid;
93	uint32_t		pm_prv;
94	int			pm_ports;
95	int			pm_step;
96	int			pm_try;
97	int			found;
98	int			reset;
99	int			frozen;
100	int			restart;
101	int			events;
102#define PMP_EV_RESET	1
103#define PMP_EV_RESCAN	2
104	u_int			caps;
105	struct task		sysctl_task;
106	struct sysctl_ctx_list	sysctl_ctx;
107	struct sysctl_oid	*sysctl_tree;
108};
109
110static	periph_init_t	pmpinit;
111static	void		pmpasync(void *callback_arg, u_int32_t code,
112				struct cam_path *path, void *arg);
113static	void		pmpsysctlinit(void *context, int pending);
114static	periph_ctor_t	pmpregister;
115static	periph_dtor_t	pmpcleanup;
116static	periph_start_t	pmpstart;
117static	periph_oninv_t	pmponinvalidate;
118static	void		pmpdone(struct cam_periph *periph,
119			       union ccb *done_ccb);
120
121#ifndef PMP_DEFAULT_TIMEOUT
122#define PMP_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
123#endif
124
125#ifndef	PMP_DEFAULT_RETRY
126#define	PMP_DEFAULT_RETRY	1
127#endif
128
129#ifndef	PMP_DEFAULT_HIDE_SPECIAL
130#define	PMP_DEFAULT_HIDE_SPECIAL	1
131#endif
132
133static int pmp_retry_count = PMP_DEFAULT_RETRY;
134static int pmp_default_timeout = PMP_DEFAULT_TIMEOUT;
135static int pmp_hide_special = PMP_DEFAULT_HIDE_SPECIAL;
136
137SYSCTL_NODE(_kern_cam, OID_AUTO, pmp, CTLFLAG_RD, 0,
138            "CAM Direct Access Disk driver");
139SYSCTL_INT(_kern_cam_pmp, OID_AUTO, retry_count, CTLFLAG_RW,
140           &pmp_retry_count, 0, "Normal I/O retry count");
141TUNABLE_INT("kern.cam.pmp.retry_count", &pmp_retry_count);
142SYSCTL_INT(_kern_cam_pmp, OID_AUTO, default_timeout, CTLFLAG_RW,
143           &pmp_default_timeout, 0, "Normal I/O timeout (in seconds)");
144TUNABLE_INT("kern.cam.pmp.default_timeout", &pmp_default_timeout);
145SYSCTL_INT(_kern_cam_pmp, OID_AUTO, hide_special, CTLFLAG_RW,
146           &pmp_hide_special, 0, "Hide extra ports");
147TUNABLE_INT("kern.cam.pmp.hide_special", &pmp_hide_special);
148
149static struct periph_driver pmpdriver =
150{
151	pmpinit, "pmp",
152	TAILQ_HEAD_INITIALIZER(pmpdriver.units), /* generation */ 0,
153	CAM_PERIPH_DRV_EARLY
154};
155
156PERIPHDRIVER_DECLARE(pmp, pmpdriver);
157
158MALLOC_DEFINE(M_ATPMP, "ata_pmp", "ata_pmp buffers");
159
160static void
161pmpinit(void)
162{
163	cam_status status;
164
165	/*
166	 * Install a global async callback.  This callback will
167	 * receive async callbacks like "new device found".
168	 */
169	status = xpt_register_async(AC_FOUND_DEVICE, pmpasync, NULL, NULL);
170
171	if (status != CAM_REQ_CMP) {
172		printf("pmp: Failed to attach master async callback "
173		       "due to status 0x%x!\n", status);
174	}
175}
176
177static void
178pmpfreeze(struct cam_periph *periph, int mask)
179{
180	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
181	struct cam_path *dpath;
182	int i;
183
184	mask &= ~softc->frozen;
185	for (i = 0; i < 15; i++) {
186		if ((mask & (1 << i)) == 0)
187			continue;
188		if (xpt_create_path(&dpath, periph,
189		    xpt_path_path_id(periph->path),
190		    i, 0) == CAM_REQ_CMP) {
191			softc->frozen |= (1 << i);
192			xpt_acquire_device(dpath->device);
193			cam_freeze_devq_arg(dpath,
194			    RELSIM_RELEASE_RUNLEVEL, CAM_RL_BUS + 1);
195			xpt_free_path(dpath);
196		}
197	}
198}
199
200static void
201pmprelease(struct cam_periph *periph, int mask)
202{
203	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
204	struct cam_path *dpath;
205	int i;
206
207	mask &= softc->frozen;
208	for (i = 0; i < 15; i++) {
209		if ((mask & (1 << i)) == 0)
210			continue;
211		if (xpt_create_path(&dpath, periph,
212		    xpt_path_path_id(periph->path),
213		    i, 0) == CAM_REQ_CMP) {
214			softc->frozen &= ~(1 << i);
215			cam_release_devq(dpath,
216			    RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_BUS + 1, FALSE);
217			xpt_release_device(dpath->device);
218			xpt_free_path(dpath);
219		}
220	}
221}
222
223static void
224pmponinvalidate(struct cam_periph *periph)
225{
226	struct cam_path *dpath;
227	int i;
228
229	/*
230	 * De-register any async callbacks.
231	 */
232	xpt_register_async(0, pmpasync, periph, periph->path);
233
234	for (i = 0; i < 15; i++) {
235		if (xpt_create_path(&dpath, periph,
236		    xpt_path_path_id(periph->path),
237		    i, 0) == CAM_REQ_CMP) {
238			xpt_async(AC_LOST_DEVICE, dpath, NULL);
239			xpt_free_path(dpath);
240		}
241	}
242	pmprelease(periph, -1);
243	xpt_print(periph->path, "lost device\n");
244}
245
246static void
247pmpcleanup(struct cam_periph *periph)
248{
249	struct pmp_softc *softc;
250
251	softc = (struct pmp_softc *)periph->softc;
252
253	xpt_print(periph->path, "removing device entry\n");
254	cam_periph_unlock(periph);
255
256	/*
257	 * If we can't free the sysctl tree, oh well...
258	 */
259	if ((softc->flags & PMP_FLAG_SCTX_INIT) != 0
260	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
261		xpt_print(periph->path, "can't remove sysctl context\n");
262	}
263
264	free(softc, M_DEVBUF);
265	cam_periph_lock(periph);
266}
267
268static void
269pmpasync(void *callback_arg, u_int32_t code,
270	struct cam_path *path, void *arg)
271{
272	struct cam_periph *periph;
273	struct pmp_softc *softc;
274
275	periph = (struct cam_periph *)callback_arg;
276	switch (code) {
277	case AC_FOUND_DEVICE:
278	{
279		struct ccb_getdev *cgd;
280		cam_status status;
281
282		cgd = (struct ccb_getdev *)arg;
283		if (cgd == NULL)
284			break;
285
286		if (cgd->protocol != PROTO_SATAPM)
287			break;
288
289		/*
290		 * Allocate a peripheral instance for
291		 * this device and start the probe
292		 * process.
293		 */
294		status = cam_periph_alloc(pmpregister, pmponinvalidate,
295					  pmpcleanup, pmpstart,
296					  "pmp", CAM_PERIPH_BIO,
297					  cgd->ccb_h.path, pmpasync,
298					  AC_FOUND_DEVICE, cgd);
299
300		if (status != CAM_REQ_CMP
301		 && status != CAM_REQ_INPROG)
302			printf("pmpasync: Unable to attach to new device "
303				"due to status 0x%x\n", status);
304		break;
305	}
306	case AC_SCSI_AEN:
307	case AC_SENT_BDR:
308	case AC_BUS_RESET:
309		softc = (struct pmp_softc *)periph->softc;
310		cam_periph_async(periph, code, path, arg);
311		if (code == AC_SCSI_AEN)
312			softc->events |= PMP_EV_RESCAN;
313		else
314			softc->events |= PMP_EV_RESET;
315		if (code == AC_SCSI_AEN && softc->state != PMP_STATE_NORMAL)
316			break;
317		xpt_hold_boot();
318		pmpfreeze(periph, softc->found);
319		if (code == AC_SENT_BDR || code == AC_BUS_RESET)
320			softc->found = 0; /* We have to reset everything. */
321		if (softc->state == PMP_STATE_NORMAL) {
322			softc->state = PMP_STATE_PRECONFIG;
323			cam_periph_acquire(periph);
324			xpt_schedule(periph, CAM_PRIORITY_DEV);
325		} else
326			softc->restart = 1;
327		break;
328	default:
329		cam_periph_async(periph, code, path, arg);
330		break;
331	}
332}
333
334static void
335pmpsysctlinit(void *context, int pending)
336{
337	struct cam_periph *periph;
338	struct pmp_softc *softc;
339	char tmpstr[80], tmpstr2[80];
340
341	periph = (struct cam_periph *)context;
342	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
343		return;
344
345	softc = (struct pmp_softc *)periph->softc;
346	snprintf(tmpstr, sizeof(tmpstr), "CAM PMP unit %d", periph->unit_number);
347	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
348
349	sysctl_ctx_init(&softc->sysctl_ctx);
350	softc->flags |= PMP_FLAG_SCTX_INIT;
351	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
352		SYSCTL_STATIC_CHILDREN(_kern_cam_pmp), OID_AUTO, tmpstr2,
353		CTLFLAG_RD, 0, tmpstr);
354	if (softc->sysctl_tree == NULL) {
355		printf("pmpsysctlinit: unable to allocate sysctl tree\n");
356		cam_periph_release(periph);
357		return;
358	}
359
360	cam_periph_release(periph);
361}
362
363static cam_status
364pmpregister(struct cam_periph *periph, void *arg)
365{
366	struct pmp_softc *softc;
367	struct ccb_getdev *cgd;
368
369	cgd = (struct ccb_getdev *)arg;
370	if (periph == NULL) {
371		printf("pmpregister: periph was NULL!!\n");
372		return(CAM_REQ_CMP_ERR);
373	}
374
375	if (cgd == NULL) {
376		printf("pmpregister: no getdev CCB, can't register device\n");
377		return(CAM_REQ_CMP_ERR);
378	}
379
380	softc = (struct pmp_softc *)malloc(sizeof(*softc), M_DEVBUF,
381	    M_NOWAIT|M_ZERO);
382
383	if (softc == NULL) {
384		printf("pmpregister: Unable to probe new device. "
385		       "Unable to allocate softc\n");
386		return(CAM_REQ_CMP_ERR);
387	}
388	periph->softc = softc;
389
390	softc->pm_pid = ((uint32_t *)&cgd->ident_data)[0];
391	softc->pm_prv = ((uint32_t *)&cgd->ident_data)[1];
392	TASK_INIT(&softc->sysctl_task, 0, pmpsysctlinit, periph);
393
394	xpt_announce_periph(periph, NULL);
395
396	/*
397	 * Add async callbacks for bus reset and
398	 * bus device reset calls.  I don't bother
399	 * checking if this fails as, in most cases,
400	 * the system will function just fine without
401	 * them and the only alternative would be to
402	 * not attach the device on failure.
403	 */
404	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
405		AC_SCSI_AEN, pmpasync, periph, periph->path);
406
407	/*
408	 * Take an exclusive refcount on the periph while pmpstart is called
409	 * to finish the probe.  The reference will be dropped in pmpdone at
410	 * the end of probe.
411	 */
412	(void)cam_periph_acquire(periph);
413	xpt_hold_boot();
414	softc->state = PMP_STATE_PORTS;
415	softc->events = PMP_EV_RESCAN;
416	xpt_schedule(periph, CAM_PRIORITY_DEV);
417
418	return(CAM_REQ_CMP);
419}
420
421static void
422pmpstart(struct cam_periph *periph, union ccb *start_ccb)
423{
424	struct ccb_trans_settings cts;
425	struct ccb_ataio *ataio;
426	struct pmp_softc *softc;
427	struct cam_path *dpath;
428	int revision = 0;
429
430	softc = (struct pmp_softc *)periph->softc;
431	ataio = &start_ccb->ataio;
432
433	if (softc->restart) {
434		softc->restart = 0;
435		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
436	}
437	/* Fetch user wanted device speed. */
438	if (softc->state == PMP_STATE_RESET ||
439	    softc->state == PMP_STATE_CONNECT) {
440		if (xpt_create_path(&dpath, periph,
441		    xpt_path_path_id(periph->path),
442		    softc->pm_step, 0) == CAM_REQ_CMP) {
443			bzero(&cts, sizeof(cts));
444			xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
445			cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
446			cts.type = CTS_TYPE_USER_SETTINGS;
447			xpt_action((union ccb *)&cts);
448			if (cts.xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
449				revision = cts.xport_specific.sata.revision;
450			xpt_free_path(dpath);
451		}
452	}
453	switch (softc->state) {
454	case PMP_STATE_PORTS:
455		cam_fill_ataio(ataio,
456		      pmp_retry_count,
457		      pmpdone,
458		      /*flags*/CAM_DIR_NONE,
459		      0,
460		      /*data_ptr*/NULL,
461		      /*dxfer_len*/0,
462		      pmp_default_timeout * 1000);
463		ata_pm_read_cmd(ataio, 2, 15);
464		break;
465	case PMP_STATE_PRECONFIG:
466		/* Get/update host SATA capabilities. */
467		bzero(&cts, sizeof(cts));
468		xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE);
469		cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
470		cts.type = CTS_TYPE_CURRENT_SETTINGS;
471		xpt_action((union ccb *)&cts);
472		if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
473			softc->caps = cts.xport_specific.sata.caps;
474		cam_fill_ataio(ataio,
475		      pmp_retry_count,
476		      pmpdone,
477		      /*flags*/CAM_DIR_NONE,
478		      0,
479		      /*data_ptr*/NULL,
480		      /*dxfer_len*/0,
481		      pmp_default_timeout * 1000);
482		ata_pm_write_cmd(ataio, 0x60, 15, 0x0);
483		break;
484	case PMP_STATE_RESET:
485		cam_fill_ataio(ataio,
486		      pmp_retry_count,
487		      pmpdone,
488		      /*flags*/CAM_DIR_NONE,
489		      0,
490		      /*data_ptr*/NULL,
491		      /*dxfer_len*/0,
492		      pmp_default_timeout * 1000);
493		ata_pm_write_cmd(ataio, 2, softc->pm_step,
494		    (revision << 4) |
495		    ((softc->found & (1 << softc->pm_step)) ? 0 : 1));
496		break;
497	case PMP_STATE_CONNECT:
498		cam_fill_ataio(ataio,
499		      pmp_retry_count,
500		      pmpdone,
501		      /*flags*/CAM_DIR_NONE,
502		      0,
503		      /*data_ptr*/NULL,
504		      /*dxfer_len*/0,
505		      pmp_default_timeout * 1000);
506		ata_pm_write_cmd(ataio, 2, softc->pm_step,
507		    (revision << 4));
508		break;
509	case PMP_STATE_CHECK:
510		cam_fill_ataio(ataio,
511		      pmp_retry_count,
512		      pmpdone,
513		      /*flags*/CAM_DIR_NONE,
514		      0,
515		      /*data_ptr*/NULL,
516		      /*dxfer_len*/0,
517		      pmp_default_timeout * 1000);
518		ata_pm_read_cmd(ataio, 0, softc->pm_step);
519		break;
520	case PMP_STATE_CLEAR:
521		softc->reset = 0;
522		cam_fill_ataio(ataio,
523		      pmp_retry_count,
524		      pmpdone,
525		      /*flags*/CAM_DIR_NONE,
526		      0,
527		      /*data_ptr*/NULL,
528		      /*dxfer_len*/0,
529		      pmp_default_timeout * 1000);
530		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
531		break;
532	case PMP_STATE_CONFIG:
533		cam_fill_ataio(ataio,
534		      pmp_retry_count,
535		      pmpdone,
536		      /*flags*/CAM_DIR_NONE,
537		      0,
538		      /*data_ptr*/NULL,
539		      /*dxfer_len*/0,
540		      pmp_default_timeout * 1000);
541		ata_pm_write_cmd(ataio, 0x60, 15, 0x07 |
542		    ((softc->caps & CTS_SATA_CAPS_H_AN) ? 0x08 : 0));
543		break;
544	default:
545		break;
546	}
547	xpt_action(start_ccb);
548}
549
550static void
551pmpdone(struct cam_periph *periph, union ccb *done_ccb)
552{
553	struct ccb_trans_settings cts;
554	struct pmp_softc *softc;
555	struct ccb_ataio *ataio;
556	struct cam_path *dpath;
557	u_int32_t  priority, res;
558	int i;
559
560	softc = (struct pmp_softc *)periph->softc;
561	ataio = &done_ccb->ataio;
562
563	CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("pmpdone\n"));
564
565	priority = done_ccb->ccb_h.pinfo.priority;
566
567	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
568		if (cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) {
569			return;
570		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
571			cam_release_devq(done_ccb->ccb_h.path,
572			    /*relsim_flags*/0,
573			    /*reduction*/0,
574			    /*timeout*/0,
575			    /*getcount_only*/0);
576		}
577		goto done;
578	}
579
580	if (softc->restart) {
581		softc->restart = 0;
582		xpt_release_ccb(done_ccb);
583		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
584		xpt_schedule(periph, priority);
585		return;
586	}
587
588	switch (softc->state) {
589	case PMP_STATE_PORTS:
590		softc->pm_ports = (ataio->res.lba_high << 24) +
591		    (ataio->res.lba_mid << 16) +
592		    (ataio->res.lba_low << 8) +
593		    ataio->res.sector_count;
594		if (pmp_hide_special) {
595			/*
596			 * This PMP declares 6 ports, while only 5 of them
597			 * are real. Port 5 is a SEMB port, probing which
598			 * causes timeouts if external SEP is not connected
599			 * to PMP over I2C.
600			 */
601			if (softc->pm_pid == 0x37261095 && softc->pm_ports == 6)
602				softc->pm_ports = 5;
603
604			/*
605			 * This PMP declares 7 ports, while only 5 of them
606			 * are real. Port 5 is a fake "Config  Disk" with
607			 * 640 sectors size. Port 6 is a SEMB port.
608			 */
609			if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
610				softc->pm_ports = 5;
611
612			/*
613			 * These PMPs have extra configuration port.
614			 */
615			if (softc->pm_pid == 0x57231095 ||
616			    softc->pm_pid == 0x57331095 ||
617			    softc->pm_pid == 0x57341095 ||
618			    softc->pm_pid == 0x57441095)
619				softc->pm_ports--;
620		}
621		printf("%s%d: %d fan-out ports\n",
622		    periph->periph_name, periph->unit_number,
623		    softc->pm_ports);
624		softc->state = PMP_STATE_PRECONFIG;
625		xpt_release_ccb(done_ccb);
626		xpt_schedule(periph, priority);
627		return;
628	case PMP_STATE_PRECONFIG:
629		softc->pm_step = 0;
630		softc->state = PMP_STATE_RESET;
631		softc->reset |= ~softc->found;
632		xpt_release_ccb(done_ccb);
633		xpt_schedule(periph, priority);
634		return;
635	case PMP_STATE_RESET:
636		softc->pm_step++;
637		if (softc->pm_step >= softc->pm_ports) {
638			softc->pm_step = 0;
639			cam_freeze_devq(periph->path);
640			cam_release_devq(periph->path,
641			    RELSIM_RELEASE_AFTER_TIMEOUT,
642			    /*reduction*/0,
643			    /*timeout*/5,
644			    /*getcount_only*/0);
645			softc->state = PMP_STATE_CONNECT;
646		}
647		xpt_release_ccb(done_ccb);
648		xpt_schedule(periph, priority);
649		return;
650	case PMP_STATE_CONNECT:
651		softc->pm_step++;
652		if (softc->pm_step >= softc->pm_ports) {
653			softc->pm_step = 0;
654			softc->pm_try = 0;
655			cam_freeze_devq(periph->path);
656			cam_release_devq(periph->path,
657			    RELSIM_RELEASE_AFTER_TIMEOUT,
658			    /*reduction*/0,
659			    /*timeout*/10,
660			    /*getcount_only*/0);
661			softc->state = PMP_STATE_CHECK;
662		}
663		xpt_release_ccb(done_ccb);
664		xpt_schedule(periph, priority);
665		return;
666	case PMP_STATE_CHECK:
667		res = (ataio->res.lba_high << 24) +
668		    (ataio->res.lba_mid << 16) +
669		    (ataio->res.lba_low << 8) +
670		    ataio->res.sector_count;
671		if (((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) ||
672		    (res & 0x600) != 0) {
673			if (bootverbose) {
674				printf("%s%d: port %d status: %08x\n",
675				    periph->periph_name, periph->unit_number,
676				    softc->pm_step, res);
677			}
678			/* Report device speed if it is online. */
679			if ((res & 0xf0f) == 0x103 &&
680			    xpt_create_path(&dpath, periph,
681			    xpt_path_path_id(periph->path),
682			    softc->pm_step, 0) == CAM_REQ_CMP) {
683				bzero(&cts, sizeof(cts));
684				xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
685				cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
686				cts.type = CTS_TYPE_CURRENT_SETTINGS;
687				cts.xport_specific.sata.revision = (res & 0x0f0) >> 4;
688				cts.xport_specific.sata.valid = CTS_SATA_VALID_REVISION;
689				cts.xport_specific.sata.caps = softc->caps &
690				    (CTS_SATA_CAPS_H_PMREQ |
691				     CTS_SATA_CAPS_H_DMAAA |
692				     CTS_SATA_CAPS_H_AN);
693				cts.xport_specific.sata.valid |= CTS_SATA_VALID_CAPS;
694				xpt_action((union ccb *)&cts);
695				xpt_free_path(dpath);
696			}
697			softc->found |= (1 << softc->pm_step);
698			softc->pm_step++;
699		} else {
700			if (softc->pm_try < 10) {
701				cam_freeze_devq(periph->path);
702				cam_release_devq(periph->path,
703				    RELSIM_RELEASE_AFTER_TIMEOUT,
704				    /*reduction*/0,
705				    /*timeout*/10,
706				    /*getcount_only*/0);
707				softc->pm_try++;
708			} else {
709				if (bootverbose) {
710					printf("%s%d: port %d status: %08x\n",
711					    periph->periph_name, periph->unit_number,
712					    softc->pm_step, res);
713				}
714				softc->found &= ~(1 << softc->pm_step);
715				if (xpt_create_path(&dpath, periph,
716				    done_ccb->ccb_h.path_id,
717				    softc->pm_step, 0) == CAM_REQ_CMP) {
718					xpt_async(AC_LOST_DEVICE, dpath, NULL);
719					xpt_free_path(dpath);
720				}
721				softc->pm_step++;
722			}
723		}
724		if (softc->pm_step >= softc->pm_ports) {
725			if (softc->reset & softc->found) {
726				cam_freeze_devq(periph->path);
727				cam_release_devq(periph->path,
728				    RELSIM_RELEASE_AFTER_TIMEOUT,
729				    /*reduction*/0,
730				    /*timeout*/1000,
731				    /*getcount_only*/0);
732			}
733			softc->state = PMP_STATE_CLEAR;
734			softc->pm_step = 0;
735		}
736		xpt_release_ccb(done_ccb);
737		xpt_schedule(periph, priority);
738		return;
739	case PMP_STATE_CLEAR:
740		softc->pm_step++;
741		if (softc->pm_step >= softc->pm_ports) {
742			softc->state = PMP_STATE_CONFIG;
743			softc->pm_step = 0;
744		}
745		xpt_release_ccb(done_ccb);
746		xpt_schedule(periph, priority);
747		return;
748	case PMP_STATE_CONFIG:
749		for (i = 0; i < softc->pm_ports; i++) {
750			union ccb *ccb;
751
752			if ((softc->found & (1 << i)) == 0)
753				continue;
754			if (xpt_create_path(&dpath, periph,
755			    xpt_path_path_id(periph->path),
756			    i, 0) != CAM_REQ_CMP) {
757				printf("pmpdone: xpt_create_path failed\n");
758				continue;
759			}
760			/* If we did hard reset to this device, inform XPT. */
761			if ((softc->reset & softc->found & (1 << i)) != 0)
762				xpt_async(AC_SENT_BDR, dpath, NULL);
763			/* If rescan requested, scan this device. */
764			if (softc->events & PMP_EV_RESCAN) {
765				ccb = xpt_alloc_ccb_nowait();
766				if (ccb == NULL) {
767					xpt_free_path(dpath);
768					goto done;
769				}
770				xpt_setup_ccb(&ccb->ccb_h, dpath, CAM_PRIORITY_XPT);
771				xpt_rescan(ccb);
772			} else
773				xpt_free_path(dpath);
774		}
775		break;
776	default:
777		break;
778	}
779done:
780	xpt_release_ccb(done_ccb);
781	softc->state = PMP_STATE_NORMAL;
782	softc->events = 0;
783	xpt_release_boot();
784	pmprelease(periph, -1);
785	cam_periph_release_locked(periph);
786}
787
788#endif /* _KERNEL */
789