ata_pmp.c revision 236602
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: head/sys/cam/ata/ata_pmp.c 236602 2012-06-05 09:45:42Z 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
137static SYSCTL_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
158static MALLOC_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	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("pmpstart\n"));
434
435	if (softc->restart) {
436		softc->restart = 0;
437		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
438	}
439	/* Fetch user wanted device speed. */
440	if (softc->state == PMP_STATE_RESET ||
441	    softc->state == PMP_STATE_CONNECT) {
442		if (xpt_create_path(&dpath, periph,
443		    xpt_path_path_id(periph->path),
444		    softc->pm_step, 0) == CAM_REQ_CMP) {
445			bzero(&cts, sizeof(cts));
446			xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
447			cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
448			cts.type = CTS_TYPE_USER_SETTINGS;
449			xpt_action((union ccb *)&cts);
450			if (cts.xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
451				revision = cts.xport_specific.sata.revision;
452			xpt_free_path(dpath);
453		}
454	}
455	switch (softc->state) {
456	case PMP_STATE_PORTS:
457		cam_fill_ataio(ataio,
458		      pmp_retry_count,
459		      pmpdone,
460		      /*flags*/CAM_DIR_NONE,
461		      0,
462		      /*data_ptr*/NULL,
463		      /*dxfer_len*/0,
464		      pmp_default_timeout * 1000);
465		ata_pm_read_cmd(ataio, 2, 15);
466		break;
467	case PMP_STATE_PRECONFIG:
468		/* Get/update host SATA capabilities. */
469		bzero(&cts, sizeof(cts));
470		xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE);
471		cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
472		cts.type = CTS_TYPE_CURRENT_SETTINGS;
473		xpt_action((union ccb *)&cts);
474		if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
475			softc->caps = cts.xport_specific.sata.caps;
476		cam_fill_ataio(ataio,
477		      pmp_retry_count,
478		      pmpdone,
479		      /*flags*/CAM_DIR_NONE,
480		      0,
481		      /*data_ptr*/NULL,
482		      /*dxfer_len*/0,
483		      pmp_default_timeout * 1000);
484		ata_pm_write_cmd(ataio, 0x60, 15, 0x0);
485		break;
486	case PMP_STATE_RESET:
487		cam_fill_ataio(ataio,
488		      pmp_retry_count,
489		      pmpdone,
490		      /*flags*/CAM_DIR_NONE,
491		      0,
492		      /*data_ptr*/NULL,
493		      /*dxfer_len*/0,
494		      pmp_default_timeout * 1000);
495		ata_pm_write_cmd(ataio, 2, softc->pm_step,
496		    (revision << 4) |
497		    ((softc->found & (1 << softc->pm_step)) ? 0 : 1));
498		break;
499	case PMP_STATE_CONNECT:
500		cam_fill_ataio(ataio,
501		      pmp_retry_count,
502		      pmpdone,
503		      /*flags*/CAM_DIR_NONE,
504		      0,
505		      /*data_ptr*/NULL,
506		      /*dxfer_len*/0,
507		      pmp_default_timeout * 1000);
508		ata_pm_write_cmd(ataio, 2, softc->pm_step,
509		    (revision << 4));
510		break;
511	case PMP_STATE_CHECK:
512		cam_fill_ataio(ataio,
513		      pmp_retry_count,
514		      pmpdone,
515		      /*flags*/CAM_DIR_NONE,
516		      0,
517		      /*data_ptr*/NULL,
518		      /*dxfer_len*/0,
519		      pmp_default_timeout * 1000);
520		ata_pm_read_cmd(ataio, 0, softc->pm_step);
521		break;
522	case PMP_STATE_CLEAR:
523		softc->reset = 0;
524		cam_fill_ataio(ataio,
525		      pmp_retry_count,
526		      pmpdone,
527		      /*flags*/CAM_DIR_NONE,
528		      0,
529		      /*data_ptr*/NULL,
530		      /*dxfer_len*/0,
531		      pmp_default_timeout * 1000);
532		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
533		break;
534	case PMP_STATE_CONFIG:
535		cam_fill_ataio(ataio,
536		      pmp_retry_count,
537		      pmpdone,
538		      /*flags*/CAM_DIR_NONE,
539		      0,
540		      /*data_ptr*/NULL,
541		      /*dxfer_len*/0,
542		      pmp_default_timeout * 1000);
543		ata_pm_write_cmd(ataio, 0x60, 15, 0x07 |
544		    ((softc->caps & CTS_SATA_CAPS_H_AN) ? 0x08 : 0));
545		break;
546	default:
547		break;
548	}
549	xpt_action(start_ccb);
550}
551
552static void
553pmpdone(struct cam_periph *periph, union ccb *done_ccb)
554{
555	struct ccb_trans_settings cts;
556	struct pmp_softc *softc;
557	struct ccb_ataio *ataio;
558	struct cam_path *dpath;
559	u_int32_t  priority, res;
560	int i;
561
562	softc = (struct pmp_softc *)periph->softc;
563	ataio = &done_ccb->ataio;
564
565	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("pmpdone\n"));
566
567	priority = done_ccb->ccb_h.pinfo.priority;
568
569	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
570		if (cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) {
571			return;
572		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
573			cam_release_devq(done_ccb->ccb_h.path,
574			    /*relsim_flags*/0,
575			    /*reduction*/0,
576			    /*timeout*/0,
577			    /*getcount_only*/0);
578		}
579		goto done;
580	}
581
582	if (softc->restart) {
583		softc->restart = 0;
584		xpt_release_ccb(done_ccb);
585		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
586		xpt_schedule(periph, priority);
587		return;
588	}
589
590	switch (softc->state) {
591	case PMP_STATE_PORTS:
592		softc->pm_ports = (ataio->res.lba_high << 24) +
593		    (ataio->res.lba_mid << 16) +
594		    (ataio->res.lba_low << 8) +
595		    ataio->res.sector_count;
596		if (pmp_hide_special) {
597			/*
598			 * This PMP declares 6 ports, while only 5 of them
599			 * are real. Port 5 is a SEMB port, probing which
600			 * causes timeouts if external SEP is not connected
601			 * to PMP over I2C.
602			 */
603			if (softc->pm_pid == 0x37261095 && softc->pm_ports == 6)
604				softc->pm_ports = 5;
605
606			/*
607			 * This PMP declares 7 ports, while only 5 of them
608			 * are real. Port 5 is a fake "Config  Disk" with
609			 * 640 sectors size. Port 6 is a SEMB port.
610			 */
611			if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
612				softc->pm_ports = 5;
613
614			/*
615			 * These PMPs have extra configuration port.
616			 */
617			if (softc->pm_pid == 0x57231095 ||
618			    softc->pm_pid == 0x57331095 ||
619			    softc->pm_pid == 0x57341095 ||
620			    softc->pm_pid == 0x57441095)
621				softc->pm_ports--;
622		}
623		printf("%s%d: %d fan-out ports\n",
624		    periph->periph_name, periph->unit_number,
625		    softc->pm_ports);
626		softc->state = PMP_STATE_PRECONFIG;
627		xpt_release_ccb(done_ccb);
628		xpt_schedule(periph, priority);
629		return;
630	case PMP_STATE_PRECONFIG:
631		softc->pm_step = 0;
632		softc->state = PMP_STATE_RESET;
633		softc->reset |= ~softc->found;
634		xpt_release_ccb(done_ccb);
635		xpt_schedule(periph, priority);
636		return;
637	case PMP_STATE_RESET:
638		softc->pm_step++;
639		if (softc->pm_step >= softc->pm_ports) {
640			softc->pm_step = 0;
641			cam_freeze_devq(periph->path);
642			cam_release_devq(periph->path,
643			    RELSIM_RELEASE_AFTER_TIMEOUT,
644			    /*reduction*/0,
645			    /*timeout*/5,
646			    /*getcount_only*/0);
647			softc->state = PMP_STATE_CONNECT;
648		}
649		xpt_release_ccb(done_ccb);
650		xpt_schedule(periph, priority);
651		return;
652	case PMP_STATE_CONNECT:
653		softc->pm_step++;
654		if (softc->pm_step >= softc->pm_ports) {
655			softc->pm_step = 0;
656			softc->pm_try = 0;
657			cam_freeze_devq(periph->path);
658			cam_release_devq(periph->path,
659			    RELSIM_RELEASE_AFTER_TIMEOUT,
660			    /*reduction*/0,
661			    /*timeout*/10,
662			    /*getcount_only*/0);
663			softc->state = PMP_STATE_CHECK;
664		}
665		xpt_release_ccb(done_ccb);
666		xpt_schedule(periph, priority);
667		return;
668	case PMP_STATE_CHECK:
669		res = (ataio->res.lba_high << 24) +
670		    (ataio->res.lba_mid << 16) +
671		    (ataio->res.lba_low << 8) +
672		    ataio->res.sector_count;
673		if (((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) ||
674		    (res & 0x600) != 0) {
675			if (bootverbose) {
676				printf("%s%d: port %d status: %08x\n",
677				    periph->periph_name, periph->unit_number,
678				    softc->pm_step, res);
679			}
680			/* Report device speed if it is online. */
681			if ((res & 0xf0f) == 0x103 &&
682			    xpt_create_path(&dpath, periph,
683			    xpt_path_path_id(periph->path),
684			    softc->pm_step, 0) == CAM_REQ_CMP) {
685				bzero(&cts, sizeof(cts));
686				xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
687				cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
688				cts.type = CTS_TYPE_CURRENT_SETTINGS;
689				cts.xport_specific.sata.revision = (res & 0x0f0) >> 4;
690				cts.xport_specific.sata.valid = CTS_SATA_VALID_REVISION;
691				cts.xport_specific.sata.caps = softc->caps &
692				    (CTS_SATA_CAPS_H_PMREQ |
693				     CTS_SATA_CAPS_H_DMAAA |
694				     CTS_SATA_CAPS_H_AN);
695				cts.xport_specific.sata.valid |= CTS_SATA_VALID_CAPS;
696				xpt_action((union ccb *)&cts);
697				xpt_free_path(dpath);
698			}
699			softc->found |= (1 << softc->pm_step);
700			softc->pm_step++;
701		} else {
702			if (softc->pm_try < 10) {
703				cam_freeze_devq(periph->path);
704				cam_release_devq(periph->path,
705				    RELSIM_RELEASE_AFTER_TIMEOUT,
706				    /*reduction*/0,
707				    /*timeout*/10,
708				    /*getcount_only*/0);
709				softc->pm_try++;
710			} else {
711				if (bootverbose) {
712					printf("%s%d: port %d status: %08x\n",
713					    periph->periph_name, periph->unit_number,
714					    softc->pm_step, res);
715				}
716				softc->found &= ~(1 << softc->pm_step);
717				if (xpt_create_path(&dpath, periph,
718				    done_ccb->ccb_h.path_id,
719				    softc->pm_step, 0) == CAM_REQ_CMP) {
720					xpt_async(AC_LOST_DEVICE, dpath, NULL);
721					xpt_free_path(dpath);
722				}
723				softc->pm_step++;
724			}
725		}
726		if (softc->pm_step >= softc->pm_ports) {
727			if (softc->reset & softc->found) {
728				cam_freeze_devq(periph->path);
729				cam_release_devq(periph->path,
730				    RELSIM_RELEASE_AFTER_TIMEOUT,
731				    /*reduction*/0,
732				    /*timeout*/1000,
733				    /*getcount_only*/0);
734			}
735			softc->state = PMP_STATE_CLEAR;
736			softc->pm_step = 0;
737		}
738		xpt_release_ccb(done_ccb);
739		xpt_schedule(periph, priority);
740		return;
741	case PMP_STATE_CLEAR:
742		softc->pm_step++;
743		if (softc->pm_step >= softc->pm_ports) {
744			softc->state = PMP_STATE_CONFIG;
745			softc->pm_step = 0;
746		}
747		xpt_release_ccb(done_ccb);
748		xpt_schedule(periph, priority);
749		return;
750	case PMP_STATE_CONFIG:
751		for (i = 0; i < softc->pm_ports; i++) {
752			union ccb *ccb;
753
754			if ((softc->found & (1 << i)) == 0)
755				continue;
756			if (xpt_create_path(&dpath, periph,
757			    xpt_path_path_id(periph->path),
758			    i, 0) != CAM_REQ_CMP) {
759				printf("pmpdone: xpt_create_path failed\n");
760				continue;
761			}
762			/* If we did hard reset to this device, inform XPT. */
763			if ((softc->reset & softc->found & (1 << i)) != 0)
764				xpt_async(AC_SENT_BDR, dpath, NULL);
765			/* If rescan requested, scan this device. */
766			if (softc->events & PMP_EV_RESCAN) {
767				ccb = xpt_alloc_ccb_nowait();
768				if (ccb == NULL) {
769					xpt_free_path(dpath);
770					goto done;
771				}
772				xpt_setup_ccb(&ccb->ccb_h, dpath, CAM_PRIORITY_XPT);
773				xpt_rescan(ccb);
774			} else
775				xpt_free_path(dpath);
776		}
777		break;
778	default:
779		break;
780	}
781done:
782	xpt_release_ccb(done_ccb);
783	softc->state = PMP_STATE_NORMAL;
784	softc->events = 0;
785	xpt_release_boot();
786	pmprelease(periph, -1);
787	cam_periph_release_locked(periph);
788}
789
790#endif /* _KERNEL */
791