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)
/* for us by the main VIA driver in arch/m68k/mac/via.c */
#ifndef CONFIG_MAC
via[IFR] = 0x7f; eieio(); /* clear interrupts by writing 1s */
via[IER] = IER_SET|SR_INT; eieio(); /* enable interrupt from SR */
out_8(&via[IFR], 0x7f); /* clear interrupts by writing 1s */
out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
#endif
/* enable autopoll */
......@@ -246,7 +246,8 @@ cuda_init(void)
#endif /* CONFIG_ADB */
#define WAIT_FOR(cond, what) \
do { \
do { \
int x; \
for (x = 1000; !(cond); --x) { \
if (x == 0) { \
printk("Timeout waiting for " what "\n"); \
......@@ -259,40 +260,40 @@ cuda_init(void)
static int
cuda_init_via()
{
int x;
via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ; /* TACK & TIP out */
via[B] |= TACK | TIP; /* negate them */
via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; /* SR data in */
eieio();
x = via[SR]; eieio(); /* clear any left-over data */
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 */
out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT); /* SR data in */
(void)in_8(&via[SR]); /* clear any left-over data */
#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
eieio();
/* delay 4ms and then clear any pending interrupt */
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 */
via[B] &= ~TACK; eieio();
out_8(&via[B], in_8(&via[B]) & ~TACK);
/* 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(via[IFR] & SR_INT, "CUDA response to sync (2)");
x = via[SR]; eieio();
WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
(void)in_8(&via[SR]);
out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
/* 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(via[B] & TREQ, "CUDA response to sync (3)");
WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (4)");
x = via[SR]; eieio();
via[B] |= TIP; eieio(); /* should be unnecessary */
WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
(void)in_8(&via[SR]);
out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
out_8(&via[B], in_8(&via[B]) | TIP); /* should be unnecessary */
return 0;
}
......@@ -415,55 +416,59 @@ cuda_start()
req = current_req;
if (req == 0)
return;
if ((via[B] & TREQ) == 0)
if ((in_8(&via[B]) & TREQ) == 0)
return; /* a byte is coming in from the CUDA */
/* set the shift register to shift out and send a byte */
via[ACR] |= SR_OUT; eieio();
via[SR] = req->data[0]; eieio();
via[B] &= ~TIP;
out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
out_8(&via[SR], req->data[0]);
out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = sent_first_byte;
}
void
cuda_poll()
{
if (via[IFR] & SR_INT) {
unsigned long flags;
/* cuda_interrupt only takes a normal lock, we disable
* interrupts here to avoid re-entering and thus deadlocking.
* An option would be to disable only the IRQ source with
* disable_irq(), would that work on m68k ? --BenH
*/
local_irq_save(flags);
cuda_interrupt(0, 0, 0);
local_irq_restore(flags);
}
unsigned long flags;
/* cuda_interrupt only takes a normal lock, we disable
* interrupts here to avoid re-entering and thus deadlocking.
* An option would be to disable only the IRQ source with
* disable_irq(), would that work on m68k ? --BenH
*/
local_irq_save(flags);
cuda_interrupt(0, 0, 0);
local_irq_restore(flags);
}
static void
cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
{
int x, status;
int status;
struct adb_request *req = NULL;
unsigned char ibuf[16];
int ibuf_len = 0;
int complete = 0;
unsigned char virq;
if ((via[IFR] & SR_INT) == 0)
return;
spin_lock(&cuda_lock);
status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio();
virq = in_8(&via[IFR]) & 0x7f;
out_8(&via[IFR], virq);
if ((virq & SR_INT) == 0) {
spin_unlock(&cuda_lock);
return;
}
status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
/* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */
switch (cuda_state) {
case idle:
/* CUDA has sent us the first byte of data - unsolicited */
if (status != TREQ)
printk("cuda: state=idle, status=%x\n", status);
x = via[SR]; eieio();
via[B] &= ~TIP; eieio();
(void)in_8(&via[SR]);
out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading;
reply_ptr = cuda_rbuf;
reading_reply = 0;
......@@ -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 */
if (status != TREQ)
printk("cuda: state=awaiting_reply, status=%x\n", status);
x = via[SR]; eieio();
via[B] &= ~TIP; eieio();
(void)in_8(&via[SR]);
out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading;
reply_ptr = current_req->reply;
reading_reply = 1;
......@@ -483,16 +488,16 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
case sent_first_byte:
if (status == TREQ + TIP + SR_OUT) {
/* collision */
via[ACR] &= ~SR_OUT; eieio();
x = via[SR]; eieio();
via[B] |= TIP | TACK; eieio();
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
out_8(&via[B], in_8(&via[B]) | TIP | TACK);
cuda_state = idle;
} else {
/* assert status == TIP + SR_OUT */
if (status != TIP + SR_OUT)
printk("cuda: state=sent_first_byte status=%x\n", status);
via[SR] = current_req->data[1]; eieio();
via[B] ^= TACK; eieio();
out_8(&via[SR], current_req->data[1]);
out_8(&via[B], in_8(&via[B]) ^ TACK);
data_index = 2;
cuda_state = sending;
}
......@@ -501,9 +506,9 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
case sending:
req = current_req;
if (data_index >= req->nbytes) {
via[ACR] &= ~SR_OUT; eieio();
x = via[SR]; eieio();
via[B] |= TACK | TIP; eieio();
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
out_8(&via[B], in_8(&via[B]) | TACK | TIP);
req->sent = 1;
if (req->reply_expected) {
cuda_state = awaiting_reply;
......@@ -515,27 +520,27 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
cuda_start();
}
} else {
via[SR] = req->data[data_index++]; eieio();
via[B] ^= TACK; eieio();
out_8(&via[SR], req->data[data_index++]);
out_8(&via[B], in_8(&via[B]) ^ TACK);
}
break;
case reading:
*reply_ptr++ = via[SR]; eieio();
*reply_ptr++ = in_8(&via[SR]);
if (status == TIP) {
/* that's all folks */
via[B] |= TACK | TIP; eieio();
out_8(&via[B], in_8(&via[B]) | TACK | TIP);
cuda_state = read_done;
} else {
/* assert status == TIP | TREQ */
if (status != TIP + TREQ)
printk("cuda: state=reading status=%x\n", status);
via[B] ^= TACK; eieio();
out_8(&via[B], in_8(&via[B]) ^ TACK);
}
break;
case read_done:
x = via[SR]; eieio();
(void)in_8(&via[SR]);
if (reading_reply) {
req = current_req;
req->reply_len = reply_ptr - req->reply;
......@@ -564,7 +569,7 @@ cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
memcpy(ibuf, cuda_rbuf, ibuf_len);
}
if (status == TREQ) {
via[B] &= ~TIP; eieio();
out_8(&via[B], in_8(&via[B]) & ~TIP);
cuda_state = reading;
reply_ptr = cuda_rbuf;
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