ata_pmp.c revision 256215
1147072Sbrooks/*-
2147072Sbrooks * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
3147072Sbrooks * All rights reserved.
4147086Sbrooks *
5147072Sbrooks * Redistribution and use in source and binary forms, with or without
6147072Sbrooks * modification, are permitted provided that the following conditions
7147072Sbrooks * are met:
8147072Sbrooks * 1. Redistributions of source code must retain the above copyright
9147072Sbrooks *    notice, this list of conditions and the following disclaimer,
10147072Sbrooks *    without modification, immediately at the beginning of the file.
11147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
12147072Sbrooks *    notice, this list of conditions and the following disclaimer in the
13147072Sbrooks *    documentation and/or other materials provided with the distribution.
14147072Sbrooks *
15147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16147072Sbrooks * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17147072Sbrooks * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18147072Sbrooks * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19147072Sbrooks * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20147072Sbrooks * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21147072Sbrooks * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22154164Sbrooks * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23147086Sbrooks * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24147086Sbrooks * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25154164Sbrooks */
26147086Sbrooks
27147086Sbrooks#include <sys/cdefs.h>
28147086Sbrooks__FBSDID("$FreeBSD: stable/9/sys/cam/ata/ata_pmp.c 256215 2013-10-09 18:48:10Z mav $");
29147086Sbrooks
30147086Sbrooks#include <sys/param.h>
31147086Sbrooks
32147086Sbrooks#ifdef _KERNEL
33147086Sbrooks#include <sys/systm.h>
34147086Sbrooks#include <sys/kernel.h>
35147072Sbrooks#include <sys/bio.h>
36147072Sbrooks#include <sys/sysctl.h>
37147072Sbrooks#include <sys/taskqueue.h>
38147072Sbrooks#include <sys/lock.h>
39147086Sbrooks#include <sys/mutex.h>
40147086Sbrooks#include <sys/conf.h>
41147086Sbrooks#include <sys/devicestat.h>
42147086Sbrooks#include <sys/eventhandler.h>
43147086Sbrooks#include <sys/malloc.h>
44147086Sbrooks#include <sys/cons.h>
45147086Sbrooks#include <geom/geom_disk.h>
46147086Sbrooks#endif /* _KERNEL */
47147086Sbrooks
48147072Sbrooks#ifndef _KERNEL
49147072Sbrooks#include <stdio.h>
50147072Sbrooks#include <string.h>
51147086Sbrooks#endif /* _KERNEL */
52147086Sbrooks
53147086Sbrooks#include <cam/cam.h>
54147086Sbrooks#include <cam/cam_ccb.h>
55147086Sbrooks#include <cam/cam_periph.h>
56147086Sbrooks#include <cam/cam_xpt_periph.h>
57147086Sbrooks#include <cam/cam_xpt_internal.h>
58149519Sbrooks#include <cam/cam_sim.h>
59147086Sbrooks
60147086Sbrooks#include <cam/ata/ata_all.h>
61147072Sbrooks
62149519Sbrooks#ifdef _KERNEL
63147072Sbrooks
64147072Sbrookstypedef enum {
65147072Sbrooks	PMP_STATE_NORMAL,
66149519Sbrooks	PMP_STATE_PORTS,
67147072Sbrooks	PMP_STATE_PM_QUIRKS_1,
68147086Sbrooks	PMP_STATE_PM_QUIRKS_2,
69147086Sbrooks	PMP_STATE_PM_QUIRKS_3,
70147086Sbrooks	PMP_STATE_PRECONFIG,
71147086Sbrooks	PMP_STATE_RESET,
72147072Sbrooks	PMP_STATE_CONNECT,
73147072Sbrooks	PMP_STATE_CHECK,
74147072Sbrooks	PMP_STATE_CLEAR,
75147072Sbrooks	PMP_STATE_CONFIG,
76147072Sbrooks	PMP_STATE_SCAN
77149479Sbrooks} pmp_state;
78147072Sbrooks
79147072Sbrookstypedef enum {
80147072Sbrooks	PMP_FLAG_SCTX_INIT	= 0x200
81147072Sbrooks} pmp_flags;
82147072Sbrooks
83147072Sbrookstypedef enum {
84147072Sbrooks	PMP_CCB_PROBE		= 0x01,
85149479Sbrooks} pmp_ccb_state;
86147072Sbrooks
87147072Sbrooks/* Offsets into our private area for storing information */
88147072Sbrooks#define ccb_state	ppriv_field0
89147072Sbrooks#define ccb_bp		ppriv_ptr1
90149479Sbrooks
91147086Sbrooksstruct pmp_softc {
92147086Sbrooks	SLIST_ENTRY(pmp_softc)	links;
93147086Sbrooks	pmp_state		state;
94147086Sbrooks	pmp_flags		flags;
95147086Sbrooks	uint32_t		pm_pid;
96147072Sbrooks	uint32_t		pm_prv;
97147072Sbrooks	int			pm_ports;
98147072Sbrooks	int			pm_step;
99147072Sbrooks	int			pm_try;
100147072Sbrooks	int			found;
101147072Sbrooks	int			reset;
102147072Sbrooks	int			frozen;
103147072Sbrooks	int			restart;
104147072Sbrooks	int			events;
105147086Sbrooks#define PMP_EV_RESET	1
106147072Sbrooks#define PMP_EV_RESCAN	2
107147072Sbrooks	u_int			caps;
108147072Sbrooks	struct task		sysctl_task;
109149479Sbrooks	struct sysctl_ctx_list	sysctl_ctx;
110147072Sbrooks	struct sysctl_oid	*sysctl_tree;
111147072Sbrooks};
112147072Sbrooks
113147072Sbrooksstatic	periph_init_t	pmpinit;
114147072Sbrooksstatic	void		pmpasync(void *callback_arg, u_int32_t code,
115147072Sbrooks				struct cam_path *path, void *arg);
116147072Sbrooksstatic	void		pmpsysctlinit(void *context, int pending);
117147072Sbrooksstatic	periph_ctor_t	pmpregister;
118147072Sbrooksstatic	periph_dtor_t	pmpcleanup;
119147072Sbrooksstatic	periph_start_t	pmpstart;
120147072Sbrooksstatic	periph_oninv_t	pmponinvalidate;
121147072Sbrooksstatic	void		pmpdone(struct cam_periph *periph,
122147086Sbrooks			       union ccb *done_ccb);
123147072Sbrooks
124147072Sbrooks#ifndef PMP_DEFAULT_TIMEOUT
125147072Sbrooks#define PMP_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
126147072Sbrooks#endif
127147072Sbrooks
128147072Sbrooks#ifndef	PMP_DEFAULT_RETRY
129147072Sbrooks#define	PMP_DEFAULT_RETRY	1
130147072Sbrooks#endif
131147072Sbrooks
132147072Sbrooks#ifndef	PMP_DEFAULT_HIDE_SPECIAL
133147072Sbrooks#define	PMP_DEFAULT_HIDE_SPECIAL	1
134147072Sbrooks#endif
135147072Sbrooks
136147072Sbrooksstatic int pmp_retry_count = PMP_DEFAULT_RETRY;
137147072Sbrooksstatic int pmp_default_timeout = PMP_DEFAULT_TIMEOUT;
138154869Sbrooksstatic int pmp_hide_special = PMP_DEFAULT_HIDE_SPECIAL;
139154702Swes
140147072Sbrooksstatic SYSCTL_NODE(_kern_cam, OID_AUTO, pmp, CTLFLAG_RD, 0,
141147072Sbrooks            "CAM Direct Access Disk driver");
142154702SwesSYSCTL_INT(_kern_cam_pmp, OID_AUTO, retry_count, CTLFLAG_RW,
143147072Sbrooks           &pmp_retry_count, 0, "Normal I/O retry count");
144147072SbrooksTUNABLE_INT("kern.cam.pmp.retry_count", &pmp_retry_count);
145147072SbrooksSYSCTL_INT(_kern_cam_pmp, OID_AUTO, default_timeout, CTLFLAG_RW,
146147072Sbrooks           &pmp_default_timeout, 0, "Normal I/O timeout (in seconds)");
147154702SwesTUNABLE_INT("kern.cam.pmp.default_timeout", &pmp_default_timeout);
148147072SbrooksSYSCTL_INT(_kern_cam_pmp, OID_AUTO, hide_special, CTLFLAG_RW,
149147072Sbrooks           &pmp_hide_special, 0, "Hide extra ports");
150147072SbrooksTUNABLE_INT("kern.cam.pmp.hide_special", &pmp_hide_special);
151154702Swes
152147072Sbrooksstatic struct periph_driver pmpdriver =
153154702Swes{
154147072Sbrooks	pmpinit, "pmp",
155147072Sbrooks	TAILQ_HEAD_INITIALIZER(pmpdriver.units), /* generation */ 0,
156149898Sbrooks	CAM_PERIPH_DRV_EARLY
157149898Sbrooks};
158149898Sbrooks
159149898SbrooksPERIPHDRIVER_DECLARE(pmp, pmpdriver);
160149898Sbrooks
161154702Swesstatic MALLOC_DEFINE(M_ATPMP, "ata_pmp", "ata_pmp buffers");
162154702Swes
163149898Sbrooksstatic void
164149898Sbrookspmpinit(void)
165149898Sbrooks{
166147072Sbrooks	cam_status status;
167147072Sbrooks
168147072Sbrooks	/*
169147072Sbrooks	 * Install a global async callback.  This callback will
170147072Sbrooks	 * receive async callbacks like "new device found".
171147072Sbrooks	 */
172147072Sbrooks	status = xpt_register_async(AC_FOUND_DEVICE, pmpasync, NULL, NULL);
173154702Swes
174154702Swes	if (status != CAM_REQ_CMP) {
175147072Sbrooks		printf("pmp: Failed to attach master async callback "
176147072Sbrooks		       "due to status 0x%x!\n", status);
177147072Sbrooks	}
178147072Sbrooks}
179147072Sbrooks
180147072Sbrooksstatic void
181147072Sbrookspmpfreeze(struct cam_periph *periph, int mask)
182147072Sbrooks{
183147072Sbrooks	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
184147072Sbrooks	struct cam_path *dpath;
185147072Sbrooks	int i;
186147138Sbrooks
187147138Sbrooks	mask &= ~softc->frozen;
188147138Sbrooks	for (i = 0; i < 15; i++) {
189147138Sbrooks		if ((mask & (1 << i)) == 0)
190147138Sbrooks			continue;
191147138Sbrooks		if (xpt_create_path(&dpath, periph,
192147138Sbrooks		    xpt_path_path_id(periph->path),
193147138Sbrooks		    i, 0) == CAM_REQ_CMP) {
194147138Sbrooks			softc->frozen |= (1 << i);
195147138Sbrooks			xpt_acquire_device(dpath->device);
196147072Sbrooks			cam_freeze_devq(dpath);
197147072Sbrooks			xpt_free_path(dpath);
198147072Sbrooks		}
199147072Sbrooks	}
200147218Sbrooks}
201147218Sbrooks
202147218Sbrooksstatic void
203147218Sbrookspmprelease(struct cam_periph *periph, int mask)
204147218Sbrooks{
205147218Sbrooks	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
206147218Sbrooks	struct cam_path *dpath;
207147218Sbrooks	int i;
208147218Sbrooks
209147218Sbrooks	mask &= softc->frozen;
210147218Sbrooks	for (i = 0; i < 15; i++) {
211147086Sbrooks		if ((mask & (1 << i)) == 0)
212149480Sbrooks			continue;
213147086Sbrooks		if (xpt_create_path(&dpath, periph,
214147086Sbrooks		    xpt_path_path_id(periph->path),
215147072Sbrooks		    i, 0) == CAM_REQ_CMP) {
216147072Sbrooks			softc->frozen &= ~(1 << i);
217147072Sbrooks			cam_release_devq(dpath, 0, 0, 0, FALSE);
218147072Sbrooks			xpt_release_device(dpath->device);
219149519Sbrooks			xpt_free_path(dpath);
220149519Sbrooks		}
221147072Sbrooks	}
222147072Sbrooks}
223147072Sbrooks
224147072Sbrooksstatic void
225147072Sbrookspmponinvalidate(struct cam_periph *periph)
226147072Sbrooks{
227147072Sbrooks	struct cam_path *dpath;
228147072Sbrooks	int i;
229147072Sbrooks
230147072Sbrooks	/*
231147072Sbrooks	 * De-register any async callbacks.
232147072Sbrooks	 */
233147086Sbrooks	xpt_register_async(0, pmpasync, periph, periph->path);
234147072Sbrooks
235147072Sbrooks	for (i = 0; i < 15; i++) {
236147072Sbrooks		if (xpt_create_path(&dpath, periph,
237147072Sbrooks		    xpt_path_path_id(periph->path),
238147072Sbrooks		    i, 0) == CAM_REQ_CMP) {
239147072Sbrooks			xpt_async(AC_LOST_DEVICE, dpath, NULL);
240147072Sbrooks			xpt_free_path(dpath);
241147072Sbrooks		}
242147072Sbrooks	}
243147072Sbrooks	pmprelease(periph, -1);
244147072Sbrooks	xpt_print(periph->path, "lost device\n");
245147072Sbrooks}
246147072Sbrooks
247147072Sbrooksstatic void
248147072Sbrookspmpcleanup(struct cam_periph *periph)
249147072Sbrooks{
250147072Sbrooks	struct pmp_softc *softc;
251147072Sbrooks
252147072Sbrooks	softc = (struct pmp_softc *)periph->softc;
253147072Sbrooks
254147072Sbrooks	xpt_print(periph->path, "removing device entry\n");
255147072Sbrooks	cam_periph_unlock(periph);
256147072Sbrooks
257147072Sbrooks	/*
258147072Sbrooks	 * If we can't free the sysctl tree, oh well...
259147072Sbrooks	 */
260147072Sbrooks	if ((softc->flags & PMP_FLAG_SCTX_INIT) != 0
261147072Sbrooks	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
262154164Sbrooks		xpt_print(periph->path, "can't remove sysctl context\n");
263154164Sbrooks	}
264154164Sbrooks
265147072Sbrooks	free(softc, M_DEVBUF);
266147072Sbrooks	cam_periph_lock(periph);
267147072Sbrooks}
268147072Sbrooks
269147072Sbrooksstatic void
270147072Sbrookspmpasync(void *callback_arg, u_int32_t code,
271147072Sbrooks	struct cam_path *path, void *arg)
272147072Sbrooks{
273147072Sbrooks	struct cam_periph *periph;
274147072Sbrooks	struct pmp_softc *softc;
275147072Sbrooks
276147072Sbrooks	periph = (struct cam_periph *)callback_arg;
277147086Sbrooks	switch (code) {
278147072Sbrooks	case AC_FOUND_DEVICE:
279154760Sbrooks	{
280147072Sbrooks		struct ccb_getdev *cgd;
281147072Sbrooks		cam_status status;
282147072Sbrooks
283147072Sbrooks		cgd = (struct ccb_getdev *)arg;
284147072Sbrooks		if (cgd == NULL)
285147138Sbrooks			break;
286147072Sbrooks
287147072Sbrooks		if (cgd->protocol != PROTO_SATAPM)
288147072Sbrooks			break;
289149519Sbrooks
290147072Sbrooks		/*
291147138Sbrooks		 * Allocate a peripheral instance for
292147072Sbrooks		 * this device and start the probe
293147072Sbrooks		 * process.
294147072Sbrooks		 */
295147138Sbrooks		status = cam_periph_alloc(pmpregister, pmponinvalidate,
296					  pmpcleanup, pmpstart,
297					  "pmp", CAM_PERIPH_BIO,
298					  cgd->ccb_h.path, pmpasync,
299					  AC_FOUND_DEVICE, cgd);
300
301		if (status != CAM_REQ_CMP
302		 && status != CAM_REQ_INPROG)
303			printf("pmpasync: Unable to attach to new device "
304				"due to status 0x%x\n", status);
305		break;
306	}
307	case AC_SCSI_AEN:
308	case AC_SENT_BDR:
309	case AC_BUS_RESET:
310		softc = (struct pmp_softc *)periph->softc;
311		cam_periph_async(periph, code, path, arg);
312		if (code == AC_SCSI_AEN)
313			softc->events |= PMP_EV_RESCAN;
314		else
315			softc->events |= PMP_EV_RESET;
316		if (code == AC_SCSI_AEN && softc->state != PMP_STATE_NORMAL)
317			break;
318		xpt_hold_boot();
319		pmpfreeze(periph, softc->found);
320		if (code == AC_SENT_BDR || code == AC_BUS_RESET)
321			softc->found = 0; /* We have to reset everything. */
322		if (softc->state == PMP_STATE_NORMAL) {
323			if (softc->pm_pid == 0x37261095 ||
324			    softc->pm_pid == 0x38261095)
325				softc->state = PMP_STATE_PM_QUIRKS_1;
326			else
327				softc->state = PMP_STATE_PRECONFIG;
328			cam_periph_acquire(periph);
329			xpt_schedule(periph, CAM_PRIORITY_DEV);
330		} else
331			softc->restart = 1;
332		break;
333	default:
334		cam_periph_async(periph, code, path, arg);
335		break;
336	}
337}
338
339static void
340pmpsysctlinit(void *context, int pending)
341{
342	struct cam_periph *periph;
343	struct pmp_softc *softc;
344	char tmpstr[80], tmpstr2[80];
345
346	periph = (struct cam_periph *)context;
347	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
348		return;
349
350	softc = (struct pmp_softc *)periph->softc;
351	snprintf(tmpstr, sizeof(tmpstr), "CAM PMP unit %d", periph->unit_number);
352	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
353
354	sysctl_ctx_init(&softc->sysctl_ctx);
355	softc->flags |= PMP_FLAG_SCTX_INIT;
356	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
357		SYSCTL_STATIC_CHILDREN(_kern_cam_pmp), OID_AUTO, tmpstr2,
358		CTLFLAG_RD, 0, tmpstr);
359	if (softc->sysctl_tree == NULL) {
360		printf("pmpsysctlinit: unable to allocate sysctl tree\n");
361		cam_periph_release(periph);
362		return;
363	}
364
365	cam_periph_release(periph);
366}
367
368static cam_status
369pmpregister(struct cam_periph *periph, void *arg)
370{
371	struct pmp_softc *softc;
372	struct ccb_getdev *cgd;
373
374	cgd = (struct ccb_getdev *)arg;
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		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
438			softc->state = min(softc->state, PMP_STATE_PM_QUIRKS_1);
439		else
440			softc->state = min(softc->state, PMP_STATE_PRECONFIG);
441	}
442	/* Fetch user wanted device speed. */
443	if (softc->state == PMP_STATE_RESET ||
444	    softc->state == PMP_STATE_CONNECT) {
445		if (xpt_create_path(&dpath, periph,
446		    xpt_path_path_id(periph->path),
447		    softc->pm_step, 0) == CAM_REQ_CMP) {
448			bzero(&cts, sizeof(cts));
449			xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
450			cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
451			cts.type = CTS_TYPE_USER_SETTINGS;
452			xpt_action((union ccb *)&cts);
453			if (cts.xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
454				revision = cts.xport_specific.sata.revision;
455			xpt_free_path(dpath);
456		}
457	}
458	switch (softc->state) {
459	case PMP_STATE_PORTS:
460		cam_fill_ataio(ataio,
461		      pmp_retry_count,
462		      pmpdone,
463		      /*flags*/CAM_DIR_NONE,
464		      0,
465		      /*data_ptr*/NULL,
466		      /*dxfer_len*/0,
467		      pmp_default_timeout * 1000);
468		ata_pm_read_cmd(ataio, 2, 15);
469		break;
470
471	case PMP_STATE_PM_QUIRKS_1:
472	case PMP_STATE_PM_QUIRKS_3:
473		cam_fill_ataio(ataio,
474		      pmp_retry_count,
475		      pmpdone,
476		      /*flags*/CAM_DIR_NONE,
477		      0,
478		      /*data_ptr*/NULL,
479		      /*dxfer_len*/0,
480		      pmp_default_timeout * 1000);
481		ata_pm_read_cmd(ataio, 129, 15);
482		break;
483
484	case PMP_STATE_PM_QUIRKS_2:
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, 129, 15, softc->caps & ~0x1);
494		break;
495
496	case PMP_STATE_PRECONFIG:
497		/* Get/update host SATA capabilities. */
498		bzero(&cts, sizeof(cts));
499		xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE);
500		cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
501		cts.type = CTS_TYPE_CURRENT_SETTINGS;
502		xpt_action((union ccb *)&cts);
503		if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
504			softc->caps = cts.xport_specific.sata.caps;
505		else
506			softc->caps = 0;
507		cam_fill_ataio(ataio,
508		      pmp_retry_count,
509		      pmpdone,
510		      /*flags*/CAM_DIR_NONE,
511		      0,
512		      /*data_ptr*/NULL,
513		      /*dxfer_len*/0,
514		      pmp_default_timeout * 1000);
515		ata_pm_write_cmd(ataio, 0x60, 15, 0x0);
516		break;
517	case PMP_STATE_RESET:
518		cam_fill_ataio(ataio,
519		      pmp_retry_count,
520		      pmpdone,
521		      /*flags*/CAM_DIR_NONE,
522		      0,
523		      /*data_ptr*/NULL,
524		      /*dxfer_len*/0,
525		      pmp_default_timeout * 1000);
526		ata_pm_write_cmd(ataio, 2, softc->pm_step,
527		    (revision << 4) |
528		    ((softc->found & (1 << softc->pm_step)) ? 0 : 1));
529		break;
530	case PMP_STATE_CONNECT:
531		cam_fill_ataio(ataio,
532		      pmp_retry_count,
533		      pmpdone,
534		      /*flags*/CAM_DIR_NONE,
535		      0,
536		      /*data_ptr*/NULL,
537		      /*dxfer_len*/0,
538		      pmp_default_timeout * 1000);
539		ata_pm_write_cmd(ataio, 2, softc->pm_step,
540		    (revision << 4));
541		break;
542	case PMP_STATE_CHECK:
543		cam_fill_ataio(ataio,
544		      pmp_retry_count,
545		      pmpdone,
546		      /*flags*/CAM_DIR_NONE,
547		      0,
548		      /*data_ptr*/NULL,
549		      /*dxfer_len*/0,
550		      pmp_default_timeout * 1000);
551		ata_pm_read_cmd(ataio, 0, softc->pm_step);
552		break;
553	case PMP_STATE_CLEAR:
554		softc->reset = 0;
555		cam_fill_ataio(ataio,
556		      pmp_retry_count,
557		      pmpdone,
558		      /*flags*/CAM_DIR_NONE,
559		      0,
560		      /*data_ptr*/NULL,
561		      /*dxfer_len*/0,
562		      pmp_default_timeout * 1000);
563		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
564		break;
565	case PMP_STATE_CONFIG:
566		cam_fill_ataio(ataio,
567		      pmp_retry_count,
568		      pmpdone,
569		      /*flags*/CAM_DIR_NONE,
570		      0,
571		      /*data_ptr*/NULL,
572		      /*dxfer_len*/0,
573		      pmp_default_timeout * 1000);
574		ata_pm_write_cmd(ataio, 0x60, 15, 0x07 |
575		    ((softc->caps & CTS_SATA_CAPS_H_AN) ? 0x08 : 0));
576		break;
577	default:
578		break;
579	}
580	xpt_action(start_ccb);
581}
582
583static void
584pmpdone(struct cam_periph *periph, union ccb *done_ccb)
585{
586	struct ccb_trans_settings cts;
587	struct pmp_softc *softc;
588	struct ccb_ataio *ataio;
589	struct cam_path *dpath;
590	u_int32_t  priority, res;
591	int i;
592
593	softc = (struct pmp_softc *)periph->softc;
594	ataio = &done_ccb->ataio;
595
596	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("pmpdone\n"));
597
598	priority = done_ccb->ccb_h.pinfo.priority;
599
600	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
601		if (cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) {
602			return;
603		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
604			cam_release_devq(done_ccb->ccb_h.path,
605			    /*relsim_flags*/0,
606			    /*reduction*/0,
607			    /*timeout*/0,
608			    /*getcount_only*/0);
609		}
610		goto done;
611	}
612
613	if (softc->restart) {
614		softc->restart = 0;
615		xpt_release_ccb(done_ccb);
616		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
617			softc->state = min(softc->state, PMP_STATE_PM_QUIRKS_1);
618		else
619			softc->state = min(softc->state, PMP_STATE_PRECONFIG);
620		xpt_schedule(periph, priority);
621		return;
622	}
623
624	switch (softc->state) {
625	case PMP_STATE_PORTS:
626		softc->pm_ports = (ataio->res.lba_high << 24) +
627		    (ataio->res.lba_mid << 16) +
628		    (ataio->res.lba_low << 8) +
629		    ataio->res.sector_count;
630		if (pmp_hide_special) {
631			/*
632			 * This PMP declares 6 ports, while only 5 of them
633			 * are real. Port 5 is a SEMB port, probing which
634			 * causes timeouts if external SEP is not connected
635			 * to PMP over I2C.
636			 */
637			if ((softc->pm_pid == 0x37261095 ||
638			     softc->pm_pid == 0x38261095) &&
639			    softc->pm_ports == 6)
640				softc->pm_ports = 5;
641
642			/*
643			 * This PMP declares 7 ports, while only 5 of them
644			 * are real. Port 5 is a fake "Config  Disk" with
645			 * 640 sectors size. Port 6 is a SEMB port.
646			 */
647			if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
648				softc->pm_ports = 5;
649
650			/*
651			 * These PMPs have extra configuration port.
652			 */
653			if (softc->pm_pid == 0x57231095 ||
654			    softc->pm_pid == 0x57331095 ||
655			    softc->pm_pid == 0x57341095 ||
656			    softc->pm_pid == 0x57441095)
657				softc->pm_ports--;
658		}
659		printf("%s%d: %d fan-out ports\n",
660		    periph->periph_name, periph->unit_number,
661		    softc->pm_ports);
662		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
663			softc->state = PMP_STATE_PM_QUIRKS_1;
664		else
665			softc->state = PMP_STATE_PRECONFIG;
666		xpt_release_ccb(done_ccb);
667		xpt_schedule(periph, priority);
668		return;
669
670	case PMP_STATE_PM_QUIRKS_1:
671		softc->caps = (ataio->res.lba_high << 24) +
672		    (ataio->res.lba_mid << 16) +
673		    (ataio->res.lba_low << 8) +
674		    ataio->res.sector_count;
675		if (softc->caps & 0x1)
676			softc->state = PMP_STATE_PM_QUIRKS_2;
677		else
678			softc->state = PMP_STATE_PRECONFIG;
679		xpt_release_ccb(done_ccb);
680		xpt_schedule(periph, priority);
681		return;
682
683	case PMP_STATE_PM_QUIRKS_2:
684		if (bootverbose)
685			softc->state = PMP_STATE_PM_QUIRKS_3;
686		else
687			softc->state = PMP_STATE_PRECONFIG;
688		xpt_release_ccb(done_ccb);
689		xpt_schedule(periph, priority);
690		return;
691
692	case PMP_STATE_PM_QUIRKS_3:
693		res = (ataio->res.lba_high << 24) +
694		    (ataio->res.lba_mid << 16) +
695		    (ataio->res.lba_low << 8) +
696		    ataio->res.sector_count;
697		printf("%s%d: Disabling SiI3x26 R_OK in GSCR_POLL: %x->%x\n",
698		    periph->periph_name, periph->unit_number, softc->caps, res);
699		softc->state = PMP_STATE_PRECONFIG;
700		xpt_release_ccb(done_ccb);
701		xpt_schedule(periph, priority);
702		return;
703
704	case PMP_STATE_PRECONFIG:
705		softc->pm_step = 0;
706		softc->state = PMP_STATE_RESET;
707		softc->reset |= ~softc->found;
708		xpt_release_ccb(done_ccb);
709		xpt_schedule(periph, priority);
710		return;
711	case PMP_STATE_RESET:
712		softc->pm_step++;
713		if (softc->pm_step >= softc->pm_ports) {
714			softc->pm_step = 0;
715			cam_freeze_devq(periph->path);
716			cam_release_devq(periph->path,
717			    RELSIM_RELEASE_AFTER_TIMEOUT,
718			    /*reduction*/0,
719			    /*timeout*/5,
720			    /*getcount_only*/0);
721			softc->state = PMP_STATE_CONNECT;
722		}
723		xpt_release_ccb(done_ccb);
724		xpt_schedule(periph, priority);
725		return;
726	case PMP_STATE_CONNECT:
727		softc->pm_step++;
728		if (softc->pm_step >= softc->pm_ports) {
729			softc->pm_step = 0;
730			softc->pm_try = 0;
731			cam_freeze_devq(periph->path);
732			cam_release_devq(periph->path,
733			    RELSIM_RELEASE_AFTER_TIMEOUT,
734			    /*reduction*/0,
735			    /*timeout*/10,
736			    /*getcount_only*/0);
737			softc->state = PMP_STATE_CHECK;
738		}
739		xpt_release_ccb(done_ccb);
740		xpt_schedule(periph, priority);
741		return;
742	case PMP_STATE_CHECK:
743		res = (ataio->res.lba_high << 24) +
744		    (ataio->res.lba_mid << 16) +
745		    (ataio->res.lba_low << 8) +
746		    ataio->res.sector_count;
747		if (((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) ||
748		    (res & 0x600) != 0) {
749			if (bootverbose) {
750				printf("%s%d: port %d status: %08x\n",
751				    periph->periph_name, periph->unit_number,
752				    softc->pm_step, res);
753			}
754			/* Report device speed if it is online. */
755			if ((res & 0xf0f) == 0x103 &&
756			    xpt_create_path(&dpath, periph,
757			    xpt_path_path_id(periph->path),
758			    softc->pm_step, 0) == CAM_REQ_CMP) {
759				bzero(&cts, sizeof(cts));
760				xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
761				cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
762				cts.type = CTS_TYPE_CURRENT_SETTINGS;
763				cts.xport_specific.sata.revision = (res & 0x0f0) >> 4;
764				cts.xport_specific.sata.valid = CTS_SATA_VALID_REVISION;
765				cts.xport_specific.sata.caps = softc->caps &
766				    (CTS_SATA_CAPS_H_PMREQ |
767				     CTS_SATA_CAPS_H_DMAAA |
768				     CTS_SATA_CAPS_H_AN);
769				cts.xport_specific.sata.valid |= CTS_SATA_VALID_CAPS;
770				xpt_action((union ccb *)&cts);
771				xpt_free_path(dpath);
772			}
773			softc->found |= (1 << softc->pm_step);
774			softc->pm_step++;
775		} else {
776			if (softc->pm_try < 10) {
777				cam_freeze_devq(periph->path);
778				cam_release_devq(periph->path,
779				    RELSIM_RELEASE_AFTER_TIMEOUT,
780				    /*reduction*/0,
781				    /*timeout*/10,
782				    /*getcount_only*/0);
783				softc->pm_try++;
784			} else {
785				if (bootverbose) {
786					printf("%s%d: port %d status: %08x\n",
787					    periph->periph_name, periph->unit_number,
788					    softc->pm_step, res);
789				}
790				softc->found &= ~(1 << softc->pm_step);
791				if (xpt_create_path(&dpath, periph,
792				    done_ccb->ccb_h.path_id,
793				    softc->pm_step, 0) == CAM_REQ_CMP) {
794					xpt_async(AC_LOST_DEVICE, dpath, NULL);
795					xpt_free_path(dpath);
796				}
797				softc->pm_step++;
798			}
799		}
800		if (softc->pm_step >= softc->pm_ports) {
801			if (softc->reset & softc->found) {
802				cam_freeze_devq(periph->path);
803				cam_release_devq(periph->path,
804				    RELSIM_RELEASE_AFTER_TIMEOUT,
805				    /*reduction*/0,
806				    /*timeout*/1000,
807				    /*getcount_only*/0);
808			}
809			softc->state = PMP_STATE_CLEAR;
810			softc->pm_step = 0;
811		}
812		xpt_release_ccb(done_ccb);
813		xpt_schedule(periph, priority);
814		return;
815	case PMP_STATE_CLEAR:
816		softc->pm_step++;
817		if (softc->pm_step >= softc->pm_ports) {
818			softc->state = PMP_STATE_CONFIG;
819			softc->pm_step = 0;
820		}
821		xpt_release_ccb(done_ccb);
822		xpt_schedule(periph, priority);
823		return;
824	case PMP_STATE_CONFIG:
825		for (i = 0; i < softc->pm_ports; i++) {
826			union ccb *ccb;
827
828			if ((softc->found & (1 << i)) == 0)
829				continue;
830			if (xpt_create_path(&dpath, periph,
831			    xpt_path_path_id(periph->path),
832			    i, 0) != CAM_REQ_CMP) {
833				printf("pmpdone: xpt_create_path failed\n");
834				continue;
835			}
836			/* If we did hard reset to this device, inform XPT. */
837			if ((softc->reset & softc->found & (1 << i)) != 0)
838				xpt_async(AC_SENT_BDR, dpath, NULL);
839			/* If rescan requested, scan this device. */
840			if (softc->events & PMP_EV_RESCAN) {
841				ccb = xpt_alloc_ccb_nowait();
842				if (ccb == NULL) {
843					xpt_free_path(dpath);
844					goto done;
845				}
846				xpt_setup_ccb(&ccb->ccb_h, dpath, CAM_PRIORITY_XPT);
847				xpt_rescan(ccb);
848			} else
849				xpt_free_path(dpath);
850		}
851		break;
852	default:
853		break;
854	}
855done:
856	xpt_release_ccb(done_ccb);
857	softc->state = PMP_STATE_NORMAL;
858	softc->events = 0;
859	xpt_release_boot();
860	pmprelease(periph, -1);
861	cam_periph_release_locked(periph);
862}
863
864#endif /* _KERNEL */
865