in via-macii.c [370:564]
static irqreturn_t macii_interrupt(int irq, void *arg)
{
int x;
struct adb_request *req;
unsigned long flags;
local_irq_save(flags);
if (!arg) {
/* Clear the SR IRQ flag when polling. */
if (via[IFR] & SR_INT)
via[IFR] = SR_INT;
else {
local_irq_restore(flags);
return IRQ_NONE;
}
}
status = via[B] & (ST_MASK | CTLR_IRQ);
switch (macii_state) {
case idle:
WARN_ON((status & ST_MASK) != ST_IDLE);
reply_ptr = reply_buf;
reading_reply = false;
bus_timeout = false;
srq_asserted = false;
x = via[SR];
if (!(status & CTLR_IRQ)) {
/* /CTLR_IRQ asserted in idle state means we must
* read an autopoll reply from the transceiver buffer.
*/
macii_state = reading;
*reply_ptr = x;
reply_len = 1;
} else {
/* bus timeout */
reply_len = 0;
break;
}
/* set ADB state = even for first data byte */
via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
break;
case sending:
req = current_req;
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 = false;
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;
if (req->reply_expected) {
macii_state = reading;
reading_reply = true;
reply_ptr = req->reply;
*reply_ptr = req->data[1];
reply_len = 1;
via[ACR] &= ~SR_OUT;
x = via[SR];
} else if ((req->data[1] & OP_MASK) == TALK) {
macii_state = reading;
reading_reply = false;
reply_ptr = reply_buf;
*reply_ptr = req->data[1];
reply_len = 1;
via[ACR] &= ~SR_OUT;
x = via[SR];
req->complete = 1;
current_req = req->next;
if (req->done)
(*req->done)(req);
} else {
macii_state = idle;
req->complete = 1;
current_req = req->next;
if (req->done)
(*req->done)(req);
break;
}
} else {
via[SR] = req->data[data_index++];
}
if ((via[B] & ST_MASK) == ST_CMD) {
/* just sent the command byte, set to EVEN */
via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
} else {
/* invert state bits, toggle ODD/EVEN */
via[B] ^= ST_MASK;
}
break;
case reading:
x = via[SR];
WARN_ON((status & ST_MASK) == ST_CMD ||
(status & ST_MASK) == ST_IDLE);
if (!(status & CTLR_IRQ)) {
if (status == ST_EVEN && reply_len == 1) {
bus_timeout = true;
} else if (status == ST_ODD && reply_len == 2) {
srq_asserted = true;
} else {
macii_state = idle;
if (bus_timeout)
reply_len = 0;
if (reading_reply) {
struct adb_request *req = current_req;
req->reply_len = reply_len;
req->complete = 1;
current_req = req->next;
if (req->done)
(*req->done)(req);
} else if (reply_len && autopoll_devs &&
reply_buf[0] == last_poll_cmd) {
adb_input(reply_buf, reply_len, 1);
}
break;
}
}
if (reply_len < ARRAY_SIZE(reply_buf)) {
reply_ptr++;
*reply_ptr = x;
reply_len++;
}
/* invert state bits, toggle ODD/EVEN */
via[B] ^= ST_MASK;
break;
default:
break;
}
if (macii_state == idle) {
if (!current_req)
macii_queue_poll();
if (current_req)
macii_start();
if (macii_state == idle) {
via[ACR] &= ~SR_OUT;
x = via[SR];
via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
}
}
local_irq_restore(flags);
return IRQ_HANDLED;
}