Commit b4d76c28 authored by Finn Thain's avatar Finn Thain Committed by Michael Ellerman

macintosh/via-macii: Handle /CTLR_IRQ signal correctly

I'm told that the /CTLR_IRQ signal from the ADB transceiver gets
interpreted by MacOS to mean SRQ, bus timeout or end-of-packet depending
on the circumstances, and that Linux's via-macii driver does not
correctly interpret this signal.

Instead, the via-macii driver interprets certain received byte values
(0x00 and 0xFF) as signalling end of packet and bus timeout
(respectively). Problem is, those values can also appear under other
circumstances.

This patch changes the bus timeout, end of packet and SRQ detection logic
to bring it closer to the logic that MacOS reportedly uses.

Fixes: 1da177e4 ("Linux-2.6.12-rc2") # v5.0+
Reported-by: default avatarMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: default avatarFinn Thain <fthain@telegraphics.com.au>
Tested-by: default avatarStan Johnson <userm57@yahoo.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/6541fda1d8db3ae87c3abe17d189a10dc96e2382.1593318192.git.fthain@telegraphics.com.au
parent f93bfeb5
...@@ -80,6 +80,8 @@ static volatile unsigned char *via; ...@@ -80,6 +80,8 @@ static volatile unsigned char *via;
/* ADB command byte structure */ /* ADB command byte structure */
#define ADDR_MASK 0xF0 #define ADDR_MASK 0xF0
#define CMD_MASK 0x0F #define CMD_MASK 0x0F
#define OP_MASK 0x0C
#define TALK 0x0C
static int macii_init_via(void); static int macii_init_via(void);
static void macii_start(void); static void macii_start(void);
...@@ -119,9 +121,10 @@ static int reading_reply; /* store reply in reply_buf else req->reply */ ...@@ -119,9 +121,10 @@ static int reading_reply; /* store reply in reply_buf else req->reply */
static int data_index; /* index of the next byte to send from req->data */ static int data_index; /* index of the next byte to send from req->data */
static int reply_len; /* number of bytes received in reply_buf or req->reply */ static int reply_len; /* number of bytes received in reply_buf or req->reply */
static int status; /* VIA's ADB status bits captured upon interrupt */ static int status; /* VIA's ADB status bits captured upon interrupt */
static int last_status; /* status bits as at previous interrupt */ static bool bus_timeout; /* no data was sent by the device */
static int srq_asserted; /* have to poll for the device that asserted it */ static bool srq_asserted; /* have to poll for the device that asserted it */
static u8 last_cmd; /* the most recent command byte transmitted */ static u8 last_cmd; /* the most recent command byte transmitted */
static u8 last_talk_cmd; /* the most recent Talk command byte transmitted */
static u8 last_poll_cmd; /* the most recent Talk R0 command byte transmitted */ static u8 last_poll_cmd; /* the most recent Talk R0 command byte transmitted */
static int autopoll_devs; /* bits set are device addresses to be polled */ static int autopoll_devs; /* bits set are device addresses to be polled */
...@@ -170,7 +173,6 @@ static int macii_init_via(void) ...@@ -170,7 +173,6 @@ static int macii_init_via(void)
/* Set up state: idle */ /* Set up state: idle */
via[B] |= ST_IDLE; via[B] |= ST_IDLE;
last_status = via[B] & (ST_MASK | CTLR_IRQ);
/* Shift register on input */ /* Shift register on input */
via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
...@@ -336,13 +338,6 @@ static void macii_start(void) ...@@ -336,13 +338,6 @@ static void macii_start(void)
* And req->nbytes is the number of bytes of real data plus one. * And req->nbytes is the number of bytes of real data plus one.
*/ */
/* store command byte */
last_cmd = req->data[1];
/* If this is a Talk Register 0 command, store the command byte */
if ((last_cmd & CMD_MASK) == ADB_READREG(0, 0))
last_poll_cmd = last_cmd;
/* Output mode */ /* Output mode */
via[ACR] |= SR_OUT; via[ACR] |= SR_OUT;
/* Load data */ /* Load data */
...@@ -352,6 +347,9 @@ static void macii_start(void) ...@@ -352,6 +347,9 @@ static void macii_start(void)
macii_state = sending; macii_state = sending;
data_index = 2; data_index = 2;
bus_timeout = false;
srq_asserted = false;
} }
/* /*
...@@ -360,15 +358,17 @@ static void macii_start(void) ...@@ -360,15 +358,17 @@ static void macii_start(void)
* generating shift register interrupts (SR_INT) for us. This means there has * generating shift register interrupts (SR_INT) for us. This means there has
* to be activity on the ADB bus. The chip will poll to achieve this. * to be activity on the ADB bus. The chip will poll to achieve this.
* *
* The basic ADB state machine was left unchanged from the original MacII code * The VIA Port B output signalling works as follows. After the ADB transceiver
* by Alan Cox, which was based on the CUDA driver for PowerMac. * sees a transition on the PB4 and PB5 lines it will crank over the VIA shift
* The syntax of the ADB status lines is totally different on MacII, * register which eventually raises the SR_INT interrupt. The PB4/PB5 outputs
* though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle * are toggled with each byte as the ADB transaction progresses.
* for sending and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. *
* Start and end of a receive packet are signalled by asserting /IRQ on the * Request with no reply expected (and empty transceiver buffer):
* interrupt line (/IRQ means the CTLR_IRQ bit in port B; not to be confused * CMD -> IDLE
* with the VIA shift register interrupt. /IRQ never actually interrupts the * Request with expected reply packet (or with buffered autopoll packet):
* processor, it's just an ordinary input.) * CMD -> EVEN -> ODD -> EVEN -> ... -> IDLE
* Unsolicited packet:
* IDLE -> EVEN -> ODD -> EVEN -> ... -> IDLE
*/ */
static irqreturn_t macii_interrupt(int irq, void *arg) static irqreturn_t macii_interrupt(int irq, void *arg)
{ {
...@@ -388,31 +388,31 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -388,31 +388,31 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
} }
} }
last_status = status;
status = via[B] & (ST_MASK | CTLR_IRQ); status = via[B] & (ST_MASK | CTLR_IRQ);
switch (macii_state) { switch (macii_state) {
case idle: case idle:
if (reading_reply) { WARN_ON((status & ST_MASK) != ST_IDLE);
reply_ptr = current_req->reply;
} else {
WARN_ON(current_req);
reply_ptr = reply_buf; reply_ptr = reply_buf;
} reading_reply = 0;
bus_timeout = false;
srq_asserted = false;
x = via[SR]; x = via[SR];
if ((status & CTLR_IRQ) && (x == 0xFF)) { if (!(status & CTLR_IRQ)) {
/* Bus timeout without SRQ sequence: /* /CTLR_IRQ asserted in idle state means we must
* data is "FF" while CTLR_IRQ is "H" * read an autopoll reply from the transceiver buffer.
*/ */
reply_len = 0;
srq_asserted = 0;
macii_state = read_done;
} else {
macii_state = reading; macii_state = reading;
*reply_ptr = x; *reply_ptr = x;
reply_len = 1; reply_len = 1;
} else {
/* bus timeout */
macii_state = read_done;
reply_len = 0;
} }
/* set ADB state = even for first data byte */ /* set ADB state = even for first data byte */
...@@ -421,13 +421,52 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -421,13 +421,52 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
case sending: case sending:
req = current_req; req = current_req;
if (data_index >= req->nbytes) {
if (status == (ST_CMD | CTLR_IRQ)) {
/* /CTLR_IRQ de-asserted after the command byte means
* the host can continue with the transaction.
*/
/* Store command byte */
last_cmd = req->data[1];
if ((last_cmd & OP_MASK) == TALK) {
last_talk_cmd = last_cmd;
if ((last_cmd & CMD_MASK) == ADB_READREG(0, 0))
last_poll_cmd = last_cmd;
}
}
if (status == ST_CMD) {
/* /CTLR_IRQ asserted after the command byte means we
* must read an autopoll reply. The first byte was
* lost because the shift register was an output.
*/
macii_state = reading;
reading_reply = 0;
reply_ptr = reply_buf;
*reply_ptr = last_talk_cmd;
reply_len = 1;
/* reset to shift in */
via[ACR] &= ~SR_OUT;
x = via[SR];
} else if (data_index >= req->nbytes) {
req->sent = 1; req->sent = 1;
macii_state = idle;
if (req->reply_expected) { if (req->reply_expected) {
macii_state = reading;
reading_reply = 1; reading_reply = 1;
reply_ptr = req->reply;
*reply_ptr = req->data[1];
reply_len = 1;
via[ACR] &= ~SR_OUT;
x = via[SR];
} else { } else {
macii_state = idle;
req->complete = 1; req->complete = 1;
current_req = req->next; current_req = req->next;
if (req->done) if (req->done)
...@@ -438,7 +477,6 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -438,7 +477,6 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
if (current_req && macii_state == idle) if (current_req && macii_state == idle)
macii_start(); macii_start();
}
if (macii_state == idle) { if (macii_state == idle) {
/* reset to shift in */ /* reset to shift in */
...@@ -447,8 +485,11 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -447,8 +485,11 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
/* set ADB state idle - might get SRQ */ /* set ADB state idle - might get SRQ */
via[B] = (via[B] & ~ST_MASK) | ST_IDLE; via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
} }
break;
}
} else { } else {
via[SR] = req->data[data_index++]; via[SR] = req->data[data_index++];
}
if ((via[B] & ST_MASK) == ST_CMD) { if ((via[B] & ST_MASK) == ST_CMD) {
/* just sent the command byte, set to EVEN */ /* just sent the command byte, set to EVEN */
...@@ -457,7 +498,6 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -457,7 +498,6 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
/* invert state bits, toggle ODD/EVEN */ /* invert state bits, toggle ODD/EVEN */
via[B] ^= ST_MASK; via[B] ^= ST_MASK;
} }
}
break; break;
case reading: case reading:
...@@ -465,28 +505,13 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -465,28 +505,13 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
WARN_ON((status & ST_MASK) == ST_CMD || WARN_ON((status & ST_MASK) == ST_CMD ||
(status & ST_MASK) == ST_IDLE); (status & ST_MASK) == ST_IDLE);
/* Bus timeout with SRQ sequence:
* data is "XX FF" while CTLR_IRQ is "L L"
* End of packet without SRQ sequence:
* data is "XX...YY 00" while CTLR_IRQ is "L...H L"
* End of packet SRQ sequence:
* data is "XX...YY 00" while CTLR_IRQ is "L...L L"
* (where XX is the first response byte and
* YY is the last byte of valid response data.)
*/
srq_asserted = 0;
if (!(status & CTLR_IRQ)) { if (!(status & CTLR_IRQ)) {
if (x == 0xFF) { if (status == ST_EVEN && reply_len == 1) {
if (!(last_status & CTLR_IRQ)) { bus_timeout = true;
macii_state = read_done; } else if (status == ST_ODD && reply_len == 2) {
reply_len = 0; srq_asserted = true;
srq_asserted = 1; } else {
}
} else if (x == 0x00) {
macii_state = read_done; macii_state = read_done;
if (!(last_status & CTLR_IRQ))
srq_asserted = 1;
} }
} }
...@@ -504,6 +529,9 @@ static irqreturn_t macii_interrupt(int irq, void *arg) ...@@ -504,6 +529,9 @@ static irqreturn_t macii_interrupt(int irq, void *arg)
case read_done: case read_done:
x = via[SR]; x = via[SR];
if (bus_timeout)
reply_len = 0;
if (reading_reply) { if (reading_reply) {
reading_reply = 0; reading_reply = 0;
req = current_req; req = current_req;
......
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