diff options
Diffstat (limited to 'drivers/sbus/char/envctrl.c')
-rw-r--r-- | drivers/sbus/char/envctrl.c | 1373 |
1 files changed, 1311 insertions, 62 deletions
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index fbc93415d..b6ec0a42d 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1,17 +1,23 @@ -/* $Id: envctrl.c,v 1.9 1998/11/06 07:38:20 ecd Exp $ +/* $Id: envctrl.c,v 1.12 1999/08/31 06:58:04 davem Exp $ * envctrl.c: Temperature and Fan monitoring on Machines providing it. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ +#include <linux/version.h> #include <linux/config.h> #include <linux/module.h> + +#define __KERNEL_SYSCALLS__ #include <linux/sched.h> +#include <linux/unistd.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/ioport.h> #include <linux/init.h> +#include <linux/malloc.h> #include <linux/miscdevice.h> +#include <linux/smp_lock.h> #include <asm/ebus.h> #include <asm/uaccess.h> @@ -20,8 +26,16 @@ #define ENVCTRL_MINOR 162 -#undef DEBUG_BUS_SCAN +#undef U450_SUPPORT /* might fry you machine, careful here !!! */ + +#define DEBUG 1 +#define DEBUG_BUS_SCAN 1 + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +#define schedule_timeout(a) { current->timeout = jiffies + (a); schedule(); } +#endif #define PCF8584_ADDRESS 0x55 @@ -61,13 +75,196 @@ #define I2C_WRITE 0x00 #define I2C_READ 0x01 -struct pcf8584_reg +/* PCF8584 register offsets */ +#define I2C_DATA 0x00UL +#define I2C_CSR 0x01UL + +struct i2c_device { + unsigned char addr; + struct i2c_device *next; +}; + +static unsigned long i2c_regs; +static struct i2c_device *i2c_devices; + +static int errno; + +#define MAX_TEMPERATURE 111 +#define MAX_FAN_SPEED 63 + + +/* + * UltraAXi constants. + */ +#define AXI_THERM_ADDR 0x9e +#define AXI_THERM_PORT_CPU 0 +#define AXI_THERM_PORT_MOD 1 +#define AXI_THERM_PORT_PCI 2 +#define AXI_THERM_PORT_DISK 3 + +#define AXI_FAN_ADDR 0x4e +#define AXI_FAN_PORT_FRONT 0 +#define AXI_FAN_PORT_BACK 1 + +#define AXI_PIO_ADDR 0x70 + +/* + * Ultra 450 constants. + */ +#define U450_FAN_ADDR 0x4e +#define U450_FAN_PORT_CPU 0 +#define U450_FAN_PORT_PS 1 + +#define U450_PIO_ADDR 0x70 +#define U450_TIMER_ADDR 0xa0 + +static unsigned char +axi_cpu_temp_table[256] = +{ + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x67, + 0x66, 0x65, 0x64, 0x63, 0x61, 0x60, 0x5f, 0x5e, + 0x5d, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x55, 0x54, + 0x53, 0x52, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4a, + 0x49, 0x48, 0x47, 0x46, 0x44, 0x43, 0x42, 0x41, + 0x40, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3b, 0x3a, + 0x3a, 0x39, 0x39, 0x38, 0x38, 0x37, 0x37, 0x36, + 0x36, 0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, + 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, + 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, + 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, + 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, + 0x21, 0x21, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, + 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, + 0x15, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, + 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, + 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, + 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char +axi_mod_temp_table[256] = { - __volatile__ unsigned char data; - __volatile__ unsigned char csr; + 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, + 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, + 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, + 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, + 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x39, + 0x39, 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, + 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2e, + 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, + 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, + 0x26, 0x26, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, + 0x23, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x1f, + 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, + 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, + 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, + 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static struct pcf8584_reg *i2c; +static unsigned char +axi_fan_speeds[112] = +{ + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, + 0x22, 0x23, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2d, + 0x2f, 0x30, 0x32, 0x33, 0x35, 0x37, 0x38, 0x3a, + 0x3b, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f +}; + + +struct therm_regs { + u32 addr; + u32 port; + u32 min_temp; + u32 warning; + u32 shutdown; + u32 num; + u32 den; +}; + +struct thermistor { + char name[8]; + struct therm_regs regs; + unsigned char (*temperature) (struct thermistor *); + unsigned char (*fan_speed) (struct thermistor *); + struct thermistor *next; /* all thermistors */ + struct thermistor *chain; /* thermistors for one fan */ +}; + +struct fan_regs { + u32 addr; + u32 port; +}; + +struct fan { + char name[8]; + struct fan_regs regs; + int (*set_speed)(struct fan *, unsigned char value); + int (*check_failure)(struct fan *); + unsigned char value; + struct thermistor *monitor; + struct fan *next; +}; + + +struct environment { + struct thermistor *thermistors; + struct fan *fans; + unsigned char *cpu_temp_table; + unsigned char *cpu_fan_speeds; + unsigned char *ps_temp_table; + unsigned char *ps_fan_speeds; + void (*enable) (struct environment *); + void (*disable) (struct environment *); + void (*keep_alive) (struct environment *); + int interval; + pid_t kenvd_pid; + wait_queue_head_t kenvd_wait; + int terminate; +}; + + +static struct environment envctrl; #ifdef DEBUG_BUS_SCAN @@ -78,37 +275,42 @@ struct i2c_addr_map { }; static struct i2c_addr_map devmap[] = { - { 0x38, 0x78, "PCF8574A" }, - { 0x20, 0x78, "TDA8444" }, - { 0x48, 0x78, "PCF8591" }, + { 0x70, 0xf0, "PCF8574A" }, + { 0x40, 0xf0, "TDA8444" }, + { 0x90, 0xf0, "PCF8591" }, + { 0xa0, 0xf0, "PCF8583" }, }; #define NR_DEVMAP (sizeof(devmap) / sizeof(devmap[0])) #endif static __inline__ int -PUT_DATA(__volatile__ unsigned char *data, char *buffer, int user) +PUT_DATA(unsigned long data, char *buffer, int user) { if (user) { - if (put_user(*data, buffer)) + u8 tmp = readb(data); + if (put_user(tmp, buffer)) return -EFAULT; } else { - *buffer = *data; + *buffer = readb(data); } return 0; } static __inline__ int -GET_DATA(__volatile__ unsigned char *data, const char *buffer, int user) +GET_DATA(unsigned long data, const char *buffer, int user) { if (user) { - if (get_user(*data, buffer)) + u8 tmp; + if (get_user(tmp, buffer)) return -EFAULT; + writeb(tmp, data); } else { - *data = *buffer; + writeb(*buffer, data); } return 0; } + static int i2c_read(unsigned char dev, char *buffer, int len, int user) { @@ -117,16 +319,17 @@ i2c_read(unsigned char dev, char *buffer, int len, int user) int error = -ENODEV; int count = 0; - i2c->data = (dev << 1) | I2C_READ; + writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA); - while (!(i2c->csr & STATUS_BB)) + while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB)) udelay(1); - i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK; + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, + i2c_regs + I2C_CSR); do { udelay(1); - while ((stat = i2c->csr) & STATUS_PIN) + while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) udelay(1); if (stat & STATUS_LRB) @@ -142,29 +345,30 @@ i2c_read(unsigned char dev, char *buffer, int len, int user) break; if (count++ > 0) { - error = PUT_DATA(&i2c->data, buffer++, user); + error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); if (error) break; } else - dummy = i2c->data; + dummy = readb(i2c_regs + I2C_DATA); } while (1); - i2c->csr = CONTROL_ES0; + writeb(CONTROL_ES0, i2c_regs + I2C_CSR); if (!error && (count++ > 0)) - error = PUT_DATA(&i2c->data, buffer++, user); + error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); else - dummy = i2c->data; + dummy = readb(i2c_regs + I2C_DATA); udelay(1); - while ((stat = i2c->csr) & STATUS_PIN) + while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) udelay(1); stop: - i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, + i2c_regs + I2C_CSR); if (!error && (count++ > 0)) - error = PUT_DATA(&i2c->data, buffer++, user); + error = PUT_DATA(i2c_regs + I2C_DATA, buffer++, user); else - dummy = i2c->data; + dummy = readb(i2c_regs + I2C_DATA); if (error) return error; @@ -176,20 +380,33 @@ i2c_write(unsigned char dev, const char *buffer, int len, int user) { int error = -ENODEV; int count = 0; + int timeout; - while (!(i2c->csr & STATUS_BB)) + timeout = 1000000; + while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB) && --timeout) udelay(1); + if (!timeout) { + printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__); + return -ENODEV; + } - i2c->data = (dev << 1) | I2C_WRITE; - i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK; + writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA); + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, + i2c_regs + I2C_CSR); do { unsigned char stat; udelay(1); - while ((stat = i2c->csr) & STATUS_PIN) + timeout = 1000000; + while (((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) && --timeout) udelay(1); + if (!timeout) { + printk("%s [%d]: TIMEOUT\n", __FUNCTION__, __LINE__); + break; + } + if (stat & STATUS_LRB) break; @@ -197,32 +414,172 @@ i2c_write(unsigned char dev, const char *buffer, int len, int user) if (count == len) break; - error = GET_DATA(&i2c->data, buffer++, user); + error = GET_DATA(i2c_regs + I2C_DATA, buffer++, user); if (error) break; count++; } while (1); - i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, + i2c_regs + I2C_CSR); return error; } -__initfunc(static int i2c_scan_bus(void)) +#ifdef U450_SUPPORT +static int +i2c_write_read(unsigned char dev, char *outbuf, int outlen, + char *inbuf, int inlen, int user) +{ + unsigned char dummy; + unsigned char stat; + int error = -ENODEV; + int count = 0; + + while (!(readb(i2c_regs + I2C_CSR) & STATUS_BB)) + udelay(1); + + writeb((dev & 0xfe) | I2C_WRITE, i2c_regs + I2C_DATA); + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, + i2c_regs + I2C_CSR); + + do { + unsigned char stat; + + udelay(1); + while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) + udelay(1); + + if (stat & STATUS_LRB) + break; + + error = count; + if (count == outlen) + break; + + error = GET_DATA(i2c_regs + I2C_DATA, outbuf++, user); + if (error) + break; + + count++; + } while (1); + + if (error < 0) { + writeb(CONTROL_PIN | CONTROL_ES0 | + CONTROL_STO | CONTROL_ACK, i2c_regs + I2C_CSR); + return error; + } + + writeb(CONTROL_ES0 | CONTROL_STA | CONTROL_ACK, i2c_regs + I2C_CSR); + udelay(1); + writeb((dev & 0xfe) | I2C_READ, i2c_regs + I2C_DATA); + + count = 0; + do { + udelay(1); + while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) + udelay(1); + + if (stat & STATUS_LRB) + goto stop; + + error = 0; + if (inlen == 0) { + count--; + break; + } + + if (count == (inlen - 1)) + break; + + if (count++ > 0) { + error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); + if (error) + break; + } else + dummy = readb(i2c_regs + I2C_DATA); + } while (1); + + writeb(CONTROL_ES0, i2c_regs + I2C_CSR); + if (!error && (count++ > 0)) + error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); + else + dummy = readb(i2c_regs + I2C_DATA); + + udelay(1); + while ((stat = readb(i2c_regs + I2C_CSR)) & STATUS_PIN) + udelay(1); + +stop: + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK, + i2c_regs + I2C_CSR); + if (!error && (count++ > 0)) + error = PUT_DATA(i2c_regs + I2C_DATA, inbuf++, user); + else + dummy = readb(i2c_regs + I2C_DATA); + + if (error) + return error; + return count - 1; +} +#endif /* U450_SUPPORT */ + +static struct i2c_device * +i2c_find_device(unsigned char addr) { - unsigned char dev; + struct i2c_device *dev; + + for (dev = i2c_devices; dev; dev = dev->next) { + if (dev->addr == addr) + return dev; + } + return 0; +} + +static void +i2c_free_devices(void) +{ + struct i2c_device *dev; + + dev = i2c_devices; + while (dev) { + i2c_devices = dev->next; + kfree(dev); + dev = i2c_devices; + } +} + +static __init int i2c_scan_bus(void) +{ + struct i2c_device *dev, **last; + unsigned int addr; int count = 0; - for (dev = 1; dev < 128; dev++) { - if (i2c_read(dev, 0, 0, 0) == 0) { + last = &i2c_devices; + for (addr = 0; addr < 256; addr += 2) { + if (i2c_write(addr, 0, 0, 0) == 0) { #ifdef DEBUG_BUS_SCAN int i; for (i = 0; i < NR_DEVMAP; i++) - if ((dev & devmap[i].mask) == devmap[i].addr) + if ((addr & devmap[i].mask) == devmap[i].addr) break; - printk("envctrl: i2c device at %02x: %s\n", dev, + printk("envctrl: i2c device at %02x: %s\n", addr, i < NR_DEVMAP ? devmap[i].name : "unknown"); #endif + + dev = kmalloc(sizeof(struct i2c_device), GFP_KERNEL); + if (!dev) { + printk("i2c: can't alloc i2c_device\n"); + i2c_free_devices(); + return -ENOMEM; + } + memset(dev, 0, sizeof(struct i2c_device)); + + dev->addr = addr; + + *last = dev; + last = &dev->next; + count++; } } @@ -233,6 +590,872 @@ __initfunc(static int i2c_scan_bus(void)) return 0; } + +static int +read_8591(unsigned char dev, unsigned char offset, unsigned char *value) +{ + unsigned char data[2]; + + data[0] = 0x40 | offset; + if (i2c_write(dev, data, 1, 0) != 1) + return -1; + if (i2c_read(dev, data, 2, 0) != 2) + return -1; + *value = data[1]; + return 0; +} + +static int +write_8444(unsigned char dev, unsigned char offset, unsigned char value) +{ + unsigned char data[2]; + + data[0] = offset; + data[1] = value; + if (i2c_write(dev, data, 2, 0) != 2) + return -1; + return 0; +} + +#ifdef U450_SUPPORT +static int +read_8583(unsigned char dev, unsigned char offset, unsigned char *value) +{ + unsigned char data; + + data = offset; + if (i2c_write_read(dev, &data, 1, &data, 1, 0) != 1) + return -1; + *value = data; + return 0; +} + +static int +write_8583(unsigned char dev, unsigned char offset, unsigned char value) +{ + unsigned char data[2]; + + data[0] = offset; + data[1] = value; + if (i2c_write(dev, data, 2, 0) != 2) + return -1; + return 0; +} +#endif /* U450_SUPPORT */ + +struct thermistor * +find_thermistor(const char *name, struct thermistor *from) +{ + int n; + + if (!from) + from = envctrl.thermistors; + else + from = from->next; + + n = strlen(name); + while (from && strncmp(from->name, name, n)) + from = from->next; + + return from; +} + +void +check_temperatures(struct environment *env) +{ + struct thermistor *t; + + for (t = env->thermistors; t; t = t->next) { +#ifdef DEBUG + printk("Thermistor `%s' [%02x:%d]: " + "%d C (%d C, %d C)\n", + t->name, t->regs.addr, t->regs.port, + t->temperature(t), t->regs.warning, t->regs.shutdown); +#endif + + /* + * Implement slow-down or shutdown here... + */ + } +} + +void +check_fan_speeds(struct environment *env) +{ + unsigned char speed, max; + struct thermistor *t; + struct fan *f; + + for (f = env->fans; f; f = f->next) { +#ifdef DEBUG + printk("Fan `%s' [%02x:%d]:", f->name, + f->regs.addr, f->regs.port); +#endif + max = 0; + for (t = f->monitor; t; t = t->chain) { + speed = t->fan_speed(t); + if (speed > max) + max = speed; +#ifdef DEBUG + printk(" %s:%02x", t->name, speed); +#endif + } + + f->set_speed(f, max); +#ifdef DEBUG + printk(" -> %02x\n", f->value); +#endif + } +} + +void +envctrl_fans_blast(struct environment *env) +{ + struct fan *f; + + for (f = env->fans; f; f = f->next) + f->set_speed(f, MAX_FAN_SPEED); +} + +int +kenvd(void *data) +{ + struct environment *env = data; + + MOD_INC_USE_COUNT; + lock_kernel(); + + env->kenvd_pid = current->pid; + + exit_files(current); + exit_mm(current); + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL)); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + current->session = 1; + current->pgrp = 1; + strcpy(current->comm, "kenvd"); + + if (env->enable) + env->enable(env); + + while (!env->terminate) { + + check_temperatures(env); + check_fan_speeds(env); + if (env->keep_alive) + env->keep_alive(env); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(env->interval * HZ); + + if (signal_pending(current)) { + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + break; + } + } + + if (env->disable) + env->disable(env); + + env->kenvd_pid = 0; + wake_up(&envctrl.kenvd_wait); + + MOD_DEC_USE_COUNT; + return 0; +} + +void +envctrl_stop(void) +{ + DECLARE_WAITQUEUE(wait, current); + struct thermistor *t; + struct fan *f; + pid_t pid; + + if (envctrl.kenvd_pid) { + pid = envctrl.kenvd_pid; + + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&envctrl.kenvd_wait, &wait); + + envctrl.terminate = 1; + kill_proc(pid, SIGKILL, 1); + + schedule(); + + remove_wait_queue(&envctrl.kenvd_wait, &wait); + current->state = TASK_RUNNING; + } + + t = envctrl.thermistors; + while (t) { + envctrl.thermistors = t->next; + kfree(t); + t = envctrl.thermistors; + } + + f = envctrl.fans; + while (f) { + envctrl.fans = f->next; + kfree(f); + f = envctrl.fans; + } + + if (envctrl.cpu_temp_table) + kfree(envctrl.cpu_temp_table); + + if (envctrl.cpu_fan_speeds) + kfree(envctrl.cpu_fan_speeds); + + if (envctrl.ps_temp_table) + kfree(envctrl.ps_temp_table); + + if (envctrl.ps_fan_speeds) + kfree(envctrl.ps_fan_speeds); +} + + +static unsigned char +axi_get_temperature(struct thermistor *t) +{ + unsigned char value; + + if (read_8591(t->regs.addr, t->regs.port, &value) < 0) + return MAX_TEMPERATURE; + if (t->regs.port == AXI_THERM_PORT_CPU) + return axi_cpu_temp_table[value]; + else + return axi_mod_temp_table[value]; +} + +static unsigned char +axi_get_fan_speed(struct thermistor *t) +{ + unsigned char temp; + + temp = t->temperature(t); + if (temp >= MAX_TEMPERATURE) + return MAX_FAN_SPEED; + + return axi_fan_speeds[temp]; +} + +static int +axi_set_fan_speed(struct fan *f, unsigned char value) +{ + if (value != f->value) { + if (write_8444(f->regs.addr, f->regs.port, value)) + return -1; + f->value = value; + } + return 0; +} + +static void +axi_toggle_i2c_int(struct environment *env) +{ + unsigned char data; + + if (i2c_read(AXI_PIO_ADDR, &data, 1, 0) != 1) + return; + + data &= ~(0x08); + if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1) + return; + mdelay(1); + + data |= 0x08; + if (i2c_write(AXI_PIO_ADDR, &data, 1, 0) != 1) + return; + mdelay(1); +} + + +static int +rasctrl_setup(int node) +{ + struct thermistor *t, **tlast; + struct fan *f, **flast; + char tmp[32]; + int monitor; + int shutdown; + int warning; + int i; + + prom_getstring(prom_root_node, "name", tmp, sizeof(tmp)); + if (strcmp(tmp, "SUNW,UltraSPARC-IIi-Engine")) { + printk("SUNW,rasctrl will work only on Ultra AXi\n"); + return -ENODEV; + } + + monitor = prom_getintdefault(node, "env-monitor", 0); + if (monitor == 0) + return -ENODEV; + + envctrl.interval = prom_getintdefault(node, "env-mon-interval", 60); + warning = prom_getintdefault(node, "warning-temp", 55); + shutdown = prom_getintdefault(node, "shutdown-temp", 58); + + tlast = &envctrl.thermistors; + for (i = 0; i < 4; i++) { + t = kmalloc(sizeof(struct thermistor), GFP_KERNEL); + if (!t) + goto out; + memset(t, 0, sizeof(struct thermistor)); + + t->regs.addr = AXI_THERM_ADDR; + t->regs.port = i; + t->regs.warning = warning; + t->regs.shutdown = shutdown; + + switch (i) { + case AXI_THERM_PORT_CPU: + sprintf(t->name, "%.7s", "CPU"); + break; + case AXI_THERM_PORT_MOD: + sprintf(t->name, "%.7s", "MOD"); + break; + case AXI_THERM_PORT_PCI: + sprintf(t->name, "%.7s", "PCI"); + break; + case AXI_THERM_PORT_DISK: + sprintf(t->name, "%.7s", "DISK"); + break; + } + + t->temperature = axi_get_temperature; + t->fan_speed = axi_get_fan_speed; + + if (!i2c_find_device(t->regs.addr)) { + printk("envctrl: `%s': i2c device %02x not found\n", + t->name, t->regs.addr); + kfree(t); + continue; + } + + *tlast = t; + tlast = &t->next; + } + + flast = &envctrl.fans; + for (i = 0; i < 2; i++) { + f = kmalloc(sizeof(struct fan), GFP_KERNEL); + if (!f) + goto out; + memset(f, 0, sizeof(struct fan)); + + f->regs.addr = AXI_FAN_ADDR; + f->regs.port = i; + + switch (i) { + case AXI_FAN_PORT_FRONT: + sprintf(f->name, "%.7s", "FRONT"); + t = NULL; + while ((t = find_thermistor("CPU", t))) { + t->chain = f->monitor; + f->monitor = t; + } + break; + case AXI_FAN_PORT_BACK: + sprintf(f->name, "%.7s", "BACK"); + t = NULL; + while ((t = find_thermistor("PCI", t))) { + t->chain = f->monitor; + f->monitor = t; + } + break; + } + + if (!f->monitor) { + kfree(f); + continue; + } + + if (!i2c_find_device(f->regs.addr)) { + printk("envctrl: `%s': i2c device %02x not found\n", + f->name, f->regs.addr); + kfree(f); + continue; + } + + *flast = f; + flast = &f->next; + + f->check_failure = NULL; + f->set_speed = axi_set_fan_speed; + } + + envctrl.enable = axi_toggle_i2c_int; + envctrl.disable = envctrl_fans_blast; + +#ifdef DEBUG + printk("Warn: %d C, Shutdown %d C, Interval %d s, Monitor %d\n", + warning, shutdown, envctrl.interval, monitor); +#endif + return 0; + +out: + envctrl_stop(); + return -ENODEV; +} + + +#ifdef U450_SUPPORT + +static unsigned char +envctrl_get_temperature(struct thermistor *t) +{ + unsigned char value; + + if (read_8591(t->regs.addr, t->regs.port, &value) < 0) + return MAX_TEMPERATURE; + if (!strncmp(t->name, "CPU", 3)) + return envctrl.cpu_temp_table[value]; + else + return envctrl.ps_temp_table[value]; +} + +static unsigned char +envctrl_get_fan_speed(struct thermistor *t) +{ + unsigned char temp; + + temp = t->temperature(t); + if (temp >= MAX_TEMPERATURE) + return MAX_FAN_SPEED; + + if (!strncmp(t->name, "CPU", 3)) + return envctrl.cpu_fan_speeds[temp]; + else + return envctrl.ps_fan_speeds[temp]; +} + +static int +envctrl_set_fan_speed(struct fan *f, unsigned char value) +{ + if (value != f->value) { + if (write_8444(f->regs.addr, f->regs.port, value)) + return -1; + f->value = value; + } + + return 0; +} + +static unsigned char u450_default_thermisters[] = +{ + /* CPU0 */ + 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x30, 0x00, + /* CPU1 */ + 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x31, 0x00, + /* CPU2 */ + 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x32, 0x00, + /* CPU3 */ + 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x55, 0x33, 0x00, + /* PS0 */ + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x30, 0x00, + /* PS1 */ + 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x31, 0x00, + /* PS2 */ + 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5a, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x32, 0x00, + /* AMB */ + 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x41, 0x4d, 0x42, 0x00 +}; + +static unsigned char u450_default_cpu_temp_factors[] = +{ + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x94, 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, + 0x8a, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, + 0x79, 0x79, 0x78, 0x78, 0x77, 0x76, 0x75, 0x74, + 0x73, 0x72, 0x71, 0x70, 0x70, 0x6f, 0x6f, 0x6e, + 0x6e, 0x6e, 0x6d, 0x6d, 0x6c, 0x6b, 0x6a, 0x6a, + 0x69, 0x69, 0x68, 0x67, 0x66, 0x65, 0x65, 0x64, + 0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x61, 0x61, + 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5d, 0x5d, + 0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, + 0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x56, 0x56, + 0x55, 0x55, 0x54, 0x54, 0x53, 0x53, 0x52, 0x52, + 0x52, 0x51, 0x51, 0x50, 0x50, 0x50, 0x50, 0x4f, + 0x4f, 0x4f, 0x4e, 0x4e, 0x4e, 0x4d, 0x4d, 0x4d, + 0x4c, 0x4c, 0x4c, 0x4b, 0x4b, 0x4b, 0x4a, 0x4a, + 0x4a, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x47, + 0x47, 0x47, 0x46, 0x46, 0x46, 0x46, 0x45, 0x45, + 0x45, 0x44, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, + 0x43, 0x42, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, + 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, + 0x3e, 0x3d, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, + 0x37, 0x37, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, + 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32, + 0x32, 0x32, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, + 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, + 0x2d, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2a, + 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28 +}; + +static unsigned char u450_default_cpu_fan_speeds[] = +{ + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x2a, 0x2b, 0x2d, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f +}; + +static unsigned char u450_default_ps_temp_factors[] = +{ + 0x9a, 0x96, 0x82, 0x7d, 0x78, 0x73, 0x6e, 0x6b, + 0x69, 0x67, 0x64, 0x5f, 0x5a, 0x57, 0x55, 0x53, + 0x51, 0x50, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x47, + 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3f, + 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x39, + 0x38, 0x37, 0x37, 0x36, 0x35, 0x35, 0x34, 0x33, + 0x32, 0x32, 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, + 0x2f, 0x2e, 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2b, + 0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, + 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, + 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, + 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, + 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, + 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19, + 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, + 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, + 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, + 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char u450_default_ps_fan_speeds[] = +{ + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f +}; + +static void +u450_toggle_i2c_int(struct environment *env) +{ + unsigned char tmp[80]; + unsigned char data; + int i, n; + + write_8583(U450_TIMER_ADDR, 0, 0x84); + write_8583(U450_TIMER_ADDR, 8, 0x0a); + write_8583(U450_TIMER_ADDR, 7, 0x00); + write_8583(U450_TIMER_ADDR, 0, 0x04); + + n = sprintf(tmp, "envctrl: PCF8583:"); + for (i = 0; i < 16; i++) { + if (read_8583(U450_TIMER_ADDR, i, &data) < 0) { + printk("envctrl: error reading PCF8583\n"); + break; + } + n += sprintf(tmp+n, " %02x", data); + } + printk("%s\n", tmp); + +#if 1 + data = 0x70; + if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1) + return; + mdelay(1); + + data = 0x78; + if (i2c_write(U450_PIO_ADDR, &data, 1, 0) != 1) + return; + mdelay(1); +#endif +} + +static void +u450_set_egg_timer(struct environment *env) +{ + unsigned char value; + +#if 0 + write_8583(U450_TIMER_ADDR, 0x00, 0x84); + read_8583(U450_TIMER_ADDR, 0x07, &value); + write_8583(U450_TIMER_ADDR, 0x07, 0x00); + write_8583(U450_TIMER_ADDR, 0x00, 0x04); +#else + read_8583(U450_TIMER_ADDR, 0x07, &value); + printk("envctrl: TIMER [%02x:07]: %02x\n", U450_TIMER_ADDR, value); + read_8583(U450_TIMER_ADDR, 0x00, &value); + printk("envctrl: TIMER [%02x:00]: %02x\n", U450_TIMER_ADDR, value); +#endif +} + +static int +envctrl_setup(int node) +{ + struct thermistor *t, **tlast; + struct fan *f, **flast; + unsigned char *tmp = NULL, *p; + int len, n, err; + int defaults = 0; + + len = prom_getproplen(node, "thermisters"); + if (len <= 0) { + printk("envctrl: no property `thermisters', using defaults\n"); + defaults++; + len = sizeof(u450_default_thermisters); + } + + tmp = (unsigned char *)kmalloc(len, GFP_KERNEL); + if (!tmp) { + printk("envctrl: can't allocate property buffer\n"); + return -ENODEV; + } + + if (defaults) { + memcpy(tmp, u450_default_thermisters, len); + } else { + err = prom_getproperty(node, "thermisters", tmp, len); + if (err < 0) { + printk("envctrl: error reading property `thermisters'\n"); + kfree(tmp); + return -ENODEV; + } + } + + p = tmp; + err = -ENOMEM; + + tlast = &envctrl.thermistors; + while (len > sizeof(struct therm_regs)) { + t = kmalloc(sizeof(struct thermistor), GFP_KERNEL); + if (!t) { + printk("envctrl: can't allocate thermistor struct\n"); + goto out; + } + memset(t, 0, sizeof(struct thermistor)); + + memcpy(&t->regs, p, sizeof(struct therm_regs)); + p += sizeof(struct therm_regs); + len -= sizeof(struct therm_regs); + + n = strlen(p) + 1; + strncpy(t->name, p, 7); + p += n; + len -= n; + + if (!i2c_find_device(t->regs.addr)) { + printk("envctrl: `%s': i2c device %02x not found\n", + t->name, t->regs.addr); + kfree(t); + continue; + } + + t->temperature = envctrl_get_temperature; + t->fan_speed = envctrl_get_fan_speed; + + *tlast = t; + tlast = &t->next; + } + + flast = &envctrl.fans; + for (n = 0; n < 2; n++) { + f = kmalloc(sizeof(struct fan), GFP_KERNEL); + if (!f) + goto out; + memset(f, 0, sizeof(struct fan)); + + f->regs.addr = U450_FAN_ADDR; + f->regs.port = n; + + switch (n) { + case U450_FAN_PORT_CPU: + sprintf(f->name, "%.7s", "CPU"); + t = NULL; + while ((t = find_thermistor("CPU", t))) { + t->chain = f->monitor; + f->monitor = t; + } + break; + case U450_FAN_PORT_PS: + sprintf(f->name, "%.7s", "PS"); + t = NULL; + while ((t = find_thermistor("PS", t))) { + t->chain = f->monitor; + f->monitor = t; + } + break; + } + + if (!f->monitor) { + kfree(f); + continue; + } + + if (!i2c_find_device(f->regs.addr)) { + printk("envctrl: `%s': i2c device %02x not found\n", + f->name, f->regs.addr); + kfree(f); + continue; + } + + *flast = f; + flast = &f->next; + + f->check_failure = NULL; + f->set_speed = envctrl_set_fan_speed; + } + + envctrl.cpu_temp_table = kmalloc(256, GFP_KERNEL); + if (!envctrl.cpu_temp_table) { + printk("envctrl: can't allocate temperature table\n"); + goto out; + } + if (defaults) { + memcpy(envctrl.cpu_temp_table, + u450_default_cpu_temp_factors, 256); + } else { + err = prom_getproperty(node, "cpu-temp-factors", + envctrl.cpu_temp_table, 256); + if (err) { + printk("envctrl: can't read `cpu-temp-factors'\n"); + goto out; + } + } + + envctrl.cpu_fan_speeds = kmalloc(112, GFP_KERNEL); + if (!envctrl.cpu_fan_speeds) { + printk("envctrl: can't allocate fan speed table\n"); + goto out; + } + if (defaults) { + memcpy(envctrl.cpu_fan_speeds, + u450_default_cpu_fan_speeds, 112); + } else { + err = prom_getproperty(node, "cpu-fan-speeds", + envctrl.cpu_fan_speeds, 112); + if (err) { + printk("envctrl: can't read `cpu-fan-speeds'\n"); + goto out; + } + } + + envctrl.ps_temp_table = kmalloc(256, GFP_KERNEL); + if (!envctrl.ps_temp_table) { + printk("envctrl: can't allocate temperature table\n"); + goto out; + } + if (defaults) { + memcpy(envctrl.ps_temp_table, + u450_default_ps_temp_factors, 256); + } else { + err = prom_getproperty(node, "ps-temp-factors", + envctrl.ps_temp_table, 256); + if (err) { + printk("envctrl: can't read `ps-temp-factors'\n"); + goto out; + } + } + + envctrl.ps_fan_speeds = kmalloc(112, GFP_KERNEL); + if (!envctrl.ps_fan_speeds) { + printk("envctrl: can't allocate fan speed table\n"); + goto out; + } + if (defaults) { + memcpy(envctrl.ps_fan_speeds, + u450_default_ps_fan_speeds, 112); + } else { + err = prom_getproperty(node, "ps-fan-speeds", + envctrl.ps_fan_speeds, 112); + if (err) { + printk("envctrl: can't read `ps-fan-speeds'\n"); + goto out; + } + } + + envctrl.enable = u450_toggle_i2c_int; + envctrl.keep_alive = u450_set_egg_timer; + envctrl.disable = envctrl_fans_blast; + envctrl.interval = 60; + + kfree(tmp); + return 0; + +out: + if (tmp) + kfree(tmp); + + envctrl_stop(); + return err; +} +#endif /* U450_SUPPORT */ + + + static loff_t envctrl_llseek(struct file *file, loff_t offset, int type) { @@ -266,7 +1489,9 @@ envctrl_ioctl(struct inode *inode, struct file *file, case I2CIOCSADR: if (get_user(addr, (int *)arg)) return -EFAULT; - data = addr & 0x7f; + data = addr & 0xfe; + if (!i2c_find_device(addr & 0xfe)) + return -ENODEV; file->private_data = (void *)data; break; case I2CIOCGADR: @@ -317,12 +1542,13 @@ static struct miscdevice envctrl_dev = { #ifdef MODULE int init_module(void) #else -__initfunc(int envctrl_init(void)) +int __init envctrl_init(void) #endif { #ifdef CONFIG_PCI struct linux_ebus *ebus; struct linux_ebus_device *edev = 0; + pid_t pid; int err; for_each_ebus(ebus) { @@ -339,33 +1565,55 @@ ebus_done: return -ENODEV; } - if (check_region(edev->base_address[0], sizeof(*i2c))) { - printk("%s: Can't get region %lx, %d\n", - __FUNCTION__, edev->base_address[0], (int)sizeof(*i2c)); - return -ENODEV; - } - - i2c = (struct pcf8584_reg *)edev->base_address[0]; - - request_region((unsigned long)i2c, sizeof(*i2c), "i2c"); - - i2c->csr = CONTROL_PIN; - i2c->data = PCF8584_ADDRESS; - i2c->csr = CONTROL_PIN | CONTROL_ES1; - i2c->data = CLK_4_43 | BUS_CLK_90; - i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK; + i2c_regs = edev->resource[0].start; + writeb(CONTROL_PIN, i2c_regs + I2C_CSR); + writeb(PCF8584_ADDRESS >> 1, i2c_regs + I2C_DATA); + writeb(CONTROL_PIN | CONTROL_ES1, i2c_regs + I2C_CSR); + writeb(CLK_4_43 | BUS_CLK_90, i2c_regs + I2C_DATA); + writeb(CONTROL_PIN | CONTROL_ES0 | CONTROL_ACK, i2c_regs + I2C_CSR); mdelay(10); if (misc_register(&envctrl_dev)) { printk("%s: unable to get misc minor %d\n", __FUNCTION__, envctrl_dev.minor); - release_region((unsigned long)i2c, sizeof(*i2c)); + return -ENODEV; } err = i2c_scan_bus(); - if (err) - release_region((unsigned long)i2c, sizeof(*i2c)); - return err; + if (err) { + i2c_free_devices(); + misc_deregister(&envctrl_dev); + return err; + } + + memset(&envctrl, 0, sizeof(struct environment)); + + err = -ENODEV; + if (!strcmp(edev->prom_name, "SUNW,rasctrl")) + err = rasctrl_setup(edev->prom_node); +#ifdef U450_SUPPORT + else if (!strcmp(edev->prom_name, "SUNW,envctrl")) + err = envctrl_setup(edev->prom_node); +#endif + + if (err) { + envctrl_stop(); + i2c_free_devices(); + misc_deregister(&envctrl_dev); + return err; + } + + init_waitqueue_head(&envctrl.kenvd_wait); + + pid = kernel_thread(kenvd, (void *)&envctrl, CLONE_FS); + if (pid < 0) { + envctrl_stop(); + i2c_free_devices(); + misc_deregister(&envctrl_dev); + return -ENODEV; + } + + return 0; #else return -ENODEV; #endif @@ -375,7 +1623,8 @@ ebus_done: #ifdef MODULE void cleanup_module(void) { + envctrl_stop(); + i2c_free_devices(); misc_deregister(&envctrl_dev); - release_region((unsigned long)i2c, sizeof(*i2c)); } #endif |