Blame view

drivers/macintosh/via-cuda.c 19.3 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
d23eee88b   Finn Thain   via-cuda: Add sup...
3
4
   * Device driver for the Cuda and Egret system controllers found on PowerMacs
   * and 68k Macs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   *
d23eee88b   Finn Thain   via-cuda: Add sup...
6
7
8
   * 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.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
   *
   * Copyright (C) 1996 Paul Mackerras.
   */
  #include <stdarg.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/delay.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
24
25
26
  #include <linux/adb.h>
  #include <linux/cuda.h>
  #include <linux/spinlock.h>
  #include <linux/interrupt.h>
  #ifdef CONFIG_PPC
  #include <asm/prom.h>
  #include <asm/machdep.h>
  #else
  #include <asm/macintosh.h>
  #include <asm/macints.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
  #include <asm/mac_via.h>
  #endif
  #include <asm/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
  #include <linux/init.h>
  
  static volatile unsigned char __iomem *via;
  static DEFINE_SPINLOCK(cuda_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  /* VIA registers - spaced 0x200 bytes apart */
  #define RS		0x200		/* skip between registers */
  #define B		0		/* B-side data */
  #define A		RS		/* A-side data */
  #define DIRB		(2*RS)		/* B-side direction (1=output) */
  #define DIRA		(3*RS)		/* A-side direction (1=output) */
  #define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
  #define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
  #define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
  #define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
  #define T2CL		(8*RS)		/* Timer 2 ctr/latch (low 8 bits) */
  #define T2CH		(9*RS)		/* Timer 2 counter (high 8 bits) */
  #define SR		(10*RS)		/* Shift register */
  #define ACR		(11*RS)		/* Auxiliary control register */
  #define PCR		(12*RS)		/* Peripheral control register */
  #define IFR		(13*RS)		/* Interrupt flag register */
  #define IER		(14*RS)		/* Interrupt enable register */
  #define ANH		(15*RS)		/* A-side data, no handshake */
d23eee88b   Finn Thain   via-cuda: Add sup...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  /*
   * 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 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
77
78
79
80
81
82
  
  /* 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 */
d23eee88b   Finn Thain   via-cuda: Add sup...
83
84
85
86
87
88
89
90
91
92
93
94
  /* 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
fd7a65a27   Finn Thain   via-cuda: Add TRE...
95
96
97
98
99
100
101
  static inline bool TREQ_asserted(u8 portb)
  {
  	return !(portb & TREQ);
  }
  
  static inline void assert_TIP(void)
  {
d23eee88b   Finn Thain   via-cuda: Add sup...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  	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));
fd7a65a27   Finn Thain   via-cuda: Add TRE...
116
117
118
119
  }
  
  static inline void assert_TACK(void)
  {
d23eee88b   Finn Thain   via-cuda: Add sup...
120
121
122
123
124
  	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);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
125
126
127
128
129
130
131
132
133
  }
  
  static inline void toggle_TACK(void)
  {
  	out_8(&via[B], in_8(&via[B]) ^ TACK);
  }
  
  static inline void negate_TACK(void)
  {
d23eee88b   Finn Thain   via-cuda: Add sup...
134
135
136
137
138
  	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);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
139
140
141
142
  }
  
  static inline void negate_TIP_and_TACK(void)
  {
d23eee88b   Finn Thain   via-cuda: Add sup...
143
144
145
146
147
  	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);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
148
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  static enum cuda_state {
      idle,
      sent_first_byte,
      sending,
      reading,
      read_done,
      awaiting_reply
  } cuda_state;
  
  static struct adb_request *current_req;
  static struct adb_request *last_req;
  static unsigned char cuda_rbuf[16];
  static unsigned char *reply_ptr;
  static int reading_reply;
  static int data_index;
0251c38ce   Finn Thain   CUDA ADB fixes
164
  static int cuda_irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
  #ifdef CONFIG_PPC
  static struct device_node *vias;
  #endif
872758563   Olaf Hering   [POWERPC] move va...
168
  static int cuda_fully_inited;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
  
  #ifdef CONFIG_ADB
  static int cuda_probe(void);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
  static int cuda_send_request(struct adb_request *req, int sync);
  static int cuda_adb_autopoll(int devs);
  static int cuda_reset_adb_bus(void);
  #endif /* CONFIG_ADB */
  
  static int cuda_init_via(void);
  static void cuda_start(void);
7d12e780e   David Howells   IRQ: Maintain reg...
179
180
  static irqreturn_t cuda_interrupt(int irq, void *arg);
  static void cuda_input(unsigned char *buf, int nb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
186
187
188
  void cuda_poll(void);
  static int cuda_write(struct adb_request *req);
  
  int cuda_request(struct adb_request *req,
  		 void (*done)(struct adb_request *), int nbytes, ...);
  
  #ifdef CONFIG_ADB
  struct adb_driver via_cuda_driver = {
18814ee84   Finn Thain   mac68k: start CUD...
189
190
191
192
193
194
  	.name         = "CUDA",
  	.probe        = cuda_probe,
  	.send_request = cuda_send_request,
  	.autopoll     = cuda_adb_autopoll,
  	.poll         = cuda_poll,
  	.reset_bus    = cuda_reset_adb_bus,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
  };
  #endif /* CONFIG_ADB */
18814ee84   Finn Thain   mac68k: start CUD...
197
198
199
200
201
  #ifdef CONFIG_MAC
  int __init find_via_cuda(void)
  {
      struct adb_request req;
      int err;
f74faec6b   Finn Thain   m68k/mac: Replace...
202
203
      if (macintosh_config->adb_type != MAC_ADB_CUDA &&
          macintosh_config->adb_type != MAC_ADB_EGRET)
18814ee84   Finn Thain   mac68k: start CUD...
204
205
206
207
  	return 0;
  
      via = via1;
      cuda_state = idle;
f74faec6b   Finn Thain   m68k/mac: Replace...
208
      mcu_is_egret = macintosh_config->adb_type == MAC_ADB_EGRET;
18814ee84   Finn Thain   mac68k: start CUD...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  
      err = cuda_init_via();
      if (err) {
  	printk(KERN_ERR "cuda_init_via() failed
  ");
  	via = NULL;
  	return 0;
      }
  
      /* enable autopoll */
      cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
      while (!req.complete)
  	cuda_poll();
  
      return 1;
  }
  #else
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
226
  int __init find_via_cuda(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
      struct adb_request req;
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
229
      phys_addr_t taddr;
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
230
      const u32 *reg;
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
231
      int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
  
      if (vias != 0)
  	return 1;
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
235
      vias = of_find_node_by_name(NULL, "via-cuda");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
      if (vias == 0)
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238

01b2726dd   Stephen Rothwell   [POWERPC] Rename ...
239
      reg = of_get_property(vias, "reg", NULL);
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
      if (reg == NULL) {
  	    printk(KERN_ERR "via-cuda: No \"reg\" property !
  ");
  	    goto fail;
      }
      taddr = of_translate_address(vias, reg);
      if (taddr == 0) {
  	    printk(KERN_ERR "via-cuda: Can't translate address !
  ");
  	    goto fail;
      }
      via = ioremap(taddr, 0x2000);
      if (via == NULL) {
  	    printk(KERN_ERR "via-cuda: Can't map address !
  ");
  	    goto fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
      }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  
      cuda_state = idle;
      sys_ctrler = SYS_CTRLER_CUDA;
  
      err = cuda_init_via();
      if (err) {
  	printk(KERN_ERR "cuda_init_via() failed
  ");
  	via = NULL;
  	return 0;
      }
  
      /* Clear and enable interrupts, but only on PPC. On 68K it's done  */
      /* for us by the main VIA driver in arch/m68k/mac/via.c        */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
      out_8(&via[IFR], 0x7f);	/* clear interrupts by writing 1s */
      out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
  
      /* enable autopoll */
      cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
      while (!req.complete)
  	cuda_poll();
  
      return 1;
51d3082fe   Benjamin Herrenschmidt   [PATCH] powerpc: ...
280
281
282
283
284
  
   fail:
      of_node_put(vias);
      vias = NULL;
      return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  }
18814ee84   Finn Thain   mac68k: start CUD...
286
  #endif /* !defined CONFIG_MAC */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
  
  static int __init via_cuda_start(void)
  {
      if (via == NULL)
  	return -ENODEV;
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
292
  #ifdef CONFIG_MAC
0251c38ce   Finn Thain   CUDA ADB fixes
293
      cuda_irq = IRQ_MAC_ADB;
18814ee84   Finn Thain   mac68k: start CUD...
294
  #else
0251c38ce   Finn Thain   CUDA ADB fixes
295
      cuda_irq = irq_of_parse_and_map(vias, 0);
ef24ba709   Michael Ellerman   powerpc: Remove a...
296
      if (!cuda_irq) {
b6a945ae0   Rob Herring   macintosh: Conver...
297
298
299
  	printk(KERN_ERR "via-cuda: can't map interrupts for %pOF
  ",
  	       vias);
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
300
301
  	return -ENODEV;
      }
18814ee84   Finn Thain   mac68k: start CUD...
302
  #endif
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
303

0251c38ce   Finn Thain   CUDA ADB fixes
304
305
306
      if (request_irq(cuda_irq, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
  	printk(KERN_ERR "via-cuda: can't request irq %d
  ", cuda_irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
  	return -EAGAIN;
      }
d23eee88b   Finn Thain   via-cuda: Add sup...
309
310
      pr_info("Macintosh Cuda and Egret driver.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  
      cuda_fully_inited = 1;
      return 0;
  }
  
  device_initcall(via_cuda_start);
  
  #ifdef CONFIG_ADB
  static int
  cuda_probe(void)
  {
  #ifdef CONFIG_PPC
      if (sys_ctrler != SYS_CTRLER_CUDA)
  	return -ENODEV;
  #else
f74faec6b   Finn Thain   m68k/mac: Replace...
326
327
      if (macintosh_config->adb_type != MAC_ADB_CUDA &&
          macintosh_config->adb_type != MAC_ADB_EGRET)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  	return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
      if (via == NULL)
  	return -ENODEV;
      return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
  }
  #endif /* CONFIG_ADB */
d23eee88b   Finn Thain   via-cuda: Add sup...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  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;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
  #define WAIT_FOR(cond, what)					\
      do {                                                        \
      	int x;							\
  	for (x = 1000; !(cond); --x) {				\
  	    if (x == 0) {					\
523717d14   Finn Thain   via-cuda: Cleanup...
366
367
  		pr_err("Timeout waiting for " what "
  ");	\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
373
374
  		return -ENXIO;					\
  	    }							\
  	    udelay(100);					\
  	}							\
      } while (0)
  
  static int
330dae199   Geert Uytterhoeven   mac: Make cuda_in...
375
  __init cuda_init_via(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  {
0251c38ce   Finn Thain   CUDA ADB fixes
377
  #ifdef CONFIG_PPC
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
      out_8(&via[IER], 0x7f);					/* disable interrupts from VIA */
      (void)in_8(&via[IER]);
0251c38ce   Finn Thain   CUDA ADB fixes
380
381
  #else
      out_8(&via[IER], SR_INT);					/* disable SR interrupt from VIA */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  #endif
d23eee88b   Finn Thain   via-cuda: Add sup...
383
384
385
386
387
388
389
390
      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();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
      /* delay 4ms and then clear any pending interrupt */
      mdelay(4);
      (void)in_8(&via[SR]);
0251c38ce   Finn Thain   CUDA ADB fixes
394
      out_8(&via[IFR], SR_INT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
  
      /* sync with the CUDA - assert TACK without TIP */
fd7a65a27   Finn Thain   via-cuda: Add TRE...
397
      assert_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
  
      /* wait for the CUDA to assert TREQ in response */
fd7a65a27   Finn Thain   via-cuda: Add TRE...
400
      WAIT_FOR(TREQ_asserted(in_8(&via[B])), "CUDA response to sync");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
  
      /* 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]);
0251c38ce   Finn Thain   CUDA ADB fixes
405
      out_8(&via[IFR], SR_INT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
  
      /* finish the sync by negating TACK */
fd7a65a27   Finn Thain   via-cuda: Add TRE...
408
      negate_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
  
      /* wait for the CUDA to negate TREQ and the corresponding interrupt */
fd7a65a27   Finn Thain   via-cuda: Add TRE...
411
      WAIT_FOR(!TREQ_asserted(in_8(&via[B])), "CUDA response to sync (3)");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
      WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
      (void)in_8(&via[SR]);
0251c38ce   Finn Thain   CUDA ADB fixes
414
      out_8(&via[IFR], SR_INT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  
      return 0;
  }
  
  #ifdef CONFIG_ADB
  /* Send an ADB command */
  static int
  cuda_send_request(struct adb_request *req, int sync)
  {
      int i;
  
      if ((via == NULL) || !cuda_fully_inited) {
  	req->complete = 1;
  	return -ENXIO;
      }
    
      req->reply_expected = 1;
  
      i = cuda_write(req);
      if (i)
  	return i;
  
      if (sync) {
  	while (!req->complete)
  	    cuda_poll();
      }
      return 0;
  }
  
  
  /* Enable/disable autopolling */
  static int
  cuda_adb_autopoll(int devs)
  {
      struct adb_request req;
  
      if ((via == NULL) || !cuda_fully_inited)
  	return -ENXIO;
  
      cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, (devs? 1: 0));
      while (!req.complete)
  	cuda_poll();
      return 0;
  }
  
  /* Reset adb bus - how do we do this?? */
  static int
  cuda_reset_adb_bus(void)
  {
      struct adb_request req;
  
      if ((via == NULL) || !cuda_fully_inited)
  	return -ENXIO;
  
      cuda_request(&req, NULL, 2, ADB_PACKET, 0);		/* maybe? */
      while (!req.complete)
  	cuda_poll();
      return 0;
  }
  #endif /* CONFIG_ADB */
523717d14   Finn Thain   via-cuda: Cleanup...
475

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
  /* 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;
  
      if (via == NULL) {
  	req->complete = 1;
  	return -ENXIO;
      }
  
      req->nbytes = nbytes;
      req->done = done;
      va_start(list, nbytes);
      for (i = 0; i < nbytes; ++i)
  	req->data[i] = va_arg(list, int);
      va_end(list);
      req->reply_expected = 1;
      return cuda_write(req);
  }
4a1b08e84   Anton Blanchard   powerpc: Move via...
498
  EXPORT_SYMBOL(cuda_request);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  
  static int
  cuda_write(struct adb_request *req)
  {
      unsigned long flags;
  
      if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
  	req->complete = 1;
  	return -EINVAL;
      }
      req->next = NULL;
      req->sent = 0;
      req->complete = 0;
      req->reply_len = 0;
  
      spin_lock_irqsave(&cuda_lock, flags);
      if (current_req != 0) {
  	last_req->next = req;
  	last_req = req;
      } else {
  	current_req = req;
  	last_req = req;
  	if (cuda_state == idle)
  	    cuda_start();
      }
      spin_unlock_irqrestore(&cuda_lock, flags);
  
      return 0;
  }
  
  static void
  cuda_start(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
      /* assert cuda_state == idle */
06d7e9940   Finn Thain   via-cuda: Remove ...
533
      if (current_req == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
  	return;
97ced1aac   Finn Thain   via-cuda: Initial...
535
      data_index = 0;
fd7a65a27   Finn Thain   via-cuda: Add TRE...
536
      if (TREQ_asserted(in_8(&via[B])))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
  	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);
97ced1aac   Finn Thain   via-cuda: Initial...
541
      out_8(&via[SR], current_req->data[data_index++]);
d23eee88b   Finn Thain   via-cuda: Add sup...
542
543
544
545
      if (mcu_is_egret)
  	assert_TIP_and_TACK();
      else
  	assert_TIP();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
551
      cuda_state = sent_first_byte;
  }
  
  void
  cuda_poll(void)
  {
ac39452e9   Finn Thain   via-cuda: Use spi...
552
  	cuda_interrupt(0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  }
4a1b08e84   Anton Blanchard   powerpc: Move via...
554
  EXPORT_SYMBOL(cuda_poll);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555

fe73b582f   Finn Thain   via-cuda: Prevent...
556
  #define ARRAY_FULL(a, p)	((p) - (a) == ARRAY_SIZE(a))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
  static irqreturn_t
7d12e780e   David Howells   IRQ: Maintain reg...
558
  cuda_interrupt(int irq, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  {
ac39452e9   Finn Thain   via-cuda: Use spi...
560
      unsigned long flags;
fd7a65a27   Finn Thain   via-cuda: Add TRE...
561
      u8 status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
      struct adb_request *req = NULL;
      unsigned char ibuf[16];
      int ibuf_len = 0;
      int complete = 0;
458c77f3d   Finn Thain   macintosh/via-cud...
566
      bool full;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
      
ac39452e9   Finn Thain   via-cuda: Use spi...
568
      spin_lock_irqsave(&cuda_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569

18814ee84   Finn Thain   mac68k: start CUD...
570
      /* On powermacs, this handler is registered for the VIA IRQ. But they use
0251c38ce   Finn Thain   CUDA ADB fixes
571
572
573
574
575
576
577
578
579
580
       * 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.
       */
  
  #ifdef CONFIG_MAC
      if (!arg)
  #endif
      {
          if ((in_8(&via[IFR]) & SR_INT) == 0) {
ac39452e9   Finn Thain   via-cuda: Use spi...
581
              spin_unlock_irqrestore(&cuda_lock, flags);
0251c38ce   Finn Thain   CUDA ADB fixes
582
583
584
585
              return IRQ_NONE;
          } else {
              out_8(&via[IFR], SR_INT);
          }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
      }
fd7a65a27   Finn Thain   via-cuda: Add TRE...
587
588
  
      status = in_8(&via[B]) & (TIP | TACK | TREQ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
      switch (cuda_state) {
      case idle:
d23eee88b   Finn Thain   via-cuda: Add sup...
591
  	/* System controller has unsolicited data for us */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
  	(void)in_8(&via[SR]);
d23eee88b   Finn Thain   via-cuda: Add sup...
593
  idle_state:
fd7a65a27   Finn Thain   via-cuda: Add TRE...
594
  	assert_TIP();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
599
600
  	cuda_state = reading;
  	reply_ptr = cuda_rbuf;
  	reading_reply = 0;
  	break;
  
      case awaiting_reply:
d23eee88b   Finn Thain   via-cuda: Add sup...
601
  	/* System controller has reply data for us */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  	(void)in_8(&via[SR]);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
603
  	assert_TIP();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
609
  	cuda_state = reading;
  	reply_ptr = current_req->reply;
  	reading_reply = 1;
  	break;
  
      case sent_first_byte:
fd7a65a27   Finn Thain   via-cuda: Add TRE...
610
  	if (TREQ_asserted(status)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
  	    /* collision */
  	    out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
  	    (void)in_8(&via[SR]);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
614
  	    negate_TIP_and_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  	    cuda_state = idle;
d23eee88b   Finn Thain   via-cuda: Add sup...
616
617
618
  	    /* Egret does not raise an "aborted" interrupt */
  	    if (mcu_is_egret)
  		goto idle_state;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
  	} else {
97ced1aac   Finn Thain   via-cuda: Initial...
620
  	    out_8(&via[SR], current_req->data[data_index++]);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
621
  	    toggle_TACK();
d23eee88b   Finn Thain   via-cuda: Add sup...
622
623
  	    if (mcu_is_egret)
  		assert_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
626
627
628
629
630
631
632
  	    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]);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
633
  	    negate_TIP_and_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
636
637
638
639
640
641
642
643
644
645
  	    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++]);
fd7a65a27   Finn Thain   via-cuda: Add TRE...
646
  	    toggle_TACK();
d23eee88b   Finn Thain   via-cuda: Add sup...
647
648
  	    if (mcu_is_egret)
  		assert_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
  	}
  	break;
  
      case reading:
458c77f3d   Finn Thain   macintosh/via-cud...
653
654
655
  	full = reading_reply ? ARRAY_FULL(current_req->reply, reply_ptr)
  	                     : ARRAY_FULL(cuda_rbuf, reply_ptr);
  	if (full)
fe73b582f   Finn Thain   via-cuda: Prevent...
656
657
658
  	    (void)in_8(&via[SR]);
  	else
  	    *reply_ptr++ = in_8(&via[SR]);
458c77f3d   Finn Thain   macintosh/via-cud...
659
  	if (!TREQ_asserted(status) || full) {
d23eee88b   Finn Thain   via-cuda: Add sup...
660
661
  	    if (mcu_is_egret)
  		assert_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
  	    /* that's all folks */
fd7a65a27   Finn Thain   via-cuda: Add TRE...
663
  	    negate_TIP_and_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
  	    cuda_state = read_done;
d23eee88b   Finn Thain   via-cuda: Add sup...
665
666
667
  	    /* Egret does not raise a "read done" interrupt */
  	    if (mcu_is_egret)
  		goto read_done_state;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
  	} else {
fd7a65a27   Finn Thain   via-cuda: Add TRE...
669
  	    toggle_TACK();
d23eee88b   Finn Thain   via-cuda: Add sup...
670
671
  	    if (mcu_is_egret)
  		negate_TACK();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
674
675
676
  	}
  	break;
  
      case read_done:
  	(void)in_8(&via[SR]);
d23eee88b   Finn Thain   via-cuda: Add sup...
677
  read_done_state:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  	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;
cfbf99801   Finn Thain   via-cuda: Fix re-...
694
  	    reading_reply = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
698
699
700
701
702
703
704
705
  	} 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);
  	}
cfbf99801   Finn Thain   via-cuda: Fix re-...
706
  	reply_ptr = cuda_rbuf;
a64662432   Finn Thain   via-cuda: Avoid T...
707
708
709
  	cuda_state = idle;
  	cuda_start();
  	if (cuda_state == idle && TREQ_asserted(in_8(&via[B]))) {
fd7a65a27   Finn Thain   via-cuda: Add TRE...
710
  	    assert_TIP();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  	    cuda_state = reading;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
715
  	}
  	break;
  
      default:
523717d14   Finn Thain   via-cuda: Cleanup...
716
717
  	pr_err("cuda_interrupt: unknown cuda_state %d?
  ", cuda_state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
      }
ac39452e9   Finn Thain   via-cuda: Use spi...
719
      spin_unlock_irqrestore(&cuda_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
724
725
726
727
728
729
730
      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)
7d12e780e   David Howells   IRQ: Maintain reg...
731
  	cuda_input(ibuf, ibuf_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
735
      return IRQ_HANDLED;
  }
  
  static void
7d12e780e   David Howells   IRQ: Maintain reg...
736
  cuda_input(unsigned char *buf, int nb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
739
740
741
742
743
744
745
746
747
748
749
      switch (buf[0]) {
      case ADB_PACKET:
  #ifdef CONFIG_XMON
  	if (nb == 5 && buf[2] == 0x2c) {
  	    extern int xmon_wants_key, xmon_adb_keycode;
  	    if (xmon_wants_key) {
  		xmon_adb_keycode = buf[3];
  		return;
  	    }
  	}
  #endif /* CONFIG_XMON */
  #ifdef CONFIG_ADB
7d12e780e   David Howells   IRQ: Maintain reg...
750
  	adb_input(buf+2, nb-2, buf[1] & 0x40);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
  #endif /* CONFIG_ADB */
  	break;
d23eee88b   Finn Thain   via-cuda: Add sup...
753
754
755
756
757
      case TIMER_PACKET:
  	/* Egret sends these periodically. Might be useful as a 'heartbeat'
  	 * to trigger a recovery for the VIA shift register errata.
  	 */
  	break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
      default:
523717d14   Finn Thain   via-cuda: Cleanup...
759
760
  	print_hex_dump(KERN_INFO, "cuda_input: ", DUMP_PREFIX_NONE, 32, 1,
  	               buf, nb, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
      }
  }
0792a2c8e   Finn Thain   macintosh: Use co...
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  
  /* Offset between Unix time (1970-based) and Mac time (1904-based) */
  #define RTC_OFFSET	2082844800
  
  time64_t cuda_get_time(void)
  {
  	struct adb_request req;
  	u32 now;
  
  	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
  		return 0;
  	while (!req.complete)
  		cuda_poll();
  	if (req.reply_len != 7)
  		pr_err("%s: got %d byte reply
  ", __func__, req.reply_len);
  	now = (req.reply[3] << 24) + (req.reply[4] << 16) +
  	      (req.reply[5] << 8) + req.reply[6];
  	return (time64_t)now - RTC_OFFSET;
  }
  
  int cuda_set_rtc_time(struct rtc_time *tm)
  {
  	u32 now;
  	struct adb_request req;
  
  	now = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET);
  	if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
  	                 now >> 24, now >> 16, now >> 8, now) < 0)
  		return -ENXIO;
  	while (!req.complete)
  		cuda_poll();
  	if ((req.reply_len != 3) && (req.reply_len != 7))
  		pr_err("%s: got %d byte reply
  ", __func__, req.reply_len);
  	return 0;
  }