/*
- * 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.
*/
#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 */
#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);
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)
{
- 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 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)
{
- 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 {
via = via1;
cuda_state = idle;
+ mcu_is_egret = false;
err = cuda_init_via();
if (err) {
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;
}
#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; \
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]);
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]);
/* 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++]);
- assert_TIP();
+ if (mcu_is_egret)
+ assert_TIP_and_TACK();
+ else
+ assert_TIP();
cuda_state = sent_first_byte;
}
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;
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;
(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;
} else {
out_8(&via[SR], req->data[data_index++]);
toggle_TACK();
+ if (mcu_is_egret)
+ assert_TACK();
}
break;
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;
#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);