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