2 * Hitachi H8/337 Microcontroller driver
4 * The H8 is used to deal with the power and thermal environment
8 #include <linux/config.h>
9 #include <linux/module.h>
11 #include <asm/system.h>
12 #include <asm/segment.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>
22 #include <linux/stat.h>
23 #include <linux/proc_fs.h>
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>
38 #define Dprintk printk
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.
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
);
60 static int h8_get_info(char *, char **, off_t
, int, int);
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);
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
= {
122 static struct miscdevice h8_device
= {
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
134 union intr_buf intrbuf
;
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
);
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
);
201 WRITE_CMD(H8_RD_EVENT_STATUS
);
203 Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
207 Dprintk("h8_intr: bogus interrupt\n");
211 case H8_INTR_MODE
: { /* H8_INTR_MODE */
212 XDprintk("H8 intr/intr_mode\n");
213 if (data_reg
== H8_BYTE_LEVEL_ACK
) {
215 } else if (data_reg
== H8_CMD_ACK
) {
218 intrbuf
.byte
[intr_buf_ptr
] = data_reg
;
221 h8_read_event_status();
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");
234 if (qp
->nacks
> qp
->ncmd
)
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();
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 */
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");
264 Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg
);
269 case H8_RESYNC
: { /* H8_RESYNC */
270 XDprintk("H8 intr/resync\n");
271 if (data_reg
== H8_BYTE_LEVEL_ACK
) {
273 } else if (data_reg
== H8_SYNC_BYTE
) {
275 if (!QUEUE_EMPTY(&h8_actq
, link
))
276 h8_send_next_cmd_byte();
278 Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg
);
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
;
288 /* If command reception finished. */
289 if (qp
->cnt
== qp
->nrsp
) {
291 QUEUE_REMOVE(&h8_actq
, qp
, link
);
293 /* More commands to send over? */
294 if (!QUEUE_EMPTY(&h8_cmdq
, link
))
299 Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp
->cmdbuf
[0]);
303 default: /* default */
304 Dprintk("H8 intr/unknown\n");
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
);
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
);
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
*);
335 kernel_thread(h8_monitor_thread
, NULL
, 0);
340 void cleanup_module(void)
342 misc_deregister(&h8_device
);
343 release_region(h8_base
, 8);
344 free_irq(h8_irq
, NULL
);
351 if(request_irq(h8_irq
, h8_intr
, SA_INTERRUPT
, "h8", NULL
))
353 printk("H8: error: IRQ %d is not free\n", h8_irq
);
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
);
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
*);
372 kernel_thread(h8_monitor_thread
, NULL
, 0);
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
);
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
;
399 /* Turn on built-in and external mice, capture power switch */
400 buf
[0] = H8_DEVICE_CONTROL
;
402 buf
[2] = H8_ENAB_INT_PTR
| H8_ENAB_EXT_PTR
|
403 /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND
;
410 #ifdef CONFIG_PROC_FS
411 int h8_get_info(char *buf
, char **start
, off_t fpos
, int length
, int dummy
)
421 0) Linux driver version (this will change if format changes)
428 p
+= sprintf(p
, "%s \n",
436 /* Called from console driver -- must make sure h8_enabled. */
437 int h8_display_blank(void)
439 #ifdef CONFIG_H8_DISPLAY_BLANK
444 error
= h8_set_display_power_state(H8_STATE_STANDBY
);
445 if (error
== H8_SUCCESS
)
447 h8_error("set display standby", error
);
452 /* Called from console driver -- must make sure h8_enabled. */
453 int h8_display_unblank(void)
455 #ifdef CONFIG_H8_DISPLAY_BLANK
460 error
= h8_set_display_power_state(H8_STATE_READY
);
461 if (error
== H8_SUCCESS
)
463 h8_error("set display ready", error
);
469 h8_alloc_queues(void)
475 qp
= (h8_cmd_q_t
*)kmalloc((sizeof (h8_cmd_q_t
) * H8_Q_ALLOC_AMOUNT
),
479 printk("H8: could not allocate memory for command queue\n");
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
);
493 * Basic means by which commands are sent to the H8.
496 h8_q_cmd(u_char
*cmd
, int cmd_size
, int resp_size
)
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
);
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
);
517 for (i
= 0; i
< cmd_size
; i
++)
518 qp
->cmdbuf
[i
] = cmd
[i
];
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
);
533 h8_start_new_cmd(void)
538 save_flags(flags
); cli();
539 if (h8_state
!= H8_IDLE
) {
541 Dprintk("h8_start_new_cmd: not idle\n");
542 restore_flags(flags
);
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
);
552 if (QUEUE_EMPTY(&h8_cmdq
, link
)) {
553 Dprintk("h8_start_new_cmd: no command to dequeue\n");
554 restore_flags(flags
);
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
*);
566 Dprintk("h8_start_new_cmd: Starting a command\n");
569 WRITE_CMD(qp
->cmdbuf
[0]); /* Kick it off */
571 restore_flags(flags
);
576 h8_send_next_cmd_byte(void)
578 h8_cmd_q_t
*qp
= (h8_cmd_q_t
*)QUEUE_FIRST(&h8_actq
, link
);
585 Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n",
586 cnt
, qp
->cmdbuf
[cnt
]);
589 WRITE_DATA(qp
->cmdbuf
[cnt
]);
591 WRITE_CMD(qp
->cmdbuf
[cnt
]);
597 * Synchronize H8 communications channel for command transmission.
602 u_char buf
[H8_MAX_CMD_SIZE
];
605 buf
[1] = H8_SYNC_BYTE
;
610 * Responds to external interrupt. Reads event status word and
611 * decodes type of interrupt.
614 h8_read_event_status(void)
618 printk("h8_read_event_status: value 0x%x\n", intrbuf
.word
);
621 * Power related items
623 if (intrbuf
.word
& H8_DC_CHANGE
) {
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
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
) {
644 printk("h8_read_event_status: THERMAL_THRESHOLD\n");
645 h8_set_event_mask(H8_MANAGE_UTHERM
);
646 wake_up(&h8_monitor_wait
);
652 if (intrbuf
.word
& H8_DOCKING_STATION_STATUS
) {
654 printk("h8_read_event_status: DOCKING_STATION_STATUS\n");
655 /* read_ext_status */
657 if (intrbuf
.word
& H8_EXT_BATT_STATUS
) {
659 printk("h8_read_event_status: EXT_BATT_STATUS\n");
662 if (intrbuf
.word
& H8_EXT_BATT_CHARGE_STATE
) {
664 printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
667 if (intrbuf
.word
& H8_BATT_CHANGE_OVER
) {
669 printk("h8_read_event_status: BATT_CHANGE_OVER\n");
672 if (intrbuf
.word
& H8_WATCHDOG
) {
674 printk("h8_read_event_status: WATCHDOG\n");
677 if (intrbuf
.word
& H8_SHUTDOWN
) {
679 printk("h8_read_event_status: SHUTDOWN\n");
682 if (intrbuf
.word
& H8_KEYBOARD
) {
684 printk("h8_read_event_status: KEYBOARD\n");
687 if (intrbuf
.word
& H8_EXT_MOUSE_OR_CASE_SWITCH
) {
689 printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
692 if (intrbuf
.word
& H8_INT_BATT_LOW
) {
694 printk("h8_read_event_status: INT_BATT_LOW\n");
695 /* post event, warn user */
697 if (intrbuf
.word
& H8_INT_BATT_CHARGE_STATE
) {
699 printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n");
700 /* nop - happens often */
702 if (intrbuf
.word
& H8_INT_BATT_STATUS
) {
704 printk("h8_read_event_status: INT_BATT_STATUS\n");
707 if (intrbuf
.word
& H8_INT_BATT_CHARGE_THRESHOLD
) {
709 printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
710 /* nop - happens often */
712 if (intrbuf
.word
& H8_EXT_BATT_LOW
) {
714 printk("h8_read_event_status: EXT_BATT_LOW\n");
715 /*if no internal, post event, warn user */
723 * Function called when H8 has performed requested command.
726 h8_cmd_done(h8_cmd_q_t
*qp
)
730 switch (qp
->cmdbuf
[0]) {
732 if (h8_debug
& 0x40000)
733 printk("H8: Sync command done - byte returned was 0x%x\n",
735 QUEUE_ENTER(&h8_freeq
, qp
, link
, h8_cmd_q_t
*);
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
*);
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
*);
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
*);
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
*);
768 case H8_RD_SYS_VARIENT
:
769 case H8_RD_PWR_ON_CYCLES
:
770 printk(" H8: RD_PWR_ON_CYCLES command done\n");
773 case H8_RD_PWR_ON_SECS
:
774 printk("H8: RD_PWR_ON_SECS command done\n");
777 case H8_RD_RESET_STATUS
:
778 case H8_RD_PWR_DN_STATUS
:
779 case H8_RD_EVENT_STATUS
:
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
*);
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
*);
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",
813 QUEUE_ENTER(&h8_freeq
, qp
, link
, h8_cmd_q_t
*);
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");
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",
834 QUEUE_ENTER(&h8_freeq
, qp
, link
, h8_cmd_q_t
*);
837 case H8_CTL_LOWER_TEMP
:
838 case H8_CTL_TEMP_CUTOUT
:
840 case H8_CTL_CHG_THRESHOLD
:
841 case H8_CTL_TURBO_MODE
:
842 case H8_SET_DIAG_STATUS
:
843 case H8_SOFTWARE_RESET
:
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
:
852 case H8_WRT_TO_STATUS_DISP
:
853 printk("H8: Write IO status display command done\n");
856 case H8_DEFINE_SPC_CHAR
:
857 case H8_DEFINE_TABLE_STRING_ENTRY
:
858 case H8_PERFORM_EMU_CMD
:
866 printk ("H8: misc command completed\n");
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
];
884 memset(buf
, 0, H8_MAX_CMD_SIZE
);
885 buf
[0] = H8_RD_CURR_TEMP
;
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];
902 printk("H8: curr CPU temp %d, Sys temp %d\n",
903 curr_temp
[0], curr_temp
[1]);
908 h8_get_max_temp(void)
910 u_char buf
[H8_MAX_CMD_SIZE
];
912 buf
[0] = H8_RD_MAX_TEMP
;
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.
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
;
933 buf
[0] = H8_CTL_UPPER_TEMP
;
940 h8_get_upper_therm_thold(void)
942 u_char buf
[H8_MAX_CMD_SIZE
];
944 buf
[0] = H8_CTL_UPPER_TEMP
;
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
];
961 memset(buf
, 0, H8_MAX_CMD_SIZE
);
962 buf
[0] = H8_RD_EXT_STATUS
;
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];
979 printk("H8: curr ext status %x, %x\n",
980 stat_word
[0], stat_word
[1]);
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
)
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
);
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
);
1020 sleep_on(&h8_monitor_wait
);
1023 printk("h8_monitor_thread awakened, mask:%x\n",
1026 if (h8_event_mask
& (H8_MANAGE_UTHERM
|H8_MANAGE_LTHERM
)) {
1031 if (h8_event_mask
& H8_POWER_BUTTON
) {
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
);
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. */
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 */
1085 printk("H8: CPU cool, applying cpu_divisor: %d \n",
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
);
1098 * Function conditions the value of global_rpb_counter before
1099 * calling the primitive which causes the actual speed change.
1102 h8_set_cpu_speed(int speed_divisor
)
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
) {
1115 global_rpb_counter
= rpb
->rpb_counter
* 2L;
1118 global_rpb_counter
= rpb
->rpb_counter
* 4L / 3L ;
1121 global_rpb_counter
= rpb
->rpb_counter
/ 2L;
1124 global_rpb_counter
= rpb
->rpb_counter
/ 4L;
1127 global_rpb_counter
= rpb
->rpb_counter
/ 8L;
1130 * This case most commonly needed for cpu_speed_divisor
1131 * of 2 which is the value assigned by the firmware.
1134 global_rpb_counter
= rpb
->rpb_counter
;
1137 #endif /* NOT_YET */
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.
1152 h8_get_cpu_speed(void)
1158 counter
= rpb
->rpb_counter
/ 1000000L;
1160 switch (alphabook_get_clock()) {
1162 speed
= counter
* 2L;
1165 speed
= counter
* 4L / 3L ;
1171 speed
= counter
/ 2L;
1174 speed
= counter
/ 4L;
1177 speed
= counter
/ 8L;
1183 printk("H8: CPU speed current setting: %d MHz\n", speed
);
1184 #endif /* NOT_YET */
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
);
1201 h8_start_monitor_timer(unsigned long secs
)
1203 unsigned long flags
;
1205 if (h8_monitor_timer_active
)
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
);