diff options
Diffstat (limited to 'drivers/sbus/char/envctrl.c')
-rw-r--r-- | drivers/sbus/char/envctrl.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c new file mode 100644 index 000000000..be5bafb07 --- /dev/null +++ b/drivers/sbus/char/envctrl.c @@ -0,0 +1,241 @@ +/* $Id: envctrl.c,v 1.3 1998/04/10 08:42:24 jj Exp $ + * envctrl.c: Temperature and Fan monitoring on Machines providing it. + * + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/init.h> + +#include <asm/ebus.h> + +#define PCF8584_ADDRESS 0x55 + +#define CONTROL_PIN 0x80 +#define CONTROL_ES0 0x40 +#define CONTROL_ES1 0x20 +#define CONTROL_ES2 0x10 +#define CONTROL_ENI 0x08 +#define CONTROL_STA 0x04 +#define CONTROL_STO 0x02 +#define CONTROL_ACK 0x01 + +#define STATUS_PIN 0x80 +#define STATUS_STS 0x20 +#define STATUS_BER 0x10 +#define STATUS_LRB 0x08 +#define STATUS_AD0 0x08 +#define STATUS_AAB 0x04 +#define STATUS_LAB 0x02 +#define STATUS_BB 0x01 + +/* + * CLK Mode Register. + */ +#define BUS_CLK_90 0x00 +#define BUS_CLK_45 0x01 +#define BUS_CLK_11 0x02 +#define BUS_CLK_1_5 0x03 + +#define CLK_3 0x00 +#define CLK_4_43 0x10 +#define CLK_6 0x14 +#define CLK_8 0x18 +#define CLK_12 0x1c + + +#define I2C_WRITE 0x00 +#define I2C_READ 0x01 + +struct pcf8584_reg +{ + __volatile__ unsigned char data; + __volatile__ unsigned char csr; +}; + +static struct pcf8584_reg *i2c; + + +struct i2c_addr_map { + unsigned char addr; + unsigned char mask; + char *name; +}; + +static struct i2c_addr_map devmap[] = { + { 0x38, 0x78, "PCF8574A" }, + { 0x20, 0x78, "TDA8444" }, + { 0x48, 0x78, "PCF8591" }, +}; +#define NR_DEVMAP (sizeof(devmap) / sizeof(devmap[0])) + +static int +envctrl_read(unsigned char dev, char *buffer, int len) +{ + unsigned char dummy; + unsigned char stat; + int error = -ENODEV; + int count = -1; + + if (len == 0) + return 0; + + i2c->data = (dev << 1) | I2C_READ; + + while (!(i2c->csr & STATUS_BB)) + udelay(1); + + i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK; + + do { + udelay(1); + while ((stat = i2c->csr) & STATUS_PIN) + udelay(1); + + if (stat & STATUS_LRB) + goto stop; + error = 0; + if (count == (len - 2)) + goto final; + + if (++count > 0) + *buffer++ = i2c->data; + else + dummy = i2c->data; + } while (1); + +final: + i2c->csr = CONTROL_ES0; + if (++count > 0) + *buffer++ = i2c->data; + else + dummy = i2c->data; + + udelay(1); + while ((stat = i2c->csr) & STATUS_PIN) + udelay(1); + +stop: + i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; + if (++count > 0) + *buffer++ = i2c->data; + else + dummy = i2c->data; + + if (error) + return error; + return count; +} + +static int +envctrl_write(unsigned char dev, char *buffer, int len) +{ + int error = -ENODEV; + int count = 0; + + while (!(i2c->csr & STATUS_BB)) + udelay(1); + + i2c->data = (dev << 1) | I2C_WRITE; + i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STA | CONTROL_ACK; + + do { + unsigned char stat; + + udelay(1); + while ((stat = i2c->csr) & STATUS_PIN) + udelay(1); + + if (stat & STATUS_LRB) + goto stop; + error = count; + if (count == len) + goto stop; + + i2c->data = *buffer++; + count++; + } while (1); + +stop: + i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; + return error; +} + +__initfunc(static int scan_bus(void)) +{ + unsigned char dev; + int count = 0; + int i; + + /* scan */ + for (dev = 1; dev < 128; dev++) + if (envctrl_write(dev, 0, 0) == 0) { + for (i = 0; i < NR_DEVMAP; i++) + if ((dev & devmap[i].mask) == devmap[i].addr) + break; + printk("envctrl: i2c device at %02x: %s\n", dev, + i < NR_DEVMAP ? devmap[i].name : "unknown"); +{ + unsigned char buf[4]; + if (envctrl_read(dev, buf, 4) == 4) + printk("envctrl: read %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3]); +} + count++; + } + return count ? 0 : -ENODEV; +} + +#ifdef MODULE +int init_module(void) +#else +__initfunc(int envctrl_init(void)) +#endif +{ +#ifdef CONFIG_PCI + struct linux_ebus *ebus; + struct linux_ebus_device *edev; + + for_all_ebusdev(edev, ebus) + if (!strcmp(edev->prom_name, "SUNW,envctrl")) + break; + + if (!edev) + return -ENODEV; + + if (check_region(edev->base_address[0], sizeof(*i2c))) { + prom_printf("%s: Can't get region %lx, %d\n", + __FUNCTION__, edev->base_address[0], + sizeof(*i2c)); + prom_halt(); + } + + request_region(edev->base_address[0], + sizeof(*i2c), "i2c"); + + i2c = (struct pcf8584_reg *)edev->base_address[0]; + + 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; + udelay(10000); + + return scan_bus(); +#else + return -ENODEV; +#endif +} + + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif |