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: releng/10.3/sys/cam/ata/ata_pmp.c 290768 2015-11-13 10:34:14Z 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,
67250508Smav	PMP_STATE_PM_QUIRKS_1,
68250508Smav	PMP_STATE_PM_QUIRKS_2,
69250508Smav	PMP_STATE_PM_QUIRKS_3,
70199321Smav	PMP_STATE_PRECONFIG,
71198389Smav	PMP_STATE_RESET,
72198389Smav	PMP_STATE_CONNECT,
73198389Smav	PMP_STATE_CHECK,
74198389Smav	PMP_STATE_CLEAR,
75199321Smav	PMP_STATE_CONFIG,
76198389Smav	PMP_STATE_SCAN
77198389Smav} pmp_state;
78198389Smav
79198389Smavtypedef enum {
80198389Smav	PMP_FLAG_SCTX_INIT	= 0x200
81198389Smav} pmp_flags;
82198389Smav
83198389Smavtypedef enum {
84198389Smav	PMP_CCB_PROBE		= 0x01,
85198389Smav} pmp_ccb_state;
86198389Smav
87198389Smav/* Offsets into our private area for storing information */
88198389Smav#define ccb_state	ppriv_field0
89198389Smav#define ccb_bp		ppriv_ptr1
90198389Smav
91198389Smavstruct pmp_softc {
92198389Smav	SLIST_ENTRY(pmp_softc)	links;
93198389Smav	pmp_state		state;
94198389Smav	pmp_flags		flags;
95198389Smav	uint32_t		pm_pid;
96198389Smav	uint32_t		pm_prv;
97198389Smav	int			pm_ports;
98198389Smav	int			pm_step;
99198389Smav	int			pm_try;
100198389Smav	int			found;
101198708Smav	int			reset;
102198389Smav	int			frozen;
103198708Smav	int			restart;
104203108Smav	int			events;
105203108Smav#define PMP_EV_RESET	1
106203108Smav#define PMP_EV_RESCAN	2
107207499Smav	u_int			caps;
108198389Smav	struct task		sysctl_task;
109198389Smav	struct sysctl_ctx_list	sysctl_ctx;
110198389Smav	struct sysctl_oid	*sysctl_tree;
111198389Smav};
112198389Smav
113198389Smavstatic	periph_init_t	pmpinit;
114198389Smavstatic	void		pmpasync(void *callback_arg, u_int32_t code,
115198389Smav				struct cam_path *path, void *arg);
116198389Smavstatic	void		pmpsysctlinit(void *context, int pending);
117198389Smavstatic	periph_ctor_t	pmpregister;
118198389Smavstatic	periph_dtor_t	pmpcleanup;
119198389Smavstatic	periph_start_t	pmpstart;
120198389Smavstatic	periph_oninv_t	pmponinvalidate;
121198389Smavstatic	void		pmpdone(struct cam_periph *periph,
122198389Smav			       union ccb *done_ccb);
123198389Smav
124198389Smav#ifndef PMP_DEFAULT_TIMEOUT
125198389Smav#define PMP_DEFAULT_TIMEOUT 30	/* Timeout in seconds */
126198389Smav#endif
127198389Smav
128198389Smav#ifndef	PMP_DEFAULT_RETRY
129198389Smav#define	PMP_DEFAULT_RETRY	1
130198389Smav#endif
131198389Smav
132235982Smav#ifndef	PMP_DEFAULT_HIDE_SPECIAL
133235982Smav#define	PMP_DEFAULT_HIDE_SPECIAL	1
134235982Smav#endif
135235982Smav
136198389Smavstatic int pmp_retry_count = PMP_DEFAULT_RETRY;
137198389Smavstatic int pmp_default_timeout = PMP_DEFAULT_TIMEOUT;
138235982Smavstatic int pmp_hide_special = PMP_DEFAULT_HIDE_SPECIAL;
139198389Smav
140227309Sedstatic SYSCTL_NODE(_kern_cam, OID_AUTO, pmp, CTLFLAG_RD, 0,
141198389Smav            "CAM Direct Access Disk driver");
142198389SmavSYSCTL_INT(_kern_cam_pmp, OID_AUTO, retry_count, CTLFLAG_RW,
143198389Smav           &pmp_retry_count, 0, "Normal I/O retry count");
144198389SmavTUNABLE_INT("kern.cam.pmp.retry_count", &pmp_retry_count);
145198389SmavSYSCTL_INT(_kern_cam_pmp, OID_AUTO, default_timeout, CTLFLAG_RW,
146198389Smav           &pmp_default_timeout, 0, "Normal I/O timeout (in seconds)");
147198389SmavTUNABLE_INT("kern.cam.pmp.default_timeout", &pmp_default_timeout);
148235982SmavSYSCTL_INT(_kern_cam_pmp, OID_AUTO, hide_special, CTLFLAG_RW,
149235982Smav           &pmp_hide_special, 0, "Hide extra ports");
150235982SmavTUNABLE_INT("kern.cam.pmp.hide_special", &pmp_hide_special);
151198389Smav
152198389Smavstatic struct periph_driver pmpdriver =
153198389Smav{
154198389Smav	pmpinit, "pmp",
155198708Smav	TAILQ_HEAD_INITIALIZER(pmpdriver.units), /* generation */ 0,
156198708Smav	CAM_PERIPH_DRV_EARLY
157198389Smav};
158198389Smav
159198389SmavPERIPHDRIVER_DECLARE(pmp, pmpdriver);
160198389Smav
161198389Smavstatic void
162198389Smavpmpinit(void)
163198389Smav{
164198389Smav	cam_status status;
165198389Smav
166198389Smav	/*
167198389Smav	 * Install a global async callback.  This callback will
168198389Smav	 * receive async callbacks like "new device found".
169198389Smav	 */
170198389Smav	status = xpt_register_async(AC_FOUND_DEVICE, pmpasync, NULL, NULL);
171198389Smav
172198389Smav	if (status != CAM_REQ_CMP) {
173198389Smav		printf("pmp: Failed to attach master async callback "
174198389Smav		       "due to status 0x%x!\n", status);
175198389Smav	}
176198389Smav}
177198389Smav
178198389Smavstatic void
179198389Smavpmpfreeze(struct cam_periph *periph, int mask)
180198389Smav{
181198389Smav	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
182198389Smav	struct cam_path *dpath;
183198389Smav	int i;
184198389Smav
185198389Smav	mask &= ~softc->frozen;
186198389Smav	for (i = 0; i < 15; i++) {
187198389Smav		if ((mask & (1 << i)) == 0)
188198389Smav			continue;
189198389Smav		if (xpt_create_path(&dpath, periph,
190198389Smav		    xpt_path_path_id(periph->path),
191198389Smav		    i, 0) == CAM_REQ_CMP) {
192198389Smav			softc->frozen |= (1 << i);
193200218Smav			xpt_acquire_device(dpath->device);
194249438Smav			cam_freeze_devq(dpath);
195198389Smav			xpt_free_path(dpath);
196198389Smav		}
197198389Smav	}
198198389Smav}
199198389Smav
200198389Smavstatic void
201198389Smavpmprelease(struct cam_periph *periph, int mask)
202198389Smav{
203198389Smav	struct pmp_softc *softc = (struct pmp_softc *)periph->softc;
204198389Smav	struct cam_path *dpath;
205198389Smav	int i;
206198389Smav
207198389Smav	mask &= softc->frozen;
208198389Smav	for (i = 0; i < 15; i++) {
209198389Smav		if ((mask & (1 << i)) == 0)
210198389Smav			continue;
211198389Smav		if (xpt_create_path(&dpath, periph,
212198389Smav		    xpt_path_path_id(periph->path),
213198389Smav		    i, 0) == CAM_REQ_CMP) {
214198389Smav			softc->frozen &= ~(1 << i);
215249438Smav			cam_release_devq(dpath, 0, 0, 0, FALSE);
216200218Smav			xpt_release_device(dpath->device);
217198389Smav			xpt_free_path(dpath);
218198389Smav		}
219198389Smav	}
220198389Smav}
221198389Smav
222198389Smavstatic void
223198389Smavpmponinvalidate(struct cam_periph *periph)
224198389Smav{
225198389Smav	struct cam_path *dpath;
226198389Smav	int i;
227198389Smav
228198389Smav	/*
229198389Smav	 * De-register any async callbacks.
230198389Smav	 */
231198389Smav	xpt_register_async(0, pmpasync, periph, periph->path);
232198389Smav
233198389Smav	for (i = 0; i < 15; i++) {
234198389Smav		if (xpt_create_path(&dpath, periph,
235198389Smav		    xpt_path_path_id(periph->path),
236198389Smav		    i, 0) == CAM_REQ_CMP) {
237198389Smav			xpt_async(AC_LOST_DEVICE, dpath, NULL);
238198389Smav			xpt_free_path(dpath);
239198389Smav		}
240198389Smav	}
241200218Smav	pmprelease(periph, -1);
242198389Smav}
243198389Smav
244198389Smavstatic void
245198389Smavpmpcleanup(struct cam_periph *periph)
246198389Smav{
247198389Smav	struct pmp_softc *softc;
248198389Smav
249198389Smav	softc = (struct pmp_softc *)periph->softc;
250198389Smav
251198389Smav	cam_periph_unlock(periph);
252198389Smav
253198389Smav	/*
254198389Smav	 * If we can't free the sysctl tree, oh well...
255198389Smav	 */
256198389Smav	if ((softc->flags & PMP_FLAG_SCTX_INIT) != 0
257198389Smav	    && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
258198389Smav		xpt_print(periph->path, "can't remove sysctl context\n");
259198389Smav	}
260198389Smav
261198389Smav	free(softc, M_DEVBUF);
262198389Smav	cam_periph_lock(periph);
263198389Smav}
264198389Smav
265198389Smavstatic void
266198389Smavpmpasync(void *callback_arg, u_int32_t code,
267198389Smav	struct cam_path *path, void *arg)
268198389Smav{
269198389Smav	struct cam_periph *periph;
270198389Smav	struct pmp_softc *softc;
271198389Smav
272198389Smav	periph = (struct cam_periph *)callback_arg;
273198389Smav	switch (code) {
274198389Smav	case AC_FOUND_DEVICE:
275198389Smav	{
276198389Smav		struct ccb_getdev *cgd;
277198389Smav		cam_status status;
278198389Smav
279198389Smav		cgd = (struct ccb_getdev *)arg;
280198389Smav		if (cgd == NULL)
281198389Smav			break;
282198389Smav
283198389Smav		if (cgd->protocol != PROTO_SATAPM)
284198389Smav			break;
285198389Smav
286198389Smav		/*
287198389Smav		 * Allocate a peripheral instance for
288198389Smav		 * this device and start the probe
289198389Smav		 * process.
290198389Smav		 */
291198389Smav		status = cam_periph_alloc(pmpregister, pmponinvalidate,
292198389Smav					  pmpcleanup, pmpstart,
293198389Smav					  "pmp", CAM_PERIPH_BIO,
294260387Sscottl					  path, pmpasync,
295198389Smav					  AC_FOUND_DEVICE, cgd);
296198389Smav
297198389Smav		if (status != CAM_REQ_CMP
298198389Smav		 && status != CAM_REQ_INPROG)
299198389Smav			printf("pmpasync: Unable to attach to new device "
300198389Smav				"due to status 0x%x\n", status);
301198389Smav		break;
302198389Smav	}
303198389Smav	case AC_SCSI_AEN:
304198389Smav	case AC_SENT_BDR:
305198389Smav	case AC_BUS_RESET:
306198389Smav		softc = (struct pmp_softc *)periph->softc;
307198389Smav		cam_periph_async(periph, code, path, arg);
308203108Smav		if (code == AC_SCSI_AEN)
309203108Smav			softc->events |= PMP_EV_RESCAN;
310203108Smav		else
311203108Smav			softc->events |= PMP_EV_RESET;
312203108Smav		if (code == AC_SCSI_AEN && softc->state != PMP_STATE_NORMAL)
313198389Smav			break;
314203108Smav		xpt_hold_boot();
315203108Smav		pmpfreeze(periph, softc->found);
316198389Smav		if (code == AC_SENT_BDR || code == AC_BUS_RESET)
317198389Smav			softc->found = 0; /* We have to reset everything. */
318198708Smav		if (softc->state == PMP_STATE_NORMAL) {
319260387Sscottl			if (cam_periph_acquire(periph) == CAM_REQ_CMP) {
320260387Sscottl				if (softc->pm_pid == 0x37261095 ||
321260387Sscottl				    softc->pm_pid == 0x38261095)
322260387Sscottl					softc->state = PMP_STATE_PM_QUIRKS_1;
323260387Sscottl				else
324260387Sscottl					softc->state = PMP_STATE_PRECONFIG;
325260387Sscottl				xpt_schedule(periph, CAM_PRIORITY_DEV);
326260387Sscottl			} else {
327260387Sscottl				pmprelease(periph, softc->found);
328260387Sscottl				xpt_release_boot();
329260387Sscottl			}
330198708Smav		} else
331198708Smav			softc->restart = 1;
332198389Smav		break;
333198389Smav	default:
334198389Smav		cam_periph_async(periph, code, path, arg);
335198389Smav		break;
336198389Smav	}
337198389Smav}
338198389Smav
339198389Smavstatic void
340198389Smavpmpsysctlinit(void *context, int pending)
341198389Smav{
342198389Smav	struct cam_periph *periph;
343198389Smav	struct pmp_softc *softc;
344198389Smav	char tmpstr[80], tmpstr2[80];
345198389Smav
346198389Smav	periph = (struct cam_periph *)context;
347198389Smav	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
348198389Smav		return;
349198389Smav
350198389Smav	softc = (struct pmp_softc *)periph->softc;
351198389Smav	snprintf(tmpstr, sizeof(tmpstr), "CAM PMP unit %d", periph->unit_number);
352198389Smav	snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
353198389Smav
354198389Smav	sysctl_ctx_init(&softc->sysctl_ctx);
355198389Smav	softc->flags |= PMP_FLAG_SCTX_INIT;
356198389Smav	softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
357198389Smav		SYSCTL_STATIC_CHILDREN(_kern_cam_pmp), OID_AUTO, tmpstr2,
358198389Smav		CTLFLAG_RD, 0, tmpstr);
359198389Smav	if (softc->sysctl_tree == NULL) {
360198389Smav		printf("pmpsysctlinit: unable to allocate sysctl tree\n");
361198389Smav		cam_periph_release(periph);
362198389Smav		return;
363198389Smav	}
364198389Smav
365198389Smav	cam_periph_release(periph);
366198389Smav}
367198389Smav
368198389Smavstatic cam_status
369198389Smavpmpregister(struct cam_periph *periph, void *arg)
370198389Smav{
371198389Smav	struct pmp_softc *softc;
372198389Smav	struct ccb_getdev *cgd;
373198389Smav
374198389Smav	cgd = (struct ccb_getdev *)arg;
375198389Smav	if (cgd == NULL) {
376198389Smav		printf("pmpregister: no getdev CCB, can't register device\n");
377198389Smav		return(CAM_REQ_CMP_ERR);
378198389Smav	}
379198389Smav
380198389Smav	softc = (struct pmp_softc *)malloc(sizeof(*softc), M_DEVBUF,
381198389Smav	    M_NOWAIT|M_ZERO);
382198389Smav
383198389Smav	if (softc == NULL) {
384198389Smav		printf("pmpregister: Unable to probe new device. "
385198389Smav		       "Unable to allocate softc\n");
386198389Smav		return(CAM_REQ_CMP_ERR);
387198389Smav	}
388198389Smav	periph->softc = softc;
389198389Smav
390198389Smav	softc->pm_pid = ((uint32_t *)&cgd->ident_data)[0];
391198389Smav	softc->pm_prv = ((uint32_t *)&cgd->ident_data)[1];
392198389Smav	TASK_INIT(&softc->sysctl_task, 0, pmpsysctlinit, periph);
393198389Smav
394198389Smav	xpt_announce_periph(periph, NULL);
395198389Smav
396198389Smav	/*
397198389Smav	 * Add async callbacks for bus reset and
398198389Smav	 * bus device reset calls.  I don't bother
399198389Smav	 * checking if this fails as, in most cases,
400198389Smav	 * the system will function just fine without
401198389Smav	 * them and the only alternative would be to
402198389Smav	 * not attach the device on failure.
403198389Smav	 */
404198389Smav	xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE |
405198389Smav		AC_SCSI_AEN, pmpasync, periph, periph->path);
406198389Smav
407198389Smav	/*
408198389Smav	 * Take an exclusive refcount on the periph while pmpstart is called
409198389Smav	 * to finish the probe.  The reference will be dropped in pmpdone at
410198389Smav	 * the end of probe.
411198389Smav	 */
412198389Smav	(void)cam_periph_acquire(periph);
413203108Smav	xpt_hold_boot();
414203108Smav	softc->state = PMP_STATE_PORTS;
415203108Smav	softc->events = PMP_EV_RESCAN;
416203108Smav	xpt_schedule(periph, CAM_PRIORITY_DEV);
417198389Smav
418198389Smav	return(CAM_REQ_CMP);
419198389Smav}
420198389Smav
421198389Smavstatic void
422198389Smavpmpstart(struct cam_periph *periph, union ccb *start_ccb)
423198389Smav{
424203108Smav	struct ccb_trans_settings cts;
425198389Smav	struct ccb_ataio *ataio;
426198389Smav	struct pmp_softc *softc;
427203108Smav	struct cam_path *dpath;
428203108Smav	int revision = 0;
429198389Smav
430198389Smav	softc = (struct pmp_softc *)periph->softc;
431198389Smav	ataio = &start_ccb->ataio;
432236602Smav
433236602Smav	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("pmpstart\n"));
434236602Smav
435198708Smav	if (softc->restart) {
436198708Smav		softc->restart = 0;
437250508Smav		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
438250508Smav			softc->state = min(softc->state, PMP_STATE_PM_QUIRKS_1);
439250508Smav		else
440250508Smav			softc->state = min(softc->state, PMP_STATE_PRECONFIG);
441198708Smav	}
442203108Smav	/* Fetch user wanted device speed. */
443203108Smav	if (softc->state == PMP_STATE_RESET ||
444203108Smav	    softc->state == PMP_STATE_CONNECT) {
445203108Smav		if (xpt_create_path(&dpath, periph,
446203108Smav		    xpt_path_path_id(periph->path),
447203108Smav		    softc->pm_step, 0) == CAM_REQ_CMP) {
448203108Smav			bzero(&cts, sizeof(cts));
449203108Smav			xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
450203108Smav			cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
451203108Smav			cts.type = CTS_TYPE_USER_SETTINGS;
452203108Smav			xpt_action((union ccb *)&cts);
453203108Smav			if (cts.xport_specific.sata.valid & CTS_SATA_VALID_REVISION)
454203108Smav				revision = cts.xport_specific.sata.revision;
455203108Smav			xpt_free_path(dpath);
456203108Smav		}
457203108Smav	}
458198389Smav	switch (softc->state) {
459198389Smav	case PMP_STATE_PORTS:
460198389Smav		cam_fill_ataio(ataio,
461198389Smav		      pmp_retry_count,
462198389Smav		      pmpdone,
463198389Smav		      /*flags*/CAM_DIR_NONE,
464198389Smav		      0,
465198389Smav		      /*data_ptr*/NULL,
466198389Smav		      /*dxfer_len*/0,
467198389Smav		      pmp_default_timeout * 1000);
468198389Smav		ata_pm_read_cmd(ataio, 2, 15);
469198389Smav		break;
470250508Smav
471250508Smav	case PMP_STATE_PM_QUIRKS_1:
472250508Smav	case PMP_STATE_PM_QUIRKS_3:
473250508Smav		cam_fill_ataio(ataio,
474250508Smav		      pmp_retry_count,
475250508Smav		      pmpdone,
476250508Smav		      /*flags*/CAM_DIR_NONE,
477250508Smav		      0,
478250508Smav		      /*data_ptr*/NULL,
479250508Smav		      /*dxfer_len*/0,
480250508Smav		      pmp_default_timeout * 1000);
481250508Smav		ata_pm_read_cmd(ataio, 129, 15);
482250508Smav		break;
483250508Smav
484250508Smav	case PMP_STATE_PM_QUIRKS_2:
485250508Smav		cam_fill_ataio(ataio,
486250508Smav		      pmp_retry_count,
487250508Smav		      pmpdone,
488250508Smav		      /*flags*/CAM_DIR_NONE,
489250508Smav		      0,
490250508Smav		      /*data_ptr*/NULL,
491250508Smav		      /*dxfer_len*/0,
492250508Smav		      pmp_default_timeout * 1000);
493250508Smav		ata_pm_write_cmd(ataio, 129, 15, softc->caps & ~0x1);
494250508Smav		break;
495250508Smav
496199321Smav	case PMP_STATE_PRECONFIG:
497207499Smav		/* Get/update host SATA capabilities. */
498207499Smav		bzero(&cts, sizeof(cts));
499207499Smav		xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE);
500207499Smav		cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
501207499Smav		cts.type = CTS_TYPE_CURRENT_SETTINGS;
502207499Smav		xpt_action((union ccb *)&cts);
503207499Smav		if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
504207499Smav			softc->caps = cts.xport_specific.sata.caps;
505250508Smav		else
506250508Smav			softc->caps = 0;
507198389Smav		cam_fill_ataio(ataio,
508198389Smav		      pmp_retry_count,
509198389Smav		      pmpdone,
510198389Smav		      /*flags*/CAM_DIR_NONE,
511198389Smav		      0,
512198389Smav		      /*data_ptr*/NULL,
513198389Smav		      /*dxfer_len*/0,
514198389Smav		      pmp_default_timeout * 1000);
515199321Smav		ata_pm_write_cmd(ataio, 0x60, 15, 0x0);
516198389Smav		break;
517198389Smav	case PMP_STATE_RESET:
518198389Smav		cam_fill_ataio(ataio,
519198389Smav		      pmp_retry_count,
520198389Smav		      pmpdone,
521198389Smav		      /*flags*/CAM_DIR_NONE,
522198389Smav		      0,
523198389Smav		      /*data_ptr*/NULL,
524198389Smav		      /*dxfer_len*/0,
525198389Smav		      pmp_default_timeout * 1000);
526198389Smav		ata_pm_write_cmd(ataio, 2, softc->pm_step,
527203108Smav		    (revision << 4) |
528203108Smav		    ((softc->found & (1 << softc->pm_step)) ? 0 : 1));
529198389Smav		break;
530198389Smav	case PMP_STATE_CONNECT:
531198389Smav		cam_fill_ataio(ataio,
532198389Smav		      pmp_retry_count,
533198389Smav		      pmpdone,
534198389Smav		      /*flags*/CAM_DIR_NONE,
535198389Smav		      0,
536198389Smav		      /*data_ptr*/NULL,
537198389Smav		      /*dxfer_len*/0,
538198389Smav		      pmp_default_timeout * 1000);
539203108Smav		ata_pm_write_cmd(ataio, 2, softc->pm_step,
540203108Smav		    (revision << 4));
541198389Smav		break;
542198389Smav	case PMP_STATE_CHECK:
543198389Smav		cam_fill_ataio(ataio,
544198389Smav		      pmp_retry_count,
545198389Smav		      pmpdone,
546198389Smav		      /*flags*/CAM_DIR_NONE,
547198389Smav		      0,
548198389Smav		      /*data_ptr*/NULL,
549198389Smav		      /*dxfer_len*/0,
550198389Smav		      pmp_default_timeout * 1000);
551198389Smav		ata_pm_read_cmd(ataio, 0, softc->pm_step);
552198389Smav		break;
553198389Smav	case PMP_STATE_CLEAR:
554198708Smav		softc->reset = 0;
555198389Smav		cam_fill_ataio(ataio,
556198389Smav		      pmp_retry_count,
557198389Smav		      pmpdone,
558198389Smav		      /*flags*/CAM_DIR_NONE,
559198389Smav		      0,
560198389Smav		      /*data_ptr*/NULL,
561198389Smav		      /*dxfer_len*/0,
562198389Smav		      pmp_default_timeout * 1000);
563198389Smav		ata_pm_write_cmd(ataio, 1, softc->pm_step, 0xFFFFFFFF);
564198389Smav		break;
565199321Smav	case PMP_STATE_CONFIG:
566199321Smav		cam_fill_ataio(ataio,
567199321Smav		      pmp_retry_count,
568199321Smav		      pmpdone,
569199321Smav		      /*flags*/CAM_DIR_NONE,
570199321Smav		      0,
571199321Smav		      /*data_ptr*/NULL,
572199321Smav		      /*dxfer_len*/0,
573199321Smav		      pmp_default_timeout * 1000);
574220602Smav		ata_pm_write_cmd(ataio, 0x60, 15, 0x07 |
575220602Smav		    ((softc->caps & CTS_SATA_CAPS_H_AN) ? 0x08 : 0));
576199321Smav		break;
577198389Smav	default:
578198389Smav		break;
579198389Smav	}
580198389Smav	xpt_action(start_ccb);
581198389Smav}
582198389Smav
583198389Smavstatic void
584198389Smavpmpdone(struct cam_periph *periph, union ccb *done_ccb)
585198389Smav{
586199747Smav	struct ccb_trans_settings cts;
587198389Smav	struct pmp_softc *softc;
588198389Smav	struct ccb_ataio *ataio;
589220778Smav	struct cam_path *dpath;
590198708Smav	u_int32_t  priority, res;
591203108Smav	int i;
592198389Smav
593198389Smav	softc = (struct pmp_softc *)periph->softc;
594198389Smav	ataio = &done_ccb->ataio;
595198389Smav
596236602Smav	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("pmpdone\n"));
597198389Smav
598198389Smav	priority = done_ccb->ccb_h.pinfo.priority;
599198389Smav
600198708Smav	if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
601203385Smav		if (cam_periph_error(done_ccb, 0, 0, NULL) == ERESTART) {
602198389Smav			return;
603198389Smav		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
604198708Smav			cam_release_devq(done_ccb->ccb_h.path,
605198708Smav			    /*relsim_flags*/0,
606198708Smav			    /*reduction*/0,
607198708Smav			    /*timeout*/0,
608198708Smav			    /*getcount_only*/0);
609198389Smav		}
610198708Smav		goto done;
611198708Smav	}
612198708Smav
613198708Smav	if (softc->restart) {
614198708Smav		softc->restart = 0;
615198389Smav		xpt_release_ccb(done_ccb);
616250508Smav		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
617250508Smav			softc->state = min(softc->state, PMP_STATE_PM_QUIRKS_1);
618250508Smav		else
619250508Smav			softc->state = min(softc->state, PMP_STATE_PRECONFIG);
620198708Smav		xpt_schedule(periph, priority);
621198708Smav		return;
622198708Smav	}
623198708Smav
624198708Smav	switch (softc->state) {
625198708Smav	case PMP_STATE_PORTS:
626220778Smav		softc->pm_ports = (ataio->res.lba_high << 24) +
627220778Smav		    (ataio->res.lba_mid << 16) +
628220778Smav		    (ataio->res.lba_low << 8) +
629220778Smav		    ataio->res.sector_count;
630235982Smav		if (pmp_hide_special) {
631235982Smav			/*
632235982Smav			 * This PMP declares 6 ports, while only 5 of them
633235982Smav			 * are real. Port 5 is a SEMB port, probing which
634235982Smav			 * causes timeouts if external SEP is not connected
635235982Smav			 * to PMP over I2C.
636235982Smav			 */
637247161Smav			if ((softc->pm_pid == 0x37261095 ||
638247161Smav			     softc->pm_pid == 0x38261095) &&
639247161Smav			    softc->pm_ports == 6)
640235982Smav				softc->pm_ports = 5;
641235982Smav
642235982Smav			/*
643235982Smav			 * This PMP declares 7 ports, while only 5 of them
644235982Smav			 * are real. Port 5 is a fake "Config  Disk" with
645235982Smav			 * 640 sectors size. Port 6 is a SEMB port.
646235982Smav			 */
647235982Smav			if (softc->pm_pid == 0x47261095 && softc->pm_ports == 7)
648235982Smav				softc->pm_ports = 5;
649235982Smav
650235982Smav			/*
651235982Smav			 * These PMPs have extra configuration port.
652235982Smav			 */
653235982Smav			if (softc->pm_pid == 0x57231095 ||
654235982Smav			    softc->pm_pid == 0x57331095 ||
655235982Smav			    softc->pm_pid == 0x57341095 ||
656235982Smav			    softc->pm_pid == 0x57441095)
657235982Smav				softc->pm_ports--;
658235982Smav		}
659200218Smav		printf("%s%d: %d fan-out ports\n",
660200218Smav		    periph->periph_name, periph->unit_number,
661200218Smav		    softc->pm_ports);
662250508Smav		if (softc->pm_pid == 0x37261095 || softc->pm_pid == 0x38261095)
663250508Smav			softc->state = PMP_STATE_PM_QUIRKS_1;
664250508Smav		else
665250508Smav			softc->state = PMP_STATE_PRECONFIG;
666250508Smav		xpt_release_ccb(done_ccb);
667250508Smav		xpt_schedule(periph, priority);
668250508Smav		return;
669250508Smav
670250508Smav	case PMP_STATE_PM_QUIRKS_1:
671250508Smav		softc->caps = (ataio->res.lba_high << 24) +
672250508Smav		    (ataio->res.lba_mid << 16) +
673250508Smav		    (ataio->res.lba_low << 8) +
674250508Smav		    ataio->res.sector_count;
675250508Smav		if (softc->caps & 0x1)
676250508Smav			softc->state = PMP_STATE_PM_QUIRKS_2;
677250508Smav		else
678250508Smav			softc->state = PMP_STATE_PRECONFIG;
679250508Smav		xpt_release_ccb(done_ccb);
680250508Smav		xpt_schedule(periph, priority);
681250508Smav		return;
682250508Smav
683250508Smav	case PMP_STATE_PM_QUIRKS_2:
684250508Smav		if (bootverbose)
685250508Smav			softc->state = PMP_STATE_PM_QUIRKS_3;
686250508Smav		else
687250508Smav			softc->state = PMP_STATE_PRECONFIG;
688250508Smav		xpt_release_ccb(done_ccb);
689250508Smav		xpt_schedule(periph, priority);
690250508Smav		return;
691250508Smav
692250508Smav	case PMP_STATE_PM_QUIRKS_3:
693250508Smav		res = (ataio->res.lba_high << 24) +
694250508Smav		    (ataio->res.lba_mid << 16) +
695250508Smav		    (ataio->res.lba_low << 8) +
696250508Smav		    ataio->res.sector_count;
697250508Smav		printf("%s%d: Disabling SiI3x26 R_OK in GSCR_POLL: %x->%x\n",
698250508Smav		    periph->periph_name, periph->unit_number, softc->caps, res);
699199321Smav		softc->state = PMP_STATE_PRECONFIG;
700198708Smav		xpt_release_ccb(done_ccb);
701198708Smav		xpt_schedule(periph, priority);
702198708Smav		return;
703250508Smav
704199321Smav	case PMP_STATE_PRECONFIG:
705198708Smav		softc->pm_step = 0;
706198708Smav		softc->state = PMP_STATE_RESET;
707198708Smav		softc->reset |= ~softc->found;
708198389Smav		xpt_release_ccb(done_ccb);
709198708Smav		xpt_schedule(periph, priority);
710198708Smav		return;
711198389Smav	case PMP_STATE_RESET:
712198708Smav		softc->pm_step++;
713198708Smav		if (softc->pm_step >= softc->pm_ports) {
714198708Smav			softc->pm_step = 0;
715198708Smav			cam_freeze_devq(periph->path);
716198708Smav			cam_release_devq(periph->path,
717198708Smav			    RELSIM_RELEASE_AFTER_TIMEOUT,
718198708Smav			    /*reduction*/0,
719198708Smav			    /*timeout*/5,
720198708Smav			    /*getcount_only*/0);
721198708Smav			softc->state = PMP_STATE_CONNECT;
722198389Smav		}
723198389Smav		xpt_release_ccb(done_ccb);
724198708Smav		xpt_schedule(periph, priority);
725198708Smav		return;
726198389Smav	case PMP_STATE_CONNECT:
727198708Smav		softc->pm_step++;
728198708Smav		if (softc->pm_step >= softc->pm_ports) {
729198708Smav			softc->pm_step = 0;
730198708Smav			softc->pm_try = 0;
731198708Smav			cam_freeze_devq(periph->path);
732198708Smav			cam_release_devq(periph->path,
733198708Smav			    RELSIM_RELEASE_AFTER_TIMEOUT,
734198708Smav			    /*reduction*/0,
735198708Smav			    /*timeout*/10,
736198708Smav			    /*getcount_only*/0);
737198708Smav			softc->state = PMP_STATE_CHECK;
738198389Smav		}
739198389Smav		xpt_release_ccb(done_ccb);
740198708Smav		xpt_schedule(periph, priority);
741198708Smav		return;
742198389Smav	case PMP_STATE_CHECK:
743220778Smav		res = (ataio->res.lba_high << 24) +
744220778Smav		    (ataio->res.lba_mid << 16) +
745220778Smav		    (ataio->res.lba_low << 8) +
746220778Smav		    ataio->res.sector_count;
747207499Smav		if (((res & 0xf0f) == 0x103 && (res & 0x0f0) != 0) ||
748207499Smav		    (res & 0x600) != 0) {
749200218Smav			if (bootverbose) {
750200218Smav				printf("%s%d: port %d status: %08x\n",
751200218Smav				    periph->periph_name, periph->unit_number,
752200218Smav				    softc->pm_step, res);
753200218Smav			}
754207499Smav			/* Report device speed if it is online. */
755207499Smav			if ((res & 0xf0f) == 0x103 &&
756207499Smav			    xpt_create_path(&dpath, periph,
757199747Smav			    xpt_path_path_id(periph->path),
758199747Smav			    softc->pm_step, 0) == CAM_REQ_CMP) {
759199747Smav				bzero(&cts, sizeof(cts));
760203108Smav				xpt_setup_ccb(&cts.ccb_h, dpath, CAM_PRIORITY_NONE);
761199747Smav				cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
762199747Smav				cts.type = CTS_TYPE_CURRENT_SETTINGS;
763199747Smav				cts.xport_specific.sata.revision = (res & 0x0f0) >> 4;
764199747Smav				cts.xport_specific.sata.valid = CTS_SATA_VALID_REVISION;
765207499Smav				cts.xport_specific.sata.caps = softc->caps &
766220602Smav				    (CTS_SATA_CAPS_H_PMREQ |
767220602Smav				     CTS_SATA_CAPS_H_DMAAA |
768220602Smav				     CTS_SATA_CAPS_H_AN);
769207499Smav				cts.xport_specific.sata.valid |= CTS_SATA_VALID_CAPS;
770199747Smav				xpt_action((union ccb *)&cts);
771199747Smav				xpt_free_path(dpath);
772199747Smav			}
773198708Smav			softc->found |= (1 << softc->pm_step);
774198708Smav			softc->pm_step++;
775198708Smav		} else {
776198708Smav			if (softc->pm_try < 10) {
777198708Smav				cam_freeze_devq(periph->path);
778198708Smav				cam_release_devq(periph->path,
779198708Smav				    RELSIM_RELEASE_AFTER_TIMEOUT,
780198708Smav				    /*reduction*/0,
781198708Smav				    /*timeout*/10,
782198708Smav				    /*getcount_only*/0);
783198708Smav				softc->pm_try++;
784198708Smav			} else {
785200218Smav				if (bootverbose) {
786200218Smav					printf("%s%d: port %d status: %08x\n",
787200218Smav					    periph->periph_name, periph->unit_number,
788200218Smav					    softc->pm_step, res);
789200218Smav				}
790198708Smav				softc->found &= ~(1 << softc->pm_step);
791198708Smav				if (xpt_create_path(&dpath, periph,
792198708Smav				    done_ccb->ccb_h.path_id,
793198708Smav				    softc->pm_step, 0) == CAM_REQ_CMP) {
794198708Smav					xpt_async(AC_LOST_DEVICE, dpath, NULL);
795198708Smav					xpt_free_path(dpath);
796198708Smav				}
797198389Smav				softc->pm_step++;
798198389Smav			}
799198708Smav		}
800198708Smav		if (softc->pm_step >= softc->pm_ports) {
801198708Smav			if (softc->reset & softc->found) {
802198708Smav				cam_freeze_devq(periph->path);
803198708Smav				cam_release_devq(periph->path,
804198708Smav				    RELSIM_RELEASE_AFTER_TIMEOUT,
805198708Smav				    /*reduction*/0,
806198708Smav				    /*timeout*/1000,
807198708Smav				    /*getcount_only*/0);
808198389Smav			}
809198708Smav			softc->state = PMP_STATE_CLEAR;
810198708Smav			softc->pm_step = 0;
811198389Smav		}
812198389Smav		xpt_release_ccb(done_ccb);
813198708Smav		xpt_schedule(periph, priority);
814198708Smav		return;
815198389Smav	case PMP_STATE_CLEAR:
816198708Smav		softc->pm_step++;
817199321Smav		if (softc->pm_step >= softc->pm_ports) {
818199321Smav			softc->state = PMP_STATE_CONFIG;
819198708Smav			softc->pm_step = 0;
820199321Smav		}
821199321Smav		xpt_release_ccb(done_ccb);
822199321Smav		xpt_schedule(periph, priority);
823199321Smav		return;
824199321Smav	case PMP_STATE_CONFIG:
825203108Smav		for (i = 0; i < softc->pm_ports; i++) {
826203108Smav			union ccb *ccb;
827203108Smav
828203108Smav			if ((softc->found & (1 << i)) == 0)
829203108Smav				continue;
830203108Smav			if (xpt_create_path(&dpath, periph,
831203108Smav			    xpt_path_path_id(periph->path),
832203108Smav			    i, 0) != CAM_REQ_CMP) {
833208819Smav				printf("pmpdone: xpt_create_path failed\n");
834208819Smav				continue;
835203108Smav			}
836203108Smav			/* If we did hard reset to this device, inform XPT. */
837203108Smav			if ((softc->reset & softc->found & (1 << i)) != 0)
838203108Smav				xpt_async(AC_SENT_BDR, dpath, NULL);
839203108Smav			/* If rescan requested, scan this device. */
840203108Smav			if (softc->events & PMP_EV_RESCAN) {
841203108Smav				ccb = xpt_alloc_ccb_nowait();
842203108Smav				if (ccb == NULL) {
843203108Smav					xpt_free_path(dpath);
844203108Smav					goto done;
845203108Smav				}
846203108Smav				xpt_setup_ccb(&ccb->ccb_h, dpath, CAM_PRIORITY_XPT);
847203108Smav				xpt_rescan(ccb);
848203108Smav			} else
849203108Smav				xpt_free_path(dpath);
850198389Smav		}
851198389Smav		break;
852198389Smav	default:
853198389Smav		break;
854198389Smav	}
855198708Smavdone:
856198708Smav	xpt_release_ccb(done_ccb);
857198389Smav	softc->state = PMP_STATE_NORMAL;
858203108Smav	softc->events = 0;
859203108Smav	xpt_release_boot();
860198389Smav	pmprelease(periph, -1);
861198389Smav	cam_periph_release_locked(periph);
862198389Smav}
863198389Smav
864198389Smav#endif /* _KERNEL */
865