File: /usr/src/linux/drivers/char/h8.c
1 /*
2 * Hitachi H8/337 Microcontroller driver
3 *
4 * The H8 is used to deal with the power and thermal environment
5 * of a system.
6 *
7 * Fixes:
8 * June 1999, AV added releasing /proc/driver/h8
9 * Feb 2000, Borislav Deianov
10 * changed queues to use list.h instead of lists.h
11 */
12
13 #include <linux/config.h>
14 #include <linux/module.h>
15
16 #include <asm/system.h>
17 #include <asm/segment.h>
18 #include <asm/io.h>
19
20 #include <linux/types.h>
21 #include <linux/stddef.h>
22 #include <linux/timer.h>
23 #include <linux/fcntl.h>
24 #include <linux/linkage.h>
25 #include <linux/stat.h>
26 #include <linux/proc_fs.h>
27 #include <linux/miscdevice.h>
28 #include <linux/list.h>
29 #include <linux/ioport.h>
30 #include <linux/poll.h>
31 #include <linux/init.h>
32 #include <linux/slab.h>
33
34 #define __KERNEL_SYSCALLS__
35 #include <asm/unistd.h>
36
37 #include "h8.h"
38
39 #define DEBUG_H8
40
41 #ifdef DEBUG_H8
42 #define Dprintk printk
43 #else
44 #define Dprintk
45 #endif
46
47 #define XDprintk if(h8_debug==-1)printk
48
49 /*
50 * The h8 device is one of the misc char devices.
51 */
52 #define H8_MINOR_DEV 140
53
54 /*
55 * Forward declarations.
56 */
57 static int h8_init(void);
58 static int h8_display_blank(void);
59 static int h8_display_unblank(void);
60
61 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs);
62
63 static int h8_get_info(char *, char **, off_t, int);
64
65 /*
66 * Support Routines.
67 */
68 static void h8_hw_init(void);
69 static void h8_start_new_cmd(void);
70 static void h8_send_next_cmd_byte(void);
71 static void h8_read_event_status(void);
72 static void h8_sync(void);
73 static void h8_q_cmd(u_char *, int, int);
74 static void h8_cmd_done(h8_cmd_q_t *qp);
75 static int h8_alloc_queues(void);
76
77 static u_long h8_get_cpu_speed(void);
78 static int h8_get_curr_temp(u_char curr_temp[]);
79 static void h8_get_max_temp(void);
80 static void h8_get_upper_therm_thold(void);
81 static void h8_set_upper_therm_thold(int);
82 static int h8_get_ext_status(u_char stat_word[]);
83
84 static int h8_monitor_thread(void *);
85
86 static int h8_manage_therm(void);
87 static void h8_set_cpu_speed(int speed_divisor);
88
89 static void h8_start_monitor_timer(unsigned long secs);
90 static void h8_activate_monitor(unsigned long unused);
91
92 /* in arch/alpha/kernel/lca.c */
93 extern void lca_clock_print(void);
94 extern int lca_get_clock(void);
95 extern void lca_clock_fiddle(int);
96
97 static void h8_set_event_mask(int);
98 static void h8_clear_event_mask(int);
99
100 /*
101 * Driver structures
102 */
103
104 static struct timer_list h8_monitor_timer;
105 static int h8_monitor_timer_active = 0;
106
107 static char driver_version[] = "X0.0";/* no spaces */
108
109 static union intr_buf intrbuf;
110 static int intr_buf_ptr;
111 static union intr_buf xx;
112 static u_char last_temp;
113
114 /*
115 * I/O Macros for register reads and writes.
116 */
117 #define H8_READ(a) inb((a))
118 #define H8_WRITE(d,a) outb((d),(a))
119
120 #define H8_GET_STATUS H8_READ((h8_base) + H8_STATUS_REG_OFF)
121 #define H8_READ_DATA H8_READ((h8_base) + H8_DATA_REG_OFF)
122 #define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
123 #define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
124
125 static unsigned int h8_base = H8_BASE_ADDR;
126 static unsigned int h8_irq = H8_IRQ;
127 static unsigned int h8_state = H8_IDLE;
128 static unsigned int h8_index = -1;
129 static unsigned int h8_enabled = 0;
130
131 static LIST_HEAD(h8_actq);
132 static LIST_HEAD(h8_cmdq);
133 static LIST_HEAD(h8_freeq);
134
135 /*
136 * Globals used in thermal control of Alphabook1.
137 */
138 static int cpu_speed_divisor = -1;
139 static int h8_event_mask = 0;
140 static DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait);
141 static unsigned int h8_command_mask = 0;
142 static int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
143 static int h8_uthermal_window = UTH_HYSTERESIS;
144 static int h8_debug = 0xfffffdfc;
145 static int h8_ldamp = MHZ_115;
146 static int h8_udamp = MHZ_57;
147 static u_char h8_current_temp = 0;
148 static u_char h8_system_temp = 0;
149 static int h8_sync_channel = 0;
150 static DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait);
151 static int h8_init_performed;
152
153 /* CPU speeds and clock divisor values */
154 static int speed_tab[6] = {230, 153, 115, 57, 28, 14};
155
156 /*
157 * H8 interrupt handler
158 */
159 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
160 {
161 u_char stat_reg, data_reg;
162 h8_cmd_q_t *qp = list_entry(h8_actq.next, h8_cmd_q_t, link);
163
164 stat_reg = H8_GET_STATUS;
165 data_reg = H8_READ_DATA;
166
167 XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state, stat_reg, data_reg);
168
169 switch (h8_state) {
170 /* Response to an asynchronous event. */
171 case H8_IDLE: { /* H8_IDLE */
172 if (stat_reg & H8_OFULL) {
173 if (data_reg == H8_INTR) {
174 h8_state = H8_INTR_MODE;
175 /* Executing a command to determine what happened. */
176 WRITE_CMD(H8_RD_EVENT_STATUS);
177 intr_buf_ptr = 1;
178 WRITE_CMD(H8_RD_EVENT_STATUS);
179 } else {
180 Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
181 stat_reg, data_reg);
182 }
183 } else {
184 Dprintk("h8_intr: bogus interrupt\n");
185 }
186 break;
187 }
188 case H8_INTR_MODE: { /* H8_INTR_MODE */
189 XDprintk("H8 intr/intr_mode\n");
190 if (data_reg == H8_BYTE_LEVEL_ACK) {
191 return;
192 } else if (data_reg == H8_CMD_ACK) {
193 return;
194 } else {
195 intrbuf.byte[intr_buf_ptr] = data_reg;
196 if(!intr_buf_ptr) {
197 h8_state = H8_IDLE;
198 h8_read_event_status();
199 }
200 intr_buf_ptr--;
201 }
202 break;
203 }
204 /* Placed in this state by h8_start_new_cmd(). */
205 case H8_XMIT: { /* H8_XMIT */
206 XDprintk("H8 intr/xmit\n");
207 /* If a byte level acknowledgement has been received */
208 if (data_reg == H8_BYTE_LEVEL_ACK) {
209 XDprintk("H8 intr/xmit BYTE ACK\n");
210 qp->nacks++;
211 if (qp->nacks > qp->ncmd)
212 if(h8_debug & 0x1)
213 Dprintk("h8intr: bogus # of acks!\n");
214 /*
215 * If the number of bytes sent is less than the total
216 * number of bytes in the command.
217 */
218 if (qp->cnt < qp->ncmd) {
219 h8_send_next_cmd_byte();
220 }
221 return;
222 /* If the complete command has produced an acknowledgement. */
223 } else if (data_reg == H8_CMD_ACK) {
224 XDprintk("H8 intr/xmit CMD ACK\n");
225 /* If there are response bytes */
226 if (qp->nrsp)
227 h8_state = H8_RCV;
228 else
229 h8_state = H8_IDLE;
230 qp->cnt = 0;
231 return;
232 /* Error, need to start over with a clean slate. */
233 } else if (data_reg == H8_NACK) {
234 XDprintk("h8_intr: NACK received restarting command\n");
235 qp->nacks = 0;
236 qp->cnt = 0;
237 h8_state = H8_IDLE;
238 WRITE_CMD(H8_SYNC);
239 return;
240 } else {
241 Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg);
242 return;
243 }
244 break;
245 }
246 case H8_RESYNC: { /* H8_RESYNC */
247 XDprintk("H8 intr/resync\n");
248 if (data_reg == H8_BYTE_LEVEL_ACK) {
249 return;
250 } else if (data_reg == H8_SYNC_BYTE) {
251 h8_state = H8_IDLE;
252 if (!list_empty(&h8_actq))
253 h8_send_next_cmd_byte();
254 } else {
255 Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
256 return;
257 }
258 break;
259 }
260 case H8_RCV: { /* H8_RCV */
261 XDprintk("H8 intr/rcv\n");
262 if (qp->cnt < qp->nrsp) {
263 qp->rcvbuf[qp->cnt] = data_reg;
264 qp->cnt++;
265 /* If command reception finished. */
266 if (qp->cnt == qp->nrsp) {
267 h8_state = H8_IDLE;
268 list_del(&qp->link);
269 h8_cmd_done (qp);
270 /* More commands to send over? */
271 if (!list_empty(&h8_cmdq))
272 h8_start_new_cmd();
273 }
274 return;
275 } else {
276 Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp->cmdbuf[0]);
277 }
278 break;
279 }
280 default: /* default */
281 Dprintk("H8 intr/unknown\n");
282 break;
283 }
284 return;
285 }
286
287 static void __exit h8_cleanup (void)
288 {
289 remove_proc_entry("driver/h8", NULL);
290 release_region(h8_base, 8);
291 free_irq(h8_irq, NULL);
292 }
293
294 static int __init h8_init(void)
295 {
296 if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
297 {
298 printk(KERN_ERR "H8: error: IRQ %d is not free\n", h8_irq);
299 return -EIO;
300 }
301 printk(KERN_INFO "H8 at 0x%x IRQ %d\n", h8_base, h8_irq);
302
303 create_proc_info_entry("driver/h8", 0, NULL, h8_get_info);
304
305 request_region(h8_base, 8, "h8");
306
307 h8_alloc_queues();
308
309 h8_hw_init();
310
311 kernel_thread(h8_monitor_thread, NULL, 0);
312
313 return 0;
314 }
315
316 module_init(h8_init);
317 module_exit(h8_cleanup);
318
319 static void __init h8_hw_init(void)
320 {
321 u_char buf[H8_MAX_CMD_SIZE];
322
323 /* set CPU speed to max for booting */
324 h8_set_cpu_speed(MHZ_230);
325
326 /*
327 * Initialize the H8
328 */
329 h8_sync(); /* activate interrupts */
330
331 /* To clear conditions left by console */
332 h8_read_event_status();
333
334 /* Perform a conditioning read */
335 buf[0] = H8_DEVICE_CONTROL;
336 buf[1] = 0xff;
337 buf[2] = 0x0;
338 h8_q_cmd(buf, 3, 1);
339
340 /* Turn on built-in and external mice, capture power switch */
341 buf[0] = H8_DEVICE_CONTROL;
342 buf[1] = 0x0;
343 buf[2] = H8_ENAB_INT_PTR | H8_ENAB_EXT_PTR |
344 /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND;
345 h8_q_cmd(buf, 3, 1);
346
347 h8_enabled = 1;
348 return;
349 }
350
351 static int h8_get_info(char *buf, char **start, off_t fpos, int length)
352 {
353 #ifdef CONFIG_PROC_FS
354 char *p;
355
356 if (!h8_enabled)
357 return 0;
358 p = buf;
359
360
361 /*
362 0) Linux driver version (this will change if format changes)
363 1)
364 2)
365 3)
366 4)
367 */
368
369 p += sprintf(p, "%s \n",
370 driver_version
371 );
372
373 return p - buf;
374 #else
375 return 0;
376 #endif
377 }
378
379 /* Called from console driver -- must make sure h8_enabled. */
380 static int h8_display_blank(void)
381 {
382 #ifdef CONFIG_H8_DISPLAY_BLANK
383 int error;
384
385 if (!h8_enabled)
386 return 0;
387 error = h8_set_display_power_state(H8_STATE_STANDBY);
388 if (error == H8_SUCCESS)
389 return 1;
390 h8_error("set display standby", error);
391 #endif
392 return 0;
393 }
394
395 /* Called from console driver -- must make sure h8_enabled. */
396 static int h8_display_unblank(void)
397 {
398 #ifdef CONFIG_H8_DISPLAY_BLANK
399 int error;
400
401 if (!h8_enabled)
402 return 0;
403 error = h8_set_display_power_state(H8_STATE_READY);
404 if (error == H8_SUCCESS)
405 return 1;
406 h8_error("set display ready", error);
407 #endif
408 return 0;
409 }
410
411 static int h8_alloc_queues(void)
412 {
413 h8_cmd_q_t *qp;
414 unsigned long flags;
415 int i;
416
417 qp = (h8_cmd_q_t *)kmalloc((sizeof (h8_cmd_q_t) * H8_Q_ALLOC_AMOUNT),
418 GFP_KERNEL);
419
420 if (!qp) {
421 printk(KERN_ERR "H8: could not allocate memory for command queue\n");
422 return(0);
423 }
424 /* add to the free queue */
425 save_flags(flags); cli();
426 for (i = 0; i < H8_Q_ALLOC_AMOUNT; i++) {
427 /* place each at front of freeq */
428 list_add(&qp[i].link, &h8_freeq);
429 }
430 restore_flags(flags);
431 return (1);
432 }
433
434 /*
435 * Basic means by which commands are sent to the H8.
436 */
437 void
438 h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
439 {
440 h8_cmd_q_t *qp;
441 unsigned long flags;
442 int i;
443
444 /* get cmd buf */
445 save_flags(flags); cli();
446 while (list_empty(&h8_freeq)) {
447 Dprintk("H8: need to allocate more cmd buffers\n");
448 restore_flags(flags);
449 h8_alloc_queues();
450 save_flags(flags); cli();
451 }
452 /* get first element from queue */
453 qp = list_entry(h8_freeq.next, h8_cmd_q_t, link);
454 list_del(&qp->link);
455
456 restore_flags(flags);
457
458 /* fill it in */
459 for (i = 0; i < cmd_size; i++)
460 qp->cmdbuf[i] = cmd[i];
461 qp->ncmd = cmd_size;
462 qp->nrsp = resp_size;
463
464 /* queue it at the end of the cmd queue */
465 save_flags(flags); cli();
466
467 /* XXX this actually puts it at the start of cmd queue, bug? */
468 list_add(&qp->link, &h8_cmdq);
469
470 restore_flags(flags);
471
472 h8_start_new_cmd();
473 }
474
475 void
476 h8_start_new_cmd(void)
477 {
478 unsigned long flags;
479 h8_cmd_q_t *qp;
480
481 save_flags(flags); cli();
482 if (h8_state != H8_IDLE) {
483 if (h8_debug & 0x1)
484 Dprintk("h8_start_new_cmd: not idle\n");
485 restore_flags(flags);
486 return;
487 }
488
489 if (!list_empty(&h8_actq)) {
490 Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
491 restore_flags(flags);
492 return;
493 }
494
495 if (list_empty(&h8_cmdq)) {
496 Dprintk("h8_start_new_cmd: no command to dequeue\n");
497 restore_flags(flags);
498 return;
499 }
500 /*
501 * Take first command off of the command queue and put
502 * it on the active queue.
503 */
504 qp = list_entry(h8_cmdq.next, h8_cmd_q_t, link);
505 list_del(&qp->link);
506 /* XXX should this go to the end of the active queue? */
507 list_add(&qp->link, &h8_actq);
508 h8_state = H8_XMIT;
509 if (h8_debug & 0x1)
510 Dprintk("h8_start_new_cmd: Starting a command\n");
511
512 qp->cnt = 1;
513 WRITE_CMD(qp->cmdbuf[0]); /* Kick it off */
514
515 restore_flags(flags);
516 return;
517 }
518
519 void
520 h8_send_next_cmd_byte(void)
521 {
522 h8_cmd_q_t *qp = list_entry(h8_actq.next, h8_cmd_q_t, link);
523 int cnt;
524
525 cnt = qp->cnt;
526 qp->cnt++;
527
528 if (h8_debug & 0x1)
529 Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n",
530 cnt, qp->cmdbuf[cnt]);
531
532 if (cnt) {
533 WRITE_DATA(qp->cmdbuf[cnt]);
534 } else {
535 WRITE_CMD(qp->cmdbuf[cnt]);
536 }
537 return;
538 }
539
540 /*
541 * Synchronize H8 communications channel for command transmission.
542 */
543 void
544 h8_sync(void)
545 {
546 u_char buf[H8_MAX_CMD_SIZE];
547
548 buf[0] = H8_SYNC;
549 buf[1] = H8_SYNC_BYTE;
550 h8_q_cmd(buf, 2, 1);
551 }
552
553 /*
554 * Responds to external interrupt. Reads event status word and
555 * decodes type of interrupt.
556 */
557 void
558 h8_read_event_status(void)
559 {
560
561 if(h8_debug & 0x200)
562 printk(KERN_DEBUG "h8_read_event_status: value 0x%x\n", intrbuf.word);
563
564 /*
565 * Power related items
566 */
567 if (intrbuf.word & H8_DC_CHANGE) {
568 if(h8_debug & 0x4)
569 printk(KERN_DEBUG "h8_read_event_status: DC_CHANGE\n");
570 /* see if dc added or removed, set batt/dc flag, send event */
571
572 h8_set_event_mask(H8_MANAGE_BATTERY);
573 wake_up(&h8_monitor_wait);
574 }
575
576 if (intrbuf.word & H8_POWER_BUTTON) {
577 printk(KERN_CRIT "Power switch pressed - please wait - preparing to power
578 off\n");
579 h8_set_event_mask(H8_POWER_BUTTON);
580 wake_up(&h8_monitor_wait);
581 }
582
583 /*
584 * Thermal related items
585 */
586 if (intrbuf.word & H8_THERMAL_THRESHOLD) {
587 if(h8_debug & 0x4)
588 printk(KERN_DEBUG "h8_read_event_status: THERMAL_THRESHOLD\n");
589 h8_set_event_mask(H8_MANAGE_UTHERM);
590 wake_up(&h8_monitor_wait);
591 }
592
593 /*
594 * nops -for now
595 */
596 if (intrbuf.word & H8_DOCKING_STATION_STATUS) {
597 if(h8_debug & 0x4)
598 printk(KERN_DEBUG "h8_read_event_status: DOCKING_STATION_STATUS\n");
599 /* read_ext_status */
600 }
601 if (intrbuf.word & H8_EXT_BATT_STATUS) {
602 if(h8_debug & 0x4)
603 printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_STATUS\n");
604
605 }
606 if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) {
607 if(h8_debug & 0x4)
608 printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
609
610 }
611 if (intrbuf.word & H8_BATT_CHANGE_OVER) {
612 if(h8_debug & 0x4)
613 printk(KERN_DEBUG "h8_read_event_status: BATT_CHANGE_OVER\n");
614
615 }
616 if (intrbuf.word & H8_WATCHDOG) {
617 if(h8_debug & 0x4)
618 printk(KERN_DEBUG "h8_read_event_status: WATCHDOG\n");
619 /* nop */
620 }
621 if (intrbuf.word & H8_SHUTDOWN) {
622 if(h8_debug & 0x4)
623 printk(KERN_DEBUG "h8_read_event_status: SHUTDOWN\n");
624 /* nop */
625 }
626 if (intrbuf.word & H8_KEYBOARD) {
627 if(h8_debug & 0x4)
628 printk(KERN_DEBUG "h8_read_event_status: KEYBOARD\n");
629 /* nop */
630 }
631 if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) {
632 if(h8_debug & 0x4)
633 printk(KERN_DEBUG "h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
634 /* read_ext_status*/
635 }
636 if (intrbuf.word & H8_INT_BATT_LOW) {
637 if(h8_debug & 0x4)
638 printk(KERN_DEBUG "h8_read_event_status: INT_BATT_LOW\n"); post
639 /* event, warn user */
640 }
641 if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) {
642 if(h8_debug & 0x4)
643 printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_STATE\n");
644 /* nop - happens often */
645 }
646 if (intrbuf.word & H8_INT_BATT_STATUS) {
647 if(h8_debug & 0x4)
648 printk(KERN_DEBUG "h8_read_event_status: INT_BATT_STATUS\n");
649
650 }
651 if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) {
652 if(h8_debug & 0x4)
653 printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
654 /* nop - happens often */
655 }
656 if (intrbuf.word & H8_EXT_BATT_LOW) {
657 if(h8_debug & 0x4)
658 printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_LOW\n");
659 /*if no internal, post event, warn user */
660 /* else nop */
661 }
662
663 return;
664 }
665
666 /*
667 * Function called when H8 has performed requested command.
668 */
669 static void
670 h8_cmd_done(h8_cmd_q_t *qp)
671 {
672
673 /* what to do */
674 switch (qp->cmdbuf[0]) {
675 case H8_SYNC:
676 if (h8_debug & 0x40000)
677 printk(KERN_DEBUG "H8: Sync command done - byte returned was 0x%x\n",
678 qp->rcvbuf[0]);
679 list_add(&qp->link, &h8_freeq);
680 break;
681
682 case H8_RD_SN:
683 case H8_RD_ENET_ADDR:
684 printk(KERN_DEBUG "H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n",
685 qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2],
686 qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]);
687 list_add(&qp->link, &h8_freeq);
688 break;
689
690 case H8_RD_HW_VER:
691 case H8_RD_MIC_VER:
692 case H8_RD_MAX_TEMP:
693 printk(KERN_DEBUG "H8: Max recorded CPU temp %d, Sys temp %d\n",
694 qp->rcvbuf[0], qp->rcvbuf[1]);
695 list_add(&qp->link, &h8_freeq);
696 break;
697
698 case H8_RD_MIN_TEMP:
699 printk(KERN_DEBUG "H8: Min recorded CPU temp %d, Sys temp %d\n",
700 qp->rcvbuf[0], qp->rcvbuf[1]);
701 list_add(&qp->link, &h8_freeq);
702 break;
703
704 case H8_RD_CURR_TEMP:
705 h8_sync_channel |= H8_RD_CURR_TEMP;
706 xx.byte[0] = qp->rcvbuf[0];
707 xx.byte[1] = qp->rcvbuf[1];
708 wake_up(&h8_sync_wait);
709 list_add(&qp->link, &h8_freeq);
710 break;
711
712 case H8_RD_SYS_VARIENT:
713 case H8_RD_PWR_ON_CYCLES:
714 printk(KERN_DEBUG " H8: RD_PWR_ON_CYCLES command done\n");
715 break;
716
717 case H8_RD_PWR_ON_SECS:
718 printk(KERN_DEBUG "H8: RD_PWR_ON_SECS command done\n");
719 break;
720
721 case H8_RD_RESET_STATUS:
722 case H8_RD_PWR_DN_STATUS:
723 case H8_RD_EVENT_STATUS:
724 case H8_RD_ROM_CKSM:
725 case H8_RD_EXT_STATUS:
726 xx.byte[1] = qp->rcvbuf[0];
727 xx.byte[0] = qp->rcvbuf[1];
728 h8_sync_channel |= H8_GET_EXT_STATUS;
729 wake_up(&h8_sync_wait);
730 list_add(&qp->link, &h8_freeq);
731 break;
732
733 case H8_RD_USER_CFG:
734 case H8_RD_INT_BATT_VOLT:
735 case H8_RD_DC_INPUT_VOLT:
736 case H8_RD_HORIZ_PTR_VOLT:
737 case H8_RD_VERT_PTR_VOLT:
738 case H8_RD_EEPROM_STATUS:
739 case H8_RD_ERR_STATUS:
740 case H8_RD_NEW_BUSY_SPEED:
741 case H8_RD_CONFIG_INTERFACE:
742 case H8_RD_INT_BATT_STATUS:
743 printk(KERN_DEBUG "H8: Read int batt status cmd done - returned was %x %x %x\n",
744 qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]);
745 list_add(&qp->link, &h8_freeq);
746 break;
747
748 case H8_RD_EXT_BATT_STATUS:
749 case H8_RD_PWR_UP_STATUS:
750 case H8_RD_EVENT_STATUS_MASK:
751 case H8_CTL_EMU_BITPORT:
752 case H8_DEVICE_CONTROL:
753 if(h8_debug & 0x20000) {
754 printk(KERN_DEBUG "H8: Device control cmd done - byte returned was 0x%x\n",
755 qp->rcvbuf[0]);
756 }
757 list_add(&qp->link, &h8_freeq);
758 break;
759
760 case H8_CTL_TFT_BRT_DC:
761 case H8_CTL_WATCHDOG:
762 case H8_CTL_MIC_PROT:
763 case H8_CTL_INT_BATT_CHG:
764 case H8_CTL_EXT_BATT_CHG:
765 case H8_CTL_MARK_SPACE:
766 case H8_CTL_MOUSE_SENSITIVITY:
767 case H8_CTL_DIAG_MODE:
768 case H8_CTL_IDLE_AND_BUSY_SPDS:
769 printk(KERN_DEBUG "H8: Idle and busy speed command done\n");
770 break;
771
772 case H8_CTL_TFT_BRT_BATT:
773 case H8_CTL_UPPER_TEMP:
774 if(h8_debug & 0x10) {
775 XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n",
776 qp->rcvbuf[0]);
777 }
778 list_add(&qp->link, &h8_freeq);
779 break;
780
781 case H8_CTL_LOWER_TEMP:
782 case H8_CTL_TEMP_CUTOUT:
783 case H8_CTL_WAKEUP:
784 case H8_CTL_CHG_THRESHOLD:
785 case H8_CTL_TURBO_MODE:
786 case H8_SET_DIAG_STATUS:
787 case H8_SOFTWARE_RESET:
788 case H8_RECAL_PTR:
789 case H8_SET_INT_BATT_PERCENT:
790 case H8_WRT_CFG_INTERFACE_REG:
791 case H8_WRT_EVENT_STATUS_MASK:
792 case H8_ENTER_POST_MODE:
793 case H8_EXIT_POST_MODE:
794 case H8_RD_EEPROM:
795 case H8_WRT_EEPROM:
796 case H8_WRT_TO_STATUS_DISP:
797 printk("H8: Write IO status display command done\n");
798 break;
799
800 case H8_DEFINE_SPC_CHAR:
801 case H8_DEFINE_TABLE_STRING_ENTRY:
802 case H8_PERFORM_EMU_CMD:
803 case H8_EMU_RD_REG:
804 case H8_EMU_WRT_REG:
805 case H8_EMU_RD_RAM:
806 case H8_EMU_WRT_RAM:
807 case H8_BQ_RD_REG:
808 case H8_BQ_WRT_REG:
809 case H8_PWR_OFF:
810 printk (KERN_DEBUG "H8: misc command completed\n");
811 break;
812 }
813 return;
814 }
815
816 /*
817 * Retrieve the current CPU temperature and case temperature. Provides
818 * the feedback for the thermal control algorithm. Synchcronized via
819 * sleep() for priority so that no other actions in the process will take
820 * place before the data becomes available.
821 */
822 int
823 h8_get_curr_temp(u_char curr_temp[])
824 {
825 u_char buf[H8_MAX_CMD_SIZE];
826 unsigned long flags;
827
828 memset(buf, 0, H8_MAX_CMD_SIZE);
829 buf[0] = H8_RD_CURR_TEMP;
830
831 h8_q_cmd(buf, 1, 2);
832
833 save_flags(flags); cli();
834
835 while((h8_sync_channel & H8_RD_CURR_TEMP) == 0)
836 sleep_on(&h8_sync_wait);
837
838 restore_flags(flags);
839
840 h8_sync_channel &= ~H8_RD_CURR_TEMP;
841 curr_temp[0] = xx.byte[0];
842 curr_temp[1] = xx.byte[1];
843 xx.word = 0;
844
845 if(h8_debug & 0x8)
846 printk("H8: curr CPU temp %d, Sys temp %d\n",
847 curr_temp[0], curr_temp[1]);
848 return 0;
849 }
850
851 static void
852 h8_get_max_temp(void)
853 {
854 u_char buf[H8_MAX_CMD_SIZE];
855
856 buf[0] = H8_RD_MAX_TEMP;
857 h8_q_cmd(buf, 1, 2);
858 }
859
860 /*
861 * Assigns an upper limit to the value of the H8 thermal interrupt.
862 * As an example setting a value of 115 F here will cause the
863 * interrupt to trigger when the CPU temperature reaches 115 F.
864 */
865 static void
866 h8_set_upper_therm_thold(int thold)
867 {
868 u_char buf[H8_MAX_CMD_SIZE];
869
870 /* write 0 to reinitialize interrupt */
871 buf[0] = H8_CTL_UPPER_TEMP;
872 buf[1] = 0x0;
873 buf[2] = 0x0;
874 h8_q_cmd(buf, 3, 1);
875
876 /* Do it for real */
877 buf[0] = H8_CTL_UPPER_TEMP;
878 buf[1] = 0x0;
879 buf[2] = thold;
880 h8_q_cmd(buf, 3, 1);
881 }
882
883 static void
884 h8_get_upper_therm_thold(void)
885 {
886 u_char buf[H8_MAX_CMD_SIZE];
887
888 buf[0] = H8_CTL_UPPER_TEMP;
889 buf[1] = 0xff;
890 buf[2] = 0;
891 h8_q_cmd(buf, 3, 1);
892 }
893
894 /*
895 * The external status word contains information on keyboard controller,
896 * power button, changes in external batt status, change in DC state,
897 * docking station, etc. General purpose querying use.
898 */
899 int
900 h8_get_ext_status(u_char stat_word[])
901 {
902 u_char buf[H8_MAX_CMD_SIZE];
903 unsigned long flags;
904
905 memset(buf, 0, H8_MAX_CMD_SIZE);
906 buf[0] = H8_RD_EXT_STATUS;
907
908 h8_q_cmd(buf, 1, 2);
909
910 save_flags(flags); cli();
911
912 while((h8_sync_channel & H8_GET_EXT_STATUS) == 0)
913 sleep_on(&h8_sync_wait);
914
915 restore_flags(flags);
916
917 h8_sync_channel &= ~H8_GET_EXT_STATUS;
918 stat_word[0] = xx.byte[0];
919 stat_word[1] = xx.byte[1];
920 xx.word = 0;
921
922 if(h8_debug & 0x8)
923 printk("H8: curr ext status %x, %x\n",
924 stat_word[0], stat_word[1]);
925
926 return 0;
927 }
928
929 /*
930 * Thread attached to task 0 manages thermal/physcial state of Alphabook.
931 * When a condition is detected by the interrupt service routine, the
932 * isr does a wakeup() on h8_monitor_wait. The mask value is then
933 * screened for the appropriate action.
934 */
935
936 int
937 h8_monitor_thread(void * unused)
938 {
939 u_char curr_temp[2];
940
941 /*
942 * Need a logic based safety valve here. During boot when this thread is
943 * started and the thermal interrupt is not yet initialized this logic
944 * checks the temperature and acts accordingly. When this path is acted
945 * upon system boot is painfully slow, however, the priority associated
946 * with overheating is high enough to warrant this action.
947 */
948 h8_get_curr_temp(curr_temp);
949
950 printk(KERN_INFO "H8: Initial CPU temp: %d\n", curr_temp[0]);
951
952 if(curr_temp[0] >= h8_uthermal_threshold) {
953 h8_set_event_mask(H8_MANAGE_UTHERM);
954 h8_manage_therm();
955 } else {
956 /*
957 * Arm the upper thermal limit of the H8 so that any temp in
958 * excess will trigger the thermal control mechanism.
959 */
960 h8_set_upper_therm_thold(h8_uthermal_threshold);
961 }
962
963 for(;;) {
964 sleep_on(&h8_monitor_wait);
965
966 if(h8_debug & 0x2)
967 printk(KERN_DEBUG "h8_monitor_thread awakened, mask:%x\n",
968 h8_event_mask);
969
970 if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) {
971 h8_manage_therm();
972 }
973
974 #if 0
975 if (h8_event_mask & H8_POWER_BUTTON) {
976 h8_system_down();
977 }
978
979 /*
980 * If an external DC supply is removed or added make
981 * appropriate CPU speed adjustments.
982 */
983 if (h8_event_mask & H8_MANAGE_BATTERY) {
984 h8_run_level_3_manage(H8_RUN);
985 h8_clear_event_mask(H8_MANAGE_BATTERY);
986 }
987 #endif
988 }
989 }
990
991 /*
992 * Function implements the following policy. When the machine is booted
993 * the system is set to run at full clock speed. When the upper thermal
994 * threshold is reached as a result of full clock a damping factor is
995 * applied to cool off the cpu. The default value is one quarter clock
996 * (57 Mhz). When as a result of this cooling a temperature lower by
997 * hmc_uthermal_window is reached, the machine is reset to a higher
998 * speed, one half clock (115 Mhz). One half clock is maintained until
999 * the upper thermal threshold is again reached restarting the cycle.
1000 */
1001
1002 int
1003 h8_manage_therm(void)
1004 {
1005 u_char curr_temp[2];
1006
1007 if(h8_event_mask & H8_MANAGE_UTHERM) {
1008 /* Upper thermal interrupt received, need to cool down. */
1009 if(h8_debug & 0x10)
1010 printk(KERN_WARNING "H8: Thermal threshold %d F reached\n",
1011 h8_uthermal_threshold);
1012 h8_set_cpu_speed(h8_udamp);
1013 h8_clear_event_mask(H8_MANAGE_UTHERM);
1014 h8_set_event_mask(H8_MANAGE_LTHERM);
1015 /* Check again in 30 seconds for CPU temperature */
1016 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
1017 } else if (h8_event_mask & H8_MANAGE_LTHERM) {
1018 /* See how cool the system has become as a result
1019 of the reduction in speed. */
1020 h8_get_curr_temp(curr_temp);
1021 last_temp = curr_temp[0];
1022 if (curr_temp[0] < (h8_uthermal_threshold - h8_uthermal_window))
1023 {
1024 /* System cooling has progressed to a point
1025 that the CPU may be sped up. */
1026 h8_set_upper_therm_thold(h8_uthermal_threshold);
1027 h8_set_cpu_speed(h8_ldamp); /* adjustable */
1028 if(h8_debug & 0x10)
1029 printk(KERN_WARNING "H8: CPU cool, applying cpu_divisor: %d \n",
1030 h8_ldamp);
1031 h8_clear_event_mask(H8_MANAGE_LTHERM);
1032 }
1033 else /* Not cool enough yet, check again in 30 seconds. */
1034 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
1035 } else {
1036
1037 }
1038 return 0;
1039 }
1040
1041 /*
1042 * Function conditions the value of global_rpb_counter before
1043 * calling the primitive which causes the actual speed change.
1044 */
1045 void
1046 h8_set_cpu_speed(int speed_divisor)
1047 {
1048
1049 #ifdef NOT_YET
1050 /*
1051 * global_rpb_counter is consumed by alpha_delay() in determining just
1052 * how much time to delay. It is necessary that the number of microseconds
1053 * in DELAY(n) be kept consistent over a variety of CPU clock speeds.
1054 * To that end global_rpb_counter is here adjusted.
1055 */
1056
1057 switch (speed_divisor) {
1058 case 0:
1059 global_rpb_counter = rpb->rpb_counter * 2L;
1060 break;
1061 case 1:
1062 global_rpb_counter = rpb->rpb_counter * 4L / 3L ;
1063 break;
1064 case 3:
1065 global_rpb_counter = rpb->rpb_counter / 2L;
1066 break;
1067 case 4:
1068 global_rpb_counter = rpb->rpb_counter / 4L;
1069 break;
1070 case 5:
1071 global_rpb_counter = rpb->rpb_counter / 8L;
1072 break;
1073 /*
1074 * This case most commonly needed for cpu_speed_divisor
1075 * of 2 which is the value assigned by the firmware.
1076 */
1077 default:
1078 global_rpb_counter = rpb->rpb_counter;
1079 break;
1080 }
1081 #endif /* NOT_YET */
1082
1083 if(h8_debug & 0x8)
1084 printk(KERN_DEBUG "H8: Setting CPU speed to %d MHz\n",
1085 speed_tab[speed_divisor]);
1086
1087 /* Make the actual speed change */
1088 lca_clock_fiddle(speed_divisor);
1089 }
1090
1091 /*
1092 * Gets value stored in rpb representing CPU clock speed and adjusts this
1093 * value based on the current clock speed divisor.
1094 */
1095 u_long
1096 h8_get_cpu_speed(void)
1097 {
1098 u_long speed = 0;
1099 u_long counter;
1100
1101 #ifdef NOT_YET
1102 counter = rpb->rpb_counter / 1000000L;
1103
1104 switch (alphabook_get_clock()) {
1105 case 0:
1106 speed = counter * 2L;
1107 break;
1108 case 1:
1109 speed = counter * 4L / 3L ;
1110 break;
1111 case 2:
1112 speed = counter;
1113 break;
1114 case 3:
1115 speed = counter / 2L;
1116 break;
1117 case 4:
1118 speed = counter / 4L;
1119 break;
1120 case 5:
1121 speed = counter / 8L;
1122 break;
1123 default:
1124 break;
1125 }
1126 if(h8_debug & 0x8)
1127 printk(KERN_DEBUG "H8: CPU speed current setting: %d MHz\n", speed);
1128 #endif /* NOT_YET */
1129 return speed;
1130 }
1131
1132 static void
1133 h8_activate_monitor(unsigned long unused)
1134 {
1135 unsigned long flags;
1136
1137 save_flags(flags); cli();
1138 h8_monitor_timer_active = 0;
1139 restore_flags(flags);
1140
1141 wake_up(&h8_monitor_wait);
1142 }
1143
1144 static void
1145 h8_start_monitor_timer(unsigned long secs)
1146 {
1147 unsigned long flags;
1148
1149 if (h8_monitor_timer_active)
1150 return;
1151
1152 save_flags(flags); cli();
1153 h8_monitor_timer_active = 1;
1154 restore_flags(flags);
1155
1156 init_timer(&h8_monitor_timer);
1157 h8_monitor_timer.function = h8_activate_monitor;
1158 h8_monitor_timer.expires = secs * HZ + jiffies;
1159 add_timer(&h8_monitor_timer);
1160 }
1161
1162 static void h8_set_event_mask(int mask)
1163 {
1164 unsigned long flags;
1165
1166 save_flags(flags); cli();
1167 h8_event_mask |= mask;
1168 restore_flags(flags);
1169 }
1170
1171 static void h8_clear_event_mask(int mask)
1172 {
1173 unsigned long flags;
1174
1175 save_flags(flags); cli();
1176 h8_event_mask &= (~mask);
1177 restore_flags(flags);
1178 }
1179
1180 MODULE_LICENSE("GPL");
1181 EXPORT_NO_SYMBOLS;
1182