patch-2.3.16 linux/drivers/sbus/char/envctrl.c
Next file: linux/drivers/sbus/char/flash.c
Previous file: linux/drivers/sbus/char/bpp.c
Back to the patch index
Back to the overall index
- Lines: 1561
- Date:
Tue Aug 31 11:25:33 1999
- Orig file:
v2.3.15/linux/drivers/sbus/char/envctrl.c
- Orig date:
Mon Nov 16 10:37:28 1998
diff -u --recursive --new-file v2.3.15/linux/drivers/sbus/char/envctrl.c linux/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] =
{
- __volatile__ unsigned char data;
- __volatile__ unsigned char csr;
+ 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] =
+{
+ 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 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
};
-static struct pcf8584_reg *i2c;
+
+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 @@
};
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 @@
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 @@
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 @@
{
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 @@
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)
+{
+ 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)
{
- unsigned char dev;
+ 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 @@
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 @@
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 @@
#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 @@
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 @@
#ifdef MODULE
void cleanup_module(void)
{
+ envctrl_stop();
+ i2c_free_devices();
misc_deregister(&envctrl_dev);
- release_region((unsigned long)i2c, sizeof(*i2c));
}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)