Import 2.1.118
[davej-history.git] / drivers / char / h8.c
blobc76aeb79b8f3d5eac6c752cb49ec9d2b155cce8d
1 /*
2 * Hitachi H8/337 Microcontroller driver
4 * The H8 is used to deal with the power and thermal environment
5 * of a system.
6 */
8 #include <linux/config.h>
9 #include <linux/module.h>
11 #include <asm/system.h>
12 #include <asm/segment.h>
13 #include <asm/io.h>
15 #include <linux/types.h>
16 #include <linux/stddef.h>
17 #include <linux/timer.h>
18 #include <linux/fcntl.h>
19 #include <linux/malloc.h>
20 #include <linux/linkage.h>
21 #ifdef CONFIG_PROC_FS
22 #include <linux/stat.h>
23 #include <linux/proc_fs.h>
24 #endif
25 #include <linux/miscdevice.h>
26 #include <linux/lists.h>
27 #include <linux/ioport.h>
28 #include <linux/poll.h>
30 #define __KERNEL_SYSCALLS__
31 #include <asm/unistd.h>
33 #include "h8.h"
35 #define DEBUG_H8
37 #ifdef DEBUG_H8
38 #define Dprintk printk
39 #else
40 #define Dprintk
41 #endif
43 #define XDprintk if(h8_debug==-1)printk
46 * The h8 device is one of the misc char devices.
48 #define H8_MINOR_DEV 140
51 * Forward declarations.
53 int h8_init(void);
54 int h8_display_blank(void);
55 int h8_display_unblank(void);
57 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs);
59 #ifdef CONFIG_PROC_FS
60 static int h8_get_info(char *, char **, off_t, int, int);
61 #endif
64 * Support Routines.
66 static void h8_hw_init(void);
67 static void h8_start_new_cmd(void);
68 static void h8_send_next_cmd_byte(void);
69 static void h8_read_event_status(void);
70 static void h8_sync(void);
71 static void h8_q_cmd(u_char *, int, int);
72 static void h8_cmd_done(h8_cmd_q_t *qp);
73 static int h8_alloc_queues(void);
75 static u_long h8_get_cpu_speed(void);
76 static int h8_get_curr_temp(u_char curr_temp[]);
77 static void h8_get_max_temp(void);
78 static void h8_get_upper_therm_thold(void);
79 static void h8_set_upper_therm_thold(int);
80 static int h8_get_ext_status(u_char stat_word[]);
82 static int h8_monitor_thread(void *);
84 static int h8_manage_therm(void);
85 static void h8_set_cpu_speed(int speed_divisor);
87 static void h8_start_monitor_timer(unsigned long secs);
88 static void h8_activate_monitor(unsigned long unused);
90 /* in arch/alpha/kernel/lca.c */
91 extern void lca_clock_print(void);
92 extern int lca_get_clock(void);
93 extern void lca_clock_fiddle(int);
95 static void h8_set_event_mask(int);
96 static void h8_clear_event_mask(int);
99 * Driver structures
102 static struct timer_list h8_monitor_timer;
103 static int h8_monitor_timer_active = 0;
105 static char driver_version[] = "X0.0";/* no spaces */
107 static struct file_operations h8_fops = {
108 NULL, /* lseek */
109 NULL,
110 NULL, /* write */
111 NULL, /* readdir */
112 NULL,
113 NULL,
114 NULL, /* mmap */
115 NULL,
116 NULL, /* flush */
117 NULL,
118 NULL, /* fsync */
119 NULL /* fasync */
122 static struct miscdevice h8_device = {
123 H8_MINOR_DEV,
124 "h8",
125 &h8_fops
128 #ifdef CONFIG_PROC_FS
129 static struct proc_dir_entry h8_proc_entry = {
130 0, 3, "h8", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, h8_get_info
132 #endif
134 union intr_buf intrbuf;
135 int intr_buf_ptr;
136 union intr_buf xx;
137 u_char last_temp;
140 * I/O Macros for register reads and writes.
142 #define H8_READ(a) inb((a))
143 #define H8_WRITE(d,a) outb((d),(a))
145 #define H8_GET_STATUS H8_READ((h8_base) + H8_STATUS_REG_OFF)
146 #define H8_READ_DATA H8_READ((h8_base) + H8_DATA_REG_OFF)
147 #define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
148 #define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
150 unsigned int h8_base = H8_BASE_ADDR;
151 unsigned int h8_irq = H8_IRQ;
152 unsigned int h8_state = H8_IDLE;
153 unsigned int h8_index = -1;
154 unsigned int h8_enabled = 0;
156 queue_head_t h8_actq, h8_cmdq, h8_freeq;
159 * Globals used in thermal control of Alphabook1.
161 int cpu_speed_divisor = -1;
162 int h8_event_mask = 0;
163 struct wait_queue *h8_monitor_wait = NULL;
164 unsigned int h8_command_mask = 0;
165 int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
166 int h8_uthermal_window = UTH_HYSTERESIS;
167 int h8_debug = 0xfffffdfc;
168 int h8_ldamp = MHZ_115;
169 int h8_udamp = MHZ_57;
170 u_char h8_current_temp = 0;
171 u_char h8_system_temp = 0;
172 int h8_sync_channel = 0;
173 struct wait_queue *h8_sync_wait = NULL;
174 int h8_init_performed;
176 /* CPU speeds and clock divisor values */
177 int speed_tab[6] = {230, 153, 115, 57, 28, 14};
180 * H8 interrupt handler
182 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
184 u_char stat_reg, data_reg;
185 h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
187 stat_reg = H8_GET_STATUS;
188 data_reg = H8_READ_DATA;
190 XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state, stat_reg, data_reg);
192 switch (h8_state) {
193 /* Response to an asynchronous event. */
194 case H8_IDLE: { /* H8_IDLE */
195 if (stat_reg & H8_OFULL) {
196 if (data_reg == H8_INTR) {
197 h8_state = H8_INTR_MODE;
198 /* Executing a command to determine what happened. */
199 WRITE_CMD(H8_RD_EVENT_STATUS);
200 intr_buf_ptr = 1;
201 WRITE_CMD(H8_RD_EVENT_STATUS);
202 } else {
203 Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
204 stat_reg, data_reg);
206 } else {
207 Dprintk("h8_intr: bogus interrupt\n");
209 break;
211 case H8_INTR_MODE: { /* H8_INTR_MODE */
212 XDprintk("H8 intr/intr_mode\n");
213 if (data_reg == H8_BYTE_LEVEL_ACK) {
214 return;
215 } else if (data_reg == H8_CMD_ACK) {
216 return;
217 } else {
218 intrbuf.byte[intr_buf_ptr] = data_reg;
219 if(!intr_buf_ptr) {
220 h8_state = H8_IDLE;
221 h8_read_event_status();
223 intr_buf_ptr--;
225 break;
227 /* Placed in this state by h8_start_new_cmd(). */
228 case H8_XMIT: { /* H8_XMIT */
229 XDprintk("H8 intr/xmit\n");
230 /* If a byte level acknowledgement has been received */
231 if (data_reg == H8_BYTE_LEVEL_ACK) {
232 XDprintk("H8 intr/xmit BYTE ACK\n");
233 qp->nacks++;
234 if (qp->nacks > qp->ncmd)
235 if(h8_debug & 0x1)
236 Dprintk("h8intr: bogus # of acks!\n");
238 * If the number of bytes sent is less than the total
239 * number of bytes in the command.
241 if (qp->cnt < qp->ncmd) {
242 h8_send_next_cmd_byte();
244 return;
245 /* If the complete command has produced an acknowledgement. */
246 } else if (data_reg == H8_CMD_ACK) {
247 XDprintk("H8 intr/xmit CMD ACK\n");
248 /* If there are response bytes */
249 if (qp->nrsp)
250 h8_state = H8_RCV;
251 else
252 h8_state = H8_IDLE;
253 qp->cnt = 0;
254 return;
255 /* Error, need to start over with a clean slate. */
256 } else if (data_reg == H8_NACK) {
257 XDprintk("h8_intr: NACK received restarting command\n");
258 qp->nacks = 0;
259 qp->cnt = 0;
260 h8_state = H8_IDLE;
261 WRITE_CMD(H8_SYNC);
262 return;
263 } else {
264 Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg);
265 return;
267 break;
269 case H8_RESYNC: { /* H8_RESYNC */
270 XDprintk("H8 intr/resync\n");
271 if (data_reg == H8_BYTE_LEVEL_ACK) {
272 return;
273 } else if (data_reg == H8_SYNC_BYTE) {
274 h8_state = H8_IDLE;
275 if (!QUEUE_EMPTY(&h8_actq, link))
276 h8_send_next_cmd_byte();
277 } else {
278 Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
279 return;
281 break;
283 case H8_RCV: { /* H8_RCV */
284 XDprintk("H8 intr/rcv\n");
285 if (qp->cnt < qp->nrsp) {
286 qp->rcvbuf[qp->cnt] = data_reg;
287 qp->cnt++;
288 /* If command reception finished. */
289 if (qp->cnt == qp->nrsp) {
290 h8_state = H8_IDLE;
291 QUEUE_REMOVE(&h8_actq, qp, link);
292 h8_cmd_done (qp);
293 /* More commands to send over? */
294 if (!QUEUE_EMPTY(&h8_cmdq, link))
295 h8_start_new_cmd();
297 return;
298 } else {
299 Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp->cmdbuf[0]);
301 break;
303 default: /* default */
304 Dprintk("H8 intr/unknown\n");
305 break;
307 return;
310 #ifdef MODULE
312 int init_module(void)
314 printk("H8 module at %X(Interrupt %d)\n", h8_base, h8_irq);
315 if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
317 printk("H8: error: IRQ %d is not free.\n", h8_irq);
318 return -EIO;
321 misc_register(&h8_device);
322 request_region(h8_base, 8, "h8");
324 #ifdef CONFIG_PROC_FS
325 proc_register(&proc_root, &h8_proc_entry);
326 #endif
328 QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *);
329 QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *);
330 QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *);
331 h8_alloc_queues();
333 h8_hw_init();
335 kernel_thread(h8_monitor_thread, NULL, 0);
337 return 0;
340 void cleanup_module(void)
342 misc_deregister(&h8_device);
343 release_region(h8_base, 8);
344 free_irq(h8_irq, NULL);
347 #else /* MODULE */
349 int h8_init(void)
351 if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
353 printk("H8: error: IRQ %d is not free\n", h8_irq);
354 return -EIO;
356 printk("H8 at 0x%x IRQ %d\n", h8_base, h8_irq);
358 #ifdef CONFIG_PROC_FS
359 proc_register(&proc_root, &h8_proc_entry);
360 #endif
362 misc_register(&h8_device);
363 request_region(h8_base, 8, "h8");
365 QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *);
366 QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *);
367 QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *);
368 h8_alloc_queues();
370 h8_hw_init();
372 kernel_thread(h8_monitor_thread, NULL, 0);
374 return 0;
376 #endif /* MODULE */
378 void h8_hw_init(void)
380 u_char buf[H8_MAX_CMD_SIZE];
382 /* set CPU speed to max for booting */
383 h8_set_cpu_speed(MHZ_230);
386 * Initialize the H8
388 h8_sync(); /* activate interrupts */
390 /* To clear conditions left by console */
391 h8_read_event_status();
393 /* Perform a conditioning read */
394 buf[0] = H8_DEVICE_CONTROL;
395 buf[1] = 0xff;
396 buf[2] = 0x0;
397 h8_q_cmd(buf, 3, 1);
399 /* Turn on built-in and external mice, capture power switch */
400 buf[0] = H8_DEVICE_CONTROL;
401 buf[1] = 0x0;
402 buf[2] = H8_ENAB_INT_PTR | H8_ENAB_EXT_PTR |
403 /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND;
404 h8_q_cmd(buf, 3, 1);
406 h8_enabled = 1;
407 return;
410 #ifdef CONFIG_PROC_FS
411 int h8_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
413 char *p;
415 if (!h8_enabled)
416 return 0;
417 p = buf;
421 0) Linux driver version (this will change if format changes)
428 p += sprintf(p, "%s \n",
429 driver_version
432 return p - buf;
434 #endif
436 /* Called from console driver -- must make sure h8_enabled. */
437 int h8_display_blank(void)
439 #ifdef CONFIG_H8_DISPLAY_BLANK
440 int error;
442 if (!h8_enabled)
443 return 0;
444 error = h8_set_display_power_state(H8_STATE_STANDBY);
445 if (error == H8_SUCCESS)
446 return 1;
447 h8_error("set display standby", error);
448 #endif
449 return 0;
452 /* Called from console driver -- must make sure h8_enabled. */
453 int h8_display_unblank(void)
455 #ifdef CONFIG_H8_DISPLAY_BLANK
456 int error;
458 if (!h8_enabled)
459 return 0;
460 error = h8_set_display_power_state(H8_STATE_READY);
461 if (error == H8_SUCCESS)
462 return 1;
463 h8_error("set display ready", error);
464 #endif
465 return 0;
469 h8_alloc_queues(void)
471 h8_cmd_q_t *qp;
472 unsigned long flags;
473 int i;
475 qp = (h8_cmd_q_t *)kmalloc((sizeof (h8_cmd_q_t) * H8_Q_ALLOC_AMOUNT),
476 GFP_KERNEL);
478 if (!qp) {
479 printk("H8: could not allocate memory for command queue\n");
480 return(0);
482 /* add to the free queue */
483 save_flags(flags); cli();
484 for (i = 0; i < H8_Q_ALLOC_AMOUNT; i++) {
485 /* place each at front of freeq */
486 QUEUE_ENTER(&h8_freeq, &qp[i], link, h8_cmd_q_t *);
488 restore_flags(flags);
489 return (1);
493 * Basic means by which commands are sent to the H8.
495 void
496 h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
498 h8_cmd_q_t *qp;
499 unsigned long flags;
500 int i;
502 /* get cmd buf */
503 save_flags(flags); cli();
504 while (QUEUE_EMPTY(&h8_freeq, link)) {
505 Dprintk("H8: need to allocate more cmd buffers\n");
506 restore_flags(flags);
507 h8_alloc_queues();
508 save_flags(flags); cli();
510 /* get first element from queue */
511 qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_freeq, link);
512 QUEUE_REMOVE(&h8_freeq, qp, link);
514 restore_flags(flags);
516 /* fill it in */
517 for (i = 0; i < cmd_size; i++)
518 qp->cmdbuf[i] = cmd[i];
519 qp->ncmd = cmd_size;
520 qp->nrsp = resp_size;
522 /* queue it at the end of the cmd queue */
523 save_flags(flags); cli();
525 QUEUE_ENTER(&h8_cmdq, qp, link, h8_cmd_q_t *);
527 restore_flags(flags);
529 h8_start_new_cmd();
532 void
533 h8_start_new_cmd(void)
535 unsigned long flags;
536 h8_cmd_q_t *qp;
538 save_flags(flags); cli();
539 if (h8_state != H8_IDLE) {
540 if (h8_debug & 0x1)
541 Dprintk("h8_start_new_cmd: not idle\n");
542 restore_flags(flags);
543 return;
546 if (!QUEUE_EMPTY(&h8_actq, link)) {
547 Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
548 restore_flags(flags);
549 return;
552 if (QUEUE_EMPTY(&h8_cmdq, link)) {
553 Dprintk("h8_start_new_cmd: no command to dequeue\n");
554 restore_flags(flags);
555 return;
558 * Take first command off of the command queue and put
559 * it on the active queue.
561 qp = (h8_cmd_q_t *) QUEUE_FIRST(&h8_cmdq, link);
562 QUEUE_REMOVE(&h8_cmdq, qp, link);
563 QUEUE_ENTER(&h8_actq, qp, link, h8_cmd_q_t *);
564 h8_state = H8_XMIT;
565 if (h8_debug & 0x1)
566 Dprintk("h8_start_new_cmd: Starting a command\n");
568 qp->cnt = 1;
569 WRITE_CMD(qp->cmdbuf[0]); /* Kick it off */
571 restore_flags(flags);
572 return;
575 void
576 h8_send_next_cmd_byte(void)
578 h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
579 int cnt;
581 cnt = qp->cnt;
582 qp->cnt++;
584 if (h8_debug & 0x1)
585 Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n",
586 cnt, qp->cmdbuf[cnt]);
588 if (cnt) {
589 WRITE_DATA(qp->cmdbuf[cnt]);
590 } else {
591 WRITE_CMD(qp->cmdbuf[cnt]);
593 return;
597 * Synchronize H8 communications channel for command transmission.
599 void
600 h8_sync(void)
602 u_char buf[H8_MAX_CMD_SIZE];
604 buf[0] = H8_SYNC;
605 buf[1] = H8_SYNC_BYTE;
606 h8_q_cmd(buf, 2, 1);
610 * Responds to external interrupt. Reads event status word and
611 * decodes type of interrupt.
613 void
614 h8_read_event_status(void)
617 if(h8_debug & 0x200)
618 printk("h8_read_event_status: value 0x%x\n", intrbuf.word);
621 * Power related items
623 if (intrbuf.word & H8_DC_CHANGE) {
624 if(h8_debug & 0x4)
625 printk("h8_read_event_status: DC_CHANGE\n");
626 /* see if dc added or removed, set batt/dc flag, send event */
628 h8_set_event_mask(H8_MANAGE_BATTERY);
629 wake_up(&h8_monitor_wait);
632 if (intrbuf.word & H8_POWER_BUTTON) {
633 printk("Power switch pressed - please wait - preparing to power
634 off\n");
635 h8_set_event_mask(H8_POWER_BUTTON);
636 wake_up(&h8_monitor_wait);
640 * Thermal related items
642 if (intrbuf.word & H8_THERMAL_THRESHOLD) {
643 if(h8_debug & 0x4)
644 printk("h8_read_event_status: THERMAL_THRESHOLD\n");
645 h8_set_event_mask(H8_MANAGE_UTHERM);
646 wake_up(&h8_monitor_wait);
650 * nops -for now
652 if (intrbuf.word & H8_DOCKING_STATION_STATUS) {
653 if(h8_debug & 0x4)
654 printk("h8_read_event_status: DOCKING_STATION_STATUS\n");
655 /* read_ext_status */
657 if (intrbuf.word & H8_EXT_BATT_STATUS) {
658 if(h8_debug & 0x4)
659 printk("h8_read_event_status: EXT_BATT_STATUS\n");
662 if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) {
663 if(h8_debug & 0x4)
664 printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
667 if (intrbuf.word & H8_BATT_CHANGE_OVER) {
668 if(h8_debug & 0x4)
669 printk("h8_read_event_status: BATT_CHANGE_OVER\n");
672 if (intrbuf.word & H8_WATCHDOG) {
673 if(h8_debug & 0x4)
674 printk("h8_read_event_status: WATCHDOG\n");
675 /* nop */
677 if (intrbuf.word & H8_SHUTDOWN) {
678 if(h8_debug & 0x4)
679 printk("h8_read_event_status: SHUTDOWN\n");
680 /* nop */
682 if (intrbuf.word & H8_KEYBOARD) {
683 if(h8_debug & 0x4)
684 printk("h8_read_event_status: KEYBOARD\n");
685 /* nop */
687 if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) {
688 if(h8_debug & 0x4)
689 printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
690 /* read_ext_status*/
692 if (intrbuf.word & H8_INT_BATT_LOW) {
693 if(h8_debug & 0x4)
694 printk("h8_read_event_status: INT_BATT_LOW\n");
695 /* post event, warn user */
697 if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) {
698 if(h8_debug & 0x4)
699 printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n");
700 /* nop - happens often */
702 if (intrbuf.word & H8_INT_BATT_STATUS) {
703 if(h8_debug & 0x4)
704 printk("h8_read_event_status: INT_BATT_STATUS\n");
707 if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) {
708 if(h8_debug & 0x4)
709 printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
710 /* nop - happens often */
712 if (intrbuf.word & H8_EXT_BATT_LOW) {
713 if(h8_debug & 0x4)
714 printk("h8_read_event_status: EXT_BATT_LOW\n");
715 /*if no internal, post event, warn user */
716 /* else nop */
719 return;
723 * Function called when H8 has performed requested command.
725 void
726 h8_cmd_done(h8_cmd_q_t *qp)
729 /* what to do */
730 switch (qp->cmdbuf[0]) {
731 case H8_SYNC:
732 if (h8_debug & 0x40000)
733 printk("H8: Sync command done - byte returned was 0x%x\n",
734 qp->rcvbuf[0]);
735 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
736 break;
738 case H8_RD_SN:
739 case H8_RD_ENET_ADDR:
740 printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n",
741 qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2],
742 qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]);
743 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
744 break;
746 case H8_RD_HW_VER:
747 case H8_RD_MIC_VER:
748 case H8_RD_MAX_TEMP:
749 printk("H8: Max recorded CPU temp %d, Sys temp %d\n",
750 qp->rcvbuf[0], qp->rcvbuf[1]);
751 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
752 break;
754 case H8_RD_MIN_TEMP:
755 printk("H8: Min recorded CPU temp %d, Sys temp %d\n",
756 qp->rcvbuf[0], qp->rcvbuf[1]);
757 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
758 break;
760 case H8_RD_CURR_TEMP:
761 h8_sync_channel |= H8_RD_CURR_TEMP;
762 xx.byte[0] = qp->rcvbuf[0];
763 xx.byte[1] = qp->rcvbuf[1];
764 wake_up(&h8_sync_wait);
765 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
766 break;
768 case H8_RD_SYS_VARIENT:
769 case H8_RD_PWR_ON_CYCLES:
770 printk(" H8: RD_PWR_ON_CYCLES command done\n");
771 break;
773 case H8_RD_PWR_ON_SECS:
774 printk("H8: RD_PWR_ON_SECS command done\n");
775 break;
777 case H8_RD_RESET_STATUS:
778 case H8_RD_PWR_DN_STATUS:
779 case H8_RD_EVENT_STATUS:
780 case H8_RD_ROM_CKSM:
781 case H8_RD_EXT_STATUS:
782 xx.byte[1] = qp->rcvbuf[0];
783 xx.byte[0] = qp->rcvbuf[1];
784 h8_sync_channel |= H8_GET_EXT_STATUS;
785 wake_up(&h8_sync_wait);
786 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
787 break;
789 case H8_RD_USER_CFG:
790 case H8_RD_INT_BATT_VOLT:
791 case H8_RD_DC_INPUT_VOLT:
792 case H8_RD_HORIZ_PTR_VOLT:
793 case H8_RD_VERT_PTR_VOLT:
794 case H8_RD_EEPROM_STATUS:
795 case H8_RD_ERR_STATUS:
796 case H8_RD_NEW_BUSY_SPEED:
797 case H8_RD_CONFIG_INTERFACE:
798 case H8_RD_INT_BATT_STATUS:
799 printk("H8: Read int batt status cmd done - returned was %x %x %x\n",
800 qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]);
801 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
802 break;
804 case H8_RD_EXT_BATT_STATUS:
805 case H8_RD_PWR_UP_STATUS:
806 case H8_RD_EVENT_STATUS_MASK:
807 case H8_CTL_EMU_BITPORT:
808 case H8_DEVICE_CONTROL:
809 if(h8_debug & 0x20000) {
810 printk("H8: Device control cmd done - byte returned was 0x%x\n",
811 qp->rcvbuf[0]);
813 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
814 break;
816 case H8_CTL_TFT_BRT_DC:
817 case H8_CTL_WATCHDOG:
818 case H8_CTL_MIC_PROT:
819 case H8_CTL_INT_BATT_CHG:
820 case H8_CTL_EXT_BATT_CHG:
821 case H8_CTL_MARK_SPACE:
822 case H8_CTL_MOUSE_SENSITIVITY:
823 case H8_CTL_DIAG_MODE:
824 case H8_CTL_IDLE_AND_BUSY_SPDS:
825 printk("H8: Idle and busy speed command done\n");
826 break;
828 case H8_CTL_TFT_BRT_BATT:
829 case H8_CTL_UPPER_TEMP:
830 if(h8_debug & 0x10) {
831 XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n",
832 qp->rcvbuf[0]);
834 QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
835 break;
837 case H8_CTL_LOWER_TEMP:
838 case H8_CTL_TEMP_CUTOUT:
839 case H8_CTL_WAKEUP:
840 case H8_CTL_CHG_THRESHOLD:
841 case H8_CTL_TURBO_MODE:
842 case H8_SET_DIAG_STATUS:
843 case H8_SOFTWARE_RESET:
844 case H8_RECAL_PTR:
845 case H8_SET_INT_BATT_PERCENT:
846 case H8_WRT_CFG_INTERFACE_REG:
847 case H8_WRT_EVENT_STATUS_MASK:
848 case H8_ENTER_POST_MODE:
849 case H8_EXIT_POST_MODE:
850 case H8_RD_EEPROM:
851 case H8_WRT_EEPROM:
852 case H8_WRT_TO_STATUS_DISP:
853 printk("H8: Write IO status display command done\n");
854 break;
856 case H8_DEFINE_SPC_CHAR:
857 case H8_DEFINE_TABLE_STRING_ENTRY:
858 case H8_PERFORM_EMU_CMD:
859 case H8_EMU_RD_REG:
860 case H8_EMU_WRT_REG:
861 case H8_EMU_RD_RAM:
862 case H8_EMU_WRT_RAM:
863 case H8_BQ_RD_REG:
864 case H8_BQ_WRT_REG:
865 case H8_PWR_OFF:
866 printk ("H8: misc command completed\n");
867 break;
869 return;
873 * Retrieve the current CPU temperature and case temperature. Provides
874 * the feedback for the thermal control algorithm. Synchcronized via
875 * sleep() for priority so that no other actions in the process will take
876 * place before the data becomes available.
879 h8_get_curr_temp(u_char curr_temp[])
881 u_char buf[H8_MAX_CMD_SIZE];
882 unsigned long flags;
884 memset(buf, 0, H8_MAX_CMD_SIZE);
885 buf[0] = H8_RD_CURR_TEMP;
887 h8_q_cmd(buf, 1, 2);
889 save_flags(flags); cli();
891 while((h8_sync_channel & H8_RD_CURR_TEMP) == 0)
892 sleep_on(&h8_sync_wait);
894 restore_flags(flags);
896 h8_sync_channel &= ~H8_RD_CURR_TEMP;
897 curr_temp[0] = xx.byte[0];
898 curr_temp[1] = xx.byte[1];
899 xx.word = 0;
901 if(h8_debug & 0x8)
902 printk("H8: curr CPU temp %d, Sys temp %d\n",
903 curr_temp[0], curr_temp[1]);
904 return 0;
907 static void
908 h8_get_max_temp(void)
910 u_char buf[H8_MAX_CMD_SIZE];
912 buf[0] = H8_RD_MAX_TEMP;
913 h8_q_cmd(buf, 1, 2);
917 * Assigns an upper limit to the value of the H8 thermal interrupt.
918 * As an example setting a value of 115 F here will cause the
919 * interrupt to trigger when the CPU temperature reaches 115 F.
921 static void
922 h8_set_upper_therm_thold(int thold)
924 u_char buf[H8_MAX_CMD_SIZE];
926 /* write 0 to reinitialize interrupt */
927 buf[0] = H8_CTL_UPPER_TEMP;
928 buf[1] = 0x0;
929 buf[2] = 0x0;
930 h8_q_cmd(buf, 3, 1);
932 /* Do it for real */
933 buf[0] = H8_CTL_UPPER_TEMP;
934 buf[1] = 0x0;
935 buf[2] = thold;
936 h8_q_cmd(buf, 3, 1);
939 static void
940 h8_get_upper_therm_thold(void)
942 u_char buf[H8_MAX_CMD_SIZE];
944 buf[0] = H8_CTL_UPPER_TEMP;
945 buf[1] = 0xff;
946 buf[2] = 0;
947 h8_q_cmd(buf, 3, 1);
951 * The external status word contains information on keyboard controller,
952 * power button, changes in external batt status, change in DC state,
953 * docking station, etc. General purpose querying use.
956 h8_get_ext_status(u_char stat_word[])
958 u_char buf[H8_MAX_CMD_SIZE];
959 unsigned long flags;
961 memset(buf, 0, H8_MAX_CMD_SIZE);
962 buf[0] = H8_RD_EXT_STATUS;
964 h8_q_cmd(buf, 1, 2);
966 save_flags(flags); cli();
968 while((h8_sync_channel & H8_GET_EXT_STATUS) == 0)
969 sleep_on(&h8_sync_wait);
971 restore_flags(flags);
973 h8_sync_channel &= ~H8_GET_EXT_STATUS;
974 stat_word[0] = xx.byte[0];
975 stat_word[1] = xx.byte[1];
976 xx.word = 0;
978 if(h8_debug & 0x8)
979 printk("H8: curr ext status %x, %x\n",
980 stat_word[0], stat_word[1]);
982 return 0;
986 * Thread attached to task 0 manages thermal/physcial state of Alphabook.
987 * When a condition is detected by the interrupt service routine, the
988 * isr does a wakeup() on h8_monitor_wait. The mask value is then
989 * screened for the appropriate action.
993 h8_monitor_thread(void * unused)
995 u_char curr_temp[2];
998 * Need a logic based safety valve here. During boot when this thread is
999 * started and the thermal interrupt is not yet initialized this logic
1000 * checks the temperature and acts accordingly. When this path is acted
1001 * upon system boot is painfully slow, however, the priority associated
1002 * with overheating is high enough to warrant this action.
1004 h8_get_curr_temp(curr_temp);
1006 printk("H8: Initial CPU temp: %d\n", curr_temp[0]);
1008 if(curr_temp[0] >= h8_uthermal_threshold) {
1009 h8_set_event_mask(H8_MANAGE_UTHERM);
1010 h8_manage_therm();
1011 } else {
1013 * Arm the upper thermal limit of the H8 so that any temp in
1014 * excess will trigger the thermal control mechanism.
1016 h8_set_upper_therm_thold(h8_uthermal_threshold);
1019 for(;;) {
1020 sleep_on(&h8_monitor_wait);
1022 if(h8_debug & 0x2)
1023 printk("h8_monitor_thread awakened, mask:%x\n",
1024 h8_event_mask);
1026 if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) {
1027 h8_manage_therm();
1030 #if 0
1031 if (h8_event_mask & H8_POWER_BUTTON) {
1032 h8_system_down();
1036 * If an external DC supply is removed or added make
1037 * appropriate CPU speed adjustments.
1039 if (h8_event_mask & H8_MANAGE_BATTERY) {
1040 h8_run_level_3_manage(H8_RUN);
1041 h8_clear_event_mask(H8_MANAGE_BATTERY);
1043 #endif
1048 * Function implements the following policy. When the machine is booted
1049 * the system is set to run at full clock speed. When the upper thermal
1050 * threshold is reached as a result of full clock a damping factor is
1051 * applied to cool off the cpu. The default value is one quarter clock
1052 * (57 Mhz). When as a result of this cooling a temperature lower by
1053 * hmc_uthermal_window is reached, the machine is reset to a higher
1054 * speed, one half clock (115 Mhz). One half clock is maintained until
1055 * the upper thermal threshold is again reached restarting the cycle.
1059 h8_manage_therm(void)
1061 u_char curr_temp[2];
1063 if(h8_event_mask & H8_MANAGE_UTHERM) {
1064 /* Upper thermal interrupt received, need to cool down. */
1065 if(h8_debug & 0x10)
1066 printk("H8: Thermal threshold %d F reached\n",
1067 h8_uthermal_threshold);
1068 h8_set_cpu_speed(h8_udamp);
1069 h8_clear_event_mask(H8_MANAGE_UTHERM);
1070 h8_set_event_mask(H8_MANAGE_LTHERM);
1071 /* Check again in 30 seconds for CPU temperature */
1072 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
1073 } else if (h8_event_mask & H8_MANAGE_LTHERM) {
1074 /* See how cool the system has become as a result
1075 of the reduction in speed. */
1076 h8_get_curr_temp(curr_temp);
1077 last_temp = curr_temp[0];
1078 if (curr_temp[0] < (h8_uthermal_threshold - h8_uthermal_window))
1080 /* System cooling has progressed to a point
1081 that the CPU may be sped up. */
1082 h8_set_upper_therm_thold(h8_uthermal_threshold);
1083 h8_set_cpu_speed(h8_ldamp); /* adjustable */
1084 if(h8_debug & 0x10)
1085 printk("H8: CPU cool, applying cpu_divisor: %d \n",
1086 h8_ldamp);
1087 h8_clear_event_mask(H8_MANAGE_LTHERM);
1089 else /* Not cool enough yet, check again in 30 seconds. */
1090 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL);
1091 } else {
1094 return 0;
1098 * Function conditions the value of global_rpb_counter before
1099 * calling the primitive which causes the actual speed change.
1101 void
1102 h8_set_cpu_speed(int speed_divisor)
1105 #ifdef NOT_YET
1107 * global_rpb_counter is consumed by alpha_delay() in determining just
1108 * how much time to delay. It is necessary that the number of microseconds
1109 * in DELAY(n) be kept consistent over a variety of CPU clock speeds.
1110 * To that end global_rpb_counter is here adjusted.
1113 switch (speed_divisor) {
1114 case 0:
1115 global_rpb_counter = rpb->rpb_counter * 2L;
1116 break;
1117 case 1:
1118 global_rpb_counter = rpb->rpb_counter * 4L / 3L ;
1119 break;
1120 case 3:
1121 global_rpb_counter = rpb->rpb_counter / 2L;
1122 break;
1123 case 4:
1124 global_rpb_counter = rpb->rpb_counter / 4L;
1125 break;
1126 case 5:
1127 global_rpb_counter = rpb->rpb_counter / 8L;
1128 break;
1130 * This case most commonly needed for cpu_speed_divisor
1131 * of 2 which is the value assigned by the firmware.
1133 default:
1134 global_rpb_counter = rpb->rpb_counter;
1135 break;
1137 #endif /* NOT_YET */
1139 if(h8_debug & 0x8)
1140 printk("H8: Setting CPU speed to %d MHz\n",
1141 speed_tab[speed_divisor]);
1143 /* Make the actual speed change */
1144 lca_clock_fiddle(speed_divisor);
1148 * Gets value stored in rpb representing CPU clock speed and adjusts this
1149 * value based on the current clock speed divisor.
1151 u_long
1152 h8_get_cpu_speed(void)
1154 u_long speed = 0;
1155 u_long counter;
1157 #ifdef NOT_YET
1158 counter = rpb->rpb_counter / 1000000L;
1160 switch (alphabook_get_clock()) {
1161 case 0:
1162 speed = counter * 2L;
1163 break;
1164 case 1:
1165 speed = counter * 4L / 3L ;
1166 break;
1167 case 2:
1168 speed = counter;
1169 break;
1170 case 3:
1171 speed = counter / 2L;
1172 break;
1173 case 4:
1174 speed = counter / 4L;
1175 break;
1176 case 5:
1177 speed = counter / 8L;
1178 break;
1179 default:
1180 break;
1182 if(h8_debug & 0x8)
1183 printk("H8: CPU speed current setting: %d MHz\n", speed);
1184 #endif /* NOT_YET */
1185 return speed;
1188 static void
1189 h8_activate_monitor(unsigned long unused)
1191 unsigned long flags;
1193 save_flags(flags); cli();
1194 h8_monitor_timer_active = 0;
1195 restore_flags(flags);
1197 wake_up(&h8_monitor_wait);
1200 static void
1201 h8_start_monitor_timer(unsigned long secs)
1203 unsigned long flags;
1205 if (h8_monitor_timer_active)
1206 return;
1208 save_flags(flags); cli();
1209 h8_monitor_timer_active = 1;
1210 restore_flags(flags);
1212 init_timer(&h8_monitor_timer);
1213 h8_monitor_timer.function = h8_activate_monitor;
1214 h8_monitor_timer.expires = secs * HZ + jiffies;
1215 add_timer(&h8_monitor_timer);
1218 static void h8_set_event_mask(int mask)
1220 unsigned long flags;
1222 save_flags(flags); cli();
1223 h8_event_mask |= mask;
1224 restore_flags(flags);
1227 static void h8_clear_event_mask(int mask)
1229 unsigned long flags;
1231 save_flags(flags); cli();
1232 h8_event_mask &= (~mask);
1233 restore_flags(flags);