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:
Родитель
97ced1aac0
Коммит
d23eee88b5
|
@ -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,
|
* The Cuda or Egret is a 6805 microcontroller interfaced to the 6522 VIA.
|
||||||
* a 6805 microprocessor core which controls the ADB (Apple Desktop
|
* This MCU controls system power, Parameter RAM, Real Time Clock and the
|
||||||
* Bus) which connects to the keyboard and mouse. The CUDA also
|
* Apple Desktop Bus (ADB) that connects to the keyboard and mouse.
|
||||||
* controls system power and the RTC (real time clock) chip.
|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1996 Paul Mackerras.
|
* Copyright (C) 1996 Paul Mackerras.
|
||||||
*/
|
*/
|
||||||
|
@ -50,10 +50,27 @@ static DEFINE_SPINLOCK(cuda_lock);
|
||||||
#define IER (14*RS) /* Interrupt enable register */
|
#define IER (14*RS) /* Interrupt enable register */
|
||||||
#define ANH (15*RS) /* A-side data, no handshake */
|
#define ANH (15*RS) /* A-side data, no handshake */
|
||||||
|
|
||||||
/* Bits in B data register: all active low */
|
/*
|
||||||
#define TREQ 0x08 /* Transfer request (input) */
|
* When the Cuda design replaced the Egret, some signal names and
|
||||||
#define TACK 0x10 /* Transfer acknowledge (output) */
|
* logic sense changed. They all serve the same purposes, however.
|
||||||
#define TIP 0x20 /* Transfer in progress (output) */
|
*
|
||||||
|
* 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 */
|
/* Bits in ACR */
|
||||||
#define SR_CTRL 0x1c /* Shift register control bits */
|
#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 IER_CLR 0 /* clear bits in IER */
|
||||||
#define SR_INT 0x04 /* Shift register full/empty */
|
#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)
|
static inline bool TREQ_asserted(u8 portb)
|
||||||
{
|
{
|
||||||
return !(portb & TREQ);
|
return !(portb & TREQ);
|
||||||
|
@ -72,12 +102,29 @@ static inline bool TREQ_asserted(u8 portb)
|
||||||
|
|
||||||
static inline void assert_TIP(void)
|
static inline void assert_TIP(void)
|
||||||
{
|
{
|
||||||
out_8(&via[B], in_8(&via[B]) & ~TIP);
|
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)
|
static inline void assert_TACK(void)
|
||||||
{
|
{
|
||||||
out_8(&via[B], in_8(&via[B]) & ~TACK);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void toggle_TACK(void)
|
static inline void toggle_TACK(void)
|
||||||
|
@ -87,12 +134,20 @@ static inline void toggle_TACK(void)
|
||||||
|
|
||||||
static inline void negate_TACK(void)
|
static inline void negate_TACK(void)
|
||||||
{
|
{
|
||||||
out_8(&via[B], in_8(&via[B]) | TACK);
|
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)
|
static inline void negate_TIP_and_TACK(void)
|
||||||
{
|
{
|
||||||
out_8(&via[B], in_8(&via[B]) | TIP | TACK);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cuda_state {
|
static enum cuda_state {
|
||||||
|
@ -155,6 +210,7 @@ int __init find_via_cuda(void)
|
||||||
|
|
||||||
via = via1;
|
via = via1;
|
||||||
cuda_state = idle;
|
cuda_state = idle;
|
||||||
|
mcu_is_egret = false;
|
||||||
|
|
||||||
err = cuda_init_via();
|
err = cuda_init_via();
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -251,7 +307,7 @@ static int __init via_cuda_start(void)
|
||||||
return -EAGAIN;
|
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;
|
cuda_fully_inited = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -276,6 +332,33 @@ cuda_probe(void)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ADB */
|
#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) \
|
#define WAIT_FOR(cond, what) \
|
||||||
do { \
|
do { \
|
||||||
int x; \
|
int x; \
|
||||||
|
@ -291,10 +374,6 @@ cuda_probe(void)
|
||||||
static int
|
static int
|
||||||
__init cuda_init_via(void)
|
__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
|
#ifdef CONFIG_PPC
|
||||||
out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
|
out_8(&via[IER], 0x7f); /* disable interrupts from VIA */
|
||||||
(void)in_8(&via[IER]);
|
(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 */
|
out_8(&via[IER], SR_INT); /* disable SR interrupt from VIA */
|
||||||
#endif
|
#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 */
|
/* delay 4ms and then clear any pending interrupt */
|
||||||
mdelay(4);
|
mdelay(4);
|
||||||
(void)in_8(&via[SR]);
|
(void)in_8(&via[SR]);
|
||||||
|
@ -453,7 +541,10 @@ cuda_start(void)
|
||||||
/* set the shift register to shift out and send a byte */
|
/* set the shift register to shift out and send a byte */
|
||||||
out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
|
out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
|
||||||
out_8(&via[SR], current_req->data[data_index++]);
|
out_8(&via[SR], current_req->data[data_index++]);
|
||||||
assert_TIP();
|
if (mcu_is_egret)
|
||||||
|
assert_TIP_and_TACK();
|
||||||
|
else
|
||||||
|
assert_TIP();
|
||||||
cuda_state = sent_first_byte;
|
cuda_state = sent_first_byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,8 +591,9 @@ cuda_interrupt(int irq, void *arg)
|
||||||
|
|
||||||
switch (cuda_state) {
|
switch (cuda_state) {
|
||||||
case idle:
|
case idle:
|
||||||
/* CUDA has sent us the first byte of data - unsolicited */
|
/* System controller has unsolicited data for us */
|
||||||
(void)in_8(&via[SR]);
|
(void)in_8(&via[SR]);
|
||||||
|
idle_state:
|
||||||
assert_TIP();
|
assert_TIP();
|
||||||
cuda_state = reading;
|
cuda_state = reading;
|
||||||
reply_ptr = cuda_rbuf;
|
reply_ptr = cuda_rbuf;
|
||||||
|
@ -509,7 +601,7 @@ cuda_interrupt(int irq, void *arg)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case awaiting_reply:
|
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]);
|
(void)in_8(&via[SR]);
|
||||||
assert_TIP();
|
assert_TIP();
|
||||||
cuda_state = reading;
|
cuda_state = reading;
|
||||||
|
@ -524,9 +616,14 @@ cuda_interrupt(int irq, void *arg)
|
||||||
(void)in_8(&via[SR]);
|
(void)in_8(&via[SR]);
|
||||||
negate_TIP_and_TACK();
|
negate_TIP_and_TACK();
|
||||||
cuda_state = idle;
|
cuda_state = idle;
|
||||||
|
/* Egret does not raise an "aborted" interrupt */
|
||||||
|
if (mcu_is_egret)
|
||||||
|
goto idle_state;
|
||||||
} else {
|
} else {
|
||||||
out_8(&via[SR], current_req->data[data_index++]);
|
out_8(&via[SR], current_req->data[data_index++]);
|
||||||
toggle_TACK();
|
toggle_TACK();
|
||||||
|
if (mcu_is_egret)
|
||||||
|
assert_TACK();
|
||||||
cuda_state = sending;
|
cuda_state = sending;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -550,6 +647,8 @@ cuda_interrupt(int irq, void *arg)
|
||||||
} else {
|
} else {
|
||||||
out_8(&via[SR], req->data[data_index++]);
|
out_8(&via[SR], req->data[data_index++]);
|
||||||
toggle_TACK();
|
toggle_TACK();
|
||||||
|
if (mcu_is_egret)
|
||||||
|
assert_TACK();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -560,16 +659,24 @@ cuda_interrupt(int irq, void *arg)
|
||||||
else
|
else
|
||||||
*reply_ptr++ = in_8(&via[SR]);
|
*reply_ptr++ = in_8(&via[SR]);
|
||||||
if (!TREQ_asserted(status)) {
|
if (!TREQ_asserted(status)) {
|
||||||
|
if (mcu_is_egret)
|
||||||
|
assert_TACK();
|
||||||
/* that's all folks */
|
/* that's all folks */
|
||||||
negate_TIP_and_TACK();
|
negate_TIP_and_TACK();
|
||||||
cuda_state = read_done;
|
cuda_state = read_done;
|
||||||
|
/* Egret does not raise a "read done" interrupt */
|
||||||
|
if (mcu_is_egret)
|
||||||
|
goto read_done_state;
|
||||||
} else {
|
} else {
|
||||||
toggle_TACK();
|
toggle_TACK();
|
||||||
|
if (mcu_is_egret)
|
||||||
|
negate_TACK();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case read_done:
|
case read_done:
|
||||||
(void)in_8(&via[SR]);
|
(void)in_8(&via[SR]);
|
||||||
|
read_done_state:
|
||||||
if (reading_reply) {
|
if (reading_reply) {
|
||||||
req = current_req;
|
req = current_req;
|
||||||
req->reply_len = reply_ptr - req->reply;
|
req->reply_len = reply_ptr - req->reply;
|
||||||
|
@ -645,6 +752,12 @@ cuda_input(unsigned char *buf, int nb)
|
||||||
#endif /* CONFIG_ADB */
|
#endif /* CONFIG_ADB */
|
||||||
break;
|
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:
|
default:
|
||||||
print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1,
|
print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1,
|
||||||
buf, nb, false);
|
buf, nb, false);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче