diff options
author | Tejun Heo <htejun@gmail.com> | 2008-04-07 09:47:19 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-04-17 15:44:23 -0400 |
commit | ac371987a81c61c2efbd6931245cdcaf43baad89 (patch) | |
tree | f88970931b26d2ad344d7d67ddabc64d9b48181d | |
parent | 57c9efdfb3cee5d4564fcb5f70555e2edb1bc52a (diff) |
libata: clear SError after link resume
SError used to be cleared in ->postreset. This has small hotplug race
condition. If a device is plugged in after reset is complete but
postreset hasn't run yet, its hotplug event gets lost when SError is
cleared. This patch makes sata_link_resume() clear SError. This
kills the race condition and makes a lot of sense as some PMP and host
PHYs don't work properly without SError cleared.
This change makes sata_pmp_std_{pre|post}_reset()'s unnecessary as
they become identical to ata_std counterparts. It also simplifies
sata_pmp_hardreset() and ahci_vt8251_hardreset().
Signed-off-by: Tejun Heo <htejun@gmail.com>
-rw-r--r-- | drivers/ata/ahci.c | 5 | ||||
-rw-r--r-- | drivers/ata/libata-core.c | 35 | ||||
-rw-r--r-- | drivers/ata/libata-pmp.c | 93 | ||||
-rw-r--r-- | include/linux/libata.h | 2 |
4 files changed, 23 insertions, 112 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 0f553aaa6f79..a69bcca4eb1b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -1377,7 +1377,6 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, | |||
1377 | unsigned long deadline) | 1377 | unsigned long deadline) |
1378 | { | 1378 | { |
1379 | struct ata_port *ap = link->ap; | 1379 | struct ata_port *ap = link->ap; |
1380 | u32 serror; | ||
1381 | bool online; | 1380 | bool online; |
1382 | int rc; | 1381 | int rc; |
1383 | 1382 | ||
@@ -1388,10 +1387,6 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, | |||
1388 | rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), | 1387 | rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), |
1389 | deadline, &online, NULL); | 1388 | deadline, &online, NULL); |
1390 | 1389 | ||
1391 | /* vt8251 needs SError cleared for the port to operate */ | ||
1392 | ahci_scr_read(ap, SCR_ERROR, &serror); | ||
1393 | ahci_scr_write(ap, SCR_ERROR, serror); | ||
1394 | |||
1395 | ahci_start_engine(ap); | 1390 | ahci_start_engine(ap); |
1396 | 1391 | ||
1397 | DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); | 1392 | DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); |
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c4fd4afbf349..e00b620f161a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
@@ -90,9 +90,9 @@ const struct ata_port_operations sata_port_ops = { | |||
90 | const struct ata_port_operations sata_pmp_port_ops = { | 90 | const struct ata_port_operations sata_pmp_port_ops = { |
91 | .inherits = &sata_port_ops, | 91 | .inherits = &sata_port_ops, |
92 | 92 | ||
93 | .pmp_prereset = sata_pmp_std_prereset, | 93 | .pmp_prereset = ata_std_prereset, |
94 | .pmp_hardreset = sata_pmp_std_hardreset, | 94 | .pmp_hardreset = sata_pmp_std_hardreset, |
95 | .pmp_postreset = sata_pmp_std_postreset, | 95 | .pmp_postreset = ata_std_postreset, |
96 | .error_handler = sata_pmp_error_handler, | 96 | .error_handler = sata_pmp_error_handler, |
97 | }; | 97 | }; |
98 | 98 | ||
@@ -3493,7 +3493,7 @@ int sata_link_debounce(struct ata_link *link, const unsigned long *params, | |||
3493 | int sata_link_resume(struct ata_link *link, const unsigned long *params, | 3493 | int sata_link_resume(struct ata_link *link, const unsigned long *params, |
3494 | unsigned long deadline) | 3494 | unsigned long deadline) |
3495 | { | 3495 | { |
3496 | u32 scontrol; | 3496 | u32 scontrol, serror; |
3497 | int rc; | 3497 | int rc; |
3498 | 3498 | ||
3499 | if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol))) | 3499 | if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol))) |
@@ -3509,7 +3509,25 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params, | |||
3509 | */ | 3509 | */ |
3510 | msleep(200); | 3510 | msleep(200); |
3511 | 3511 | ||
3512 | return sata_link_debounce(link, params, deadline); | 3512 | if ((rc = sata_link_debounce(link, params, deadline))) |
3513 | return rc; | ||
3514 | |||
3515 | /* Clear SError. PMP and some host PHYs require this to | ||
3516 | * operate and clearing should be done before checking PHY | ||
3517 | * online status to avoid race condition (hotplugging between | ||
3518 | * link resume and status check). | ||
3519 | */ | ||
3520 | if (!(rc = sata_scr_read(link, SCR_ERROR, &serror))) | ||
3521 | rc = sata_scr_write(link, SCR_ERROR, serror); | ||
3522 | if (rc == 0 || rc == -EINVAL) { | ||
3523 | unsigned long flags; | ||
3524 | |||
3525 | spin_lock_irqsave(link->ap->lock, flags); | ||
3526 | link->eh_info.serror = 0; | ||
3527 | spin_unlock_irqrestore(link->ap->lock, flags); | ||
3528 | rc = 0; | ||
3529 | } | ||
3530 | return rc; | ||
3513 | } | 3531 | } |
3514 | 3532 | ||
3515 | /** | 3533 | /** |
@@ -3701,18 +3719,11 @@ int sata_std_hardreset(struct ata_link *link, unsigned int *class, | |||
3701 | */ | 3719 | */ |
3702 | void ata_std_postreset(struct ata_link *link, unsigned int *classes) | 3720 | void ata_std_postreset(struct ata_link *link, unsigned int *classes) |
3703 | { | 3721 | { |
3704 | u32 serror; | ||
3705 | |||
3706 | DPRINTK("ENTER\n"); | 3722 | DPRINTK("ENTER\n"); |
3707 | 3723 | ||
3708 | /* print link status */ | 3724 | /* print link status */ |
3709 | sata_print_link_status(link); | 3725 | sata_print_link_status(link); |
3710 | 3726 | ||
3711 | /* clear SError */ | ||
3712 | if (sata_scr_read(link, SCR_ERROR, &serror) == 0) | ||
3713 | sata_scr_write(link, SCR_ERROR, serror); | ||
3714 | link->eh_info.serror = 0; | ||
3715 | |||
3716 | DPRINTK("EXIT\n"); | 3727 | DPRINTK("EXIT\n"); |
3717 | } | 3728 | } |
3718 | 3729 | ||
@@ -6296,9 +6307,7 @@ EXPORT_SYMBOL_GPL(ata_pci_device_resume); | |||
6296 | #endif /* CONFIG_PCI */ | 6307 | #endif /* CONFIG_PCI */ |
6297 | 6308 | ||
6298 | EXPORT_SYMBOL_GPL(sata_pmp_qc_defer_cmd_switch); | 6309 | EXPORT_SYMBOL_GPL(sata_pmp_qc_defer_cmd_switch); |
6299 | EXPORT_SYMBOL_GPL(sata_pmp_std_prereset); | ||
6300 | EXPORT_SYMBOL_GPL(sata_pmp_std_hardreset); | 6310 | EXPORT_SYMBOL_GPL(sata_pmp_std_hardreset); |
6301 | EXPORT_SYMBOL_GPL(sata_pmp_std_postreset); | ||
6302 | EXPORT_SYMBOL_GPL(sata_pmp_error_handler); | 6311 | EXPORT_SYMBOL_GPL(sata_pmp_error_handler); |
6303 | 6312 | ||
6304 | EXPORT_SYMBOL_GPL(__ata_ehi_push_desc); | 6313 | EXPORT_SYMBOL_GPL(__ata_ehi_push_desc); |
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 7f1a87f01ab2..2f8a9577c26d 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c | |||
@@ -176,49 +176,6 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val) | |||
176 | } | 176 | } |
177 | 177 | ||
178 | /** | 178 | /** |
179 | * sata_pmp_std_prereset - prepare PMP link for reset | ||
180 | * @link: link to be reset | ||
181 | * @deadline: deadline jiffies for the operation | ||
182 | * | ||
183 | * @link is about to be reset. Initialize it. | ||
184 | * | ||
185 | * LOCKING: | ||
186 | * Kernel thread context (may sleep) | ||
187 | * | ||
188 | * RETURNS: | ||
189 | * 0 on success, -errno otherwise. | ||
190 | */ | ||
191 | int sata_pmp_std_prereset(struct ata_link *link, unsigned long deadline) | ||
192 | { | ||
193 | struct ata_eh_context *ehc = &link->eh_context; | ||
194 | const unsigned long *timing = sata_ehc_deb_timing(ehc); | ||
195 | int rc; | ||
196 | |||
197 | /* if we're about to do hardreset, nothing more to do */ | ||
198 | if (ehc->i.action & ATA_EH_HARDRESET) | ||
199 | return 0; | ||
200 | |||
201 | /* resume link */ | ||
202 | rc = sata_link_resume(link, timing, deadline); | ||
203 | if (rc) { | ||
204 | /* phy resume failed */ | ||
205 | ata_link_printk(link, KERN_WARNING, "failed to resume link " | ||
206 | "for reset (errno=%d)\n", rc); | ||
207 | return rc; | ||
208 | } | ||
209 | |||
210 | /* clear SError bits including .X which blocks the port when set */ | ||
211 | rc = sata_scr_write(link, SCR_ERROR, 0xffffffff); | ||
212 | if (rc) { | ||
213 | ata_link_printk(link, KERN_ERR, | ||
214 | "failed to clear SError (errno=%d)\n", rc); | ||
215 | return rc; | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * sata_pmp_std_hardreset - standard hardreset method for PMP link | 179 | * sata_pmp_std_hardreset - standard hardreset method for PMP link |
223 | * @link: link to be reset | 180 | * @link: link to be reset |
224 | * @class: resulting class of attached device | 181 | * @class: resulting class of attached device |
@@ -238,33 +195,13 @@ int sata_pmp_std_prereset(struct ata_link *link, unsigned long deadline) | |||
238 | int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class, | 195 | int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class, |
239 | unsigned long deadline) | 196 | unsigned long deadline) |
240 | { | 197 | { |
241 | const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); | ||
242 | bool online; | ||
243 | u32 tmp; | 198 | u32 tmp; |
244 | int rc; | 199 | int rc; |
245 | 200 | ||
246 | DPRINTK("ENTER\n"); | 201 | DPRINTK("ENTER\n"); |
247 | 202 | ||
248 | /* do hardreset */ | 203 | rc = sata_std_hardreset(link, class, deadline); |
249 | rc = sata_link_hardreset(link, timing, deadline, &online, NULL); | ||
250 | if (rc) { | ||
251 | ata_link_printk(link, KERN_ERR, | ||
252 | "COMRESET failed (errno=%d)\n", rc); | ||
253 | goto out; | ||
254 | } | ||
255 | |||
256 | /* clear SError bits including .X which blocks the port when set */ | ||
257 | rc = sata_scr_write(link, SCR_ERROR, 0xffffffff); | ||
258 | if (rc) { | ||
259 | ata_link_printk(link, KERN_ERR, "failed to clear SError " | ||
260 | "during hardreset (errno=%d)\n", rc); | ||
261 | goto out; | ||
262 | } | ||
263 | 204 | ||
264 | /* if device is present, follow up with srst to wait for !BSY */ | ||
265 | if (online) | ||
266 | rc = -EAGAIN; | ||
267 | out: | ||
268 | /* if SCR isn't accessible, we need to reset the PMP */ | 205 | /* if SCR isn't accessible, we need to reset the PMP */ |
269 | if (rc && rc != -EAGAIN && sata_scr_read(link, SCR_STATUS, &tmp)) | 206 | if (rc && rc != -EAGAIN && sata_scr_read(link, SCR_STATUS, &tmp)) |
270 | rc = -ERESTART; | 207 | rc = -ERESTART; |
@@ -274,34 +211,6 @@ int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class, | |||
274 | } | 211 | } |
275 | 212 | ||
276 | /** | 213 | /** |
277 | * ata_std_postreset - standard postreset method for PMP link | ||
278 | * @link: the target ata_link | ||
279 | * @classes: classes of attached devices | ||
280 | * | ||
281 | * This function is invoked after a successful reset. Note that | ||
282 | * the device might have been reset more than once using | ||
283 | * different reset methods before postreset is invoked. | ||
284 | * | ||
285 | * LOCKING: | ||
286 | * Kernel thread context (may sleep) | ||
287 | */ | ||
288 | void sata_pmp_std_postreset(struct ata_link *link, unsigned int *class) | ||
289 | { | ||
290 | u32 serror; | ||
291 | |||
292 | DPRINTK("ENTER\n"); | ||
293 | |||
294 | /* clear SError */ | ||
295 | if (sata_scr_read(link, SCR_ERROR, &serror) == 0) | ||
296 | sata_scr_write(link, SCR_ERROR, serror); | ||
297 | |||
298 | /* print link status */ | ||
299 | sata_print_link_status(link); | ||
300 | |||
301 | DPRINTK("EXIT\n"); | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * sata_pmp_read_gscr - read GSCR block of SATA PMP | 214 | * sata_pmp_read_gscr - read GSCR block of SATA PMP |
306 | * @dev: PMP device | 215 | * @dev: PMP device |
307 | * @gscr: buffer to read GSCR block into | 216 | * @gscr: buffer to read GSCR block into |
diff --git a/include/linux/libata.h b/include/linux/libata.h index c060cd3cba66..b9188371b12a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -1025,10 +1025,8 @@ static inline int ata_acpi_cbl_80wire(struct ata_port *ap, | |||
1025 | * PMP - drivers/ata/libata-pmp.c | 1025 | * PMP - drivers/ata/libata-pmp.c |
1026 | */ | 1026 | */ |
1027 | extern int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc); | 1027 | extern int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc); |
1028 | extern int sata_pmp_std_prereset(struct ata_link *link, unsigned long deadline); | ||
1029 | extern int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class, | 1028 | extern int sata_pmp_std_hardreset(struct ata_link *link, unsigned int *class, |
1030 | unsigned long deadline); | 1029 | unsigned long deadline); |
1031 | extern void sata_pmp_std_postreset(struct ata_link *link, unsigned int *class); | ||
1032 | extern void sata_pmp_error_handler(struct ata_port *ap); | 1030 | extern void sata_pmp_error_handler(struct ata_port *ap); |
1033 | 1031 | ||
1034 | /* | 1032 | /* |