2 * Hitachi H8/337 Microcontroller driver
4 * The H8 is used to deal with the power and thermal environment
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
13 #include <linux/config.h>
14 #include <linux/module.h>
16 #include <asm/system.h>
17 #include <asm/segment.h>
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>
34 #define __KERNEL_SYSCALLS__
35 #include <asm/unistd.h>
42 #define Dprintk printk
47 #define XDprintk if(h8_debug==-1)printk
50 * The h8 device is one of the misc char devices.
52 #define H8_MINOR_DEV 140
55 * Forward declarations.
57 static int h8_init(void);
58 int h8_display_blank(void);
59 int h8_display_unblank(void);
61 static void h8_intr(int irq
, void *dev_id
, struct pt_regs
*regs
);
63 static int h8_get_info(char *, char **, off_t
, int);
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);
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
[]);
84 static int h8_monitor_thread(void *);
86 static int h8_manage_therm(void);
87 static void h8_set_cpu_speed(int speed_divisor
);
89 static void h8_start_monitor_timer(unsigned long secs
);
90 static void h8_activate_monitor(unsigned long unused
);
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);
97 static void h8_set_event_mask(int);
98 static void h8_clear_event_mask(int);
104 static struct timer_list h8_monitor_timer
;
105 static int h8_monitor_timer_active
= 0;
107 static char driver_version
[] = "X0.0";/* no spaces */
109 static struct file_operations h8_fops
= {
110 /* twelve lines of crap^WNULLs were here */
113 static struct miscdevice h8_device
= {
119 union intr_buf intrbuf
;
125 * I/O Macros for register reads and writes.
127 #define H8_READ(a) inb((a))
128 #define H8_WRITE(d,a) outb((d),(a))
130 #define H8_GET_STATUS H8_READ((h8_base) + H8_STATUS_REG_OFF)
131 #define H8_READ_DATA H8_READ((h8_base) + H8_DATA_REG_OFF)
132 #define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
133 #define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
135 unsigned int h8_base
= H8_BASE_ADDR
;
136 unsigned int h8_irq
= H8_IRQ
;
137 unsigned int h8_state
= H8_IDLE
;
138 unsigned int h8_index
= -1;
139 unsigned int h8_enabled
= 0;
146 * Globals used in thermal control of Alphabook1.
148 int cpu_speed_divisor
= -1;
149 int h8_event_mask
= 0;
150 DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait
);
151 unsigned int h8_command_mask
= 0;
152 int h8_uthermal_threshold
= DEFAULT_UTHERMAL_THRESHOLD
;
153 int h8_uthermal_window
= UTH_HYSTERESIS
;
154 int h8_debug
= 0xfffffdfc;
155 int h8_ldamp
= MHZ_115
;
156 int h8_udamp
= MHZ_57
;
157 u_char h8_current_temp
= 0;
158 u_char h8_system_temp
= 0;
159 int h8_sync_channel
= 0;
160 DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait
);
161 int h8_init_performed
;
163 /* CPU speeds and clock divisor values */
164 int speed_tab
[6] = {230, 153, 115, 57, 28, 14};
167 * H8 interrupt handler
169 static void h8_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
171 u_char stat_reg
, data_reg
;
172 h8_cmd_q_t
*qp
= list_entry(h8_actq
.next
, h8_cmd_q_t
, link
);
174 stat_reg
= H8_GET_STATUS
;
175 data_reg
= H8_READ_DATA
;
177 XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state
, stat_reg
, data_reg
);
180 /* Response to an asynchronous event. */
181 case H8_IDLE
: { /* H8_IDLE */
182 if (stat_reg
& H8_OFULL
) {
183 if (data_reg
== H8_INTR
) {
184 h8_state
= H8_INTR_MODE
;
185 /* Executing a command to determine what happened. */
186 WRITE_CMD(H8_RD_EVENT_STATUS
);
188 WRITE_CMD(H8_RD_EVENT_STATUS
);
190 Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
194 Dprintk("h8_intr: bogus interrupt\n");
198 case H8_INTR_MODE
: { /* H8_INTR_MODE */
199 XDprintk("H8 intr/intr_mode\n");
200 if (data_reg
== H8_BYTE_LEVEL_ACK
) {
202 } else if (data_reg
== H8_CMD_ACK
) {
205 intrbuf
.byte
[intr_buf_ptr
] = data_reg
;
208 h8_read_event_status();
214 /* Placed in this state by h8_start_new_cmd(). */
215 case H8_XMIT
: { /* H8_XMIT */
216 XDprintk("H8 intr/xmit\n");
217 /* If a byte level acknowledgement has been received */
218 if (data_reg
== H8_BYTE_LEVEL_ACK
) {
219 XDprintk("H8 intr/xmit BYTE ACK\n");
221 if (qp
->nacks
> qp
->ncmd
)
223 Dprintk("h8intr: bogus # of acks!\n");
225 * If the number of bytes sent is less than the total
226 * number of bytes in the command.
228 if (qp
->cnt
< qp
->ncmd
) {
229 h8_send_next_cmd_byte();
232 /* If the complete command has produced an acknowledgement. */
233 } else if (data_reg
== H8_CMD_ACK
) {
234 XDprintk("H8 intr/xmit CMD ACK\n");
235 /* If there are response bytes */
242 /* Error, need to start over with a clean slate. */
243 } else if (data_reg
== H8_NACK
) {
244 XDprintk("h8_intr: NACK received restarting command\n");
251 Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg
);
256 case H8_RESYNC
: { /* H8_RESYNC */
257 XDprintk("H8 intr/resync\n");
258 if (data_reg
== H8_BYTE_LEVEL_ACK
) {
260 } else if (data_reg
== H8_SYNC_BYTE
) {
262 if (!list_empty(&h8_actq
))
263 h8_send_next_cmd_byte();
265 Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg
);
270 case H8_RCV
: { /* H8_RCV */
271 XDprintk("H8 intr/rcv\n");
272 if (qp
->cnt
< qp
->nrsp
) {
273 qp
->rcvbuf
[qp
->cnt
] = data_reg
;
275 /* If command reception finished. */
276 if (qp
->cnt
== qp
->nrsp
) {
280 /* More commands to send over? */
281 if (!list_empty(&h8_cmdq
))
286 Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp
->cmdbuf
[0]);
290 default: /* default */
291 Dprintk("H8 intr/unknown\n");
297 static void __exit
h8_cleanup (void)
299 remove_proc_entry("driver/h8", NULL
);
300 misc_deregister(&h8_device
);
301 release_region(h8_base
, 8);
302 free_irq(h8_irq
, NULL
);
305 static int __init
h8_init(void)
307 if(request_irq(h8_irq
, h8_intr
, SA_INTERRUPT
, "h8", NULL
))
309 printk(KERN_ERR
"H8: error: IRQ %d is not free\n", h8_irq
);
312 printk(KERN_INFO
"H8 at 0x%x IRQ %d\n", h8_base
, h8_irq
);
314 create_proc_info_entry("driver/h8", 0, NULL
, h8_get_info
);
316 misc_register(&h8_device
);
317 request_region(h8_base
, 8, "h8");
323 kernel_thread(h8_monitor_thread
, NULL
, 0);
328 module_init(h8_init
);
329 module_exit(h8_cleanup
);
331 static void __init
h8_hw_init(void)
333 u_char buf
[H8_MAX_CMD_SIZE
];
335 /* set CPU speed to max for booting */
336 h8_set_cpu_speed(MHZ_230
);
341 h8_sync(); /* activate interrupts */
343 /* To clear conditions left by console */
344 h8_read_event_status();
346 /* Perform a conditioning read */
347 buf
[0] = H8_DEVICE_CONTROL
;
352 /* Turn on built-in and external mice, capture power switch */
353 buf
[0] = H8_DEVICE_CONTROL
;
355 buf
[2] = H8_ENAB_INT_PTR
| H8_ENAB_EXT_PTR
|
356 /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND
;
363 static int h8_get_info(char *buf
, char **start
, off_t fpos
, int length
)
365 #ifdef CONFIG_PROC_FS
374 0) Linux driver version (this will change if format changes)
381 p
+= sprintf(p
, "%s \n",
391 /* Called from console driver -- must make sure h8_enabled. */
392 int h8_display_blank(void)
394 #ifdef CONFIG_H8_DISPLAY_BLANK
399 error
= h8_set_display_power_state(H8_STATE_STANDBY
);
400 if (error
== H8_SUCCESS
)
402 h8_error("set display standby", error
);
407 /* Called from console driver -- must make sure h8_enabled. */
408 int h8_display_unblank(void)
410 #ifdef CONFIG_H8_DISPLAY_BLANK
415 error
= h8_set_display_power_state(H8_STATE_READY
);
416 if (error
== H8_SUCCESS
)
418 h8_error("set display ready", error
);
424 h8_alloc_queues(void)
430 qp
= (h8_cmd_q_t
*)kmalloc((sizeof (h8_cmd_q_t
) * H8_Q_ALLOC_AMOUNT
),
434 printk("H8: could not allocate memory for command queue\n");
437 /* add to the free queue */
438 save_flags(flags
); cli();
439 for (i
= 0; i
< H8_Q_ALLOC_AMOUNT
; i
++) {
440 /* place each at front of freeq */
441 list_add(&qp
[i
].link
, &h8_freeq
);
443 restore_flags(flags
);
448 * Basic means by which commands are sent to the H8.
451 h8_q_cmd(u_char
*cmd
, int cmd_size
, int resp_size
)
458 save_flags(flags
); cli();
459 while (list_empty(&h8_freeq
)) {
460 Dprintk("H8: need to allocate more cmd buffers\n");
461 restore_flags(flags
);
463 save_flags(flags
); cli();
465 /* get first element from queue */
466 qp
= list_entry(h8_freeq
.next
, h8_cmd_q_t
, link
);
469 restore_flags(flags
);
472 for (i
= 0; i
< cmd_size
; i
++)
473 qp
->cmdbuf
[i
] = cmd
[i
];
475 qp
->nrsp
= resp_size
;
477 /* queue it at the end of the cmd queue */
478 save_flags(flags
); cli();
480 /* XXX this actually puts it at the start of cmd queue, bug? */
481 list_add(&qp
->link
, &h8_cmdq
);
483 restore_flags(flags
);
489 h8_start_new_cmd(void)
494 save_flags(flags
); cli();
495 if (h8_state
!= H8_IDLE
) {
497 Dprintk("h8_start_new_cmd: not idle\n");
498 restore_flags(flags
);
502 if (!list_empty(&h8_actq
)) {
503 Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
504 restore_flags(flags
);
508 if (list_empty(&h8_cmdq
)) {
509 Dprintk("h8_start_new_cmd: no command to dequeue\n");
510 restore_flags(flags
);
514 * Take first command off of the command queue and put
515 * it on the active queue.
517 qp
= list_entry(h8_cmdq
.next
, h8_cmd_q_t
, link
);
519 /* XXX should this go to the end of the active queue? */
520 list_add(&qp
->link
, &h8_actq
);
523 Dprintk("h8_start_new_cmd: Starting a command\n");
526 WRITE_CMD(qp
->cmdbuf
[0]); /* Kick it off */
528 restore_flags(flags
);
533 h8_send_next_cmd_byte(void)
535 h8_cmd_q_t
*qp
= list_entry(h8_actq
.next
, h8_cmd_q_t
, link
);
542 Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n",
543 cnt
, qp
->cmdbuf
[cnt
]);
546 WRITE_DATA(qp
->cmdbuf
[cnt
]);
548 WRITE_CMD(qp
->cmdbuf
[cnt
]);
554 * Synchronize H8 communications channel for command transmission.
559 u_char buf
[H8_MAX_CMD_SIZE
];
562 buf
[1] = H8_SYNC_BYTE
;
567 * Responds to external interrupt. Reads event status word and
568 * decodes type of interrupt.
571 h8_read_event_status(void)
575 printk("h8_read_event_status: value 0x%x\n", intrbuf
.word
);
578 * Power related items
580 if (intrbuf
.word
& H8_DC_CHANGE
) {
582 printk("h8_read_event_status: DC_CHANGE\n");
583 /* see if dc added or removed, set batt/dc flag, send event */
585 h8_set_event_mask(H8_MANAGE_BATTERY
);
586 wake_up(&h8_monitor_wait
);
589 if (intrbuf
.word
& H8_POWER_BUTTON
) {
590 printk("Power switch pressed - please wait - preparing to power
592 h8_set_event_mask(H8_POWER_BUTTON
);
593 wake_up(&h8_monitor_wait
);
597 * Thermal related items
599 if (intrbuf
.word
& H8_THERMAL_THRESHOLD
) {
601 printk("h8_read_event_status: THERMAL_THRESHOLD\n");
602 h8_set_event_mask(H8_MANAGE_UTHERM
);
603 wake_up(&h8_monitor_wait
);
609 if (intrbuf
.word
& H8_DOCKING_STATION_STATUS
) {
611 printk("h8_read_event_status: DOCKING_STATION_STATUS\n");
612 /* read_ext_status */
614 if (intrbuf
.word
& H8_EXT_BATT_STATUS
) {
616 printk("h8_read_event_status: EXT_BATT_STATUS\n");
619 if (intrbuf
.word
& H8_EXT_BATT_CHARGE_STATE
) {
621 printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
624 if (intrbuf
.word
& H8_BATT_CHANGE_OVER
) {
626 printk("h8_read_event_status: BATT_CHANGE_OVER\n");
629 if (intrbuf
.word
& H8_WATCHDOG
) {
631 printk("h8_read_event_status: WATCHDOG\n");
634 if (intrbuf
.word
& H8_SHUTDOWN
) {
636 printk("h8_read_event_status: SHUTDOWN\n");
639 if (intrbuf
.word
& H8_KEYBOARD
) {
641 printk("h8_read_event_status: KEYBOARD\n");
644 if (intrbuf
.word
& H8_EXT_MOUSE_OR_CASE_SWITCH
) {
646 printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
649 if (intrbuf
.word
& H8_INT_BATT_LOW
) {
651 printk("h8_read_event_status: INT_BATT_LOW\n");
652 /* post event, warn user */
654 if (intrbuf
.word
& H8_INT_BATT_CHARGE_STATE
) {
656 printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n");
657 /* nop - happens often */
659 if (intrbuf
.word
& H8_INT_BATT_STATUS
) {
661 printk("h8_read_event_status: INT_BATT_STATUS\n");
664 if (intrbuf
.word
& H8_INT_BATT_CHARGE_THRESHOLD
) {
666 printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
667 /* nop - happens often */
669 if (intrbuf
.word
& H8_EXT_BATT_LOW
) {
671 printk("h8_read_event_status: EXT_BATT_LOW\n");
672 /*if no internal, post event, warn user */
680 * Function called when H8 has performed requested command.
683 h8_cmd_done(h8_cmd_q_t
*qp
)
687 switch (qp
->cmdbuf
[0]) {
689 if (h8_debug
& 0x40000)
690 printk("H8: Sync command done - byte returned was 0x%x\n",
692 list_add(&qp
->link
, &h8_freeq
);
696 case H8_RD_ENET_ADDR
:
697 printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n",
698 qp
->rcvbuf
[0], qp
->rcvbuf
[1], qp
->rcvbuf
[2],
699 qp
->rcvbuf
[3], qp
->rcvbuf
[4], qp
->rcvbuf
[5]);
700 list_add(&qp
->link
, &h8_freeq
);
706 printk("H8: Max recorded CPU temp %d, Sys temp %d\n",
707 qp
->rcvbuf
[0], qp
->rcvbuf
[1]);
708 list_add(&qp
->link
, &h8_freeq
);
712 printk("H8: Min recorded CPU temp %d, Sys temp %d\n",
713 qp
->rcvbuf
[0], qp
->rcvbuf
[1]);
714 list_add(&qp
->link
, &h8_freeq
);
717 case H8_RD_CURR_TEMP
:
718 h8_sync_channel
|= H8_RD_CURR_TEMP
;
719 xx
.byte
[0] = qp
->rcvbuf
[0];
720 xx
.byte
[1] = qp
->rcvbuf
[1];
721 wake_up(&h8_sync_wait
);
722 list_add(&qp
->link
, &h8_freeq
);
725 case H8_RD_SYS_VARIENT
:
726 case H8_RD_PWR_ON_CYCLES
:
727 printk(" H8: RD_PWR_ON_CYCLES command done\n");
730 case H8_RD_PWR_ON_SECS
:
731 printk("H8: RD_PWR_ON_SECS command done\n");
734 case H8_RD_RESET_STATUS
:
735 case H8_RD_PWR_DN_STATUS
:
736 case H8_RD_EVENT_STATUS
:
738 case H8_RD_EXT_STATUS
:
739 xx
.byte
[1] = qp
->rcvbuf
[0];
740 xx
.byte
[0] = qp
->rcvbuf
[1];
741 h8_sync_channel
|= H8_GET_EXT_STATUS
;
742 wake_up(&h8_sync_wait
);
743 list_add(&qp
->link
, &h8_freeq
);
747 case H8_RD_INT_BATT_VOLT
:
748 case H8_RD_DC_INPUT_VOLT
:
749 case H8_RD_HORIZ_PTR_VOLT
:
750 case H8_RD_VERT_PTR_VOLT
:
751 case H8_RD_EEPROM_STATUS
:
752 case H8_RD_ERR_STATUS
:
753 case H8_RD_NEW_BUSY_SPEED
:
754 case H8_RD_CONFIG_INTERFACE
:
755 case H8_RD_INT_BATT_STATUS
:
756 printk("H8: Read int batt status cmd done - returned was %x %x %x\n",
757 qp
->rcvbuf
[0], qp
->rcvbuf
[1], qp
->rcvbuf
[2]);
758 list_add(&qp
->link
, &h8_freeq
);
761 case H8_RD_EXT_BATT_STATUS
:
762 case H8_RD_PWR_UP_STATUS
:
763 case H8_RD_EVENT_STATUS_MASK
:
764 case H8_CTL_EMU_BITPORT
:
765 case H8_DEVICE_CONTROL
:
766 if(h8_debug
& 0x20000) {
767 printk("H8: Device control cmd done - byte returned was 0x%x\n",
770 list_add(&qp
->link
, &h8_freeq
);
773 case H8_CTL_TFT_BRT_DC
:
774 case H8_CTL_WATCHDOG
:
775 case H8_CTL_MIC_PROT
:
776 case H8_CTL_INT_BATT_CHG
:
777 case H8_CTL_EXT_BATT_CHG
:
778 case H8_CTL_MARK_SPACE
:
779 case H8_CTL_MOUSE_SENSITIVITY
:
780 case H8_CTL_DIAG_MODE
:
781 case H8_CTL_IDLE_AND_BUSY_SPDS
:
782 printk("H8: Idle and busy speed command done\n");
785 case H8_CTL_TFT_BRT_BATT
:
786 case H8_CTL_UPPER_TEMP
:
787 if(h8_debug
& 0x10) {
788 XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n",
791 list_add(&qp
->link
, &h8_freeq
);
794 case H8_CTL_LOWER_TEMP
:
795 case H8_CTL_TEMP_CUTOUT
:
797 case H8_CTL_CHG_THRESHOLD
:
798 case H8_CTL_TURBO_MODE
:
799 case H8_SET_DIAG_STATUS
:
800 case H8_SOFTWARE_RESET
:
802 case H8_SET_INT_BATT_PERCENT
:
803 case H8_WRT_CFG_INTERFACE_REG
:
804 case H8_WRT_EVENT_STATUS_MASK
:
805 case H8_ENTER_POST_MODE
:
806 case H8_EXIT_POST_MODE
:
809 case H8_WRT_TO_STATUS_DISP
:
810 printk("H8: Write IO status display command done\n");
813 case H8_DEFINE_SPC_CHAR
:
814 case H8_DEFINE_TABLE_STRING_ENTRY
:
815 case H8_PERFORM_EMU_CMD
:
823 printk ("H8: misc command completed\n");
830 * Retrieve the current CPU temperature and case temperature. Provides
831 * the feedback for the thermal control algorithm. Synchcronized via
832 * sleep() for priority so that no other actions in the process will take
833 * place before the data becomes available.
836 h8_get_curr_temp(u_char curr_temp
[])
838 u_char buf
[H8_MAX_CMD_SIZE
];
841 memset(buf
, 0, H8_MAX_CMD_SIZE
);
842 buf
[0] = H8_RD_CURR_TEMP
;
846 save_flags(flags
); cli();
848 while((h8_sync_channel
& H8_RD_CURR_TEMP
) == 0)
849 sleep_on(&h8_sync_wait
);
851 restore_flags(flags
);
853 h8_sync_channel
&= ~H8_RD_CURR_TEMP
;
854 curr_temp
[0] = xx
.byte
[0];
855 curr_temp
[1] = xx
.byte
[1];
859 printk("H8: curr CPU temp %d, Sys temp %d\n",
860 curr_temp
[0], curr_temp
[1]);
865 h8_get_max_temp(void)
867 u_char buf
[H8_MAX_CMD_SIZE
];
869 buf
[0] = H8_RD_MAX_TEMP
;
874 * Assigns an upper limit to the value of the H8 thermal interrupt.
875 * As an example setting a value of 115 F here will cause the
876 * interrupt to trigger when the CPU temperature reaches 115 F.
879 h8_set_upper_therm_thold(int thold
)
881 u_char buf
[H8_MAX_CMD_SIZE
];
883 /* write 0 to reinitialize interrupt */
884 buf
[0] = H8_CTL_UPPER_TEMP
;
890 buf
[0] = H8_CTL_UPPER_TEMP
;
897 h8_get_upper_therm_thold(void)
899 u_char buf
[H8_MAX_CMD_SIZE
];
901 buf
[0] = H8_CTL_UPPER_TEMP
;
908 * The external status word contains information on keyboard controller,
909 * power button, changes in external batt status, change in DC state,
910 * docking station, etc. General purpose querying use.
913 h8_get_ext_status(u_char stat_word
[])
915 u_char buf
[H8_MAX_CMD_SIZE
];
918 memset(buf
, 0, H8_MAX_CMD_SIZE
);
919 buf
[0] = H8_RD_EXT_STATUS
;
923 save_flags(flags
); cli();
925 while((h8_sync_channel
& H8_GET_EXT_STATUS
) == 0)
926 sleep_on(&h8_sync_wait
);
928 restore_flags(flags
);
930 h8_sync_channel
&= ~H8_GET_EXT_STATUS
;
931 stat_word
[0] = xx
.byte
[0];
932 stat_word
[1] = xx
.byte
[1];
936 printk("H8: curr ext status %x, %x\n",
937 stat_word
[0], stat_word
[1]);
943 * Thread attached to task 0 manages thermal/physcial state of Alphabook.
944 * When a condition is detected by the interrupt service routine, the
945 * isr does a wakeup() on h8_monitor_wait. The mask value is then
946 * screened for the appropriate action.
950 h8_monitor_thread(void * unused
)
955 * Need a logic based safety valve here. During boot when this thread is
956 * started and the thermal interrupt is not yet initialized this logic
957 * checks the temperature and acts accordingly. When this path is acted
958 * upon system boot is painfully slow, however, the priority associated
959 * with overheating is high enough to warrant this action.
961 h8_get_curr_temp(curr_temp
);
963 printk("H8: Initial CPU temp: %d\n", curr_temp
[0]);
965 if(curr_temp
[0] >= h8_uthermal_threshold
) {
966 h8_set_event_mask(H8_MANAGE_UTHERM
);
970 * Arm the upper thermal limit of the H8 so that any temp in
971 * excess will trigger the thermal control mechanism.
973 h8_set_upper_therm_thold(h8_uthermal_threshold
);
977 sleep_on(&h8_monitor_wait
);
980 printk("h8_monitor_thread awakened, mask:%x\n",
983 if (h8_event_mask
& (H8_MANAGE_UTHERM
|H8_MANAGE_LTHERM
)) {
988 if (h8_event_mask
& H8_POWER_BUTTON
) {
993 * If an external DC supply is removed or added make
994 * appropriate CPU speed adjustments.
996 if (h8_event_mask
& H8_MANAGE_BATTERY
) {
997 h8_run_level_3_manage(H8_RUN
);
998 h8_clear_event_mask(H8_MANAGE_BATTERY
);
1005 * Function implements the following policy. When the machine is booted
1006 * the system is set to run at full clock speed. When the upper thermal
1007 * threshold is reached as a result of full clock a damping factor is
1008 * applied to cool off the cpu. The default value is one quarter clock
1009 * (57 Mhz). When as a result of this cooling a temperature lower by
1010 * hmc_uthermal_window is reached, the machine is reset to a higher
1011 * speed, one half clock (115 Mhz). One half clock is maintained until
1012 * the upper thermal threshold is again reached restarting the cycle.
1016 h8_manage_therm(void)
1018 u_char curr_temp
[2];
1020 if(h8_event_mask
& H8_MANAGE_UTHERM
) {
1021 /* Upper thermal interrupt received, need to cool down. */
1023 printk("H8: Thermal threshold %d F reached\n",
1024 h8_uthermal_threshold
);
1025 h8_set_cpu_speed(h8_udamp
);
1026 h8_clear_event_mask(H8_MANAGE_UTHERM
);
1027 h8_set_event_mask(H8_MANAGE_LTHERM
);
1028 /* Check again in 30 seconds for CPU temperature */
1029 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL
);
1030 } else if (h8_event_mask
& H8_MANAGE_LTHERM
) {
1031 /* See how cool the system has become as a result
1032 of the reduction in speed. */
1033 h8_get_curr_temp(curr_temp
);
1034 last_temp
= curr_temp
[0];
1035 if (curr_temp
[0] < (h8_uthermal_threshold
- h8_uthermal_window
))
1037 /* System cooling has progressed to a point
1038 that the CPU may be sped up. */
1039 h8_set_upper_therm_thold(h8_uthermal_threshold
);
1040 h8_set_cpu_speed(h8_ldamp
); /* adjustable */
1042 printk("H8: CPU cool, applying cpu_divisor: %d \n",
1044 h8_clear_event_mask(H8_MANAGE_LTHERM
);
1046 else /* Not cool enough yet, check again in 30 seconds. */
1047 h8_start_monitor_timer(H8_TIMEOUT_INTERVAL
);
1055 * Function conditions the value of global_rpb_counter before
1056 * calling the primitive which causes the actual speed change.
1059 h8_set_cpu_speed(int speed_divisor
)
1064 * global_rpb_counter is consumed by alpha_delay() in determining just
1065 * how much time to delay. It is necessary that the number of microseconds
1066 * in DELAY(n) be kept consistent over a variety of CPU clock speeds.
1067 * To that end global_rpb_counter is here adjusted.
1070 switch (speed_divisor
) {
1072 global_rpb_counter
= rpb
->rpb_counter
* 2L;
1075 global_rpb_counter
= rpb
->rpb_counter
* 4L / 3L ;
1078 global_rpb_counter
= rpb
->rpb_counter
/ 2L;
1081 global_rpb_counter
= rpb
->rpb_counter
/ 4L;
1084 global_rpb_counter
= rpb
->rpb_counter
/ 8L;
1087 * This case most commonly needed for cpu_speed_divisor
1088 * of 2 which is the value assigned by the firmware.
1091 global_rpb_counter
= rpb
->rpb_counter
;
1094 #endif /* NOT_YET */
1097 printk("H8: Setting CPU speed to %d MHz\n",
1098 speed_tab
[speed_divisor
]);
1100 /* Make the actual speed change */
1101 lca_clock_fiddle(speed_divisor
);
1105 * Gets value stored in rpb representing CPU clock speed and adjusts this
1106 * value based on the current clock speed divisor.
1109 h8_get_cpu_speed(void)
1115 counter
= rpb
->rpb_counter
/ 1000000L;
1117 switch (alphabook_get_clock()) {
1119 speed
= counter
* 2L;
1122 speed
= counter
* 4L / 3L ;
1128 speed
= counter
/ 2L;
1131 speed
= counter
/ 4L;
1134 speed
= counter
/ 8L;
1140 printk("H8: CPU speed current setting: %d MHz\n", speed
);
1141 #endif /* NOT_YET */
1146 h8_activate_monitor(unsigned long unused
)
1148 unsigned long flags
;
1150 save_flags(flags
); cli();
1151 h8_monitor_timer_active
= 0;
1152 restore_flags(flags
);
1154 wake_up(&h8_monitor_wait
);
1158 h8_start_monitor_timer(unsigned long secs
)
1160 unsigned long flags
;
1162 if (h8_monitor_timer_active
)
1165 save_flags(flags
); cli();
1166 h8_monitor_timer_active
= 1;
1167 restore_flags(flags
);
1169 init_timer(&h8_monitor_timer
);
1170 h8_monitor_timer
.function
= h8_activate_monitor
;
1171 h8_monitor_timer
.expires
= secs
* HZ
+ jiffies
;
1172 add_timer(&h8_monitor_timer
);
1175 static void h8_set_event_mask(int mask
)
1177 unsigned long flags
;
1179 save_flags(flags
); cli();
1180 h8_event_mask
|= mask
;
1181 restore_flags(flags
);
1184 static void h8_clear_event_mask(int mask
)
1186 unsigned long flags
;
1188 save_flags(flags
); cli();
1189 h8_event_mask
&= (~mask
);
1190 restore_flags(flags
);