Commit c2970ed3 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] update via-cuda driver

This patch updates the CUDA driver (the power/reset/ADB controller on
older powermacs) to fix some SMP issues and to match the 2.4 version
of the driver.

From Ben Herrenschmidt.
parent 8f7d7732
...@@ -176,8 +176,8 @@ find_via_cuda(void) ...@@ -176,8 +176,8 @@ find_via_cuda(void)
/* for us by the main VIA driver in arch/m68k/mac/via.c */ /* for us by the main VIA driver in arch/m68k/mac/via.c */
#ifndef CONFIG_MAC #ifndef CONFIG_MAC
via[IFR] = 0x7f; eieio(); /* clear interrupts by writing 1s */ out_8(&via[IFR], 0x7f); /* clear interrupts by writing 1s */
via[IER] = IER_SET|SR_INT; eieio(); /* enable interrupt from SR */ out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
#endif #endif
/* enable autopoll */ /* enable autopoll */
...@@ -247,6 +247,7 @@ cuda_init(void) ...@@ -247,6 +247,7 @@ cuda_init(void)
#define WAIT_FOR(cond, what) \ #define WAIT_FOR(cond, what) \
do { \ do { \
int x; \
for (x = 1000; !(cond); --x) { \ for (x = 1000; !(cond); --x) { \
if (x == 0) { \ if (x == 0) { \
printk("Timeout waiting for " what "\n"); \ printk("Timeout waiting for " what "\n"); \
...@@ -259,40 +260,40 @@ cuda_init(void) ...@@ -259,40 +260,40 @@ cuda_init(void)
static int static int
cuda_init_via() cuda_init_via()
{ {
int x; out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */
out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */
via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ; /* TACK & TIP out */ out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT); /* SR data in */
via[B] |= TACK | TIP; /* negate them */ (void)in_8(&via[SR]); /* clear any left-over data */
via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; /* SR data in */
eieio();
x = via[SR]; eieio(); /* clear any left-over data */
#ifndef CONFIG_MAC #ifndef CONFIG_MAC
via[IER] = 0x7f; eieio(); /* disable interrupts from VIA */ out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
(void)in_8(&via[IER]);
#endif #endif
eieio();
/* delay 4ms and then clear any pending interrupt */ /* delay 4ms and then clear any pending interrupt */
mdelay(4); mdelay(4);
x = via[SR]; eieio(); (void)in_8(&via[SR]);
out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
/* sync with the CUDA - assert TACK without TIP */ /* sync with the CUDA - assert TACK without TIP */
via[B] &= ~TACK; eieio(); out_8(&via[B], in_8(&via[B]) & ~TACK);
/* wait for the CUDA to assert TREQ in response */ /* wait for the CUDA to assert TREQ in response */
WAIT_FOR((via[B] & TREQ) == 0, "CUDA response to sync"); WAIT_FOR((in_8(&via[B]) & TREQ) == 0, "CUDA response to sync");
/* wait for the interrupt and then clear it */ /* wait for the interrupt and then clear it */
WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (2)"); WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
x = via[SR]; eieio(); (void)in_8(&via[SR]);
out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
/* finish the sync by negating TACK */ /* finish the sync by negating TACK */
via[B] |= TACK; eieio(); out_8(&via[B], in_8(&via[B]) | TACK);
/* wait for the CUDA to negate TREQ and the corresponding interrupt */ /* wait for the CUDA to negate TREQ and the corresponding interrupt */
WAIT_FOR(via[B] & TREQ, "CUDA response to sync (3)"); WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (4)"); WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
x = via[SR]; eieio(); (void)in_8(&via[SR]);
via[B] |= TIP; eieio(); /* should be unnecessary */ out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
out_8(&via[B], in_8(&via[B]) | TIP); /* should be unnecessary */
return 0; return 0;
} }
...@@ -415,20 +416,19 @@ cuda_start() ...@@ -415,20 +416,19 @@ cuda_start()
req = current_req; req = current_req;
if (req == 0) if (req == 0)
return; return;
if ((via[B] & TREQ) == 0) if ((in_8(&via[B]) & TREQ) == 0)
return; /* a byte is coming in from the CUDA */ return; /* a byte is coming in from the CUDA */
/* set the shift register to shift out and send a byte */ /* set the shift register to shift out and send a byte */
via[ACR] |= SR_OUT; eieio(); out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
via[SR] = req->data[0]; eieio(); out_8(&via[SR], req->data[0]);
via[B] &= ~TIP; out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = sent_first_byte; cuda_state = sent_first_byte;
} }
void void
cuda_poll() cuda_poll()
{ {
if (via[IFR] & SR_INT) {
unsigned long flags; unsigned long flags;
/* cuda_interrupt only takes a normal lock, we disable /* cuda_interrupt only takes a normal lock, we disable
...@@ -439,31 +439,36 @@ cuda_poll() ...@@ -439,31 +439,36 @@ cuda_poll()
local_irq_save(flags); local_irq_save(flags);
cuda_interrupt(0, 0, 0); cuda_interrupt(0, 0, 0);
local_irq_restore(flags); local_irq_restore(flags);
}
} }
static void static void
cuda_interrupt(int irq, void *arg, struct pt_regs *regs) cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
{ {
int x, status; int status;
struct adb_request *req = NULL; struct adb_request *req = NULL;
unsigned char ibuf[16]; unsigned char ibuf[16];
int ibuf_len = 0; int ibuf_len = 0;
int complete = 0; int complete = 0;
unsigned char virq;
spin_lock(&cuda_lock);
if ((via[IFR] & SR_INT) == 0) virq = in_8(&via[IFR]) & 0x7f;
out_8(&via[IFR], virq);
if ((virq & SR_INT) == 0) {
spin_unlock(&cuda_lock);
return; return;
}
spin_lock(&cuda_lock); status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio();
/* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */ /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */
switch (cuda_state) { switch (cuda_state) {
case idle: case idle:
/* CUDA has sent us the first byte of data - unsolicited */ /* CUDA has sent us the first byte of data - unsolicited */
if (status != TREQ) if (status != TREQ)
printk("cuda: state=idle, status=%x\n", status); printk("cuda: state=idle, status=%x\n", status);
x = via[SR]; eieio(); (void)in_8(&via[SR]);
via[B] &= ~TIP; eieio(); out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading; cuda_state = reading;
reply_ptr = cuda_rbuf; reply_ptr = cuda_rbuf;
reading_reply = 0; reading_reply = 0;
...@@ -473,8 +478,8 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs) ...@@ -473,8 +478,8 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
/* CUDA has sent us the first byte of data of a reply */ /* CUDA has sent us the first byte of data of a reply */
if (status != TREQ) if (status != TREQ)
printk("cuda: state=awaiting_reply, status=%x\n", status); printk("cuda: state=awaiting_reply, status=%x\n", status);
x = via[SR]; eieio(); (void)in_8(&via[SR]);
via[B] &= ~TIP; eieio(); out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading; cuda_state = reading;
reply_ptr = current_req->reply; reply_ptr = current_req->reply;
reading_reply = 1; reading_reply = 1;
...@@ -483,16 +488,16 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs) ...@@ -483,16 +488,16 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
case sent_first_byte: case sent_first_byte:
if (status == TREQ + TIP + SR_OUT) { if (status == TREQ + TIP + SR_OUT) {
/* collision */ /* collision */
via[ACR] &= ~SR_OUT; eieio(); out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
x = via[SR]; eieio(); (void)in_8(&via[SR]);
via[B] |= TIP | TACK; eieio(); out_8(&via[B], in_8(&via[B]) | TIP | TACK);
cuda_state = idle; cuda_state = idle;
} else { } else {
/* assert status == TIP + SR_OUT */ /* assert status == TIP + SR_OUT */
if (status != TIP + SR_OUT) if (status != TIP + SR_OUT)
printk("cuda: state=sent_first_byte status=%x\n", status); printk("cuda: state=sent_first_byte status=%x\n", status);
via[SR] = current_req->data[1]; eieio(); out_8(&via[SR], current_req->data[1]);
via[B] ^= TACK; eieio(); out_8(&via[B], in_8(&via[B]) ^ TACK);
data_index = 2; data_index = 2;
cuda_state = sending; cuda_state = sending;
} }
...@@ -501,9 +506,9 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs) ...@@ -501,9 +506,9 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
case sending: case sending:
req = current_req; req = current_req;
if (data_index >= req->nbytes) { if (data_index >= req->nbytes) {
via[ACR] &= ~SR_OUT; eieio(); out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
x = via[SR]; eieio(); (void)in_8(&via[SR]);
via[B] |= TACK | TIP; eieio(); out_8(&via[B], in_8(&via[B]) | TACK | TIP);
req->sent = 1; req->sent = 1;
if (req->reply_expected) { if (req->reply_expected) {
cuda_state = awaiting_reply; cuda_state = awaiting_reply;
...@@ -515,27 +520,27 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs) ...@@ -515,27 +520,27 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
cuda_start(); cuda_start();
} }
} else { } else {
via[SR] = req->data[data_index++]; eieio(); out_8(&via[SR], req->data[data_index++]);
via[B] ^= TACK; eieio(); out_8(&via[B], in_8(&via[B]) ^ TACK);
} }
break; break;
case reading: case reading:
*reply_ptr++ = via[SR]; eieio(); *reply_ptr++ = in_8(&via[SR]);
if (status == TIP) { if (status == TIP) {
/* that's all folks */ /* that's all folks */
via[B] |= TACK | TIP; eieio(); out_8(&via[B], in_8(&via[B]) | TACK | TIP);
cuda_state = read_done; cuda_state = read_done;
} else { } else {
/* assert status == TIP | TREQ */ /* assert status == TIP | TREQ */
if (status != TIP + TREQ) if (status != TIP + TREQ)
printk("cuda: state=reading status=%x\n", status); printk("cuda: state=reading status=%x\n", status);
via[B] ^= TACK; eieio(); out_8(&via[B], in_8(&via[B]) ^ TACK);
} }
break; break;
case read_done: case read_done:
x = via[SR]; eieio(); (void)in_8(&via[SR]);
if (reading_reply) { if (reading_reply) {
req = current_req; req = current_req;
req->reply_len = reply_ptr - req->reply; req->reply_len = reply_ptr - req->reply;
...@@ -564,7 +569,7 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs) ...@@ -564,7 +569,7 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
memcpy(ibuf, cuda_rbuf, ibuf_len); memcpy(ibuf, cuda_rbuf, ibuf_len);
} }
if (status == TREQ) { if (status == TREQ) {
via[B] &= ~TIP; eieio(); out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading; cuda_state = reading;
reply_ptr = cuda_rbuf; reply_ptr = cuda_rbuf;
reading_reply = 0; reading_reply = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment