summaryrefslogtreecommitdiff
path: root/drivers/macintosh/via-cuda.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-22 10:30:38 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-22 10:30:38 -0800
commit38705613b74ab090eee55c327cd0cb77fb10eb26 (patch)
treeb219755a7eaaab097fbda4041cf2ba21df44fed5 /drivers/macintosh/via-cuda.c
parentff47d8c05019d6e7753cef270d6399cb5a33be57 (diff)
parent438e69b52be776c035aa2a851ccc1709033d729b (diff)
Merge tag 'powerpc-4.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman: "Highlights include: - Support for direct mapped LPC on POWER9, giving Linux direct access to devices that may be on there such as a UART. - Memory hotplug support for the Power9 Radix MMU. - Add new AUX vectors describing the processor's cache geometry, to be used by glibc. - The ability for a guest to ask the hypervisor to resize the guest's hash table, and in addition support for doing so automatically when memory is hotplugged into/out-of the guest. This allows the hash table to be sized based on the current memory usage of the guest, rather than the maximum possible memory usage. - Implementation of optprobes (kprobe optimisation) for powerpc. In addition there's the topic branch shared with the KVM tree, which includes support for guests to use the Radix MMU on Power9. Thanks to: Alistair Popple, Andrew Donnellan, Aneesh Kumar K.V, Anju T, Anton Blanchard, Benjamin Herrenschmidt, Chris Packham, Daniel Axtens, Daniel Borkmann, David Gibson, Finn Thain, Gautham R. Shenoy, Gavin Shan, Greg Kurz, Joel Stanley, John Allen, Madhavan Srinivasan, Mahesh Salgaonkar, Markus Elfring, Michael Neuling, Nathan Fontenot, Naveen N. Rao, Nicholas Piggin, Paul Mackerras, Ravi Bangoria, Reza Arbab, Shailendra Singh, Vaibhav Jain, Wei Yongjun" * tag 'powerpc-4.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (129 commits) powerpc/mm/radix: Skip ptesync in pte update helpers powerpc/mm/radix: Use ptep_get_and_clear_full when clearing pte for full mm powerpc/mm/radix: Update pte update sequence for pte clear case powerpc/mm: Update PROTFAULT handling in the page fault path powerpc/xmon: Fix data-breakpoint powerpc/mm: Fix build break with BOOK3S_64=n and MEMORY_HOTPLUG=y powerpc/mm: Fix build break when CMA=n && SPAPR_TCE_IOMMU=y powerpc/mm: Fix build break with RADIX=y & HUGETLBFS=n powerpc/pseries: Fix typo in parameter description powerpc/kprobes: Remove kprobe_exceptions_notify() kprobes: Introduce weak variant of kprobe_exceptions_notify() powerpc/ftrace: Fix confusing help text for DISABLE_MPROFILE_KERNEL powerpc/powernv: Fix opal_exit tracepoint opcode powerpc: Add a prototype for mcount() so it can be versioned powerpc: Drop GPL from of_node_to_nid() export to match other arches powerpc/kprobes: Optimize kprobe in kretprobe_trampoline() powerpc/kprobes: Implement Optprobes powerpc/kprobes: Fixes for kprobe_lookup_name() on BE powerpc: Add helper to check if offset is within relative branch range powerpc/bpf: Introduce __PPC_SH64() ...
Diffstat (limited to 'drivers/macintosh/via-cuda.c')
-rw-r--r--drivers/macintosh/via-cuda.c294
1 files changed, 210 insertions, 84 deletions
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 2088e23a8002..c60415958dfe 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -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,74 @@ 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);
+}
+
+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);
+}
+
+static inline void toggle_TACK(void)
+{
+ out_8(&via[B], in_8(&via[B]) ^ TACK);
+}
+
+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);
+}
+
static enum cuda_state {
idle,
sent_first_byte,
@@ -120,11 +205,13 @@ int __init find_via_cuda(void)
struct adb_request req;
int err;
- if (macintosh_config->adb_type != MAC_ADB_CUDA)
+ if (macintosh_config->adb_type != MAC_ADB_CUDA &&
+ macintosh_config->adb_type != MAC_ADB_EGRET)
return 0;
via = via1;
cuda_state = idle;
+ mcu_is_egret = macintosh_config->adb_type == MAC_ADB_EGRET;
err = cuda_init_via();
if (err) {
@@ -221,7 +308,7 @@ static int __init via_cuda_start(void)
return -EAGAIN;
}
- printk("Macintosh CUDA driver v0.5 for Unified ADB.\n");
+ pr_info("Macintosh Cuda and Egret driver.\n");
cuda_fully_inited = 1;
return 0;
@@ -237,7 +324,8 @@ cuda_probe(void)
if (sys_ctrler != SYS_CTRLER_CUDA)
return -ENODEV;
#else
- if (macintosh_config->adb_type != MAC_ADB_CUDA)
+ if (macintosh_config->adb_type != MAC_ADB_CUDA &&
+ macintosh_config->adb_type != MAC_ADB_EGRET)
return -ENODEV;
#endif
if (via == NULL)
@@ -246,12 +334,39 @@ 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; \
for (x = 1000; !(cond); --x) { \
if (x == 0) { \
- printk("Timeout waiting for " what "\n"); \
+ pr_err("Timeout waiting for " what "\n"); \
return -ENXIO; \
} \
udelay(100); \
@@ -261,10 +376,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 */
- out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */
- 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]);
@@ -272,16 +383,25 @@ __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]);
out_8(&via[IFR], SR_INT);
/* sync with the CUDA - assert TACK without TIP */
- out_8(&via[B], in_8(&via[B]) & ~TACK);
+ assert_TACK();
/* wait for the CUDA to assert TREQ in response */
- WAIT_FOR((in_8(&via[B]) & TREQ) == 0, "CUDA response to sync");
+ 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)");
@@ -289,14 +409,13 @@ __init cuda_init_via(void)
out_8(&via[IFR], SR_INT);
/* finish the sync by negating TACK */
- out_8(&via[B], in_8(&via[B]) | TACK);
+ negate_TACK();
/* wait for the CUDA to negate TREQ and the corresponding interrupt */
- WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
+ 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);
- out_8(&via[B], in_8(&via[B]) | TIP); /* should be unnecessary */
return 0;
}
@@ -357,6 +476,7 @@ cuda_reset_adb_bus(void)
return 0;
}
#endif /* CONFIG_ADB */
+
/* Construct and send a cuda request */
int
cuda_request(struct adb_request *req, void (*done)(struct adb_request *),
@@ -413,47 +533,43 @@ cuda_write(struct adb_request *req)
static void
cuda_start(void)
{
- struct adb_request *req;
-
/* assert cuda_state == idle */
- /* get the packet to send */
- req = current_req;
- if (req == 0)
+ if (current_req == NULL)
return;
- if ((in_8(&via[B]) & TREQ) == 0)
+ 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], req->data[0]);
- out_8(&via[B], in_8(&via[B]) & ~TIP);
+ 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;
}
void
cuda_poll(void)
{
- /* cuda_interrupt only takes a normal lock, we disable
- * interrupts here to avoid re-entering and thus deadlocking.
- */
- if (cuda_irq)
- disable_irq(cuda_irq);
- cuda_interrupt(0, NULL);
- if (cuda_irq)
- enable_irq(cuda_irq);
+ cuda_interrupt(0, NULL);
}
EXPORT_SYMBOL(cuda_poll);
+#define ARRAY_FULL(a, p) ((p) - (a) == ARRAY_SIZE(a))
+
static irqreturn_t
cuda_interrupt(int irq, void *arg)
{
- int status;
+ unsigned long flags;
+ u8 status;
struct adb_request *req = NULL;
unsigned char ibuf[16];
int ibuf_len = 0;
int complete = 0;
- spin_lock(&cuda_lock);
+ spin_lock_irqsave(&cuda_lock, flags);
/* On powermacs, this handler is registered for the VIA IRQ. But they use
* just the shift register IRQ -- other VIA interrupt sources are disabled.
@@ -466,52 +582,50 @@ cuda_interrupt(int irq, void *arg)
#endif
{
if ((in_8(&via[IFR]) & SR_INT) == 0) {
- spin_unlock(&cuda_lock);
+ spin_unlock_irqrestore(&cuda_lock, flags);
return IRQ_NONE;
} else {
out_8(&via[IFR], SR_INT);
}
}
-
- status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
- /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */
+
+ status = in_8(&via[B]) & (TIP | TACK | TREQ);
+
switch (cuda_state) {
case idle:
- /* CUDA has sent us the first byte of data - unsolicited */
- if (status != TREQ)
- printk("cuda: state=idle, status=%x\n", status);
+ /* System controller has unsolicited data for us */
(void)in_8(&via[SR]);
- out_8(&via[B], in_8(&via[B]) & ~TIP);
+idle_state:
+ assert_TIP();
cuda_state = reading;
reply_ptr = cuda_rbuf;
reading_reply = 0;
break;
case awaiting_reply:
- /* CUDA has sent us the first byte of data of a reply */
- if (status != TREQ)
- printk("cuda: state=awaiting_reply, status=%x\n", status);
+ /* System controller has reply data for us */
(void)in_8(&via[SR]);
- out_8(&via[B], in_8(&via[B]) & ~TIP);
+ assert_TIP();
cuda_state = reading;
reply_ptr = current_req->reply;
reading_reply = 1;
break;
case sent_first_byte:
- if (status == TREQ + TIP + SR_OUT) {
+ if (TREQ_asserted(status)) {
/* collision */
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
- out_8(&via[B], in_8(&via[B]) | TIP | TACK);
+ negate_TIP_and_TACK();
cuda_state = idle;
+ /* Egret does not raise an "aborted" interrupt */
+ if (mcu_is_egret)
+ goto idle_state;
} else {
- /* assert status == TIP + SR_OUT */
- if (status != TIP + SR_OUT)
- printk("cuda: state=sent_first_byte status=%x\n", status);
- out_8(&via[SR], current_req->data[1]);
- out_8(&via[B], in_8(&via[B]) ^ TACK);
- data_index = 2;
+ out_8(&via[SR], current_req->data[data_index++]);
+ toggle_TACK();
+ if (mcu_is_egret)
+ assert_TACK();
cuda_state = sending;
}
break;
@@ -521,7 +635,7 @@ cuda_interrupt(int irq, void *arg)
if (data_index >= req->nbytes) {
out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
(void)in_8(&via[SR]);
- out_8(&via[B], in_8(&via[B]) | TACK | TIP);
+ negate_TIP_and_TACK();
req->sent = 1;
if (req->reply_expected) {
cuda_state = awaiting_reply;
@@ -534,26 +648,37 @@ cuda_interrupt(int irq, void *arg)
}
} else {
out_8(&via[SR], req->data[data_index++]);
- out_8(&via[B], in_8(&via[B]) ^ TACK);
+ toggle_TACK();
+ if (mcu_is_egret)
+ assert_TACK();
}
break;
case reading:
- *reply_ptr++ = in_8(&via[SR]);
- if (status == TIP) {
+ if (reading_reply ? ARRAY_FULL(current_req->reply, reply_ptr)
+ : ARRAY_FULL(cuda_rbuf, reply_ptr))
+ (void)in_8(&via[SR]);
+ else
+ *reply_ptr++ = in_8(&via[SR]);
+ if (!TREQ_asserted(status)) {
+ if (mcu_is_egret)
+ assert_TACK();
/* that's all folks */
- out_8(&via[B], in_8(&via[B]) | TACK | TIP);
+ 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 {
- /* assert status == TIP | TREQ */
- if (status != TIP + TREQ)
- printk("cuda: state=reading status=%x\n", status);
- out_8(&via[B], in_8(&via[B]) ^ TACK);
+ 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;
@@ -570,6 +695,7 @@ cuda_interrupt(int irq, void *arg)
}
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
@@ -581,21 +707,19 @@ cuda_interrupt(int irq, void *arg)
ibuf_len = reply_ptr - cuda_rbuf;
memcpy(ibuf, cuda_rbuf, ibuf_len);
}
- if (status == TREQ) {
- out_8(&via[B], in_8(&via[B]) & ~TIP);
+ reply_ptr = cuda_rbuf;
+ cuda_state = idle;
+ cuda_start();
+ if (cuda_state == idle && TREQ_asserted(in_8(&via[B]))) {
+ assert_TIP();
cuda_state = reading;
- reply_ptr = cuda_rbuf;
- reading_reply = 0;
- } else {
- cuda_state = idle;
- cuda_start();
}
break;
default:
- printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state);
+ pr_err("cuda_interrupt: unknown cuda_state %d?\n", cuda_state);
}
- spin_unlock(&cuda_lock);
+ spin_unlock_irqrestore(&cuda_lock, flags);
if (complete && req) {
void (*done)(struct adb_request *) = req->done;
mb();
@@ -614,8 +738,6 @@ cuda_interrupt(int irq, void *arg)
static void
cuda_input(unsigned char *buf, int nb)
{
- int i;
-
switch (buf[0]) {
case ADB_PACKET:
#ifdef CONFIG_XMON
@@ -632,10 +754,14 @@ 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:
- printk("data from cuda (%d bytes):", nb);
- for (i = 0; i < nb; ++i)
- printk(" %.2x", buf[i]);
- printk("\n");
+ print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1,
+ buf, nb, false);
}
}