Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

CUDA ADB fixes

Fix the flakiness in the CUDA ADB driver on m68k macs (keypresses getting
wedged down or ADB just going AWOL altogether).

The only IRQ used by this driver is the VIA shift register IRQ. The PowerMac
conditional code disables the other VIA IRQ sources, so don't mess with the
other IRQ flags in the common code -- m68k macs need them.

When polling, don't disable local interrupts when we only need to disable the
CUDA interrupt.

Unless polling, don't clear the shift register IRQ flag. On m68k macs this
creates a race that often breaks CUDA ADB.

Tested on Quadra 840av and LC630 (both m68k); also Beige G3 (powerpc).

Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Finn Thain and committed by
Linus Torvalds
0251c38c d95fd5fc

+32 -26
+32 -26
drivers/macintosh/via-cuda.c
··· 82 82 static unsigned char *reply_ptr; 83 83 static int reading_reply; 84 84 static int data_index; 85 + static int cuda_irq; 85 86 #ifdef CONFIG_PPC 86 87 static struct device_node *vias; 87 88 #endif ··· 161 160 /* Clear and enable interrupts, but only on PPC. On 68K it's done */ 162 161 /* for us by the main VIA driver in arch/m68k/mac/via.c */ 163 162 164 - #ifndef CONFIG_MAC 165 163 out_8(&via[IFR], 0x7f); /* clear interrupts by writing 1s */ 166 164 out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */ 167 - #endif 168 165 169 166 /* enable autopoll */ 170 167 cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1); ··· 180 181 181 182 static int __init via_cuda_start(void) 182 183 { 183 - unsigned int irq; 184 - 185 184 if (via == NULL) 186 185 return -ENODEV; 187 186 188 187 #ifdef CONFIG_MAC 189 - irq = IRQ_MAC_ADB; 188 + cuda_irq = IRQ_MAC_ADB; 190 189 #else /* CONFIG_MAC */ 191 - irq = irq_of_parse_and_map(vias, 0); 192 - if (irq == NO_IRQ) { 190 + cuda_irq = irq_of_parse_and_map(vias, 0); 191 + if (cuda_irq == NO_IRQ) { 193 192 printk(KERN_ERR "via-cuda: can't map interrupts for %s\n", 194 193 vias->full_name); 195 194 return -ENODEV; 196 195 } 197 - #endif /* CONFIG_MAP */ 196 + #endif /* CONFIG_MAC */ 198 197 199 - if (request_irq(irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) { 200 - printk(KERN_ERR "via-cuda: can't request irq %d\n", irq); 198 + if (request_irq(cuda_irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) { 199 + printk(KERN_ERR "via-cuda: can't request irq %d\n", cuda_irq); 201 200 return -EAGAIN; 202 201 } 203 202 ··· 235 238 printk(KERN_ERR "cuda_init_via() failed\n"); 236 239 return -ENODEV; 237 240 } 241 + out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */ 238 242 239 243 return via_cuda_start(); 240 244 #endif ··· 261 263 out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */ 262 264 out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT); /* SR data in */ 263 265 (void)in_8(&via[SR]); /* clear any left-over data */ 264 - #ifndef CONFIG_MAC 266 + #ifdef CONFIG_PPC 265 267 out_8(&via[IER], 0x7f); /* disable interrupts from VIA */ 266 268 (void)in_8(&via[IER]); 269 + #else 270 + out_8(&via[IER], SR_INT); /* disable SR interrupt from VIA */ 267 271 #endif 268 272 269 273 /* delay 4ms and then clear any pending interrupt */ 270 274 mdelay(4); 271 275 (void)in_8(&via[SR]); 272 - out_8(&via[IFR], in_8(&via[IFR]) & 0x7f); 276 + out_8(&via[IFR], SR_INT); 273 277 274 278 /* sync with the CUDA - assert TACK without TIP */ 275 279 out_8(&via[B], in_8(&via[B]) & ~TACK); ··· 282 282 /* wait for the interrupt and then clear it */ 283 283 WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)"); 284 284 (void)in_8(&via[SR]); 285 - out_8(&via[IFR], in_8(&via[IFR]) & 0x7f); 285 + out_8(&via[IFR], SR_INT); 286 286 287 287 /* finish the sync by negating TACK */ 288 288 out_8(&via[B], in_8(&via[B]) | TACK); ··· 291 291 WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)"); 292 292 WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)"); 293 293 (void)in_8(&via[SR]); 294 - out_8(&via[IFR], in_8(&via[IFR]) & 0x7f); 294 + out_8(&via[IFR], SR_INT); 295 295 out_8(&via[B], in_8(&via[B]) | TIP); /* should be unnecessary */ 296 296 297 297 return 0; ··· 428 428 void 429 429 cuda_poll(void) 430 430 { 431 - unsigned long flags; 432 - 433 431 /* cuda_interrupt only takes a normal lock, we disable 434 432 * interrupts here to avoid re-entering and thus deadlocking. 435 - * An option would be to disable only the IRQ source with 436 - * disable_irq(), would that work on m68k ? --BenH 437 433 */ 438 - local_irq_save(flags); 434 + disable_irq(cuda_irq); 439 435 cuda_interrupt(0, NULL); 440 - local_irq_restore(flags); 436 + enable_irq(cuda_irq); 441 437 } 442 438 443 439 static irqreturn_t ··· 444 448 unsigned char ibuf[16]; 445 449 int ibuf_len = 0; 446 450 int complete = 0; 447 - unsigned char virq; 448 451 449 452 spin_lock(&cuda_lock); 450 453 451 - virq = in_8(&via[IFR]) & 0x7f; 452 - out_8(&via[IFR], virq); 453 - if ((virq & SR_INT) == 0) { 454 - spin_unlock(&cuda_lock); 455 - return IRQ_NONE; 454 + /* On powermacs, this handler is registered for the VIA IRQ. But it uses 455 + * just the shift register IRQ -- other VIA interrupt sources are disabled. 456 + * On m68k macs, the VIA IRQ sources are dispatched individually. Unless 457 + * we are polling, the shift register IRQ flag has already been cleared. 458 + */ 459 + 460 + #ifdef CONFIG_MAC 461 + if (!arg) 462 + #endif 463 + { 464 + if ((in_8(&via[IFR]) & SR_INT) == 0) { 465 + spin_unlock(&cuda_lock); 466 + return IRQ_NONE; 467 + } else { 468 + out_8(&via[IFR], SR_INT); 469 + } 456 470 } 457 471 458 472 status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);