via-cuda: Add support for Egret system controller

The Egret system controller was the predecessor to the Cuda and the
differences are minor.

On Cuda, byte acknowledgement requires one transition of the TACK
signal; on Egret two are needed. On Cuda, TIP is active low; on Egret
it is active high. And Cuda raises certain interrupts that Egret omits.

Accomodating these differences complicates the Cuda driver slightly
but avoids a lot of duplication (see next patch).

Tested-by: Stan Johnson <userm57@yahoo.com>
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
Finn Thain 2016-12-31 19:56:26 -05:00 committed by Michael Ellerman
parent 97ced1aac0
commit d23eee88b5

View File

@ -1,10 +1,10 @@
/*
* Device driver for the via-cuda on Apple Powermacs.
* Device driver for the Cuda and Egret system controllers found on PowerMacs
* and 68k Macs.
*
* The VIA (versatile interface adapter) interfaces to the CUDA,
* a 6805 microprocessor core which controls the ADB (Apple Desktop
* Bus) which connects to the keyboard and mouse. The CUDA also
* controls system power and the RTC (real time clock) chip.
* 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.
*/
@ -50,10 +50,27 @@ static DEFINE_SPINLOCK(cuda_lock);
#define IER (14*RS) /* Interrupt enable register */
#define ANH (15*RS) /* A-side data, no handshake */
/* Bits in B data register: all active low */
#define TREQ 0x08 /* Transfer request (input) */
#define TACK 0x10 /* Transfer acknowledge (output) */
#define TIP 0x20 /* Transfer in progress (output) */
/*
* 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 */
@ -65,6 +82,19 @@ static DEFINE_SPINLOCK(cuda_lock);
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
/* Duration of byte acknowledgement pulse (us) */
#define EGRET_TACK_ASSERTED_DELAY 300
#define EGRET_TACK_NEGATED_DELAY 400
/* Interval from interrupt to start of session (us) */
#define EGRET_SESSION_DELAY 450
#ifdef CONFIG_PPC
#define mcu_is_egret false
#else
static bool mcu_is_egret;
#endif
static inline bool TREQ_asserted(u8 portb)
{
return !(portb & TREQ);
@ -72,11 +102,28 @@ static inline bool TREQ_asserted(u8 portb)
static inline void assert_TIP(void)
{
if (mcu_is_egret) {
udelay(EGRET_SESSION_DELAY);
out_8(&via[B], in_8(&via[B]) | TIP);
} else
out_8(&via[B], in_8(&via[B]) & ~TIP);
}
static inline void assert_TIP_and_TACK(void)
{
if (mcu_is_egret) {
udelay(EGRET_SESSION_DELAY);
out_8(&via[B], in_8(&via[B]) | TIP | TACK);
} else
out_8(&via[B], in_8(&via[B]) & ~(TIP | TACK));
}
static inline void assert_TACK(void)
{
if (mcu_is_egret) {
udelay(EGRET_TACK_NEGATED_DELAY);
out_8(&via[B], in_8(&via[B]) | TACK);
} else
out_8(&via[B], in_8(&via[B]) & ~TACK);
}
@ -87,11 +134,19 @@ static inline void toggle_TACK(void)
static inline void negate_TACK(void)
{
if (mcu_is_egret) {
udelay(EGRET_TACK_ASSERTED_DELAY);
out_8(&via[B], in_8(&via[B]) & ~TACK);
} else
out_8(&via[B], in_8(&via[B]) | TACK);
}
static inline void negate_TIP_and_TACK(void)
{
if (mcu_is_egret) {
udelay(EGRET_TACK_ASSERTED_DELAY);
out_8(&via[B], in_8(&via[B]) & ~(TIP | TACK));
} else
out_8(&via[B], in_8(&via[B]) | TIP | TACK);
}
@ -155,6 +210,7 @@ int __init find_via_cuda(void)
via = via1;
cuda_state = idle;
mcu_is_egret = false;
err = cuda_init_via();
if (err) {
@ -251,7 +307,7 @@ static int __init via_cuda_start(void)
return -EAGAIN;
}
pr_info("Macintosh CUDA driver v0.5 for Unified ADB.\n");
pr_info("Macintosh Cuda and Egret driver.\n");
cuda_fully_inited = 1;
return 0;
@ -276,6 +332,33 @@ cuda_probe(void)
}
#endif /* CONFIG_ADB */
static int __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();
} else if (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; \
@ -291,10 +374,6 @@ cuda_probe(void)
static int
__init cuda_init_via(void)
{
out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */
negate_TIP_and_TACK();
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 */
#ifdef CONFIG_PPC
out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
(void)in_8(&via[IER]);
@ -302,6 +381,15 @@ __init cuda_init_via(void)
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]);
@ -453,6 +541,9 @@ cuda_start(void)
/* 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;
}
@ -500,8 +591,9 @@ cuda_interrupt(int irq, void *arg)
switch (cuda_state) {
case idle:
/* CUDA has sent us the first byte of data - unsolicited */
/* System controller has unsolicited data for us */
(void)in_8(&via[SR]);
idle_state:
assert_TIP();
cuda_state = reading;
reply_ptr = cuda_rbuf;
@ -509,7 +601,7 @@ cuda_interrupt(int irq, void *arg)
break;
case awaiting_reply:
/* CUDA has sent us the first byte of data of a reply */
/* System controller has reply data for us */
(void)in_8(&via[SR]);
assert_TIP();
cuda_state = reading;
@ -524,9 +616,14 @@ cuda_interrupt(int irq, void *arg)
(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;
@ -550,6 +647,8 @@ cuda_interrupt(int irq, void *arg)
} else {
out_8(&via[SR], req->data[data_index++]);
toggle_TACK();
if (mcu_is_egret)
assert_TACK();
}
break;
@ -560,16 +659,24 @@ cuda_interrupt(int irq, void *arg)
else
*reply_ptr++ = in_8(&via[SR]);
if (!TREQ_asserted(status)) {
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;
@ -645,6 +752,12 @@ cuda_input(unsigned char *buf, int nb)
#endif /* CONFIG_ADB */
break;
case TIMER_PACKET:
/* Egret sends these periodically. Might be useful as a 'heartbeat'
* to trigger a recovery for the VIA shift register errata.
*/
break;
default:
print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1,
buf, nb, false);