Root/
1 | /* |
2 | * Device driver for the PMU on 68K-based Apple PowerBooks |
3 | * |
4 | * The VIA (versatile interface adapter) interfaces to the PMU, |
5 | * a 6805 microprocessor core whose primary function is to control |
6 | * battery charging and system power on the PowerBooks. |
7 | * The PMU also controls the ADB (Apple Desktop Bus) which connects |
8 | * to the keyboard and mouse, as well as the non-volatile RAM |
9 | * and the RTC (real time clock) chip. |
10 | * |
11 | * Adapted for 68K PMU by Joshua M. Thompson |
12 | * |
13 | * Based largely on the PowerMac PMU code by Paul Mackerras and |
14 | * Fabio Riccardi. |
15 | * |
16 | * Also based on the PMU driver from MkLinux by Apple Computer, Inc. |
17 | * and the Open Software Foundation, Inc. |
18 | */ |
19 | |
20 | #include <stdarg.h> |
21 | #include <linux/types.h> |
22 | #include <linux/errno.h> |
23 | #include <linux/kernel.h> |
24 | #include <linux/delay.h> |
25 | #include <linux/miscdevice.h> |
26 | #include <linux/blkdev.h> |
27 | #include <linux/pci.h> |
28 | #include <linux/init.h> |
29 | #include <linux/interrupt.h> |
30 | |
31 | #include <linux/adb.h> |
32 | #include <linux/pmu.h> |
33 | #include <linux/cuda.h> |
34 | |
35 | #include <asm/macintosh.h> |
36 | #include <asm/macints.h> |
37 | #include <asm/mac_via.h> |
38 | |
39 | #include <asm/pgtable.h> |
40 | #include <asm/irq.h> |
41 | #include <asm/uaccess.h> |
42 | |
43 | /* Misc minor number allocated for /dev/pmu */ |
44 | #define PMU_MINOR 154 |
45 | |
46 | /* VIA registers - spaced 0x200 bytes apart */ |
47 | #define RS 0x200 /* skip between registers */ |
48 | #define B 0 /* B-side data */ |
49 | #define A RS /* A-side data */ |
50 | #define DIRB (2*RS) /* B-side direction (1=output) */ |
51 | #define DIRA (3*RS) /* A-side direction (1=output) */ |
52 | #define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ |
53 | #define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ |
54 | #define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ |
55 | #define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ |
56 | #define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ |
57 | #define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ |
58 | #define SR (10*RS) /* Shift register */ |
59 | #define ACR (11*RS) /* Auxiliary control register */ |
60 | #define PCR (12*RS) /* Peripheral control register */ |
61 | #define IFR (13*RS) /* Interrupt flag register */ |
62 | #define IER (14*RS) /* Interrupt enable register */ |
63 | #define ANH (15*RS) /* A-side data, no handshake */ |
64 | |
65 | /* Bits in B data register: both active low */ |
66 | #define TACK 0x02 /* Transfer acknowledge (input) */ |
67 | #define TREQ 0x04 /* Transfer request (output) */ |
68 | |
69 | /* Bits in ACR */ |
70 | #define SR_CTRL 0x1c /* Shift register control bits */ |
71 | #define SR_EXT 0x0c /* Shift on external clock */ |
72 | #define SR_OUT 0x10 /* Shift out if 1 */ |
73 | |
74 | /* Bits in IFR and IER */ |
75 | #define SR_INT 0x04 /* Shift register full/empty */ |
76 | #define CB1_INT 0x10 /* transition on CB1 input */ |
77 | |
78 | static enum pmu_state { |
79 | idle, |
80 | sending, |
81 | intack, |
82 | reading, |
83 | reading_intr, |
84 | } pmu_state; |
85 | |
86 | static struct adb_request *current_req; |
87 | static struct adb_request *last_req; |
88 | static struct adb_request *req_awaiting_reply; |
89 | static unsigned char interrupt_data[32]; |
90 | static unsigned char *reply_ptr; |
91 | static int data_index; |
92 | static int data_len; |
93 | static int adb_int_pending; |
94 | static int pmu_adb_flags; |
95 | static int adb_dev_map; |
96 | static struct adb_request bright_req_1, bright_req_2, bright_req_3; |
97 | static int pmu_kind = PMU_UNKNOWN; |
98 | static int pmu_fully_inited; |
99 | |
100 | int asleep; |
101 | |
102 | static int pmu_probe(void); |
103 | static int pmu_init(void); |
104 | static void pmu_start(void); |
105 | static irqreturn_t pmu_interrupt(int irq, void *arg); |
106 | static int pmu_send_request(struct adb_request *req, int sync); |
107 | static int pmu_autopoll(int devs); |
108 | void pmu_poll(void); |
109 | static int pmu_reset_bus(void); |
110 | |
111 | static void pmu_start(void); |
112 | static void send_byte(int x); |
113 | static void recv_byte(void); |
114 | static void pmu_done(struct adb_request *req); |
115 | static void pmu_handle_data(unsigned char *data, int len); |
116 | static void set_volume(int level); |
117 | static void pmu_enable_backlight(int on); |
118 | static void pmu_set_brightness(int level); |
119 | |
120 | struct adb_driver via_pmu_driver = { |
121 | "68K PMU", |
122 | pmu_probe, |
123 | pmu_init, |
124 | pmu_send_request, |
125 | pmu_autopoll, |
126 | pmu_poll, |
127 | pmu_reset_bus |
128 | }; |
129 | |
130 | /* |
131 | * This table indicates for each PMU opcode: |
132 | * - the number of data bytes to be sent with the command, or -1 |
133 | * if a length byte should be sent, |
134 | * - the number of response bytes which the PMU will return, or |
135 | * -1 if it will send a length byte. |
136 | */ |
137 | static s8 pmu_data_len[256][2] = { |
138 | /* 0 1 2 3 4 5 6 7 */ |
139 | /*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
140 | /*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
141 | /*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
142 | /*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0}, |
143 | /*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, |
144 | /*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1}, |
145 | /*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
146 | /*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0}, |
147 | /*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
148 | /*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1}, |
149 | /*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, |
150 | /*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1}, |
151 | /*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
152 | /*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1}, |
153 | /*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
154 | /*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1}, |
155 | /*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
156 | /*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
157 | /*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
158 | /*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
159 | /*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, |
160 | /*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
161 | /*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
162 | /*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
163 | /*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
164 | /*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
165 | /*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
166 | /*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1}, |
167 | /*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, |
168 | /*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0}, |
169 | /*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, |
170 | /*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, |
171 | }; |
172 | |
173 | int pmu_probe(void) |
174 | { |
175 | if (macintosh_config->adb_type == MAC_ADB_PB1) { |
176 | pmu_kind = PMU_68K_V1; |
177 | } else if (macintosh_config->adb_type == MAC_ADB_PB2) { |
178 | pmu_kind = PMU_68K_V2; |
179 | } else { |
180 | return -ENODEV; |
181 | } |
182 | |
183 | pmu_state = idle; |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static int |
189 | pmu_init(void) |
190 | { |
191 | int timeout; |
192 | volatile struct adb_request req; |
193 | |
194 | via2[B] |= TREQ; /* negate TREQ */ |
195 | via2[DIRB] = (via2[DIRB] | TREQ) & ~TACK; /* TACK in, TREQ out */ |
196 | |
197 | pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB); |
198 | timeout = 100000; |
199 | while (!req.complete) { |
200 | if (--timeout < 0) { |
201 | printk(KERN_ERR "pmu_init: no response from PMU\n"); |
202 | return -EAGAIN; |
203 | } |
204 | udelay(10); |
205 | pmu_poll(); |
206 | } |
207 | |
208 | /* ack all pending interrupts */ |
209 | timeout = 100000; |
210 | interrupt_data[0] = 1; |
211 | while (interrupt_data[0] || pmu_state != idle) { |
212 | if (--timeout < 0) { |
213 | printk(KERN_ERR "pmu_init: timed out acking intrs\n"); |
214 | return -EAGAIN; |
215 | } |
216 | if (pmu_state == idle) { |
217 | adb_int_pending = 1; |
218 | pmu_interrupt(0, NULL); |
219 | } |
220 | pmu_poll(); |
221 | udelay(10); |
222 | } |
223 | |
224 | pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK, |
225 | PMU_INT_ADB_AUTO|PMU_INT_SNDBRT|PMU_INT_ADB); |
226 | timeout = 100000; |
227 | while (!req.complete) { |
228 | if (--timeout < 0) { |
229 | printk(KERN_ERR "pmu_init: no response from PMU\n"); |
230 | return -EAGAIN; |
231 | } |
232 | udelay(10); |
233 | pmu_poll(); |
234 | } |
235 | |
236 | bright_req_1.complete = 1; |
237 | bright_req_2.complete = 1; |
238 | bright_req_3.complete = 1; |
239 | |
240 | if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "pmu-shift", |
241 | pmu_interrupt)) { |
242 | printk(KERN_ERR "pmu_init: can't get irq %d\n", |
243 | IRQ_MAC_ADB_SR); |
244 | return -EAGAIN; |
245 | } |
246 | if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "pmu-clock", |
247 | pmu_interrupt)) { |
248 | printk(KERN_ERR "pmu_init: can't get irq %d\n", |
249 | IRQ_MAC_ADB_CL); |
250 | free_irq(IRQ_MAC_ADB_SR, pmu_interrupt); |
251 | return -EAGAIN; |
252 | } |
253 | |
254 | pmu_fully_inited = 1; |
255 | |
256 | /* Enable backlight */ |
257 | pmu_enable_backlight(1); |
258 | |
259 | printk("adb: PMU 68K driver v0.5 for Unified ADB.\n"); |
260 | |
261 | return 0; |
262 | } |
263 | |
264 | int |
265 | pmu_get_model(void) |
266 | { |
267 | return pmu_kind; |
268 | } |
269 | |
270 | /* Send an ADB command */ |
271 | static int |
272 | pmu_send_request(struct adb_request *req, int sync) |
273 | { |
274 | int i, ret; |
275 | |
276 | if (!pmu_fully_inited) |
277 | { |
278 | req->complete = 1; |
279 | return -ENXIO; |
280 | } |
281 | |
282 | ret = -EINVAL; |
283 | |
284 | switch (req->data[0]) { |
285 | case PMU_PACKET: |
286 | for (i = 0; i < req->nbytes - 1; ++i) |
287 | req->data[i] = req->data[i+1]; |
288 | --req->nbytes; |
289 | if (pmu_data_len[req->data[0]][1] != 0) { |
290 | req->reply[0] = ADB_RET_OK; |
291 | req->reply_len = 1; |
292 | } else |
293 | req->reply_len = 0; |
294 | ret = pmu_queue_request(req); |
295 | break; |
296 | case CUDA_PACKET: |
297 | switch (req->data[1]) { |
298 | case CUDA_GET_TIME: |
299 | if (req->nbytes != 2) |
300 | break; |
301 | req->data[0] = PMU_READ_RTC; |
302 | req->nbytes = 1; |
303 | req->reply_len = 3; |
304 | req->reply[0] = CUDA_PACKET; |
305 | req->reply[1] = 0; |
306 | req->reply[2] = CUDA_GET_TIME; |
307 | ret = pmu_queue_request(req); |
308 | break; |
309 | case CUDA_SET_TIME: |
310 | if (req->nbytes != 6) |
311 | break; |
312 | req->data[0] = PMU_SET_RTC; |
313 | req->nbytes = 5; |
314 | for (i = 1; i <= 4; ++i) |
315 | req->data[i] = req->data[i+1]; |
316 | req->reply_len = 3; |
317 | req->reply[0] = CUDA_PACKET; |
318 | req->reply[1] = 0; |
319 | req->reply[2] = CUDA_SET_TIME; |
320 | ret = pmu_queue_request(req); |
321 | break; |
322 | case CUDA_GET_PRAM: |
323 | if (req->nbytes != 4) |
324 | break; |
325 | req->data[0] = PMU_READ_NVRAM; |
326 | req->data[1] = req->data[2]; |
327 | req->data[2] = req->data[3]; |
328 | req->nbytes = 3; |
329 | req->reply_len = 3; |
330 | req->reply[0] = CUDA_PACKET; |
331 | req->reply[1] = 0; |
332 | req->reply[2] = CUDA_GET_PRAM; |
333 | ret = pmu_queue_request(req); |
334 | break; |
335 | case CUDA_SET_PRAM: |
336 | if (req->nbytes != 5) |
337 | break; |
338 | req->data[0] = PMU_WRITE_NVRAM; |
339 | req->data[1] = req->data[2]; |
340 | req->data[2] = req->data[3]; |
341 | req->data[3] = req->data[4]; |
342 | req->nbytes = 4; |
343 | req->reply_len = 3; |
344 | req->reply[0] = CUDA_PACKET; |
345 | req->reply[1] = 0; |
346 | req->reply[2] = CUDA_SET_PRAM; |
347 | ret = pmu_queue_request(req); |
348 | break; |
349 | } |
350 | break; |
351 | case ADB_PACKET: |
352 | for (i = req->nbytes - 1; i > 1; --i) |
353 | req->data[i+2] = req->data[i]; |
354 | req->data[3] = req->nbytes - 2; |
355 | req->data[2] = pmu_adb_flags; |
356 | /*req->data[1] = req->data[1];*/ |
357 | req->data[0] = PMU_ADB_CMD; |
358 | req->nbytes += 2; |
359 | req->reply_expected = 1; |
360 | req->reply_len = 0; |
361 | ret = pmu_queue_request(req); |
362 | break; |
363 | } |
364 | if (ret) |
365 | { |
366 | req->complete = 1; |
367 | return ret; |
368 | } |
369 | |
370 | if (sync) { |
371 | while (!req->complete) |
372 | pmu_poll(); |
373 | } |
374 | |
375 | return 0; |
376 | } |
377 | |
378 | /* Enable/disable autopolling */ |
379 | static int |
380 | pmu_autopoll(int devs) |
381 | { |
382 | struct adb_request req; |
383 | |
384 | if (!pmu_fully_inited) return -ENXIO; |
385 | |
386 | if (devs) { |
387 | adb_dev_map = devs; |
388 | pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, |
389 | adb_dev_map >> 8, adb_dev_map); |
390 | pmu_adb_flags = 2; |
391 | } else { |
392 | pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF); |
393 | pmu_adb_flags = 0; |
394 | } |
395 | while (!req.complete) |
396 | pmu_poll(); |
397 | return 0; |
398 | } |
399 | |
400 | /* Reset the ADB bus */ |
401 | static int |
402 | pmu_reset_bus(void) |
403 | { |
404 | struct adb_request req; |
405 | long timeout; |
406 | int save_autopoll = adb_dev_map; |
407 | |
408 | if (!pmu_fully_inited) return -ENXIO; |
409 | |
410 | /* anyone got a better idea?? */ |
411 | pmu_autopoll(0); |
412 | |
413 | req.nbytes = 5; |
414 | req.done = NULL; |
415 | req.data[0] = PMU_ADB_CMD; |
416 | req.data[1] = 0; |
417 | req.data[2] = 3; /* ADB_BUSRESET ??? */ |
418 | req.data[3] = 0; |
419 | req.data[4] = 0; |
420 | req.reply_len = 0; |
421 | req.reply_expected = 1; |
422 | if (pmu_queue_request(&req) != 0) |
423 | { |
424 | printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n"); |
425 | return -EIO; |
426 | } |
427 | while (!req.complete) |
428 | pmu_poll(); |
429 | timeout = 100000; |
430 | while (!req.complete) { |
431 | if (--timeout < 0) { |
432 | printk(KERN_ERR "pmu_adb_reset_bus (reset): no response from PMU\n"); |
433 | return -EIO; |
434 | } |
435 | udelay(10); |
436 | pmu_poll(); |
437 | } |
438 | |
439 | if (save_autopoll != 0) |
440 | pmu_autopoll(save_autopoll); |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | /* Construct and send a pmu request */ |
446 | int |
447 | pmu_request(struct adb_request *req, void (*done)(struct adb_request *), |
448 | int nbytes, ...) |
449 | { |
450 | va_list list; |
451 | int i; |
452 | |
453 | if (nbytes < 0 || nbytes > 32) { |
454 | printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes); |
455 | req->complete = 1; |
456 | return -EINVAL; |
457 | } |
458 | req->nbytes = nbytes; |
459 | req->done = done; |
460 | va_start(list, nbytes); |
461 | for (i = 0; i < nbytes; ++i) |
462 | req->data[i] = va_arg(list, int); |
463 | va_end(list); |
464 | if (pmu_data_len[req->data[0]][1] != 0) { |
465 | req->reply[0] = ADB_RET_OK; |
466 | req->reply_len = 1; |
467 | } else |
468 | req->reply_len = 0; |
469 | req->reply_expected = 0; |
470 | return pmu_queue_request(req); |
471 | } |
472 | |
473 | int |
474 | pmu_queue_request(struct adb_request *req) |
475 | { |
476 | unsigned long flags; |
477 | int nsend; |
478 | |
479 | if (req->nbytes <= 0) { |
480 | req->complete = 1; |
481 | return 0; |
482 | } |
483 | nsend = pmu_data_len[req->data[0]][0]; |
484 | if (nsend >= 0 && req->nbytes != nsend + 1) { |
485 | req->complete = 1; |
486 | return -EINVAL; |
487 | } |
488 | |
489 | req->next = NULL; |
490 | req->sent = 0; |
491 | req->complete = 0; |
492 | local_irq_save(flags); |
493 | |
494 | if (current_req != 0) { |
495 | last_req->next = req; |
496 | last_req = req; |
497 | } else { |
498 | current_req = req; |
499 | last_req = req; |
500 | if (pmu_state == idle) |
501 | pmu_start(); |
502 | } |
503 | |
504 | local_irq_restore(flags); |
505 | return 0; |
506 | } |
507 | |
508 | static void |
509 | send_byte(int x) |
510 | { |
511 | via1[ACR] |= SR_CTRL; |
512 | via1[SR] = x; |
513 | via2[B] &= ~TREQ; /* assert TREQ */ |
514 | } |
515 | |
516 | static void |
517 | recv_byte(void) |
518 | { |
519 | char c; |
520 | |
521 | via1[ACR] = (via1[ACR] | SR_EXT) & ~SR_OUT; |
522 | c = via1[SR]; /* resets SR */ |
523 | via2[B] &= ~TREQ; |
524 | } |
525 | |
526 | static void |
527 | pmu_start(void) |
528 | { |
529 | unsigned long flags; |
530 | struct adb_request *req; |
531 | |
532 | /* assert pmu_state == idle */ |
533 | /* get the packet to send */ |
534 | local_irq_save(flags); |
535 | req = current_req; |
536 | if (req == 0 || pmu_state != idle |
537 | || (req->reply_expected && req_awaiting_reply)) |
538 | goto out; |
539 | |
540 | pmu_state = sending; |
541 | data_index = 1; |
542 | data_len = pmu_data_len[req->data[0]][0]; |
543 | |
544 | /* set the shift register to shift out and send a byte */ |
545 | send_byte(req->data[0]); |
546 | |
547 | out: |
548 | local_irq_restore(flags); |
549 | } |
550 | |
551 | void |
552 | pmu_poll(void) |
553 | { |
554 | unsigned long flags; |
555 | |
556 | local_irq_save(flags); |
557 | if (via1[IFR] & SR_INT) { |
558 | via1[IFR] = SR_INT; |
559 | pmu_interrupt(IRQ_MAC_ADB_SR, NULL); |
560 | } |
561 | if (via1[IFR] & CB1_INT) { |
562 | via1[IFR] = CB1_INT; |
563 | pmu_interrupt(IRQ_MAC_ADB_CL, NULL); |
564 | } |
565 | local_irq_restore(flags); |
566 | } |
567 | |
568 | static irqreturn_t |
569 | pmu_interrupt(int irq, void *dev_id) |
570 | { |
571 | struct adb_request *req; |
572 | int timeout, bite = 0; /* to prevent compiler warning */ |
573 | |
574 | #if 0 |
575 | printk("pmu_interrupt: irq %d state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n", |
576 | irq, pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending); |
577 | #endif |
578 | |
579 | if (irq == IRQ_MAC_ADB_CL) { /* CB1 interrupt */ |
580 | adb_int_pending = 1; |
581 | } else if (irq == IRQ_MAC_ADB_SR) { /* SR interrupt */ |
582 | if (via2[B] & TACK) { |
583 | printk(KERN_DEBUG "PMU: SR_INT but ack still high! (%x)\n", via2[B]); |
584 | } |
585 | |
586 | /* if reading grab the byte */ |
587 | if ((via1[ACR] & SR_OUT) == 0) bite = via1[SR]; |
588 | |
589 | /* reset TREQ and wait for TACK to go high */ |
590 | via2[B] |= TREQ; |
591 | timeout = 3200; |
592 | while (!(via2[B] & TACK)) { |
593 | if (--timeout < 0) { |
594 | printk(KERN_ERR "PMU not responding (!ack)\n"); |
595 | goto finish; |
596 | } |
597 | udelay(10); |
598 | } |
599 | |
600 | switch (pmu_state) { |
601 | case sending: |
602 | req = current_req; |
603 | if (data_len < 0) { |
604 | data_len = req->nbytes - 1; |
605 | send_byte(data_len); |
606 | break; |
607 | } |
608 | if (data_index <= data_len) { |
609 | send_byte(req->data[data_index++]); |
610 | break; |
611 | } |
612 | req->sent = 1; |
613 | data_len = pmu_data_len[req->data[0]][1]; |
614 | if (data_len == 0) { |
615 | pmu_state = idle; |
616 | current_req = req->next; |
617 | if (req->reply_expected) |
618 | req_awaiting_reply = req; |
619 | else |
620 | pmu_done(req); |
621 | } else { |
622 | pmu_state = reading; |
623 | data_index = 0; |
624 | reply_ptr = req->reply + req->reply_len; |
625 | recv_byte(); |
626 | } |
627 | break; |
628 | |
629 | case intack: |
630 | data_index = 0; |
631 | data_len = -1; |
632 | pmu_state = reading_intr; |
633 | reply_ptr = interrupt_data; |
634 | recv_byte(); |
635 | break; |
636 | |
637 | case reading: |
638 | case reading_intr: |
639 | if (data_len == -1) { |
640 | data_len = bite; |
641 | if (bite > 32) |
642 | printk(KERN_ERR "PMU: bad reply len %d\n", |
643 | bite); |
644 | } else { |
645 | reply_ptr[data_index++] = bite; |
646 | } |
647 | if (data_index < data_len) { |
648 | recv_byte(); |
649 | break; |
650 | } |
651 | |
652 | if (pmu_state == reading_intr) { |
653 | pmu_handle_data(interrupt_data, data_index); |
654 | } else { |
655 | req = current_req; |
656 | current_req = req->next; |
657 | req->reply_len += data_index; |
658 | pmu_done(req); |
659 | } |
660 | pmu_state = idle; |
661 | |
662 | break; |
663 | |
664 | default: |
665 | printk(KERN_ERR "pmu_interrupt: unknown state %d?\n", |
666 | pmu_state); |
667 | } |
668 | } |
669 | finish: |
670 | if (pmu_state == idle) { |
671 | if (adb_int_pending) { |
672 | pmu_state = intack; |
673 | send_byte(PMU_INT_ACK); |
674 | adb_int_pending = 0; |
675 | } else if (current_req) { |
676 | pmu_start(); |
677 | } |
678 | } |
679 | |
680 | #if 0 |
681 | printk("pmu_interrupt: exit state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n", |
682 | pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending); |
683 | #endif |
684 | return IRQ_HANDLED; |
685 | } |
686 | |
687 | static void |
688 | pmu_done(struct adb_request *req) |
689 | { |
690 | req->complete = 1; |
691 | if (req->done) |
692 | (*req->done)(req); |
693 | } |
694 | |
695 | /* Interrupt data could be the result data from an ADB cmd */ |
696 | static void |
697 | pmu_handle_data(unsigned char *data, int len) |
698 | { |
699 | static int show_pmu_ints = 1; |
700 | |
701 | asleep = 0; |
702 | if (len < 1) { |
703 | adb_int_pending = 0; |
704 | return; |
705 | } |
706 | if (data[0] & PMU_INT_ADB) { |
707 | if ((data[0] & PMU_INT_ADB_AUTO) == 0) { |
708 | struct adb_request *req = req_awaiting_reply; |
709 | if (req == 0) { |
710 | printk(KERN_ERR "PMU: extra ADB reply\n"); |
711 | return; |
712 | } |
713 | req_awaiting_reply = NULL; |
714 | if (len <= 2) |
715 | req->reply_len = 0; |
716 | else { |
717 | memcpy(req->reply, data + 1, len - 1); |
718 | req->reply_len = len - 1; |
719 | } |
720 | pmu_done(req); |
721 | } else { |
722 | adb_input(data+1, len-1, 1); |
723 | } |
724 | } else { |
725 | if (data[0] == 0x08 && len == 3) { |
726 | /* sound/brightness buttons pressed */ |
727 | pmu_set_brightness(data[1] >> 3); |
728 | set_volume(data[2]); |
729 | } else if (show_pmu_ints |
730 | && !(data[0] == PMU_INT_TICK && len == 1)) { |
731 | int i; |
732 | printk(KERN_DEBUG "pmu intr"); |
733 | for (i = 0; i < len; ++i) |
734 | printk(" %.2x", data[i]); |
735 | printk("\n"); |
736 | } |
737 | } |
738 | } |
739 | |
740 | static int backlight_level = -1; |
741 | static int backlight_enabled = 0; |
742 | |
743 | #define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1)) |
744 | |
745 | static void |
746 | pmu_enable_backlight(int on) |
747 | { |
748 | struct adb_request req; |
749 | |
750 | if (on) { |
751 | /* first call: get current backlight value */ |
752 | if (backlight_level < 0) { |
753 | switch(pmu_kind) { |
754 | case PMU_68K_V1: |
755 | case PMU_68K_V2: |
756 | pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe); |
757 | while (!req.complete) |
758 | pmu_poll(); |
759 | printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]); |
760 | backlight_level = req.reply[1]; |
761 | break; |
762 | default: |
763 | backlight_enabled = 0; |
764 | return; |
765 | } |
766 | } |
767 | pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, |
768 | LEVEL_TO_BRIGHT(backlight_level)); |
769 | while (!req.complete) |
770 | pmu_poll(); |
771 | } |
772 | pmu_request(&req, NULL, 2, PMU_POWER_CTRL, |
773 | PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); |
774 | while (!req.complete) |
775 | pmu_poll(); |
776 | backlight_enabled = on; |
777 | } |
778 | |
779 | static void |
780 | pmu_set_brightness(int level) |
781 | { |
782 | int bright; |
783 | |
784 | backlight_level = level; |
785 | bright = LEVEL_TO_BRIGHT(level); |
786 | if (!backlight_enabled) |
787 | return; |
788 | if (bright_req_1.complete) |
789 | pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT, |
790 | bright); |
791 | if (bright_req_2.complete) |
792 | pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL, |
793 | PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF)); |
794 | } |
795 | |
796 | void |
797 | pmu_enable_irled(int on) |
798 | { |
799 | struct adb_request req; |
800 | |
801 | pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED | |
802 | (on ? PMU_POW_ON : PMU_POW_OFF)); |
803 | while (!req.complete) |
804 | pmu_poll(); |
805 | } |
806 | |
807 | static void |
808 | set_volume(int level) |
809 | { |
810 | } |
811 | |
812 | int |
813 | pmu_present(void) |
814 | { |
815 | return (pmu_kind != PMU_UNKNOWN); |
816 | } |
817 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9