linux-old/drivers/char/h8.c
<<
>>
Prefs
   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 */
  57static int  h8_init(void);
  58static int  h8_display_blank(void);
  59static int  h8_display_unblank(void);
  60
  61static void  h8_intr(int irq, void *dev_id, struct pt_regs *regs);
  62
  63static int   h8_get_info(char *, char **, off_t, int);
  64
  65/*
  66 * Support Routines.
  67 */
  68static void h8_hw_init(void);
  69static void h8_start_new_cmd(void);
  70static void h8_send_next_cmd_byte(void);
  71static void h8_read_event_status(void);
  72static void h8_sync(void);
  73static void h8_q_cmd(u_char *, int, int);
  74static void h8_cmd_done(h8_cmd_q_t *qp);
  75static int  h8_alloc_queues(void);
  76
  77static u_long h8_get_cpu_speed(void);
  78static int h8_get_curr_temp(u_char curr_temp[]);
  79static void h8_get_max_temp(void);
  80static void h8_get_upper_therm_thold(void);
  81static void h8_set_upper_therm_thold(int);
  82static int h8_get_ext_status(u_char stat_word[]);
  83
  84static int h8_monitor_thread(void *);
  85
  86static int h8_manage_therm(void);
  87static void h8_set_cpu_speed(int speed_divisor);
  88
  89static void h8_start_monitor_timer(unsigned long secs);
  90static void h8_activate_monitor(unsigned long unused);
  91
  92/* in arch/alpha/kernel/lca.c */
  93extern void lca_clock_print(void);
  94extern int  lca_get_clock(void);
  95extern void lca_clock_fiddle(int);
  96
  97static void h8_set_event_mask(int);
  98static void h8_clear_event_mask(int);
  99
 100/*
 101 * Driver structures
 102 */
 103
 104static struct timer_list h8_monitor_timer;
 105static int h8_monitor_timer_active = 0;
 106
 107static char  driver_version[] = "X0.0";/* no spaces */
 108
 109static union    intr_buf intrbuf;
 110static int      intr_buf_ptr;
 111static union   intr_buf xx;     
 112static 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
 125static unsigned int h8_base = H8_BASE_ADDR;
 126static unsigned int h8_irq = H8_IRQ;
 127static unsigned int h8_state = H8_IDLE;
 128static unsigned int h8_index = -1;
 129static unsigned int h8_enabled = 0;
 130
 131static LIST_HEAD(h8_actq);
 132static LIST_HEAD(h8_cmdq);
 133static LIST_HEAD(h8_freeq);
 134
 135/* 
 136 * Globals used in thermal control of Alphabook1.
 137 */
 138static int cpu_speed_divisor = -1;                      
 139static int h8_event_mask = 0;                   
 140static DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait);
 141static unsigned int h8_command_mask = 0;
 142static int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
 143static int h8_uthermal_window = UTH_HYSTERESIS;               
 144static int h8_debug = 0xfffffdfc;
 145static int h8_ldamp = MHZ_115;
 146static int h8_udamp = MHZ_57;
 147static u_char h8_current_temp = 0;
 148static u_char h8_system_temp = 0;
 149static int h8_sync_channel = 0;
 150static DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait);
 151static int h8_init_performed;
 152
 153/* CPU speeds and clock divisor values */
 154static int speed_tab[6] = {230, 153, 115, 57, 28, 14};
 155  
 156/*
 157 * H8 interrupt handler
 158  */
 159static 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
 287static 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
 294static 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
 316module_init(h8_init);
 317module_exit(h8_cleanup);
 318
 319static 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
 351static 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. */
 380static 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. */
 396static 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
 411static 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 */
 437void
 438h8_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
 475void
 476h8_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
 519void
 520h8_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 */
 543void
 544h8_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 */
 557void
 558h8_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 
 578off\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 */
 669static void
 670h8_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 */
 822int
 823h8_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
 851static void
 852h8_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 */
 865static void
 866h8_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
 883static void
 884h8_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 */
 899int
 900h8_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
 936int
 937h8_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
1002int
1003h8_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 */
1045void
1046h8_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 */
1095u_long
1096h8_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
1132static void
1133h8_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
1144static void
1145h8_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
1162static 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
1171static 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
1180MODULE_LICENSE("GPL");
1181EXPORT_NO_SYMBOLS;
1182
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.