xref: /dflybsd-src/sys/dev/disk/sili/sili_pm.c (revision c3783d8f880de3d083d792f584d71ae24c3eea43)
11ac8d5baSMatthew Dillon /*
2fb00c6edSMatthew Dillon  * (MPSAFE)
3fb00c6edSMatthew Dillon  *
41ac8d5baSMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
51ac8d5baSMatthew Dillon  *
61ac8d5baSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
71ac8d5baSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
81ac8d5baSMatthew Dillon  *
91ac8d5baSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
101ac8d5baSMatthew Dillon  * modification, are permitted provided that the following conditions
111ac8d5baSMatthew Dillon  * are met:
121ac8d5baSMatthew Dillon  *
131ac8d5baSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
141ac8d5baSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
151ac8d5baSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
161ac8d5baSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
171ac8d5baSMatthew Dillon  *    the documentation and/or other materials provided with the
181ac8d5baSMatthew Dillon  *    distribution.
191ac8d5baSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
201ac8d5baSMatthew Dillon  *    contributors may be used to endorse or promote products derived
211ac8d5baSMatthew Dillon  *    from this software without specific, prior written permission.
221ac8d5baSMatthew Dillon  *
231ac8d5baSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
241ac8d5baSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
251ac8d5baSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
261ac8d5baSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
271ac8d5baSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
281ac8d5baSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
291ac8d5baSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
301ac8d5baSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
311ac8d5baSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
321ac8d5baSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
331ac8d5baSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341ac8d5baSMatthew Dillon  * SUCH DAMAGE.
351ac8d5baSMatthew Dillon  */
361ac8d5baSMatthew Dillon 
371ac8d5baSMatthew Dillon #include "sili.h"
381ac8d5baSMatthew Dillon 
391ac8d5baSMatthew Dillon static void sili_pm_dummy_done(struct ata_xfer *xa);
401ac8d5baSMatthew Dillon static void sili_pm_empty_done(struct sili_ccb *ccb);
411ac8d5baSMatthew Dillon 
421ac8d5baSMatthew Dillon /*
43a35ddbb4SMatthew Dillon  * This is called for PM attachments and hot-plug insertion events, and
44a35ddbb4SMatthew Dillon  * typically not called again until after an unplug/replug sequence.
45a35ddbb4SMatthew Dillon  *
46a35ddbb4SMatthew Dillon  * We just fall through to the hard-reset code, we don't need to
47a35ddbb4SMatthew Dillon  * set up any initial conditions.
48a35ddbb4SMatthew Dillon  */
49a35ddbb4SMatthew Dillon int
sili_pm_port_init(struct sili_port * ap,struct ata_port * at)50a35ddbb4SMatthew Dillon sili_pm_port_init(struct sili_port *ap, struct ata_port *at)
51a35ddbb4SMatthew Dillon {
52a35ddbb4SMatthew Dillon 	at->at_probe = ATA_PROBE_NEED_HARD_RESET;
53a35ddbb4SMatthew Dillon 	return (0);
54a35ddbb4SMatthew Dillon }
55a35ddbb4SMatthew Dillon 
56a35ddbb4SMatthew Dillon /*
57a35ddbb4SMatthew Dillon  * This is called from the port hardreset code.
58a35ddbb4SMatthew Dillon  */
59a35ddbb4SMatthew Dillon int
sili_pm_port_probe(struct sili_port * ap,int orig_error)60a35ddbb4SMatthew Dillon sili_pm_port_probe(struct sili_port *ap, int orig_error)
61a35ddbb4SMatthew Dillon {
62a35ddbb4SMatthew Dillon 	struct ata_port *at;
63a35ddbb4SMatthew Dillon 	int error;
64a35ddbb4SMatthew Dillon 	int i;
65a35ddbb4SMatthew Dillon 
66a35ddbb4SMatthew Dillon 	/*
67a35ddbb4SMatthew Dillon 	 * Clean up the port state machine
68a35ddbb4SMatthew Dillon 	 */
69a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_PMA);
70a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_INIT);
71a35ddbb4SMatthew Dillon 	if (sili_pwait_clr_to(ap, 5000, SILI_PREG_STATUS, SILI_PREG_CTL_INIT)) {
72a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: unable to init port\n",
73a35ddbb4SMatthew Dillon 			PORTNAME(ap));
74a35ddbb4SMatthew Dillon 		return (EBUSY);
75a35ddbb4SMatthew Dillon 	}
76a35ddbb4SMatthew Dillon 	if (sili_pwait_set(ap, SILI_PREG_STATUS, SILI_PREG_STATUS_READY)) {
77a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: port will not come ready\n",
78a35ddbb4SMatthew Dillon 			PORTNAME(ap));
79a35ddbb4SMatthew Dillon 		return (EBUSY);
80a35ddbb4SMatthew Dillon 	}
81a35ddbb4SMatthew Dillon 
82a35ddbb4SMatthew Dillon 	/*
83a35ddbb4SMatthew Dillon 	 * Issue a soft-reset of target 15
84a35ddbb4SMatthew Dillon 	 */
85a35ddbb4SMatthew Dillon 	ap->ap_state = AP_S_NORMAL;
86a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_SERR, -1);
87a35ddbb4SMatthew Dillon 	error = sili_pm_softreset(ap, 15);
88a35ddbb4SMatthew Dillon 
89a35ddbb4SMatthew Dillon 	if (error == 0)
90a35ddbb4SMatthew Dillon 		error = sili_pm_identify(ap);
91a35ddbb4SMatthew Dillon 
92a35ddbb4SMatthew Dillon 	/*
93a35ddbb4SMatthew Dillon 	 * Finalize.  If the softreset failed.  Re-init the port
94a35ddbb4SMatthew Dillon 	 * state machine again so the normal non-PM softreset does
95a35ddbb4SMatthew Dillon 	 * not bog down.
96a35ddbb4SMatthew Dillon 	 */
97a35ddbb4SMatthew Dillon 	if (error == 0) {
98a35ddbb4SMatthew Dillon 		for (i = 0; i < SILI_MAX_PMPORTS; ++i) {
99a35ddbb4SMatthew Dillon 			at = &ap->ap_ata[i];
100a35ddbb4SMatthew Dillon 			at->at_probe = ATA_PROBE_NEED_INIT;
101a35ddbb4SMatthew Dillon 			at->at_features |= ATA_PORT_F_RESCAN;
102a35ddbb4SMatthew Dillon 			at->at_features &= ~ATA_PORT_F_READLOG;
103a35ddbb4SMatthew Dillon 		}
104a35ddbb4SMatthew Dillon 		ap->ap_type = ATA_PORT_T_PM;
105a35ddbb4SMatthew Dillon 		return (0);
106a35ddbb4SMatthew Dillon 	}
107a35ddbb4SMatthew Dillon 
108a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
109a35ddbb4SMatthew Dillon 	sili_port_init(ap);
110a35ddbb4SMatthew Dillon 	if (orig_error == 0) {
111a35ddbb4SMatthew Dillon 		if (sili_pwait_set_to(ap, 5000, SILI_PREG_STATUS,
112a35ddbb4SMatthew Dillon 				      SILI_PREG_STATUS_READY)) {
113a35ddbb4SMatthew Dillon 			kprintf("%s: PM probe: port will not come ready\n",
114a35ddbb4SMatthew Dillon 				PORTNAME(ap));
115a35ddbb4SMatthew Dillon 			orig_error = EBUSY;
116a35ddbb4SMatthew Dillon 		}
117a35ddbb4SMatthew Dillon 	}
118a35ddbb4SMatthew Dillon 	return (orig_error);
119a35ddbb4SMatthew Dillon 
120a35ddbb4SMatthew Dillon #if 0
121a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_RESUME);
122a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_CLR, SILI_PREG_CTL_PMA);
123a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_INIT);
124a35ddbb4SMatthew Dillon 	if (sili_pwait_clr_to(ap, 5000, SILI_PREG_STATUS, SILI_PREG_CTL_INIT)) {
125a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: unable to init port\n",
126a35ddbb4SMatthew Dillon 			PORTNAME(ap));
127a35ddbb4SMatthew Dillon 		orig_error = EBUSY;
128a35ddbb4SMatthew Dillon 	}
129a35ddbb4SMatthew Dillon 	if (sili_pwait_set(ap, SILI_PREG_STATUS, SILI_PREG_STATUS_READY)) {
130a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: port will not come ready\n",
131a35ddbb4SMatthew Dillon 			PORTNAME(ap));
132a35ddbb4SMatthew Dillon 		orig_error = EBUSY;
133a35ddbb4SMatthew Dillon 	}
134a35ddbb4SMatthew Dillon 	kprintf("ORIG ERROR %d\n", orig_error);
135a35ddbb4SMatthew Dillon 	if (orig_error)
136a35ddbb4SMatthew Dillon 		return (orig_error);
137a35ddbb4SMatthew Dillon 
138a35ddbb4SMatthew Dillon 	/*
139a35ddbb4SMatthew Dillon 	 * If we originally detected a device redo the device reset to
140a35ddbb4SMatthew Dillon 	 * try to clear the mess.
141a35ddbb4SMatthew Dillon 	 */
142a35ddbb4SMatthew Dillon 	sili_pwrite(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET);
143a35ddbb4SMatthew Dillon 	if (sili_pwait_clr(ap, SILI_PREG_CTL_SET, SILI_PREG_CTL_DEVRESET)) {
144a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: unable to reset\n", PORTNAME(ap));
145a35ddbb4SMatthew Dillon 		orig_error = EBUSY;
146a35ddbb4SMatthew Dillon 	}
147a35ddbb4SMatthew Dillon 	if (sili_pwait_set(ap, SILI_PREG_STATUS, SILI_PREG_STATUS_READY)) {
148a35ddbb4SMatthew Dillon 		kprintf("%s: PM probe: port will not come ready\n",
149a35ddbb4SMatthew Dillon 			PORTNAME(ap));
150a35ddbb4SMatthew Dillon 		orig_error = EBUSY;
151a35ddbb4SMatthew Dillon 	}
152a35ddbb4SMatthew Dillon 	return (orig_error);
153a35ddbb4SMatthew Dillon #endif
154a35ddbb4SMatthew Dillon }
155a35ddbb4SMatthew Dillon 
156a35ddbb4SMatthew Dillon /*
1571ac8d5baSMatthew Dillon  * Identify the port multiplier
1581ac8d5baSMatthew Dillon  */
1591ac8d5baSMatthew Dillon int
sili_pm_identify(struct sili_port * ap)1601ac8d5baSMatthew Dillon sili_pm_identify(struct sili_port *ap)
1611ac8d5baSMatthew Dillon {
1621ac8d5baSMatthew Dillon 	u_int32_t chipid;
1631ac8d5baSMatthew Dillon 	u_int32_t rev;
1641ac8d5baSMatthew Dillon 	u_int32_t nports;
1651ac8d5baSMatthew Dillon 	u_int32_t data1;
1661ac8d5baSMatthew Dillon 	u_int32_t data2;
16719b34835SMatthew Dillon 	int	  has_dummy_port;
1681ac8d5baSMatthew Dillon 
1691ac8d5baSMatthew Dillon 	ap->ap_probe = ATA_PROBE_FAILED;
1701ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, 0, &chipid))
1711ac8d5baSMatthew Dillon 		goto err;
1721ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, 1, &rev))
1731ac8d5baSMatthew Dillon 		goto err;
1741ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, 2, &nports))
1751ac8d5baSMatthew Dillon 		goto err;
1761ac8d5baSMatthew Dillon 	nports &= 0x0000000F;	/* only the low 4 bits */
1771ac8d5baSMatthew Dillon 	ap->ap_probe = ATA_PROBE_GOOD;
17819b34835SMatthew Dillon 
17919b34835SMatthew Dillon 	/*
18019b34835SMatthew Dillon 	 * Ignore fake port on PMs which have it.  We can probe it but the
18119b34835SMatthew Dillon 	 * softreset will probably fail.
18219b34835SMatthew Dillon 	 */
18319b34835SMatthew Dillon 	switch(chipid) {
18419b34835SMatthew Dillon 	case 0x37261095:
18519b34835SMatthew Dillon 		has_dummy_port = 1;
18619b34835SMatthew Dillon 		break;
18719b34835SMatthew Dillon 	default:
18819b34835SMatthew Dillon 		has_dummy_port = 0;
18919b34835SMatthew Dillon 		break;
19019b34835SMatthew Dillon 	}
19119b34835SMatthew Dillon 	if (has_dummy_port) {
19219b34835SMatthew Dillon 		if (nports > 1)
19319b34835SMatthew Dillon 			--nports;
19419b34835SMatthew Dillon 	}
19519b34835SMatthew Dillon 
196*c3783d8fSzrj 	kprintf("%s: Port multiplier: chip=%08x rev=0x%pb%i nports=%d\n",
1971ac8d5baSMatthew Dillon 		PORTNAME(ap),
1981ac8d5baSMatthew Dillon 		chipid,
199*c3783d8fSzrj 		SATA_PFMT_PM_REV, rev,
2001ac8d5baSMatthew Dillon 		nports);
20119b34835SMatthew Dillon 	if (has_dummy_port) {
20219b34835SMatthew Dillon 		kprintf("%s: Port multiplier: Ignoring dummy port #%d\n",
20319b34835SMatthew Dillon 			PORTNAME(ap), nports);
20419b34835SMatthew Dillon 	}
2051ac8d5baSMatthew Dillon 	ap->ap_pmcount = nports;
2061ac8d5baSMatthew Dillon 
2071ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, SATA_PMREG_FEA, &data1)) {
2081ac8d5baSMatthew Dillon 		kprintf("%s: Port multiplier: Warning, "
2091ac8d5baSMatthew Dillon 			"cannot read feature register\n", PORTNAME(ap));
2101ac8d5baSMatthew Dillon 	} else {
211*c3783d8fSzrj 		kprintf("%s: Port multiplier features: 0x%pb%i\n",
212*c3783d8fSzrj 			PORTNAME(ap), SATA_PFMT_PM_FEA, data1);
2131ac8d5baSMatthew Dillon 	}
2141ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, SATA_PMREG_FEAEN, &data2) == 0) {
215*c3783d8fSzrj 		kprintf("%s: Port multiplier defaults: 0x%pb%i\n",
216*c3783d8fSzrj 			PORTNAME(ap), SATA_PFMT_PM_FEA, data2);
2171ac8d5baSMatthew Dillon 	}
2181ac8d5baSMatthew Dillon 
2191ac8d5baSMatthew Dillon 	/*
2201ac8d5baSMatthew Dillon 	 * Turn on async notification if we support and the PM supports it.
2211ac8d5baSMatthew Dillon 	 * This allows the PM to forward async notification events to us and
2221ac8d5baSMatthew Dillon 	 * it will also generate an event for target 15 for hot-plug events
2231ac8d5baSMatthew Dillon 	 * (or is supposed to anyway).
2241ac8d5baSMatthew Dillon 	 */
2251ac8d5baSMatthew Dillon 	if ((ap->ap_sc->sc_flags & SILI_F_SSNTF) &&
2261ac8d5baSMatthew Dillon 	    (data1 & SATA_PMFEA_ASYNCNOTIFY)) {
2271ac8d5baSMatthew Dillon 		u_int32_t serr_bits = SATA_PM_SERR_DIAG_N |
2281ac8d5baSMatthew Dillon 				      SATA_PM_SERR_DIAG_X;
2291ac8d5baSMatthew Dillon 		data2 |= SATA_PMFEA_ASYNCNOTIFY;
2301ac8d5baSMatthew Dillon 		if (sili_pm_write(ap, 15, SATA_PMREG_FEAEN, data2)) {
2311ac8d5baSMatthew Dillon 			kprintf("%s: Port multiplier: AsyncNotify cannot be "
2321ac8d5baSMatthew Dillon 				"enabled\n", PORTNAME(ap));
2331ac8d5baSMatthew Dillon 		} else if (sili_pm_write(ap, 15, SATA_PMREG_EEENA, serr_bits)) {
2341ac8d5baSMatthew Dillon 			kprintf("%s: Port mulltiplier: AsyncNotify unable "
2351ac8d5baSMatthew Dillon 				"to enable error info bits\n", PORTNAME(ap));
2361ac8d5baSMatthew Dillon 		} else {
2371ac8d5baSMatthew Dillon 			kprintf("%s: Port multiplier: AsyncNotify enabled\n",
2381ac8d5baSMatthew Dillon 				PORTNAME(ap));
2391ac8d5baSMatthew Dillon 		}
2401ac8d5baSMatthew Dillon 	}
2411ac8d5baSMatthew Dillon 
2421ac8d5baSMatthew Dillon 	return (0);
2431ac8d5baSMatthew Dillon err:
2441ac8d5baSMatthew Dillon 	kprintf("%s: Port multiplier cannot be identified\n", PORTNAME(ap));
2451ac8d5baSMatthew Dillon 	return (EIO);
2461ac8d5baSMatthew Dillon }
2471ac8d5baSMatthew Dillon 
2481ac8d5baSMatthew Dillon /*
2491ac8d5baSMatthew Dillon  * Do a COMRESET sequence on the target behind a port multiplier.
2501ac8d5baSMatthew Dillon  *
2511ac8d5baSMatthew Dillon  * If hard is 2 we also cycle the phy on the target.
2521ac8d5baSMatthew Dillon  *
2531ac8d5baSMatthew Dillon  * This must be done prior to any softreset or probe attempts on
2541ac8d5baSMatthew Dillon  * targets behind the port multiplier.
2551ac8d5baSMatthew Dillon  *
2561ac8d5baSMatthew Dillon  * Returns 0 on success or an error.
2571ac8d5baSMatthew Dillon  */
2581ac8d5baSMatthew Dillon int
sili_pm_hardreset(struct sili_port * ap,int target,int hard)2591ac8d5baSMatthew Dillon sili_pm_hardreset(struct sili_port *ap, int target, int hard)
2601ac8d5baSMatthew Dillon {
2611ac8d5baSMatthew Dillon 	struct ata_port *at;
2621ac8d5baSMatthew Dillon 	u_int32_t data;
2631ac8d5baSMatthew Dillon 	int loop;
2641ac8d5baSMatthew Dillon 	int error = EIO;
2651ac8d5baSMatthew Dillon 
2661ac8d5baSMatthew Dillon 	at = &ap->ap_ata[target];
2671ac8d5baSMatthew Dillon 
2681ac8d5baSMatthew Dillon 	/*
269187cac04SMatthew Dillon 	 * Ensure that no other commands are pending.  Our HW reset of
270187cac04SMatthew Dillon 	 * the PM target can skewer the port overall!
271187cac04SMatthew Dillon 	 */
272187cac04SMatthew Dillon 	sili_exclusive_access(ap);
273187cac04SMatthew Dillon 
274187cac04SMatthew Dillon 	/*
2751ac8d5baSMatthew Dillon 	 * Turn off power management and kill the phy on the target
2761ac8d5baSMatthew Dillon 	 * if requested.  Hold state for 10ms.
2771ac8d5baSMatthew Dillon 	 */
2781ac8d5baSMatthew Dillon 	data = SATA_PM_SCTL_IPM_DISABLED;
279187cac04SMatthew Dillon #if 0
2801ac8d5baSMatthew Dillon 	if (hard == 2)
2811ac8d5baSMatthew Dillon 		data |= SATA_PM_SCTL_DET_DISABLE;
282187cac04SMatthew Dillon #endif
2831ac8d5baSMatthew Dillon 	if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1))
2841ac8d5baSMatthew Dillon 		goto err;
2851ac8d5baSMatthew Dillon 	if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
2861ac8d5baSMatthew Dillon 		goto err;
2871ac8d5baSMatthew Dillon 	sili_os_sleep(10);
2881ac8d5baSMatthew Dillon 
2891ac8d5baSMatthew Dillon 	/*
2901ac8d5baSMatthew Dillon 	 * Start transmitting COMRESET.  COMRESET must be sent for at
2911ac8d5baSMatthew Dillon 	 * least 1ms.
292187cac04SMatthew Dillon 	 *
293187cac04SMatthew Dillon 	 * It takes about 100ms for the DET logic to settle down,
294187cac04SMatthew Dillon 	 * from trial and error testing.  If this is too short
295187cac04SMatthew Dillon 	 * the softreset code will fail.
296187cac04SMatthew Dillon 	 *
297187cac04SMatthew Dillon 	 * It is very important to allow the logic to settle before
298187cac04SMatthew Dillon 	 * we issue any additional commands or the target will interfere
299187cac04SMatthew Dillon 	 * with our PM commands.
3001ac8d5baSMatthew Dillon 	 */
3011ac8d5baSMatthew Dillon 	at->at_probe = ATA_PROBE_FAILED;
3021ac8d5baSMatthew Dillon 	at->at_type = ATA_PORT_T_NONE;
3031ac8d5baSMatthew Dillon 	data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_INIT;
3041ac8d5baSMatthew Dillon 	if (SiliForceGen1 & (1 << ap->ap_num)) {
3051ac8d5baSMatthew Dillon 		kprintf("%s.%d: Force 1.5GBits\n", PORTNAME(ap), target);
3061ac8d5baSMatthew Dillon 		data |= SATA_PM_SCTL_SPD_GEN1;
3071ac8d5baSMatthew Dillon 	} else {
3081ac8d5baSMatthew Dillon 		data |= SATA_PM_SCTL_SPD_ANY;
3091ac8d5baSMatthew Dillon 	}
3101ac8d5baSMatthew Dillon 	if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
3111ac8d5baSMatthew Dillon 		goto err;
3121ac8d5baSMatthew Dillon 	sili_os_sleep(100);
3131ac8d5baSMatthew Dillon 
3141ac8d5baSMatthew Dillon 	if (sili_pm_phy_status(ap, target, &data)) {
3151ac8d5baSMatthew Dillon 		kprintf("%s: (A)Cannot clear phy status\n",
3161ac8d5baSMatthew Dillon 			ATANAME(ap ,at));
3171ac8d5baSMatthew Dillon 	}
3181ac8d5baSMatthew Dillon 
3191ac8d5baSMatthew Dillon 	/*
3201ac8d5baSMatthew Dillon 	 * Flush any status, then clear DET to initiate negotiation.
3213c6bae9dSMatthew Dillon 	 *
322187cac04SMatthew Dillon 	 * It is very important to allow the negotiation to settle before
323187cac04SMatthew Dillon 	 * we issue any additional commands or the target will interfere
324187cac04SMatthew Dillon 	 * with our PM commands.
3251ac8d5baSMatthew Dillon 	 */
3261ac8d5baSMatthew Dillon 	sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
3271ac8d5baSMatthew Dillon 	data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_NONE;
3281ac8d5baSMatthew Dillon 	if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data))
3291ac8d5baSMatthew Dillon 		goto err;
330187cac04SMatthew Dillon 	sili_os_sleep(100);
3311ac8d5baSMatthew Dillon 
3321ac8d5baSMatthew Dillon 	/*
3331ac8d5baSMatthew Dillon 	 * Try to determine if there is a device on the port.
3341ac8d5baSMatthew Dillon 	 *
3351ac8d5baSMatthew Dillon 	 * Give the device 3/10 second to at least be detected.
3361ac8d5baSMatthew Dillon 	 * If we fail clear any pending status since we may have
3371ac8d5baSMatthew Dillon 	 * cycled the phy and probably caused another PRCS interrupt.
3381ac8d5baSMatthew Dillon 	 */
3391ac8d5baSMatthew Dillon 	for (loop = 3; loop; --loop) {
3401ac8d5baSMatthew Dillon 		if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data))
3411ac8d5baSMatthew Dillon 			goto err;
3421ac8d5baSMatthew Dillon 		if (data & SATA_PM_SSTS_DET)
3431ac8d5baSMatthew Dillon 			break;
3441ac8d5baSMatthew Dillon 		sili_os_sleep(100);
3451ac8d5baSMatthew Dillon 	}
3461ac8d5baSMatthew Dillon 	if (loop == 0) {
3471ac8d5baSMatthew Dillon 		kprintf("%s.%d: Port appears to be unplugged\n",
3481ac8d5baSMatthew Dillon 			PORTNAME(ap), target);
3491ac8d5baSMatthew Dillon 		error = ENODEV;
3501ac8d5baSMatthew Dillon 		goto err;
3511ac8d5baSMatthew Dillon 	}
3521ac8d5baSMatthew Dillon 
3531ac8d5baSMatthew Dillon 	/*
3541ac8d5baSMatthew Dillon 	 * There is something on the port.  Give the device 3 seconds
3551ac8d5baSMatthew Dillon 	 * to fully negotiate.
3561ac8d5baSMatthew Dillon 	 */
3571ac8d5baSMatthew Dillon 	for (loop = 30; loop; --loop) {
3581ac8d5baSMatthew Dillon 		if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data))
3591ac8d5baSMatthew Dillon 			goto err;
3601ac8d5baSMatthew Dillon 		if ((data & SATA_PM_SSTS_DET) == SATA_PM_SSTS_DET_DEV)
3611ac8d5baSMatthew Dillon 			break;
3621ac8d5baSMatthew Dillon 		sili_os_sleep(100);
3631ac8d5baSMatthew Dillon 	}
3641ac8d5baSMatthew Dillon 
3651ac8d5baSMatthew Dillon 	/*
3661ac8d5baSMatthew Dillon 	 * Device not detected
3671ac8d5baSMatthew Dillon 	 */
3681ac8d5baSMatthew Dillon 	if (loop == 0) {
3691ac8d5baSMatthew Dillon 		kprintf("%s: Device may be powered down\n",
3701ac8d5baSMatthew Dillon 			PORTNAME(ap));
3711ac8d5baSMatthew Dillon 		error = ENODEV;
3721ac8d5baSMatthew Dillon 		goto err;
3731ac8d5baSMatthew Dillon 	}
3741ac8d5baSMatthew Dillon 
3751ac8d5baSMatthew Dillon 	/*
3763c6bae9dSMatthew Dillon 	 * Device detected.
3773c6bae9dSMatthew Dillon 	 *
3783c6bae9dSMatthew Dillon 	 * Wait 200ms to give the device time to send its first D2H FIS.
3793c6bae9dSMatthew Dillon 	 * If we do not wait long enough our softreset sequence can collide
380187cac04SMatthew Dillon 	 * with the end of the device's reset sequence and brick the port.
381187cac04SMatthew Dillon 	 * Some devices may need longer and we handle those cases in the
382187cac04SMatthew Dillon 	 * pm softreset code.
3833c6bae9dSMatthew Dillon 	 *
384203d6575SMatthew Dillon 	 * XXX Looks like we have to wait a lot longer.  If the Sili chip's
385203d6575SMatthew Dillon 	 *     softreset fails due to a collision with the D2H FIS or the
386203d6575SMatthew Dillon 	 *     unbusying it bricks the port.
387203d6575SMatthew Dillon 	 *
3883c6bae9dSMatthew Dillon 	 * XXX how do we poll that particular target's BSY status via the
3893c6bae9dSMatthew Dillon 	 *     PM?
3901ac8d5baSMatthew Dillon 	 */
391187cac04SMatthew Dillon 	kprintf("%s.%d: PM Device detected ssts=%08x\n",
3921ac8d5baSMatthew Dillon 		PORTNAME(ap), target, data);
393203d6575SMatthew Dillon 	sili_os_sleep(5000);
3941ac8d5baSMatthew Dillon 
3951ac8d5baSMatthew Dillon 	error = 0;
3961ac8d5baSMatthew Dillon err:
3971ac8d5baSMatthew Dillon 	at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET;
3981ac8d5baSMatthew Dillon 	return (error);
3991ac8d5baSMatthew Dillon }
4001ac8d5baSMatthew Dillon 
4011ac8d5baSMatthew Dillon /*
4021ac8d5baSMatthew Dillon  * SILI soft reset through port multiplier.
4031ac8d5baSMatthew Dillon  *
404187cac04SMatthew Dillon  * This function generates a soft reset through the port multiplier,
405187cac04SMatthew Dillon  * keeping port communications intact.
4061ac8d5baSMatthew Dillon  *
407187cac04SMatthew Dillon  * The SII chip will do the whole mess for us.  However, the command
408187cac04SMatthew Dillon  * can brick the port if the target is still busy from the previous
409187cac04SMatthew Dillon  * COMRESET.
4101ac8d5baSMatthew Dillon  */
4111ac8d5baSMatthew Dillon int
sili_pm_softreset(struct sili_port * ap,int target)4121ac8d5baSMatthew Dillon sili_pm_softreset(struct sili_port *ap, int target)
4131ac8d5baSMatthew Dillon {
4141ac8d5baSMatthew Dillon 	struct ata_port		*at;
4151ac8d5baSMatthew Dillon 	struct sili_ccb		*ccb;
4161ac8d5baSMatthew Dillon 	struct sili_prb		*prb;
4171ac8d5baSMatthew Dillon 	int			error;
4181ac8d5baSMatthew Dillon 	u_int32_t		data;
4191ac8d5baSMatthew Dillon 	u_int32_t		sig;
420a35ddbb4SMatthew Dillon 	int			timeout;
4211ac8d5baSMatthew Dillon 
4221ac8d5baSMatthew Dillon 	error = EIO;
4231ac8d5baSMatthew Dillon 	at = &ap->ap_ata[target];
4241ac8d5baSMatthew Dillon 
425187cac04SMatthew Dillon 	kprintf("%s: PM softreset\n", ATANAME(ap, at));
4261ac8d5baSMatthew Dillon 
4271ac8d5baSMatthew Dillon 	/*
4281ac8d5baSMatthew Dillon 	 * Prep the special soft-reset SII command.
4291ac8d5baSMatthew Dillon 	 */
4301ac8d5baSMatthew Dillon 	ccb = sili_get_err_ccb(ap);
4311ac8d5baSMatthew Dillon 	ccb->ccb_done = sili_pm_empty_done;
4324383d440SMatthew Dillon 	ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
4331ac8d5baSMatthew Dillon 	ccb->ccb_xa.complete = sili_pm_dummy_done;
4341ac8d5baSMatthew Dillon 	ccb->ccb_xa.at = at;
4351ac8d5baSMatthew Dillon 
4361ac8d5baSMatthew Dillon 	prb = ccb->ccb_prb;
4371ac8d5baSMatthew Dillon 	bzero(&prb->prb_h2d, sizeof(prb->prb_h2d));
4381ac8d5baSMatthew Dillon 	prb->prb_h2d.flags = at->at_target;
4391ac8d5baSMatthew Dillon 	prb->prb_control = SILI_PRB_CTRL_SOFTRESET;
4401ac8d5baSMatthew Dillon 	prb->prb_override = 0;
4411ac8d5baSMatthew Dillon 	prb->prb_xfer_count = 0;
4421ac8d5baSMatthew Dillon 
4431ac8d5baSMatthew Dillon 	ccb->ccb_xa.state = ATA_S_PENDING;
4441ac8d5baSMatthew Dillon 
445a35ddbb4SMatthew Dillon 	timeout = (target == 15) ? 1000 : 8000;
446a35ddbb4SMatthew Dillon 
447a35ddbb4SMatthew Dillon 	/*
448a35ddbb4SMatthew Dillon 	 * NOTE: Must use sili_quick_timeout() because we hold the err_ccb
449a35ddbb4SMatthew Dillon 	 */
450a35ddbb4SMatthew Dillon 	if (sili_poll(ccb, timeout, sili_quick_timeout) != ATA_S_COMPLETE) {
451a35ddbb4SMatthew Dillon 		if (target != 15) {
452a35ddbb4SMatthew Dillon 			kprintf("%s: (PM) Softreset FIS failed\n",
453a35ddbb4SMatthew Dillon 				ATANAME(ap, at));
454a35ddbb4SMatthew Dillon 		}
4551ac8d5baSMatthew Dillon 		sili_put_err_ccb(ccb);
4561ac8d5baSMatthew Dillon 		goto err;
4571ac8d5baSMatthew Dillon 	}
4581ac8d5baSMatthew Dillon 
4591ac8d5baSMatthew Dillon 	sig = (prb->prb_d2h.lba_high << 24) |
4601ac8d5baSMatthew Dillon 	      (prb->prb_d2h.lba_mid << 16) |
4611ac8d5baSMatthew Dillon 	      (prb->prb_d2h.lba_low << 8) |
4621ac8d5baSMatthew Dillon 	      (prb->prb_d2h.sector_count);
4631ac8d5baSMatthew Dillon 	kprintf("%s: PM SOFTRESET SIGNATURE %08x\n", ATANAME(ap, at), sig);
4641ac8d5baSMatthew Dillon 
4651ac8d5baSMatthew Dillon 	sili_put_err_ccb(ccb);
4661ac8d5baSMatthew Dillon 
4671ac8d5baSMatthew Dillon 	/*
4681ac8d5baSMatthew Dillon 	 * Clear the phy status of the target so we can get a new event.
4691ac8d5baSMatthew Dillon 	 *
4701ac8d5baSMatthew Dillon 	 * Target 15 is the PM itself and these registers have
4711ac8d5baSMatthew Dillon 	 * different meanings.
4721ac8d5baSMatthew Dillon 	 */
4731ac8d5baSMatthew Dillon 	if (target != 15) {
4741ac8d5baSMatthew Dillon 		if (sili_pm_phy_status(ap, target, &data)) {
4751ac8d5baSMatthew Dillon 			kprintf("%s: (C)Cannot clear phy status\n",
4761ac8d5baSMatthew Dillon 				ATANAME(ap ,at));
4771ac8d5baSMatthew Dillon 		}
4781ac8d5baSMatthew Dillon 		sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
4791ac8d5baSMatthew Dillon 	}
4801ac8d5baSMatthew Dillon 
4811ac8d5baSMatthew Dillon 	/*
4821ac8d5baSMatthew Dillon 	 * If the softreset is trying to clear a BSY condition after a
4831ac8d5baSMatthew Dillon 	 * normal portreset we assign the port type.
4841ac8d5baSMatthew Dillon 	 *
4851ac8d5baSMatthew Dillon 	 * If the softreset is being run first as part of the ccb error
4861ac8d5baSMatthew Dillon 	 * processing code then report if the device signature changed
4871ac8d5baSMatthew Dillon 	 * unexpectedly.
4881ac8d5baSMatthew Dillon 	 */
4891ac8d5baSMatthew Dillon 	if (at->at_type == ATA_PORT_T_NONE) {
4901ac8d5baSMatthew Dillon 		at->at_type = sili_port_signature(ap, at, sig);
4911ac8d5baSMatthew Dillon 	} else {
4921ac8d5baSMatthew Dillon 		if (sili_port_signature(ap, at, sig) != at->at_type) {
4931ac8d5baSMatthew Dillon 			kprintf("%s: device signature unexpectedly "
4941ac8d5baSMatthew Dillon 				"changed\n", ATANAME(ap, at));
4951ac8d5baSMatthew Dillon 			error = EBUSY; /* XXX */
4961ac8d5baSMatthew Dillon 		}
4971ac8d5baSMatthew Dillon 	}
4981ac8d5baSMatthew Dillon 	error = 0;
4991ac8d5baSMatthew Dillon err:
5001ac8d5baSMatthew Dillon 	/*
5011ac8d5baSMatthew Dillon 	 * Clear error status so we can detect removal.
5021ac8d5baSMatthew Dillon 	 *
5031ac8d5baSMatthew Dillon 	 * Target 15 is the PM itself and these registers have
5041ac8d5baSMatthew Dillon 	 * different meanings.
5051ac8d5baSMatthew Dillon 	 */
506187cac04SMatthew Dillon 	kprintf("%s: PM softreset done error %d\n", ATANAME(ap, at), error);
5071ac8d5baSMatthew Dillon 	if (error == 0 && target != 15) {
5081ac8d5baSMatthew Dillon 		if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
5091ac8d5baSMatthew Dillon 			kprintf("%s: sili_pm_softreset unable to clear SERR\n",
5101ac8d5baSMatthew Dillon 				ATANAME(ap, at));
5111ac8d5baSMatthew Dillon 			ap->ap_flags &= ~AP_F_IGNORE_IFS;
5121ac8d5baSMatthew Dillon 		}
5131ac8d5baSMatthew Dillon 	}
5141ac8d5baSMatthew Dillon 
5151ac8d5baSMatthew Dillon 	at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
5161ac8d5baSMatthew Dillon 	return (error);
5171ac8d5baSMatthew Dillon }
5181ac8d5baSMatthew Dillon 
5191ac8d5baSMatthew Dillon 
5201ac8d5baSMatthew Dillon /*
5211ac8d5baSMatthew Dillon  * Return the phy status for a target behind a port multiplier and
5221ac8d5baSMatthew Dillon  * reset SATA_PMREG_SERR.
5231ac8d5baSMatthew Dillon  *
5241ac8d5baSMatthew Dillon  * Returned bits follow SILI_PREG_SSTS bits.  The SILI_PREG_SSTS_SPD
5251ac8d5baSMatthew Dillon  * bits can be used to determine the link speed and will be 0 if there
5261ac8d5baSMatthew Dillon  * is no link.
5271ac8d5baSMatthew Dillon  *
5281ac8d5baSMatthew Dillon  * 0 is returned if any communications error occurs.
5291ac8d5baSMatthew Dillon  */
5301ac8d5baSMatthew Dillon int
sili_pm_phy_status(struct sili_port * ap,int target,u_int32_t * datap)5311ac8d5baSMatthew Dillon sili_pm_phy_status(struct sili_port *ap, int target, u_int32_t *datap)
5321ac8d5baSMatthew Dillon {
5331ac8d5baSMatthew Dillon 	int error;
5341ac8d5baSMatthew Dillon 
5351ac8d5baSMatthew Dillon 	error = sili_pm_read(ap, target, SATA_PMREG_SSTS, datap);
5361ac8d5baSMatthew Dillon 	if (error == 0)
5371ac8d5baSMatthew Dillon 		error = sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
5381ac8d5baSMatthew Dillon 	if (error)
5391ac8d5baSMatthew Dillon 		*datap = 0;
5401ac8d5baSMatthew Dillon 	return(error);
5411ac8d5baSMatthew Dillon }
5421ac8d5baSMatthew Dillon 
5431ac8d5baSMatthew Dillon int
sili_pm_set_feature(struct sili_port * ap,int feature,int enable)5441ac8d5baSMatthew Dillon sili_pm_set_feature(struct sili_port *ap, int feature, int enable)
5451ac8d5baSMatthew Dillon {
5461ac8d5baSMatthew Dillon 	struct ata_xfer	*xa;
5471ac8d5baSMatthew Dillon 	int error;
5481ac8d5baSMatthew Dillon 
5491ac8d5baSMatthew Dillon 	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
5501ac8d5baSMatthew Dillon 
5511ac8d5baSMatthew Dillon 	xa->fis->type = ATA_FIS_TYPE_H2D;
5521ac8d5baSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
5531ac8d5baSMatthew Dillon 	xa->fis->command = enable ? ATA_C_SATA_FEATURE_ENA :
5541ac8d5baSMatthew Dillon 				    ATA_C_SATA_FEATURE_DIS;
5551ac8d5baSMatthew Dillon 	xa->fis->sector_count = feature;
5561ac8d5baSMatthew Dillon 	xa->fis->control = ATA_FIS_CONTROL_4BIT;
5571ac8d5baSMatthew Dillon 
5581ac8d5baSMatthew Dillon 	xa->complete = sili_pm_dummy_done;
5591ac8d5baSMatthew Dillon 	xa->datalen = 0;
5604383d440SMatthew Dillon 	xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
5611ac8d5baSMatthew Dillon 	xa->timeout = 1000;
5621ac8d5baSMatthew Dillon 
5631ac8d5baSMatthew Dillon 	if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
5641ac8d5baSMatthew Dillon 		error = 0;
5651ac8d5baSMatthew Dillon 	else
5661ac8d5baSMatthew Dillon 		error = EIO;
5671ac8d5baSMatthew Dillon 	sili_ata_put_xfer(xa);
5681ac8d5baSMatthew Dillon 	return(error);
5691ac8d5baSMatthew Dillon }
5701ac8d5baSMatthew Dillon 
5711ac8d5baSMatthew Dillon /*
5721ac8d5baSMatthew Dillon  * Check that a target is still good.
5731ac8d5baSMatthew Dillon  */
5741ac8d5baSMatthew Dillon void
sili_pm_check_good(struct sili_port * ap,int target)5751ac8d5baSMatthew Dillon sili_pm_check_good(struct sili_port *ap, int target)
5761ac8d5baSMatthew Dillon {
5771ac8d5baSMatthew Dillon 	struct ata_port *at;
5781ac8d5baSMatthew Dillon 	u_int32_t data;
5791ac8d5baSMatthew Dillon 
5801ac8d5baSMatthew Dillon 	/*
5811ac8d5baSMatthew Dillon 	 * It looks like we might have to read the EINFO register
5821ac8d5baSMatthew Dillon 	 * to allow the PM to generate a new event.
5831ac8d5baSMatthew Dillon 	 */
5841ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
5851ac8d5baSMatthew Dillon 		kprintf("%s: Port multiplier EINFO could not be read\n",
5861ac8d5baSMatthew Dillon 			PORTNAME(ap));
5871ac8d5baSMatthew Dillon 	}
5881ac8d5baSMatthew Dillon 
5891ac8d5baSMatthew Dillon 	if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
5901ac8d5baSMatthew Dillon 		kprintf("%s: Port multiplier: SERR could not be cleared\n",
5911ac8d5baSMatthew Dillon 			PORTNAME(ap));
5921ac8d5baSMatthew Dillon 	}
5931ac8d5baSMatthew Dillon 
5941ac8d5baSMatthew Dillon 	if (target == CAM_TARGET_WILDCARD || target >= ap->ap_pmcount)
5951ac8d5baSMatthew Dillon 		return;
5961ac8d5baSMatthew Dillon 	at = &ap->ap_ata[target];
5971ac8d5baSMatthew Dillon 
5981ac8d5baSMatthew Dillon 	/*
5991ac8d5baSMatthew Dillon 	 * If the device needs an init or hard reset also make sure the
6001ac8d5baSMatthew Dillon 	 * PHY is turned on.
6011ac8d5baSMatthew Dillon 	 */
6021ac8d5baSMatthew Dillon 	if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET) {
6031ac8d5baSMatthew Dillon 		/*kprintf("%s DOHARD\n", ATANAME(ap, at));*/
6041ac8d5baSMatthew Dillon 		sili_pm_hardreset(ap, target, 1);
6051ac8d5baSMatthew Dillon 	}
6061ac8d5baSMatthew Dillon 
6071ac8d5baSMatthew Dillon 	/*
6081ac8d5baSMatthew Dillon 	 * Read the detect status
6091ac8d5baSMatthew Dillon 	 */
6101ac8d5baSMatthew Dillon 	if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) {
6111ac8d5baSMatthew Dillon 		kprintf("%s: Unable to access PM SSTS register target %d\n",
6121ac8d5baSMatthew Dillon 			PORTNAME(ap), target);
6131ac8d5baSMatthew Dillon 		return;
6141ac8d5baSMatthew Dillon 	}
6151ac8d5baSMatthew Dillon 	if ((data & SATA_PM_SSTS_DET) != SATA_PM_SSTS_DET_DEV) {
6161ac8d5baSMatthew Dillon 		/*kprintf("%s: DETECT %08x\n", ATANAME(ap, at), data);*/
6171ac8d5baSMatthew Dillon 		if (at->at_probe != ATA_PROBE_FAILED) {
6181ac8d5baSMatthew Dillon 			at->at_probe = ATA_PROBE_FAILED;
6191ac8d5baSMatthew Dillon 			at->at_type = ATA_PORT_T_NONE;
6201ac8d5baSMatthew Dillon 			at->at_features |= ATA_PORT_F_RESCAN;
6211ac8d5baSMatthew Dillon 			kprintf("%s: HOTPLUG (PM) - Device removed\n",
6221ac8d5baSMatthew Dillon 				ATANAME(ap, at));
6231ac8d5baSMatthew Dillon 		}
6241ac8d5baSMatthew Dillon 	} else {
6251ac8d5baSMatthew Dillon 		if (at->at_probe == ATA_PROBE_FAILED) {
6261ac8d5baSMatthew Dillon 			at->at_probe = ATA_PROBE_NEED_HARD_RESET;
6271ac8d5baSMatthew Dillon 			at->at_features |= ATA_PORT_F_RESCAN;
6281ac8d5baSMatthew Dillon 			kprintf("%s: HOTPLUG (PM) - Device inserted\n",
6291ac8d5baSMatthew Dillon 				ATANAME(ap, at));
6301ac8d5baSMatthew Dillon 		}
6311ac8d5baSMatthew Dillon 	}
6321ac8d5baSMatthew Dillon }
6331ac8d5baSMatthew Dillon 
6341ac8d5baSMatthew Dillon /*
6351ac8d5baSMatthew Dillon  * Read a PM register
6361ac8d5baSMatthew Dillon  */
6371ac8d5baSMatthew Dillon int
sili_pm_read(struct sili_port * ap,int target,int which,u_int32_t * datap)6381ac8d5baSMatthew Dillon sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap)
6391ac8d5baSMatthew Dillon {
6401ac8d5baSMatthew Dillon 	struct ata_xfer	*xa;
6411ac8d5baSMatthew Dillon 	int error;
6421ac8d5baSMatthew Dillon 
6431ac8d5baSMatthew Dillon 	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
6441ac8d5baSMatthew Dillon 
6451ac8d5baSMatthew Dillon 	xa->fis->type = ATA_FIS_TYPE_H2D;
6461ac8d5baSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
6471ac8d5baSMatthew Dillon 	xa->fis->command = ATA_C_READ_PM;
6481ac8d5baSMatthew Dillon 	xa->fis->features = which;
6491ac8d5baSMatthew Dillon 	xa->fis->device = target | ATA_H2D_DEVICE_LBA;
6501ac8d5baSMatthew Dillon 	xa->fis->control = ATA_FIS_CONTROL_4BIT;
6511ac8d5baSMatthew Dillon 
6521ac8d5baSMatthew Dillon 	xa->complete = sili_pm_dummy_done;
6531ac8d5baSMatthew Dillon 	xa->datalen = 0;
6544383d440SMatthew Dillon 	xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
6551ac8d5baSMatthew Dillon 	xa->timeout = 1000;
6561ac8d5baSMatthew Dillon 
6571ac8d5baSMatthew Dillon 	if (sili_ata_cmd(xa) == ATA_S_COMPLETE) {
6581ac8d5baSMatthew Dillon 		*datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) |
6591ac8d5baSMatthew Dillon 		       (xa->rfis->lba_mid << 16) | (xa->rfis->lba_high << 24);
6601ac8d5baSMatthew Dillon 		error = 0;
6611ac8d5baSMatthew Dillon 	} else {
6621ac8d5baSMatthew Dillon 		kprintf("%s.%d pm_read SCA[%d] failed\n",
6631ac8d5baSMatthew Dillon 			PORTNAME(ap), target, which);
6641ac8d5baSMatthew Dillon 		*datap = 0;
6651ac8d5baSMatthew Dillon 		error = EIO;
6661ac8d5baSMatthew Dillon 	}
6671ac8d5baSMatthew Dillon 	sili_ata_put_xfer(xa);
6681ac8d5baSMatthew Dillon 	return (error);
6691ac8d5baSMatthew Dillon }
6701ac8d5baSMatthew Dillon 
6711ac8d5baSMatthew Dillon /*
6721ac8d5baSMatthew Dillon  * Write a PM register
6731ac8d5baSMatthew Dillon  */
6741ac8d5baSMatthew Dillon int
sili_pm_write(struct sili_port * ap,int target,int which,u_int32_t data)6751ac8d5baSMatthew Dillon sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data)
6761ac8d5baSMatthew Dillon {
6771ac8d5baSMatthew Dillon 	struct ata_xfer	*xa;
6781ac8d5baSMatthew Dillon 	int error;
6791ac8d5baSMatthew Dillon 
6801ac8d5baSMatthew Dillon 	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);
6811ac8d5baSMatthew Dillon 
6821ac8d5baSMatthew Dillon 	xa->fis->type = ATA_FIS_TYPE_H2D;
6831ac8d5baSMatthew Dillon 	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
6841ac8d5baSMatthew Dillon 	xa->fis->command = ATA_C_WRITE_PM;
6851ac8d5baSMatthew Dillon 	xa->fis->features = which;
6861ac8d5baSMatthew Dillon 	xa->fis->device = target | ATA_H2D_DEVICE_LBA;
6871ac8d5baSMatthew Dillon 	xa->fis->sector_count = (u_int8_t)data;
6881ac8d5baSMatthew Dillon 	xa->fis->lba_low = (u_int8_t)(data >> 8);
6891ac8d5baSMatthew Dillon 	xa->fis->lba_mid = (u_int8_t)(data >> 16);
6901ac8d5baSMatthew Dillon 	xa->fis->lba_high = (u_int8_t)(data >> 24);
6911ac8d5baSMatthew Dillon 	xa->fis->control = ATA_FIS_CONTROL_4BIT;
6921ac8d5baSMatthew Dillon 
6931ac8d5baSMatthew Dillon 	xa->complete = sili_pm_dummy_done;
6941ac8d5baSMatthew Dillon 	xa->datalen = 0;
6954383d440SMatthew Dillon 	xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
6961ac8d5baSMatthew Dillon 	xa->timeout = 1000;
6971ac8d5baSMatthew Dillon 
6981ac8d5baSMatthew Dillon 	if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
6991ac8d5baSMatthew Dillon 		error = 0;
7001ac8d5baSMatthew Dillon 	else
7011ac8d5baSMatthew Dillon 		error = EIO;
7021ac8d5baSMatthew Dillon 	sili_ata_put_xfer(xa);
7031ac8d5baSMatthew Dillon 	return(error);
7041ac8d5baSMatthew Dillon }
7051ac8d5baSMatthew Dillon 
7061ac8d5baSMatthew Dillon /*
7071ac8d5baSMatthew Dillon  * Dummy done callback for xa.
7081ac8d5baSMatthew Dillon  */
7091ac8d5baSMatthew Dillon static void
sili_pm_dummy_done(struct ata_xfer * xa)7101ac8d5baSMatthew Dillon sili_pm_dummy_done(struct ata_xfer *xa)
7111ac8d5baSMatthew Dillon {
7121ac8d5baSMatthew Dillon }
7131ac8d5baSMatthew Dillon 
7141ac8d5baSMatthew Dillon static void
sili_pm_empty_done(struct sili_ccb * ccb)7151ac8d5baSMatthew Dillon sili_pm_empty_done(struct sili_ccb *ccb)
7161ac8d5baSMatthew Dillon {
7171ac8d5baSMatthew Dillon }
718