// SPDX-License-Identifier: GPL-2.0 /* * Device driver for the Cuda and Egret system controllers found on PowerMacs * and 68k Macs. * * The Cuda or Egret is a 6805 microcontroller interfaced to the 6522 VIA. * This MCU controls system power, Parameter RAM, Real Time Clock and the * Apple Desktop Bus (ADB) that connects to the keyboard and mouse. * * Copyright (C) 1996 Paul Mackerras.
*/ #include <linux/stdarg.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/adb.h> #include <linux/cuda.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/of_address.h> #include <linux/of_irq.h>
/* * When the Cuda design replaced the Egret, some signal names and * logic sense changed. They all serve the same purposes, however. * * VIA pin | Egret pin * ----------------+------------------------------------------ * PB3 (input) | Transceiver session (active low) * PB4 (output) | VIA full (active high) * PB5 (output) | System session (active high) * * VIA pin | Cuda pin * ----------------+------------------------------------------ * PB3 (input) | Transfer request (active low) * PB4 (output) | Byte acknowledge (active low) * PB5 (output) | Transfer in progress (active low)
*/
/* Bits in Port B data register */ #define TREQ 0x08 /* Transfer request */ #define TACK 0x10 /* Transfer acknowledge */ #define TIP 0x20 /* Transfer in progress */
/* Bits in ACR */ #define SR_CTRL 0x1c /* Shift register control bits */ #define SR_EXT 0x0c /* Shift on external clock */ #define SR_OUT 0x10 /* Shift out if 1 */
/* Bits in IFR and IER */ #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */
staticint __init sync_egret(void)
{ if (TREQ_asserted(in_8(&via[B]))) { /* Complete the inbound transfer */
assert_TIP_and_TACK(); while (1) {
negate_TACK();
mdelay(1);
(void)in_8(&via[SR]);
assert_TACK(); if (!TREQ_asserted(in_8(&via[B]))) break;
}
negate_TIP_and_TACK();
} elseif (in_8(&via[B]) & TIP) { /* Terminate the outbound transfer */
negate_TACK();
assert_TACK();
mdelay(1);
negate_TIP_and_TACK();
} /* Clear shift register interrupt */ if (in_8(&via[IFR]) & SR_INT)
(void)in_8(&via[SR]); return 0;
}
#define WAIT_FOR(cond, what) \ do { \ int x; \ for (x = 1000; !(cond); --x) { \ if (x == 0) { \
pr_err("Timeout waiting for " what "\n"); \ return -ENXIO; \
} \
udelay(100); \
} \
} while (0)
staticint
__init cuda_init_via(void)
{ #ifdef CONFIG_PPC
out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
(void)in_8(&via[IER]); #else
out_8(&via[IER], SR_INT); /* disable SR interrupt from VIA */ #endif
out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */
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 */
if (mcu_is_egret) return sync_egret();
negate_TIP_and_TACK();
/* delay 4ms and then clear any pending interrupt */
mdelay(4);
(void)in_8(&via[SR]);
out_8(&via[IFR], SR_INT);
/* sync with the CUDA - assert TACK without TIP */
assert_TACK();
/* wait for the CUDA to assert TREQ in response */
WAIT_FOR(TREQ_asserted(in_8(&via[B])), "CUDA response to sync");
/* wait for the interrupt and then clear it */
WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
(void)in_8(&via[SR]);
out_8(&via[IFR], SR_INT);
/* finish the sync by negating TACK */
negate_TACK();
/* wait for the CUDA to negate TREQ and the corresponding interrupt */
WAIT_FOR(!TREQ_asserted(in_8(&via[B])), "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], SR_INT);
return 0;
}
#ifdef CONFIG_ADB /* Send an ADB command */ staticint
cuda_send_request(struct adb_request *req, int sync)
{ int i;
/* Construct and send a cuda request */ int
cuda_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...)
{
va_list list; int i;
staticvoid
cuda_start(void)
{ /* assert cuda_state == idle */ if (current_req == NULL) return;
data_index = 0; if (TREQ_asserted(in_8(&via[B]))) return; /* a byte is coming in from the CUDA */
/* set the shift register to shift out and send a byte */
out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
out_8(&via[SR], current_req->data[data_index++]); if (mcu_is_egret)
assert_TIP_and_TACK(); else
assert_TIP();
cuda_state = sent_first_byte;
}
/* On powermacs, this handler is registered for the VIA IRQ. But they use * just the shift register IRQ -- other VIA interrupt sources are disabled. * On m68k macs, the VIA IRQ sources are dispatched individually. Unless * we are polling, the shift register IRQ flag has already been cleared.
*/
switch (cuda_state) { case idle: /* System controller has unsolicited data for us */
(void)in_8(&via[SR]);
idle_state:
assert_TIP();
cuda_state = reading;
reply_ptr = cuda_rbuf;
reading_reply = 0; break;
case awaiting_reply: /* System controller has reply data for us */
(void)in_8(&via[SR]);
assert_TIP();
cuda_state = reading;
reply_ptr = current_req->reply;
reading_reply = 1; break;
case sent_first_byte: if (TREQ_asserted(status)) { /* collision */
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
negate_TIP_and_TACK();
cuda_state = idle; /* Egret does not raise an "aborted" interrupt */ if (mcu_is_egret) goto idle_state;
} else {
out_8(&via[SR], current_req->data[data_index++]);
toggle_TACK(); if (mcu_is_egret)
assert_TACK();
cuda_state = sending;
} break;
case sending:
req = current_req; if (data_index >= req->nbytes) {
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
negate_TIP_and_TACK();
req->sent = 1; if (req->reply_expected) {
cuda_state = awaiting_reply;
} else {
current_req = req->next;
complete = 1; /* not sure about this */
cuda_state = idle;
cuda_start();
}
} else {
out_8(&via[SR], req->data[data_index++]);
toggle_TACK(); if (mcu_is_egret)
assert_TACK();
} break;
case reading:
full = reading_reply ? ARRAY_FULL(current_req->reply, reply_ptr)
: ARRAY_FULL(cuda_rbuf, reply_ptr); if (full)
(void)in_8(&via[SR]); else
*reply_ptr++ = in_8(&via[SR]); if (!TREQ_asserted(status) || full) { if (mcu_is_egret)
assert_TACK(); /* that's all folks */
negate_TIP_and_TACK();
cuda_state = read_done; /* Egret does not raise a "read done" interrupt */ if (mcu_is_egret) goto read_done_state;
} else {
toggle_TACK(); if (mcu_is_egret)
negate_TACK();
} break;
case read_done:
(void)in_8(&via[SR]);
read_done_state: if (reading_reply) {
req = current_req;
req->reply_len = reply_ptr - req->reply; if (req->data[0] == ADB_PACKET) { /* Have to adjust the reply from ADB commands */ if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) { /* the 0x2 bit indicates no response */
req->reply_len = 0;
} else { /* leave just the command and result bytes in the reply */
req->reply_len -= 2;
memmove(req->reply, req->reply + 2, req->reply_len);
}
}
current_req = req->next;
complete = 1;
reading_reply = 0;
} else { /* This is tricky. We must break the spinlock to call * cuda_input. However, doing so means we might get * re-entered from another CPU getting an interrupt * or calling cuda_poll(). I ended up using the stack * (it's only for 16 bytes) and moving the actual * call to cuda_input to outside of the lock.
*/
ibuf_len = reply_ptr - cuda_rbuf;
memcpy(ibuf, cuda_rbuf, ibuf_len);
}
reply_ptr = cuda_rbuf;
cuda_state = idle;
cuda_start(); if (cuda_state == idle && TREQ_asserted(in_8(&via[B]))) {
assert_TIP();
cuda_state = reading;
} break;
default:
pr_err("cuda_interrupt: unknown cuda_state %d?\n", cuda_state);
}
spin_unlock_irqrestore(&cuda_lock, flags); if (complete && req) { void (*done)(struct adb_request *) = req->done;
mb();
req->complete = 1; /* Here, we assume that if the request has a done member, the * struct request will survive to setting req->complete to 1
*/ if (done)
(*done)(req);
} if (ibuf_len)
cuda_input(ibuf, ibuf_len); return IRQ_HANDLED;
}
case TIMER_PACKET: /* Egret sends these periodically. Might be useful as a 'heartbeat' * to trigger a recovery for the VIA shift register errata.
*/ break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.