ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / 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/io.h>
18
19 #include <linux/types.h>
20 #include <linux/stddef.h>
21 #include <linux/timer.h>
22 #include <linux/fcntl.h>
23 #include <linux/linkage.h>
24 #include <linux/stat.h>
25 #include <linux/proc_fs.h>
26 #include <linux/miscdevice.h>
27 #include <linux/list.h>
28 #include <linux/ioport.h>
29 #include <linux/poll.h>
30 #include <linux/init.h>
31 #include <linux/slab.h>
32
33 #include "h8.h"
34
35 #define DEBUG_H8
36
37 #ifdef DEBUG_H8
38 #define Dprintk         printk
39 #else
40 #define Dprintk
41 #endif
42
43 #define XDprintk if(h8_debug==-1)printk
44
45 /*
46  * The h8 device is one of the misc char devices.
47  */
48 #define H8_MINOR_DEV   140
49
50 /*
51  * Forward declarations.
52  */
53 static int  h8_init(void);
54 static int  h8_display_blank(void);
55 static int  h8_display_unblank(void);
56
57 static void  h8_intr(int irq, void *dev_id, struct pt_regs *regs);
58
59 static int   h8_get_info(char *, char **, off_t, int);
60
61 /*
62  * Support Routines.
63  */
64 static void h8_hw_init(void);
65 static void h8_start_new_cmd(void);
66 static void h8_send_next_cmd_byte(void);
67 static void h8_read_event_status(void);
68 static void h8_sync(void);
69 static void h8_q_cmd(u_char *, int, int);
70 static void h8_cmd_done(h8_cmd_q_t *qp);
71 static int  h8_alloc_queues(void);
72
73 static u_long h8_get_cpu_speed(void);
74 static int h8_get_curr_temp(u_char curr_temp[]);
75 static void h8_get_max_temp(void);
76 static void h8_get_upper_therm_thold(void);
77 static void h8_set_upper_therm_thold(int);
78 static int h8_get_ext_status(u_char stat_word[]);
79
80 static int h8_monitor_thread(void *);
81
82 static int h8_manage_therm(void);
83 static void h8_set_cpu_speed(int speed_divisor);
84
85 static void h8_start_monitor_timer(unsigned long secs);
86 static void h8_activate_monitor(unsigned long unused);
87
88 /* in arch/alpha/kernel/lca.c */
89 extern void lca_clock_print(void);
90 extern int  lca_get_clock(void);
91 extern void lca_clock_fiddle(int);
92
93 static void h8_set_event_mask(int);
94 static void h8_clear_event_mask(int);
95
96 /*
97  * Driver structures
98  */
99
100 static struct timer_list h8_monitor_timer;
101 static int h8_monitor_timer_active = 0;
102
103 static char  driver_version[] = "X0.0";/* no spaces */
104
105 static union    intr_buf intrbuf;
106 static int      intr_buf_ptr;
107 static union   intr_buf xx;     
108 static u_char  last_temp;
109
110 /*
111  * I/O Macros for register reads and writes.
112  */
113 #define H8_READ(a)      inb((a))
114 #define H8_WRITE(d,a)   outb((d),(a))
115
116 #define H8_GET_STATUS   H8_READ((h8_base) + H8_STATUS_REG_OFF)
117 #define H8_READ_DATA    H8_READ((h8_base) + H8_DATA_REG_OFF)
118 #define WRITE_DATA(d)   H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
119 #define WRITE_CMD(d)    H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
120
121 static unsigned int h8_base = H8_BASE_ADDR;
122 static unsigned int h8_irq = H8_IRQ;
123 static unsigned int h8_state = H8_IDLE;
124 static unsigned int h8_index = -1;
125 static unsigned int h8_enabled = 0;
126
127 static LIST_HEAD(h8_actq);
128 static LIST_HEAD(h8_cmdq);
129 static LIST_HEAD(h8_freeq);
130
131 /* 
132  * Globals used in thermal control of Alphabook1.
133  */
134 static int cpu_speed_divisor = -1;                      
135 static int h8_event_mask = 0;                   
136 static DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait);
137 static unsigned int h8_command_mask = 0;
138 static int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
139 static int h8_uthermal_window = UTH_HYSTERESIS;               
140 static int h8_debug = 0xfffffdfc;
141 static int h8_ldamp = MHZ_115;
142 static int h8_udamp = MHZ_57;
143 static u_char h8_current_temp = 0;
144 static u_char h8_system_temp = 0;
145 static int h8_sync_channel = 0;
146 static DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait);
147 static int h8_init_performed;
148
149 /* CPU speeds and clock divisor values */
150 static int speed_tab[6] = {230, 153, 115, 57, 28, 14};
151   
152 /*
153  * H8 interrupt handler
154   */
155 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
156 {
157         u_char  stat_reg, data_reg;
158         h8_cmd_q_t *qp = list_entry(h8_actq.next, h8_cmd_q_t, link);
159
160         stat_reg = H8_GET_STATUS;
161         data_reg = H8_READ_DATA;
162
163         XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state, stat_reg, data_reg);
164
165         switch (h8_state) {
166           /* Response to an asynchronous event. */
167         case H8_IDLE: { /* H8_IDLE */
168             if (stat_reg & H8_OFULL) {
169                 if (data_reg == H8_INTR) {
170                     h8_state = H8_INTR_MODE;
171                     /* Executing a command to determine what happened. */
172                     WRITE_CMD(H8_RD_EVENT_STATUS);
173                     intr_buf_ptr = 1;
174                     WRITE_CMD(H8_RD_EVENT_STATUS);
175                 } else {
176                     Dprintk("h8_intr: idle stat 0x%x data 0x%x\n",
177                             stat_reg, data_reg);
178                 }
179             } else {
180                 Dprintk("h8_intr: bogus interrupt\n");
181             }
182             break;
183         }
184         case H8_INTR_MODE: { /* H8_INTR_MODE */
185             XDprintk("H8 intr/intr_mode\n");
186             if (data_reg == H8_BYTE_LEVEL_ACK) {
187                 return;
188             } else if (data_reg == H8_CMD_ACK) {
189                 return;
190             } else {
191                 intrbuf.byte[intr_buf_ptr] = data_reg;
192                 if(!intr_buf_ptr) {
193                     h8_state = H8_IDLE;
194                     h8_read_event_status();
195                 }
196                 intr_buf_ptr--;
197             }
198             break;
199         }
200         /* Placed in this state by h8_start_new_cmd(). */
201         case H8_XMIT: { /* H8_XMIT */
202             XDprintk("H8 intr/xmit\n");
203             /* If a byte level acknowledgement has been received */
204             if (data_reg == H8_BYTE_LEVEL_ACK) {
205                 XDprintk("H8 intr/xmit BYTE ACK\n");
206                 qp->nacks++;
207                 if (qp->nacks > qp->ncmd)
208                     if(h8_debug & 0x1)
209                         Dprintk("h8intr: bogus # of acks!\n");
210                 /* 
211                  * If the number of bytes sent is less than the total 
212                  * number of bytes in the command.
213                  */ 
214                 if (qp->cnt < qp->ncmd) {
215                     h8_send_next_cmd_byte();
216                 }
217                 return;
218                 /* If the complete command has produced an acknowledgement. */
219             } else if (data_reg == H8_CMD_ACK) {
220                 XDprintk("H8 intr/xmit CMD ACK\n");
221                 /* If there are response bytes */
222                 if (qp->nrsp)
223                     h8_state = H8_RCV;
224                 else
225                     h8_state = H8_IDLE;
226                 qp->cnt = 0;
227                 return;
228                 /* Error, need to start over with a clean slate. */
229             } else if (data_reg == H8_NACK) {
230                 XDprintk("h8_intr: NACK received restarting command\n");
231                 qp->nacks = 0;
232                 qp->cnt = 0;
233                 h8_state = H8_IDLE;
234                 WRITE_CMD(H8_SYNC);
235                 return;
236             } else {
237                 Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg);
238                 return;
239             }
240             break;
241         }
242         case H8_RESYNC: { /* H8_RESYNC */
243             XDprintk("H8 intr/resync\n");
244             if (data_reg == H8_BYTE_LEVEL_ACK) {
245                 return;
246             } else if (data_reg == H8_SYNC_BYTE) {
247                 h8_state = H8_IDLE;
248                 if (!list_empty(&h8_actq))
249                     h8_send_next_cmd_byte();
250             } else {
251                 Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
252                 return;
253             }
254             break;
255         } 
256         case H8_RCV: { /* H8_RCV */
257             XDprintk("H8 intr/rcv\n");
258             if (qp->cnt < qp->nrsp) {
259                 qp->rcvbuf[qp->cnt] = data_reg;
260                 qp->cnt++;
261                 /* If command reception finished. */
262                 if (qp->cnt == qp->nrsp) {
263                     h8_state = H8_IDLE;
264                     list_del(&qp->link);
265                     h8_cmd_done (qp);
266                     /* More commands to send over? */
267                     if (!list_empty(&h8_cmdq))
268                         h8_start_new_cmd();
269                 }
270                 return;
271             } else {
272                 Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp->cmdbuf[0]);
273             }
274             break;
275         }
276         default: /* default */
277             Dprintk("H8 intr/unknown\n");
278             break;
279         }
280         return;
281 }
282
283 static void __exit h8_cleanup (void)
284 {
285         remove_proc_entry("driver/h8", NULL);
286         release_region(h8_base, 8);
287         free_irq(h8_irq, NULL);
288 }
289
290 static int __init h8_init(void)
291 {
292         if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL))
293         {
294                 printk(KERN_ERR "H8: error: IRQ %d is not free\n", h8_irq);
295                 return -EIO;
296         }
297         printk(KERN_INFO "H8 at 0x%x IRQ %d\n", h8_base, h8_irq);
298
299         if (!request_region(h8_base, 8, "h8"))
300          {
301                 free_irq(h8_irq, NULL);
302                 return -EIO;
303          }
304
305         create_proc_info_entry("driver/h8", 0, NULL, h8_get_info);
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");