ata_pmp.c revision 203108
1198389Smav/*-
2198389Smav * Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
3198389Smav * All rights reserved.
4198389Smav *
5198389Smav * Redistribution and use in source and binary forms, with or without
6198389Smav * modification, are permitted provided that the following conditions
7198389Smav * are met:
8198389Smav * 1. Redistributions of source code must retain the above copyright
9198389Smav *    notice, this list of conditions and the following disclaimer,
10198389Smav *    without modification, immediately at the beginning of the file.
11198389Smav * 2. Redistributions in binary form must reproduce the above copyright
12198389Smav *    notice, this list of conditions and the following disclaimer in the
13198389Smav *    documentation and/or other materials provided with the distribution.
14198389Smav *
15198389Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16198389Smav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17198389Smav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18198389Smav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19198389Smav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20198389Smav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21198389Smav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22198389Smav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23198389Smav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24198389Smav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25198389Smav */
26198389Smav
27198389Smav#include <sys/cdefs.h>
28198389Smav__FBSDID("$FreeBSD: head/sys/cam/ata/ata_pmp.c 203108 2010-01-28 08:41:30Z mav $");
29198389Smav
30198389Smav#include <sys/param.h>
31198389Smav
32198389Smav#ifdef _KERNEL
33198389Smav#include <sys/systm.h>
34198389Smav#include <sys/kernel.h>
35198389Smav#include <sys/bio.h>
36198389Smav#include <sys/sysctl.h>
37198389Smav#include <sys/taskqueue.h>
38198389Smav#include <sys/lock.h>
39198389Smav#include <sys/mutex.h>
40198389Smav#include <sys/conf.h>
41198389Smav#include <sys/devicestat.h>
42198389Smav#include <sys/eventhandler.h>
43198389Smav#include <sys/malloc.h>
44198389Smav#include <sys/cons.h>
45198389Smav#include <geom/geom_disk.h>
46198389Smav#endif /* _KERNEL */
47198389Smav
48198389Smav#ifndef _KERNEL
49198389Smav#include <stdio.h>
50198389Smav#include <string.h>
51198389Smav#endif /* _KERNEL */
52198389Smav
53198389Smav#include <cam/cam.h>
54198389Smav#include <cam/cam_ccb.h>
55198389Smav#include <cam/cam_periph.h>
56198389Smav#include <cam/cam_xpt_periph.h>
57200218Smav#include <cam/cam_xpt_internal.h>
58198389Smav#include <cam/cam_sim.h>
59198389Smav
60198389Smav#include <cam/ata/ata_all.h>
61198389Smav
62198389Smav#ifdef _KERNEL
63198389Smav
64198389Smavtypedef enum {
65198389Smav	PMP_STATE_NORMAL,
66198389Smav	PMP_STATE_PORTS,
67199321Smav	PMP_STATE_PRECONFIG,
68198389Smav	PMP_STATE_RESET,
69198389Smav	PMP_STATE_CONNECT,
70198389Smav	PMP_STATE_CHECK,
71198389Smav	PMP_STATE_CLEAR,
72199321Smav	PMP_STATE_CONFIG,
73198389Smav	PMP_STATE_SCAN
74198389Smav} pmp_state;
75198389Smav
76198389Smavtypedef enum {
77198389Smav	PMP_FLAG_SCTX_INIT	= 0x200
78198389Smav} pmp_flags;
79198389Smav
80198389Smavtypedef enum {
81198389Smav	PMP_CCB_PROBE		= 0x01,
82198389Smav} pmp_ccb_state;
83198389Smav
84198389Smav/* Offsets into our private area for storing information */
85198389Smav#define ccb_state	ppriv_field0
86198389Smav#define ccb_bp		ppriv_ptr1
87198389Smav
88198389Smavstruct pmp_softc {
89198389Smav	SLIST_ENTRY(pmp_softc)	links;
90198389Smav	pmp_state		state;
91198389Smav	pmp_flags		flags;
92198389Smav	uint32_t		pm_pid;
93198389Smav	uint32_t		pm_prv;
94198389Smav	int			pm_ports;
95198389Smav	int			pm_step;
96198389Smav	int			pm_try;
97198389Smav	int			found;
98198708Smav	int			reset;
99198389Smav	int			frozen;
100198708Smav	int			restart;
101203108Smav	int			events;
102203108Smav#define PMP_EV_RESET	1
103203108Smav#define PMP_EV_RESCAN	2
104198389Smav	union			ccb saved_ccb;
105198389Smav	struct task		sysctl_task;
106198389Smav	struct sysctl_ctx_list	sysctl_ctx;
107198389Smav	struct sysctl_oid	*sysctl_tree;
108198389Smav};
109198389Smav
110198389Smavstatic	periph_init_t	pmpinit;
111198389Smavstatic	void		pmpasync(void *callback_arg, u_int32_t code,
112198389Smav				struct cam_path *path, void *arg);
113198389Smavstatic	void		pmpsysctlinit(void *context, int pending);
114198389Smavstatic	periph_ctor_t	pmpregister;
115198389Smavstatic	periph_dtor_t	pmpcleanup;
116198389Smavstatic	periph_start_t	pmpstart;
117198389Smavstatic	periph_oninv_t	pmponinvalidate;
118198389Smavstatic	void		pmpdone(struct cam_periph *periph,
119198389Smav			       union ccb *done_ccb);
120198389Smav
121198389Smav#ifndef PMP_DEFAULT_TIMEOUT
122198389Smav#define PMP_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
123198389Smav#endif
124198389Smav
125198389Smav#ifndef	PMP_DEFAULT_RETRY
126198389Smav#define	PMP_DEFAULT_RETRY	1
127198389Smav#endif
128198389Smav
129198389Smavstatic int pmp_retry_count = PMP_DEFAULT_RETRY;
130198389Smavstatic int pmp_default_timeout = PMP_DEFAULT_TIMEOUT;
131198389Smav
132198389SmavSYSCTL_NODE(_kern_cam, OID_AUTO, pmp, CTLFLAG_RD, 0,
133198389Smav            "CAM Direct Access Disk driver");
134198389SmavSYSCTL_INT(_kern_cam_pmp, OID_AUTO, retry_count, CTLFLAG_RW,
135198389Smav           &pmp_retry_count, 0, "Normal I/O retry count");
136198389SmavTUNABLE_INT("kern.cam.pmp.retry_count", &pmp_retry_count);
137198389SmavSYSCTL_INT(_kern_cam_pmp, OID_AUTO, default_timeout, CTLFLAG_RW,
138198389Smav           &pmp_default_timeout, 0, "Normal I/O timeout (in seconds)");
139198389SmavTUNABLE_INT("kern.cam.pmp.default_timeout", &pmp_default_timeout);
140198389Smav
141198389Smavstatic struct periph_driver pmpdriver =
142198389Smav{
143198389Smav	pmpinit, "pmp",
144198708Smav	TAILQ_HEAD_INITIALIZER(pmpdriver.units), /* generation */ 0,
145198708Smav	CAM_PERIPH_DRV_EARLY
146198389Smav};
147198389Smav
148198389SmavPERIPHDRIVER_DECLARE(pmp, pmpdriver);
149198389Smav
150198389SmavMALLOC_DEFINE(M_ATPMP, "ata_pmp", "ata_pmp buffers");
151198389Smav
152198389Smavstatic void
153198389Smavpmpinit(void)
154198389Smav{
155198389Smav	cam_status status;
156198389Smav
157198389Smav	/*
158198389Smav	 * Install a global async callback.  This callback will
159198389Smav	 * receive async callbacks like "new device found".
160198389Smav	 */
161198389Smav	status = xpt_register_async(AC_FOUND_DEVICE, pmpasync, NULL, NULL);
162198389Smav
163198389Smav	if (status != CAM_REQ_CMP) {
164198389Smav		printf("pmp: Failed to attach master async callback "
165198389Smav		       "due to status 0x%x!\n", status);
166198389Smav	}
167198389Smav}
168198389Smav
169198389Smavstatic void
170198389Smavpmpfreeze(struct cam_periph *periph, int mask)
171198389Smav{
172198389Smav	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
173198389Smav	struct cam_path *dpath;
174198389Smav	int i;
175198389Smav
176198389Smav	mask &= ~softc->frozen;
177198389Smav	for (i = 0; i < 15; i++) {
178198389Smav		if ((mask & (1 << i)) == 0)
179198389Smav			continue;
180198389Smav		if (xpt_create_path(&dpath, periph,
181198389Smav		    xpt_path_path_id(periph->path),
182198389Smav		    i, 0) == CAM_REQ_CMP) {
183198389Smav			softc->frozen |= (1 << i);
184200218Smav			xpt_acquire_device(dpath->device);
185203108Smav			cam_freeze_devq_arg(dpath,
186203108Smav			    RELSIM_RELEASE_RUNLEVEL, CAM_RL_BUS + 1);
187198389Smav			xpt_free_path(dpath);
188198389Smav		}
189198389Smav	}
190198389Smav}
191198389Smav
192198389Smavstatic void
193198389Smavpmprelease(struct cam_periph *periph, int mask)
194198389Smav{
195198389Smav	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
196198389Smav	struct cam_path *dpath;
197198389Smav	int i;
198198389Smav
199198389Smav	mask &= softc->frozen;
200198389Smav	for (i = 0; i < 15; i++) {
201198389Smav		if ((mask & (1 << i)) == 0)
202198389Smav			continue;
203198389Smav		if (xpt_create_path(&dpath, periph,
204198389Smav		    xpt_path_path_id(periph->path),
205198389Smav		    i, 0) == CAM_REQ_CMP) {
206198389Smav			softc->frozen &= ~(1 << i);
207203108Smav			cam_release_devq(dpath,
208203108Smav			    RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_BUS + 1, FALSE);
209200218Smav			xpt_release_device(dpath->device);
210198389Smav			xpt_free_path(dpath);
211198389Smav		}
212198389Smav	}
213198389Smav}
214198389Smav
215198389Smavstatic void
216198389Smavpmponinvalidate(struct cam_periph *periph)
217198389Smav{
218198389Smav	struct pmp_softc *softc;
219198389Smav	struct cam_path *dpath;
220198389Smav	int i;
221198389Smav
222198389Smav	softc = (struct pmp_softc *)periph->softc;
223198389Smav
224198389Smav	/*
225198389Smav	 * De-register any async callbacks.
226198389Smav	 */
227198389Smav	xpt_register_async(0, pmpasync, periph, periph->path);
228198389Smav
229198389Smav	for (i = 0; i < 15; i++) {
230198389Smav		if (xpt_create_path(&dpath, periph,
231198389Smav		    xpt_path_path_id(periph->path),
232198389Smav		    i, 0) == CAM_REQ_CMP) {
233198389Smav			xpt_async(AC_LOST_DEVICE, dpath, NULL);
234198389Smav			xpt_free_path(dpath);
235198389Smav		}
236198389Smav	}
237200218Smav	pmprelease(periph, -1);
238198389Smav	xpt_print(periph->path, "lost device\n");
239198389Smav}
240198389Smav
241198389Smavstatic void
242198389Smavpmpcleanup(struct cam_periph *periph)
243198389Smav{
244198389Smav	struct pmp_softc *softc;
245198389Smav
246198389Smav	softc = (struct pmp_softc *)periph->softc;
247198389Smav
248198389Smav	xpt_print(periph->path, "removing device entry\n");
249198389Smav	cam_periph_unlock(periph);
250198389Smav
251198389Smav	/*
252198389Smav	 * If we can't free the sysctl tree, oh well...
253198389Smav	 */
254198389Smav	if ((softc->flags & PMP_FLAG_SCTX_INIT) != 0
255198389Smav	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
256198389Smav		xpt_print(periph->path, "can't remove sysctl context\n");
257198389Smav	}
258198389Smav
259198389Smav	free(softc, M_DEVBUF);
260198389Smav	cam_periph_lock(periph);
261198389Smav}
262198389Smav
263198389Smavstatic void
264198389Smavpmpasync(void *callback_arg, u_int32_t code,
265198389Smav	struct cam_path *path, void *arg)
266198389Smav{
267198389Smav	struct cam_periph *periph;
268198389Smav	struct pmp_softc *softc;
269198389Smav
270198389Smav	periph = (struct cam_periph *)callback_arg;
271198389Smav	switch (code) {
272198389Smav	case AC_FOUND_DEVICE:
273198389Smav	{
274198389Smav		struct ccb_getdev *cgd;
275198389Smav		cam_status status;
276198389Smav
277198389Smav		cgd = (struct ccb_getdev *)arg;
278198389Smav		if (cgd == NULL)
279198389Smav			break;
280198389Smav
281198389Smav		if (cgd->protocol != PROTO_SATAPM)
282198389Smav			break;
283198389Smav
284198389Smav		/*
285198389Smav		 * Allocate a peripheral instance for
286198389Smav		 * this device and start the probe
287198389Smav		 * process.
288198389Smav		 */
289198389Smav		status = cam_periph_alloc(pmpregister, pmponinvalidate,
290198389Smav					  pmpcleanup, pmpstart,
291198389Smav					  "pmp", CAM_PERIPH_BIO,
292198389Smav					  cgd->ccb_h.path, pmpasync,
293198389Smav					  AC_FOUND_DEVICE, cgd);
294198389Smav
295198389Smav		if (status != CAM_REQ_CMP
296198389Smav		 && status != CAM_REQ_INPROG)
297198389Smav			printf("pmpasync: Unable to attach to new device "
298198389Smav				"due to status 0x%x\n", status);
299198389Smav		break;
300198389Smav	}
301198389Smav	case AC_SCSI_AEN:
302198389Smav	case AC_SENT_BDR:
303198389Smav	case AC_BUS_RESET:
304198389Smav		softc = (struct pmp_softc *)periph->softc;
305198389Smav		cam_periph_async(periph, code, path, arg);
306203108Smav		if (code == AC_SCSI_AEN)
307203108Smav			softc->events |= PMP_EV_RESCAN;
308203108Smav		else
309203108Smav			softc->events |= PMP_EV_RESET;
310203108Smav		if (code == AC_SCSI_AEN && softc->state != PMP_STATE_NORMAL)
311198389Smav			break;
312203108Smav		xpt_hold_boot();
313203108Smav		pmpfreeze(periph, softc->found);
314198389Smav		if (code == AC_SENT_BDR || code == AC_BUS_RESET)
315198389Smav			softc->found = 0; /* We have to reset everything. */
316198708Smav		if (softc->state == PMP_STATE_NORMAL) {
317203108Smav			softc->state = PMP_STATE_PRECONFIG;
318198708Smav			cam_periph_acquire(periph);
319203108Smav			xpt_schedule(periph, CAM_PRIORITY_DEV);
320198708Smav		} else
321198708Smav			softc->restart = 1;
322198389Smav		break;
323198389Smav	default:
324198389Smav		cam_periph_async(periph, code, path, arg);
325198389Smav		break;
326198389Smav	}
327198389Smav}
328198389Smav
329198389Smavstatic void
330198389Smavpmpsysctlinit(void *context, int pending)
331198389Smav{
332198389Smav	struct cam_periph *periph;
333198389Smav	struct pmp_softc *softc;
334198389Smav	char tmpstr[80], tmpstr2[80];
335198389Smav
336198389Smav	periph = (struct cam_periph *)context;
337198389Smav	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
338198389Smav		return;
339198389Smav
340198389Smav	softc = (struct pmp_softc *)periph->softc;
341198389Smav	snprintf(tmpstr, sizeof(tmpstr), "CAM PMP unit %d", periph->unit_number);
342198389Smav	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
343198389Smav
344198389Smav	sysctl_ctx_init(&softc->sysctl_ctx);
345198389Smav	softc->flags |= PMP_FLAG_SCTX_INIT;
346198389Smav	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
347198389Smav		SYSCTL_STATIC_CHILDREN(_kern_cam_pmp), OID_AUTO, tmpstr2,
348198389Smav		CTLFLAG_RD, 0, tmpstr);
349198389Smav	if (softc->sysctl_tree == NULL) {
350198389Smav		printf("pmpsysctlinit: unable to allocate sysctl tree\n");
351198389Smav		cam_periph_release(periph);
352198389Smav		return;
353198389Smav	}
354198389Smav
355198389Smav	cam_periph_release(periph);
356198389Smav}
357198389Smav
358198389Smavstatic cam_status
359198389Smavpmpregister(struct cam_periph *periph, void *arg)
360198389Smav{
361198389Smav	struct pmp_softc *softc;
362198389Smav	struct ccb_getdev *cgd;
363198389Smav
364198389Smav	cgd = (struct ccb_getdev *)arg;
365198389Smav	if (periph == NULL) {
366198389Smav		printf("pmpregister: periph was NULL!!\n");
367198389Smav		return(CAM_REQ_CMP_ERR);
368198389Smav	}
369198389Smav
370198389Smav	if (cgd == NULL) {
371198389Smav		printf("pmpregister: no getdev CCB, can't register device\n");
372198389Smav		return(CAM_REQ_CMP_ERR);
373198389Smav	}
374198389Smav
375198389Smav	softc = (struct pmp_softc *)malloc(sizeof(*softc), M_DEVBUF,
376198389Smav	    M_NOWAIT|M_ZERO);
377198389Smav
378198389Smav	if (softc == NULL) {
379198389Smav		printf("pmpregister: Unable to probe new device. "
380198389Smav		       "Unable to allocate softc\n");
381198389Smav		return(CAM_REQ_CMP_ERR);
382198389Smav	}
383198389Smav	periph->softc = softc;
384198389Smav
385198389Smav	softc->pm_pid = ((uint32_t *)&cgd->ident_data)[0];
386198389Smav	softc->pm_prv = ((uint32_t *)&cgd->ident_data)[1];
387198389Smav	TASK_INIT(&softc->sysctl_task, 0, pmpsysctlinit, periph);
388198389Smav
389198389Smav	xpt_announce_periph(periph, NULL);
390198389Smav
391198389Smav	/*
392198389Smav	 * Add async callbacks for bus reset and
393198389Smav	 * bus device reset calls.  I don't bother
394198389Smav	 * checking if this fails as, in most cases,
395198389Smav	 * the system will function just fine without
396198389Smav	 * them and the only alternative would be to
397198389Smav	 * not attach the device on failure.
398198389Smav	 */
399198389Smav	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
400198389Smav		AC_SCSI_AEN, pmpasync, periph, periph->path);
401198389Smav
402198389Smav	/*
403198389Smav	 * Take an exclusive refcount on the periph while pmpstart is called
404198389Smav	 * to finish the probe.  The reference will be dropped in pmpdone at
405198389Smav	 * the end of probe.
406198389Smav	 */
407198389Smav	(void)cam_periph_acquire(periph);
408203108Smav	xpt_hold_boot();
409203108Smav	softc->state = PMP_STATE_PORTS;
410203108Smav	softc->events = PMP_EV_RESCAN;
411203108Smav	xpt_schedule(periph, CAM_PRIORITY_DEV);
412198389Smav
413198389Smav	return(CAM_REQ_CMP);
414198389Smav}
415198389Smav
416198389Smavstatic void
417198389Smavpmpstart(struct cam_periph *periph, union ccb *start_ccb)
418198389Smav{
419203108Smav	struct ccb_trans_settings cts;
420198389Smav	struct ccb_ataio *ataio;
421198389Smav	struct pmp_softc *softc;
422203108Smav	struct cam_path *dpath;
423203108Smav	int revision = 0;
424198389Smav
425198389Smav	softc = (struct pmp_softc *)periph->softc;
426198389Smav	ataio = &start_ccb->ataio;
427198708Smav
428198708Smav	if (softc->restart) {
429198708Smav		softc->restart = 0;
430203108Smav		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
431198708Smav	}
432203108Smav	/* Fetch user wanted device speed. */
433203108Smav	if (softc->state == PMP_STATE_RESET ||
434203108Smav	    softc->state == PMP_STATE_CONNECT) {
435203108Smav		if (xpt_create_path(&dpath, periph,
436203108Smav		    xpt_path_path_id(periph->path),
437203108Smav		    softc->pm_step, 0) == CAM_REQ_CMP) {
438203108Smav			bzero(&cts, sizeof(cts));
439203108Smav			xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
440203108Smav			cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
441203108Smav			cts.type = CTS_TYPE_USER_SETTINGS;
442203108Smav			xpt_action((union ccb *)&cts);
443203108Smav			if (cts.xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
444203108Smav				revision = cts.xport_specific.sata.revision;
445203108Smav			xpt_free_path(dpath);
446203108Smav		}
447203108Smav	}
448198389Smav	switch (softc->state) {
449198389Smav	case PMP_STATE_PORTS:
450198389Smav		cam_fill_ataio(ataio,
451198389Smav		      pmp_retry_count,
452198389Smav		      pmpdone,
453198389Smav		      /*flags*/CAM_DIR_NONE,
454198389Smav		      0,
455198389Smav		      /*data_ptr*/NULL,
456198389Smav		      /*dxfer_len*/0,
457198389Smav		      pmp_default_timeout * 1000);
458198389Smav		ata_pm_read_cmd(ataio, 2, 15);
459198389Smav		break;
460199321Smav	case PMP_STATE_PRECONFIG:
461198389Smav		cam_fill_ataio(ataio,
462198389Smav		      pmp_retry_count,
463198389Smav		      pmpdone,
464198389Smav		      /*flags*/CAM_DIR_NONE,
465198389Smav		      0,
466198389Smav		      /*data_ptr*/NULL,
467198389Smav		      /*dxfer_len*/0,
468198389Smav		      pmp_default_timeout * 1000);
469199321Smav		ata_pm_write_cmd(ataio, 0x60, 15, 0x0);
470198389Smav		break;
471198389Smav	case PMP_STATE_RESET:
472198389Smav		cam_fill_ataio(ataio,
473198389Smav		      pmp_retry_count,
474198389Smav		      pmpdone,
475198389Smav		      /*flags*/CAM_DIR_NONE,
476198389Smav		      0,
477198389Smav		      /*data_ptr*/NULL,
478198389Smav		      /*dxfer_len*/0,
479198389Smav		      pmp_default_timeout * 1000);
480198389Smav		ata_pm_write_cmd(ataio, 2, softc->pm_step,
481203108Smav		    (revision << 4) |
482203108Smav		    ((softc->found & (1 << softc->pm_step)) ? 0 : 1));
483198389Smav		break;
484198389Smav	case PMP_STATE_CONNECT:
485198389Smav		cam_fill_ataio(ataio,
486198389Smav		      pmp_retry_count,
487198389Smav		      pmpdone,
488198389Smav		      /*flags*/CAM_DIR_NONE,
489198389Smav		      0,
490198389Smav		      /*data_ptr*/NULL,
491198389Smav		      /*dxfer_len*/0,
492198389Smav		      pmp_default_timeout * 1000);
493203108Smav		ata_pm_write_cmd(ataio, 2, softc->pm_step,
494203108Smav		    (revision << 4));
495198389Smav		break;
496198389Smav	case PMP_STATE_CHECK:
497198389Smav		cam_fill_ataio(ataio,
498198389Smav		      pmp_retry_count,
499198389Smav		      pmpdone,
500198389Smav		      /*flags*/CAM_DIR_NONE,
501198389Smav		      0,
502198389Smav		      /*data_ptr*/NULL,
503198389Smav		      /*dxfer_len*/0,
504198389Smav		      pmp_default_timeout * 1000);
505198389Smav		ata_pm_read_cmd(ataio, 0, softc->pm_step);
506198389Smav		break;
507198389Smav	case PMP_STATE_CLEAR:
508198708Smav		softc->reset = 0;
509198389Smav		cam_fill_ataio(ataio,
510198389Smav		      pmp_retry_count,
511198389Smav		      pmpdone,
512198389Smav		      /*flags*/CAM_DIR_NONE,
513198389Smav		      0,
514198389Smav		      /*data_ptr*/NULL,
515198389Smav		      /*dxfer_len*/0,
516198389Smav		      pmp_default_timeout * 1000);
517198389Smav		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
518198389Smav		break;
519199321Smav	case PMP_STATE_CONFIG:
520199321Smav		cam_fill_ataio(ataio,
521199321Smav		      pmp_retry_count,
522199321Smav		      pmpdone,
523199321Smav		      /*flags*/CAM_DIR_NONE,
524199321Smav		      0,
525199321Smav		      /*data_ptr*/NULL,
526199321Smav		      /*dxfer_len*/0,
527199321Smav		      pmp_default_timeout * 1000);
528199321Smav		ata_pm_write_cmd(ataio, 0x60, 15, 0xf);
529199321Smav		break;
530198389Smav	default:
531198389Smav		break;
532198389Smav	}
533198389Smav	xpt_action(start_ccb);
534198389Smav}
535198389Smav
536198389Smavstatic void
537198389Smavpmpdone(struct cam_periph *periph, union ccb *done_ccb)
538198389Smav{
539199747Smav	struct ccb_trans_settings cts;
540198389Smav	struct pmp_softc *softc;
541198389Smav	struct ccb_ataio *ataio;
542198389Smav	struct cam_path *path, *dpath;
543198708Smav	u_int32_t  priority, res;
544203108Smav	int i;
545198389Smav
546198389Smav	softc = (struct pmp_softc *)periph->softc;
547198389Smav	ataio = &done_ccb->ataio;
548198389Smav
549198389Smav	CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("pmpdone\n"));
550198389Smav
551198389Smav	path = done_ccb->ccb_h.path;
552198389Smav	priority = done_ccb->ccb_h.pinfo.priority;
553198389Smav
554198708Smav	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
555198708Smav		if (cam_periph_error(done_ccb, 0, 0,
556198708Smav		    &softc->saved_ccb) == ERESTART) {
557198389Smav			return;
558198389Smav		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
559198708Smav			cam_release_devq(done_ccb->ccb_h.path,
560198708Smav			    /*relsim_flags*/0,
561198708Smav			    /*reduction*/0,
562198708Smav			    /*timeout*/0,
563198708Smav			    /*getcount_only*/0);
564198389Smav		}
565198708Smav		goto done;
566198708Smav	}
567198708Smav
568198708Smav	if (softc->restart) {
569198708Smav		softc->restart = 0;
570198389Smav		xpt_release_ccb(done_ccb);
571203108Smav		softc->state = min(softc->state, PMP_STATE_PRECONFIG);
572198708Smav		xpt_schedule(periph, priority);
573198708Smav		return;
574198708Smav	}
575198708Smav
576198708Smav	switch (softc->state) {
577198708Smav	case PMP_STATE_PORTS:
578198708Smav		softc->pm_ports = (done_ccb->ataio.res.lba_high << 24) +
579198708Smav		    (done_ccb->ataio.res.lba_mid << 16) +
580198708Smav		    (done_ccb->ataio.res.lba_low << 8) +
581198708Smav		    done_ccb->ataio.res.sector_count;
582199321Smav		/* This PMP declares 6 ports, while only 5 of them are real.
583198708Smav		 * Port 5 is enclosure management bridge port, which has implementation
584198708Smav		 * problems, causing probe faults. Hide it for now. */
585198708Smav		if (softc->pm_pid == 0x37261095 && softc->pm_ports == 6)
586198708Smav			softc->pm_ports = 5;
587199321Smav		/* This PMP declares 7 ports, while only 5 of them are real.
588198708Smav		 * Port 5 is some fake "Config  Disk" with 640 sectors size,
589198708Smav		 * port 6 is enclosure management bridge port.
590198708Smav		 * Both fake ports has implementation problems, causing
591198708Smav		 * probe faults. Hide them for now. */
592198708Smav		if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
593198708Smav			softc->pm_ports = 5;
594199321Smav		/* These PMPs declare one more port then actually have,
595199321Smav		 * for configuration purposes. Hide it for now. */
596199321Smav		if (softc->pm_pid == 0x57231095 || softc->pm_pid == 0x57331095 ||
597199321Smav		    softc->pm_pid == 0x57341095 || softc->pm_pid == 0x57441095)
598199321Smav			softc->pm_ports--;
599200218Smav		printf("%s%d: %d fan-out ports\n",
600200218Smav		    periph->periph_name, periph->unit_number,
601200218Smav		    softc->pm_ports);
602199321Smav		softc->state = PMP_STATE_PRECONFIG;
603198708Smav		xpt_release_ccb(done_ccb);
604198708Smav		xpt_schedule(periph, priority);
605198708Smav		return;
606199321Smav	case PMP_STATE_PRECONFIG:
607198708Smav		softc->pm_step = 0;
608198708Smav		softc->state = PMP_STATE_RESET;
609198708Smav		softc->reset |= ~softc->found;
610198389Smav		xpt_release_ccb(done_ccb);
611198708Smav		xpt_schedule(periph, priority);
612198708Smav		return;
613198389Smav	case PMP_STATE_RESET:
614198708Smav		softc->pm_step++;
615198708Smav		if (softc->pm_step >= softc->pm_ports) {
616198708Smav			softc->pm_step = 0;
617198708Smav			cam_freeze_devq(periph->path);
618198708Smav			cam_release_devq(periph->path,
619198708Smav			    RELSIM_RELEASE_AFTER_TIMEOUT,
620198708Smav			    /*reduction*/0,
621198708Smav			    /*timeout*/5,
622198708Smav			    /*getcount_only*/0);
623198708Smav			softc->state = PMP_STATE_CONNECT;
624198389Smav		}
625198389Smav		xpt_release_ccb(done_ccb);
626198708Smav		xpt_schedule(periph, priority);
627198708Smav		return;
628198389Smav	case PMP_STATE_CONNECT:
629198708Smav		softc->pm_step++;
630198708Smav		if (softc->pm_step >= softc->pm_ports) {
631198708Smav			softc->pm_step = 0;
632198708Smav			softc->pm_try = 0;
633198708Smav			cam_freeze_devq(periph->path);
634198708Smav			cam_release_devq(periph->path,
635198708Smav			    RELSIM_RELEASE_AFTER_TIMEOUT,
636198708Smav			    /*reduction*/0,
637198708Smav			    /*timeout*/10,
638198708Smav			    /*getcount_only*/0);
639198708Smav			softc->state = PMP_STATE_CHECK;
640198389Smav		}
641198389Smav		xpt_release_ccb(done_ccb);
642198708Smav		xpt_schedule(periph, priority);
643198708Smav		return;
644198389Smav	case PMP_STATE_CHECK:
645198708Smav		res = (done_ccb->ataio.res.lba_high << 24) +
646198708Smav		    (done_ccb->ataio.res.lba_mid << 16) +
647198708Smav		    (done_ccb->ataio.res.lba_low << 8) +
648198708Smav		    done_ccb->ataio.res.sector_count;
649198708Smav		if ((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) {
650200218Smav			if (bootverbose) {
651200218Smav				printf("%s%d: port %d status: %08x\n",
652200218Smav				    periph->periph_name, periph->unit_number,
653200218Smav				    softc->pm_step, res);
654200218Smav			}
655199747Smav			/* Report device speed. */
656199747Smav			if (xpt_create_path(&dpath, periph,
657199747Smav			    xpt_path_path_id(periph->path),
658199747Smav			    softc->pm_step, 0) == CAM_REQ_CMP) {
659199747Smav				bzero(&cts, sizeof(cts));
660203108Smav				xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
661199747Smav				cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
662199747Smav				cts.type = CTS_TYPE_CURRENT_SETTINGS;
663199747Smav				cts.xport_specific.sata.revision = (res & 0x0f0) >> 4;
664199747Smav				cts.xport_specific.sata.valid = CTS_SATA_VALID_REVISION;
665199747Smav				xpt_action((union ccb *)&cts);
666199747Smav				xpt_free_path(dpath);
667199747Smav			}
668198708Smav			softc->found |= (1 << softc->pm_step);
669198708Smav			softc->pm_step++;
670198708Smav		} else {
671198708Smav			if (softc->pm_try < 10) {
672198708Smav				cam_freeze_devq(periph->path);
673198708Smav				cam_release_devq(periph->path,
674198708Smav				    RELSIM_RELEASE_AFTER_TIMEOUT,
675198708Smav				    /*reduction*/0,
676198708Smav				    /*timeout*/10,
677198708Smav				    /*getcount_only*/0);
678198708Smav				softc->pm_try++;
679198708Smav			} else {
680200218Smav				if (bootverbose) {
681200218Smav					printf("%s%d: port %d status: %08x\n",
682200218Smav					    periph->periph_name, periph->unit_number,
683200218Smav					    softc->pm_step, res);
684200218Smav				}
685198708Smav				softc->found &= ~(1 << softc->pm_step);
686198708Smav				if (xpt_create_path(&dpath, periph,
687198708Smav				    done_ccb->ccb_h.path_id,
688198708Smav				    softc->pm_step, 0) == CAM_REQ_CMP) {
689198708Smav					xpt_async(AC_LOST_DEVICE, dpath, NULL);
690198708Smav					xpt_free_path(dpath);
691198708Smav				}
692198389Smav				softc->pm_step++;
693198389Smav			}
694198708Smav		}
695198708Smav		if (softc->pm_step >= softc->pm_ports) {
696198708Smav			if (softc->reset & softc->found) {
697198708Smav				cam_freeze_devq(periph->path);
698198708Smav				cam_release_devq(periph->path,
699198708Smav				    RELSIM_RELEASE_AFTER_TIMEOUT,
700198708Smav				    /*reduction*/0,
701198708Smav				    /*timeout*/1000,
702198708Smav				    /*getcount_only*/0);
703198389Smav			}
704198708Smav			softc->state = PMP_STATE_CLEAR;
705198708Smav			softc->pm_step = 0;
706198389Smav		}
707198389Smav		xpt_release_ccb(done_ccb);
708198708Smav		xpt_schedule(periph, priority);
709198708Smav		return;
710198389Smav	case PMP_STATE_CLEAR:
711198708Smav		softc->pm_step++;
712199321Smav		if (softc->pm_step >= softc->pm_ports) {
713199321Smav			softc->state = PMP_STATE_CONFIG;
714198708Smav			softc->pm_step = 0;
715199321Smav		}
716199321Smav		xpt_release_ccb(done_ccb);
717199321Smav		xpt_schedule(periph, priority);
718199321Smav		return;
719199321Smav	case PMP_STATE_CONFIG:
720203108Smav		for (i = 0; i < softc->pm_ports; i++) {
721203108Smav			union ccb *ccb;
722203108Smav
723203108Smav			if ((softc->found & (1 << i)) == 0)
724203108Smav				continue;
725203108Smav			if (xpt_create_path(&dpath, periph,
726203108Smav			    xpt_path_path_id(periph->path),
727203108Smav			    i, 0) != CAM_REQ_CMP) {
728203108Smav				printf("pmpdone: xpt_create_path failed"
729203108Smav				    ", bus scan halted\n");
730203108Smav				xpt_free_ccb(done_ccb);
731203108Smav				goto done;
732203108Smav			}
733203108Smav			/* If we did hard reset to this device, inform XPT. */
734203108Smav			if ((softc->reset & softc->found & (1 << i)) != 0)
735203108Smav				xpt_async(AC_SENT_BDR, dpath, NULL);
736203108Smav			/* If rescan requested, scan this device. */
737203108Smav			if (softc->events & PMP_EV_RESCAN) {
738203108Smav				ccb = xpt_alloc_ccb_nowait();
739203108Smav				if (ccb == NULL) {
740203108Smav					xpt_free_path(dpath);
741203108Smav					goto done;
742203108Smav				}
743203108Smav				xpt_setup_ccb(&ccb->ccb_h, dpath, CAM_PRIORITY_XPT);
744203108Smav				xpt_rescan(ccb);
745203108Smav			} else
746203108Smav				xpt_free_path(dpath);
747198389Smav		}
748198389Smav		break;
749198389Smav	default:
750198389Smav		break;
751198389Smav	}
752198708Smavdone:
753198708Smav	xpt_release_ccb(done_ccb);
754198389Smav	softc->state = PMP_STATE_NORMAL;
755203108Smav	softc->events = 0;
756203108Smav	xpt_release_boot();
757198389Smav	pmprelease(periph, -1);
758198389Smav	cam_periph_release_locked(periph);
759198389Smav}
760198389Smav
761198389Smav#endif /* _KERNEL */
762