static irqreturn_t macii_interrupt()

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;
}