Commit | Line | Data |
---|---|---|
1ac8d5ba MD |
1 | /* |
2 | * Copyright (c) 2009 The DragonFly Project. All rights reserved. | |
3 | * | |
4 | * This code is derived from software contributed to The DragonFly Project | |
5 | * by Matthew Dillon <dillon@backplane.com> | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific, prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include "sili.h" | |
36 | ||
37 | static void sili_pm_dummy_done(struct ata_xfer *xa); | |
38 | static void sili_pm_empty_done(struct sili_ccb *ccb); | |
39 | ||
40 | /* | |
41 | * Identify the port multiplier | |
42 | */ | |
43 | int | |
44 | sili_pm_identify(struct sili_port *ap) | |
45 | { | |
46 | u_int32_t chipid; | |
47 | u_int32_t rev; | |
48 | u_int32_t nports; | |
49 | u_int32_t data1; | |
50 | u_int32_t data2; | |
51 | ||
52 | ap->ap_pmcount = 0; | |
53 | ap->ap_probe = ATA_PROBE_FAILED; | |
54 | if (sili_pm_read(ap, 15, 0, &chipid)) | |
55 | goto err; | |
56 | if (sili_pm_read(ap, 15, 1, &rev)) | |
57 | goto err; | |
58 | if (sili_pm_read(ap, 15, 2, &nports)) | |
59 | goto err; | |
60 | nports &= 0x0000000F; /* only the low 4 bits */ | |
61 | --nports; | |
62 | ap->ap_probe = ATA_PROBE_GOOD; | |
63 | kprintf("%s: Port multiplier: chip=%08x rev=0x%b nports=%d\n", | |
64 | PORTNAME(ap), | |
65 | chipid, | |
66 | rev, SATA_PFMT_PM_REV, | |
67 | nports); | |
68 | ap->ap_pmcount = nports; | |
69 | ||
70 | if (sili_pm_read(ap, 15, SATA_PMREG_FEA, &data1)) { | |
71 | kprintf("%s: Port multiplier: Warning, " | |
72 | "cannot read feature register\n", PORTNAME(ap)); | |
73 | } else { | |
74 | kprintf("%s: Port multiplier features: 0x%b\n", | |
75 | PORTNAME(ap), | |
76 | data1, | |
77 | SATA_PFMT_PM_FEA); | |
78 | } | |
79 | if (sili_pm_read(ap, 15, SATA_PMREG_FEAEN, &data2) == 0) { | |
80 | kprintf("%s: Port multiplier defaults: 0x%b\n", | |
81 | PORTNAME(ap), | |
82 | data2, | |
83 | SATA_PFMT_PM_FEA); | |
84 | } | |
85 | ||
86 | /* | |
87 | * Turn on async notification if we support and the PM supports it. | |
88 | * This allows the PM to forward async notification events to us and | |
89 | * it will also generate an event for target 15 for hot-plug events | |
90 | * (or is supposed to anyway). | |
91 | */ | |
92 | if ((ap->ap_sc->sc_flags & SILI_F_SSNTF) && | |
93 | (data1 & SATA_PMFEA_ASYNCNOTIFY)) { | |
94 | u_int32_t serr_bits = SATA_PM_SERR_DIAG_N | | |
95 | SATA_PM_SERR_DIAG_X; | |
96 | data2 |= SATA_PMFEA_ASYNCNOTIFY; | |
97 | if (sili_pm_write(ap, 15, SATA_PMREG_FEAEN, data2)) { | |
98 | kprintf("%s: Port multiplier: AsyncNotify cannot be " | |
99 | "enabled\n", PORTNAME(ap)); | |
100 | } else if (sili_pm_write(ap, 15, SATA_PMREG_EEENA, serr_bits)) { | |
101 | kprintf("%s: Port mulltiplier: AsyncNotify unable " | |
102 | "to enable error info bits\n", PORTNAME(ap)); | |
103 | } else { | |
104 | kprintf("%s: Port multiplier: AsyncNotify enabled\n", | |
105 | PORTNAME(ap)); | |
106 | } | |
107 | } | |
108 | ||
109 | return (0); | |
110 | err: | |
111 | kprintf("%s: Port multiplier cannot be identified\n", PORTNAME(ap)); | |
112 | return (EIO); | |
113 | } | |
114 | ||
115 | /* | |
116 | * Do a COMRESET sequence on the target behind a port multiplier. | |
117 | * | |
118 | * If hard is 2 we also cycle the phy on the target. | |
119 | * | |
120 | * This must be done prior to any softreset or probe attempts on | |
121 | * targets behind the port multiplier. | |
122 | * | |
123 | * Returns 0 on success or an error. | |
124 | */ | |
125 | int | |
126 | sili_pm_hardreset(struct sili_port *ap, int target, int hard) | |
127 | { | |
128 | struct ata_port *at; | |
129 | u_int32_t data; | |
130 | int loop; | |
131 | int error = EIO; | |
132 | ||
133 | at = &ap->ap_ata[target]; | |
134 | ||
135 | /* | |
136 | * Turn off power management and kill the phy on the target | |
137 | * if requested. Hold state for 10ms. | |
138 | */ | |
139 | data = SATA_PM_SCTL_IPM_DISABLED; | |
140 | if (hard == 2) | |
141 | data |= SATA_PM_SCTL_DET_DISABLE; | |
142 | if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) | |
143 | goto err; | |
144 | if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) | |
145 | goto err; | |
146 | sili_os_sleep(10); | |
147 | ||
148 | /* | |
149 | * Start transmitting COMRESET. COMRESET must be sent for at | |
150 | * least 1ms. | |
151 | */ | |
152 | at->at_probe = ATA_PROBE_FAILED; | |
153 | at->at_type = ATA_PORT_T_NONE; | |
154 | data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_INIT; | |
155 | if (SiliForceGen1 & (1 << ap->ap_num)) { | |
156 | kprintf("%s.%d: Force 1.5GBits\n", PORTNAME(ap), target); | |
157 | data |= SATA_PM_SCTL_SPD_GEN1; | |
158 | } else { | |
159 | data |= SATA_PM_SCTL_SPD_ANY; | |
160 | } | |
161 | if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) | |
162 | goto err; | |
163 | ||
164 | /* | |
165 | * It takes about 100ms for the DET logic to settle down, | |
166 | * from trial and error testing. If this is too short | |
167 | * the softreset code will fail. | |
168 | */ | |
169 | sili_os_sleep(100); | |
170 | ||
171 | if (sili_pm_phy_status(ap, target, &data)) { | |
172 | kprintf("%s: (A)Cannot clear phy status\n", | |
173 | ATANAME(ap ,at)); | |
174 | } | |
175 | ||
176 | /* | |
177 | * Flush any status, then clear DET to initiate negotiation. | |
178 | */ | |
179 | sili_pm_write(ap, target, SATA_PMREG_SERR, -1); | |
180 | data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_NONE; | |
181 | if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) | |
182 | goto err; | |
183 | ||
184 | /* | |
185 | * Try to determine if there is a device on the port. | |
186 | * | |
187 | * Give the device 3/10 second to at least be detected. | |
188 | * If we fail clear any pending status since we may have | |
189 | * cycled the phy and probably caused another PRCS interrupt. | |
190 | */ | |
191 | for (loop = 3; loop; --loop) { | |
192 | if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) | |
193 | goto err; | |
194 | if (data & SATA_PM_SSTS_DET) | |
195 | break; | |
196 | sili_os_sleep(100); | |
197 | } | |
198 | if (loop == 0) { | |
199 | kprintf("%s.%d: Port appears to be unplugged\n", | |
200 | PORTNAME(ap), target); | |
201 | error = ENODEV; | |
202 | goto err; | |
203 | } | |
204 | ||
205 | /* | |
206 | * There is something on the port. Give the device 3 seconds | |
207 | * to fully negotiate. | |
208 | */ | |
209 | for (loop = 30; loop; --loop) { | |
210 | if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) | |
211 | goto err; | |
212 | if ((data & SATA_PM_SSTS_DET) == SATA_PM_SSTS_DET_DEV) | |
213 | break; | |
214 | sili_os_sleep(100); | |
215 | } | |
216 | ||
217 | /* | |
218 | * Device not detected | |
219 | */ | |
220 | if (loop == 0) { | |
221 | kprintf("%s: Device may be powered down\n", | |
222 | PORTNAME(ap)); | |
223 | error = ENODEV; | |
224 | goto err; | |
225 | } | |
226 | ||
227 | /* | |
228 | * Device detected | |
229 | */ | |
230 | kprintf("%s.%d: Device detected data=%08x\n", | |
231 | PORTNAME(ap), target, data); | |
232 | /* | |
233 | * Clear SERR on the target so we get a new NOTIFY event if a hot-plug | |
234 | * or hot-unplug occurs. | |
235 | */ | |
236 | sili_os_sleep(100); | |
237 | ||
238 | error = 0; | |
239 | err: | |
240 | at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET; | |
241 | return (error); | |
242 | } | |
243 | ||
244 | /* | |
245 | * SILI soft reset through port multiplier. | |
246 | * | |
247 | * This function keeps port communications intact and attempts to generate | |
248 | * a reset to the connected device using device commands. Unlike | |
249 | * hard-port operations we can't do fancy stop/starts or stuff like | |
250 | * that without messing up other commands that might be running or | |
251 | * queued. | |
252 | * | |
253 | * The SII chip will do the whole mess for us. | |
254 | */ | |
255 | int | |
256 | sili_pm_softreset(struct sili_port *ap, int target) | |
257 | { | |
258 | struct ata_port *at; | |
259 | struct sili_ccb *ccb; | |
260 | struct sili_prb *prb; | |
261 | int error; | |
262 | u_int32_t data; | |
263 | u_int32_t sig; | |
264 | ||
265 | error = EIO; | |
266 | at = &ap->ap_ata[target]; | |
267 | ||
268 | DPRINTF(SILI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap)); | |
269 | ||
270 | /* | |
271 | * Prep the special soft-reset SII command. | |
272 | */ | |
273 | ccb = sili_get_err_ccb(ap); | |
274 | ccb->ccb_done = sili_pm_empty_done; | |
4383d440 | 275 | ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE; |
1ac8d5ba MD |
276 | ccb->ccb_xa.complete = sili_pm_dummy_done; |
277 | ccb->ccb_xa.at = at; | |
278 | ||
279 | prb = ccb->ccb_prb; | |
280 | bzero(&prb->prb_h2d, sizeof(prb->prb_h2d)); | |
281 | prb->prb_h2d.flags = at->at_target; | |
282 | prb->prb_control = SILI_PRB_CTRL_SOFTRESET; | |
283 | prb->prb_override = 0; | |
284 | prb->prb_xfer_count = 0; | |
285 | ||
286 | ccb->ccb_xa.state = ATA_S_PENDING; | |
287 | ccb->ccb_xa.flags = 0; | |
288 | ||
289 | if (sili_poll(ccb, 8000, sili_ata_cmd_timeout) != ATA_S_COMPLETE) { | |
290 | kprintf("%s: (PM) Softreset FIS failed\n", ATANAME(ap, at)); | |
291 | sili_put_err_ccb(ccb); | |
292 | goto err; | |
293 | } | |
294 | ||
295 | sig = (prb->prb_d2h.lba_high << 24) | | |
296 | (prb->prb_d2h.lba_mid << 16) | | |
297 | (prb->prb_d2h.lba_low << 8) | | |
298 | (prb->prb_d2h.sector_count); | |
299 | kprintf("%s: PM SOFTRESET SIGNATURE %08x\n", ATANAME(ap, at), sig); | |
300 | ||
301 | sili_put_err_ccb(ccb); | |
302 | ||
303 | /* | |
304 | * Clear the phy status of the target so we can get a new event. | |
305 | * | |
306 | * Target 15 is the PM itself and these registers have | |
307 | * different meanings. | |
308 | */ | |
309 | if (target != 15) { | |
310 | if (sili_pm_phy_status(ap, target, &data)) { | |
311 | kprintf("%s: (C)Cannot clear phy status\n", | |
312 | ATANAME(ap ,at)); | |
313 | } | |
314 | sili_pm_write(ap, target, SATA_PMREG_SERR, -1); | |
315 | } | |
316 | ||
317 | /* | |
318 | * If the softreset is trying to clear a BSY condition after a | |
319 | * normal portreset we assign the port type. | |
320 | * | |
321 | * If the softreset is being run first as part of the ccb error | |
322 | * processing code then report if the device signature changed | |
323 | * unexpectedly. | |
324 | */ | |
325 | if (at->at_type == ATA_PORT_T_NONE) { | |
326 | at->at_type = sili_port_signature(ap, at, sig); | |
327 | } else { | |
328 | if (sili_port_signature(ap, at, sig) != at->at_type) { | |
329 | kprintf("%s: device signature unexpectedly " | |
330 | "changed\n", ATANAME(ap, at)); | |
331 | error = EBUSY; /* XXX */ | |
332 | } | |
333 | } | |
334 | error = 0; | |
335 | err: | |
336 | /* | |
337 | * Clear error status so we can detect removal. | |
338 | * | |
339 | * Target 15 is the PM itself and these registers have | |
340 | * different meanings. | |
341 | */ | |
342 | if (error == 0 && target != 15) { | |
343 | if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) { | |
344 | kprintf("%s: sili_pm_softreset unable to clear SERR\n", | |
345 | ATANAME(ap, at)); | |
346 | ap->ap_flags &= ~AP_F_IGNORE_IFS; | |
347 | } | |
348 | } | |
349 | ||
350 | at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT; | |
351 | return (error); | |
352 | } | |
353 | ||
354 | ||
355 | /* | |
356 | * Return the phy status for a target behind a port multiplier and | |
357 | * reset SATA_PMREG_SERR. | |
358 | * | |
359 | * Returned bits follow SILI_PREG_SSTS bits. The SILI_PREG_SSTS_SPD | |
360 | * bits can be used to determine the link speed and will be 0 if there | |
361 | * is no link. | |
362 | * | |
363 | * 0 is returned if any communications error occurs. | |
364 | */ | |
365 | int | |
366 | sili_pm_phy_status(struct sili_port *ap, int target, u_int32_t *datap) | |
367 | { | |
368 | int error; | |
369 | ||
370 | error = sili_pm_read(ap, target, SATA_PMREG_SSTS, datap); | |
371 | if (error == 0) | |
372 | error = sili_pm_write(ap, target, SATA_PMREG_SERR, -1); | |
373 | if (error) | |
374 | *datap = 0; | |
375 | return(error); | |
376 | } | |
377 | ||
378 | int | |
379 | sili_pm_set_feature(struct sili_port *ap, int feature, int enable) | |
380 | { | |
381 | struct ata_xfer *xa; | |
382 | int error; | |
383 | ||
384 | xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]); | |
385 | ||
386 | xa->fis->type = ATA_FIS_TYPE_H2D; | |
387 | xa->fis->flags = ATA_H2D_FLAGS_CMD | 15; | |
388 | xa->fis->command = enable ? ATA_C_SATA_FEATURE_ENA : | |
389 | ATA_C_SATA_FEATURE_DIS; | |
390 | xa->fis->sector_count = feature; | |
391 | xa->fis->control = ATA_FIS_CONTROL_4BIT; | |
392 | ||
393 | xa->complete = sili_pm_dummy_done; | |
394 | xa->datalen = 0; | |
4383d440 | 395 | xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE; |
1ac8d5ba MD |
396 | xa->timeout = 1000; |
397 | ||
398 | if (sili_ata_cmd(xa) == ATA_S_COMPLETE) | |
399 | error = 0; | |
400 | else | |
401 | error = EIO; | |
402 | sili_ata_put_xfer(xa); | |
403 | return(error); | |
404 | } | |
405 | ||
406 | /* | |
407 | * Check that a target is still good. | |
408 | */ | |
409 | void | |
410 | sili_pm_check_good(struct sili_port *ap, int target) | |
411 | { | |
412 | struct ata_port *at; | |
413 | u_int32_t data; | |
414 | ||
415 | /* | |
416 | * It looks like we might have to read the EINFO register | |
417 | * to allow the PM to generate a new event. | |
418 | */ | |
419 | if (sili_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) { | |
420 | kprintf("%s: Port multiplier EINFO could not be read\n", | |
421 | PORTNAME(ap)); | |
422 | } | |
423 | ||
424 | if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) { | |
425 | kprintf("%s: Port multiplier: SERR could not be cleared\n", | |
426 | PORTNAME(ap)); | |
427 | } | |
428 | ||
429 | if (target == CAM_TARGET_WILDCARD || target >= ap->ap_pmcount) | |
430 | return; | |
431 | at = &ap->ap_ata[target]; | |
432 | ||
433 | /* | |
434 | * If the device needs an init or hard reset also make sure the | |
435 | * PHY is turned on. | |
436 | */ | |
437 | if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET) { | |
438 | /*kprintf("%s DOHARD\n", ATANAME(ap, at));*/ | |
439 | sili_pm_hardreset(ap, target, 1); | |
440 | } | |
441 | ||
442 | /* | |
443 | * Read the detect status | |
444 | */ | |
445 | if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) { | |
446 | kprintf("%s: Unable to access PM SSTS register target %d\n", | |
447 | PORTNAME(ap), target); | |
448 | return; | |
449 | } | |
450 | if ((data & SATA_PM_SSTS_DET) != SATA_PM_SSTS_DET_DEV) { | |
451 | /*kprintf("%s: DETECT %08x\n", ATANAME(ap, at), data);*/ | |
452 | if (at->at_probe != ATA_PROBE_FAILED) { | |
453 | at->at_probe = ATA_PROBE_FAILED; | |
454 | at->at_type = ATA_PORT_T_NONE; | |
455 | at->at_features |= ATA_PORT_F_RESCAN; | |
456 | kprintf("%s: HOTPLUG (PM) - Device removed\n", | |
457 | ATANAME(ap, at)); | |
458 | } | |
459 | } else { | |
460 | if (at->at_probe == ATA_PROBE_FAILED) { | |
461 | at->at_probe = ATA_PROBE_NEED_HARD_RESET; | |
462 | at->at_features |= ATA_PORT_F_RESCAN; | |
463 | kprintf("%s: HOTPLUG (PM) - Device inserted\n", | |
464 | ATANAME(ap, at)); | |
465 | } | |
466 | } | |
467 | } | |
468 | ||
469 | /* | |
470 | * Read a PM register | |
471 | */ | |
472 | int | |
473 | sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap) | |
474 | { | |
475 | struct ata_xfer *xa; | |
476 | int error; | |
477 | ||
478 | xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]); | |
479 | ||
480 | xa->fis->type = ATA_FIS_TYPE_H2D; | |
481 | xa->fis->flags = ATA_H2D_FLAGS_CMD | 15; | |
482 | xa->fis->command = ATA_C_READ_PM; | |
483 | xa->fis->features = which; | |
484 | xa->fis->device = target | ATA_H2D_DEVICE_LBA; | |
485 | xa->fis->control = ATA_FIS_CONTROL_4BIT; | |
486 | ||
487 | xa->complete = sili_pm_dummy_done; | |
488 | xa->datalen = 0; | |
4383d440 | 489 | xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE; |
1ac8d5ba MD |
490 | xa->timeout = 1000; |
491 | ||
492 | if (sili_ata_cmd(xa) == ATA_S_COMPLETE) { | |
493 | *datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) | | |
494 | (xa->rfis->lba_mid << 16) | (xa->rfis->lba_high << 24); | |
495 | error = 0; | |
496 | } else { | |
497 | kprintf("%s.%d pm_read SCA[%d] failed\n", | |
498 | PORTNAME(ap), target, which); | |
499 | *datap = 0; | |
500 | error = EIO; | |
501 | } | |
502 | sili_ata_put_xfer(xa); | |
503 | return (error); | |
504 | } | |
505 | ||
506 | /* | |
507 | * Write a PM register | |
508 | */ | |
509 | int | |
510 | sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data) | |
511 | { | |
512 | struct ata_xfer *xa; | |
513 | int error; | |
514 | ||
515 | xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]); | |
516 | ||
517 | xa->fis->type = ATA_FIS_TYPE_H2D; | |
518 | xa->fis->flags = ATA_H2D_FLAGS_CMD | 15; | |
519 | xa->fis->command = ATA_C_WRITE_PM; | |
520 | xa->fis->features = which; | |
521 | xa->fis->device = target | ATA_H2D_DEVICE_LBA; | |
522 | xa->fis->sector_count = (u_int8_t)data; | |
523 | xa->fis->lba_low = (u_int8_t)(data >> 8); | |
524 | xa->fis->lba_mid = (u_int8_t)(data >> 16); | |
525 | xa->fis->lba_high = (u_int8_t)(data >> 24); | |
526 | xa->fis->control = ATA_FIS_CONTROL_4BIT; | |
527 | ||
528 | xa->complete = sili_pm_dummy_done; | |
529 | xa->datalen = 0; | |
4383d440 | 530 | xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE; |
1ac8d5ba MD |
531 | xa->timeout = 1000; |
532 | ||
533 | if (sili_ata_cmd(xa) == ATA_S_COMPLETE) | |
534 | error = 0; | |
535 | else | |
536 | error = EIO; | |
537 | sili_ata_put_xfer(xa); | |
538 | return(error); | |
539 | } | |
540 | ||
541 | /* | |
542 | * Dummy done callback for xa. | |
543 | */ | |
544 | static void | |
545 | sili_pm_dummy_done(struct ata_xfer *xa) | |
546 | { | |
547 | } | |
548 | ||
549 | static void | |
550 | sili_pm_empty_done(struct sili_ccb *ccb) | |
551 | { | |
552 | } |