--- linux-old/include/linux/i2c-old.h Sat Jan 15 20:08:52 CET 2000 +++ linux/include/linux/i2c-old.h Sat Jan 15 20:08:52 CET 2000 @@ -0,0 +1,190 @@ +#ifndef I2C_H +#define I2C_H + +/* + * linux i2c interface. Works a little bit like the scsi subsystem. + * There are: + * + * i2c the basic control module (like scsi_mod) + * bus driver a driver with a i2c bus (hostadapter driver) + * chip driver a driver for a chip connected + * to a i2c bus (cdrom/hd driver) + * + * A device will be attached to one bus and one chip driver. Every chip + * driver gets a unique ID. + * + * A chip driver can provide a ioctl-like callback for the + * communication with other parts of the kernel (not every i2c chip is + * useful without other devices, a TV card tuner for example). + * + * "i2c internal" parts of the structs: only the i2c module is allowed to + * write to them, for others they are read-only. + * + */ + +#define I2C_BUS_MAX 4 /* max # of bus drivers */ +#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ +#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ + +struct i2c_bus; +struct i2c_driver; +struct i2c_device; + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 +#define I2C_DRIVERID_VIDEODECODER 4 +#define I2C_DRIVERID_VIDEOENCODER 5 + +#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ + /* 2 is used in 2.3.x */ +#define I2C_BUSID_BUZ 3 /* I2C bus on a BUZ */ +#define I2C_BUSID_ZORAN 4 /* I2C bus on a Zoran */ +#define I2C_BUSID_SGIVWFB 5 /* Moved to be unique */ + +/* + * struct for a driver for a i2c chip (tuner, soundprocessor, + * videotext, ... ). + * + * a driver will register within the i2c module. The i2c module will + * callback the driver (i2c_attach) for every device it finds on a i2c + * bus at the specified address. If the driver decides to "accept" + * the, device, it must return a struct i2c_device, and NULL + * otherwise. + * + * i2c_detach = i2c_attach ** -1 + * + * i2c_command will be used to pass commands to the driver in a + * ioctl-line manner. + * + */ + +struct i2c_driver +{ + char name[32]; /* some useful label */ + int id; /* device type ID */ + unsigned char addr_l, addr_h; /* address range of the chip */ + + int (*attach)(struct i2c_device *device); + int (*detach)(struct i2c_device *device); + int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); + + /* i2c internal */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * this holds the informations about a i2c bus available in the system. + * + * a chip with a i2c bus interface (like bt848) registers the bus within + * the i2c module. This struct provides functions to access the i2c bus. + * + * One must hold the spinlock to access the i2c bus (XXX: is the irqsave + * required? Maybe better use a semaphore?). + * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing + * to bang their i2c bus from an interrupt. + * + * attach/detach_inform is a callback to inform the bus driver about + * attached chip drivers. + * + */ + +/* needed: unsigned long flags */ + +#include + +#if LINUX_VERSION_CODE >= 0x020100 +# if 0 +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); +# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); +# else +# define LOCK_FLAGS +# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); +# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); +# endif +#else +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } +# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#endif + +struct i2c_bus +{ + char name[32]; /* some useful label */ + int id; + void *data; /* free for use by the bus driver */ + +#if LINUX_VERSION_CODE >= 0x020100 + spinlock_t bus_lock; +#endif + + /* attach/detach inform callbacks */ + void (*attach_inform)(struct i2c_bus *bus, int id); + void (*detach_inform)(struct i2c_bus *bus, int id); + + /* Software I2C */ + void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); + int (*i2c_getdataline)(struct i2c_bus *bus); + + /* Hardware I2C */ + int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); + int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + + /* internal data for i2c module */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * This holds per-device data for a i2c device + */ + +struct i2c_device +{ + char name[32]; /* some useful label */ + void *data; /* free for use by the chip driver */ + unsigned char addr; /* chip addr */ + + /* i2c internal */ + struct i2c_bus *bus; + struct i2c_driver *driver; +}; + + +/* ------------------------------------------------------------------- */ +/* i2c module functions */ + +/* register/unregister a i2c bus */ +int i2c_register_bus(struct i2c_bus *bus); +int i2c_unregister_bus(struct i2c_bus *bus); + +/* register/unregister a chip driver */ +int i2c_register_driver(struct i2c_driver *driver); +int i2c_unregister_driver(struct i2c_driver *driver); + +/* send a command to a chip using the ioctl-like callback interface */ +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg); + +/* i2c bus access functions */ +void i2c_start(struct i2c_bus *bus); +void i2c_stop(struct i2c_bus *bus); +void i2c_one(struct i2c_bus *bus); +void i2c_zero(struct i2c_bus *bus); +int i2c_ack(struct i2c_bus *bus); + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); +unsigned char i2c_readbyte(struct i2c_bus *bus,int last); + +/* i2c (maybe) hardware functions */ +int i2c_read(struct i2c_bus *bus, unsigned char addr); +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + +int i2c_init(void); +#endif /* I2C_H */ --- linux-old/drivers/char/bttv.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/bttv.c Sat Jan 15 20:08:52 CET 2000 @@ -77,5 +77,5 @@ #include -#include +#include #include "bttv.h" #include "tuner.h" --- linux-old/drivers/char/bttv.h Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/bttv.h Sat Jan 15 20:08:52 CET 2000 @@ -27,5 +27,5 @@ #include -#include +#include #include "msp3400.h" #include "bt848.h" --- linux-old/drivers/char/buz.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/buz.c Sat Jan 15 20:08:52 CET 2000 @@ -55,5 +55,5 @@ #include -#include +#include #include "buz.h" #include --- linux-old/drivers/char/msp3400.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/msp3400.c Sat Jan 15 20:08:52 CET 2000 @@ -52,5 +52,5 @@ #include -#include +#include #include --- linux-old/drivers/char/saa5249.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/saa5249.c Sat Jan 15 20:08:52 CET 2000 @@ -53,5 +53,5 @@ #include #include -#include +#include #include #include --- linux-old/drivers/char/saa7111.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/saa7111.c Sat Jan 15 20:08:52 CET 2000 @@ -41,5 +41,5 @@ #include -#include +#include #include --- linux-old/drivers/char/saa7185.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/saa7185.c Sat Jan 15 20:08:52 CET 2000 @@ -41,5 +41,5 @@ #include -#include +#include #include --- linux-old/drivers/char/tuner.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/tuner.c Sat Jan 15 20:08:52 CET 2000 @@ -9,5 +9,5 @@ #include -#include +#include #include --- linux-old/drivers/char/i2c-old.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/i2c-old.c Sat Jan 15 20:08:52 CET 2000 @@ -0,0 +1,460 @@ +/* + * Generic i2c interface for linux + * + * (c) 1998 Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REGPRINT(x) if (verbose) (x) +#define I2C_DEBUG(x) if (i2c_debug) (x) + +static int scan = 0; +static int verbose = 0; +static int i2c_debug = 0; + +#if LINUX_VERSION_CODE >= 0x020117 +MODULE_PARM(scan,"i"); +MODULE_PARM(verbose,"i"); +MODULE_PARM(i2c_debug,"i"); +#endif + +/* ----------------------------------------------------------------------- */ + +static struct i2c_bus *busses[I2C_BUS_MAX]; +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int bus_count = 0, driver_count = 0; + +#ifdef CONFIG_VIDEO_BT848 +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); +#endif +#ifdef CONFIG_VIDEO_BUZ +extern int saa7111_init(void); +extern int saa7185_init(void); +#endif +#ifdef CONFIG_VIDEO_LML33 +extern int bt819_init(void); +extern int bt856_init(void); +#endif + +int i2c_init(void) +{ + printk(KERN_INFO "i2c: initialized%s\n", + scan ? " (i2c bus scan enabled)" : ""); + /* anything to do here ? */ +#ifdef CONFIG_VIDEO_BT848 + i2c_tuner_init(); +#endif +#ifdef CONFIG_VIDEO_MSP3400 + msp3400c_init(); +#endif +#ifdef CONFIG_VIDEO_BUZ + saa7111_init(); + saa7185_init(); +#endif +#ifdef CONFIG_VIDEO_LML33 + bt819_init(); + bt856_init(); +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) +{ + struct i2c_device *device; + int i,j,ack=1; + unsigned char addr; + LOCK_FLAGS; + + /* probe for device */ + LOCK_I2C_BUS(bus); + for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,addr,0); + i2c_stop(bus); + if (!ack) + break; + } + UNLOCK_I2C_BUS(bus); + if (ack) + return; + + /* got answer */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (NULL == driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + return; + + for (j = 0; j < I2C_DEVICE_MAX; j++) + if (NULL == bus->devices[j]) + break; + if (I2C_DEVICE_MAX == j) + return; + + if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) + return; + device->bus = bus; + device->driver = driver; + device->addr = addr; + + /* Attach */ + + if (driver->attach(device)!=0) + { + kfree(device); + return; + } + driver->devices[i] = device; + driver->devcount++; + bus->devices[j] = device; + bus->devcount++; + + if (bus->attach_inform) + bus->attach_inform(bus,driver->id); + REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); +} + +static void i2c_detach_device(struct i2c_device *device) +{ + int i; + + if (device->bus->detach_inform) + device->bus->detach_inform(device->bus,device->driver->id); + device->driver->detach(device); + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", + device->name); + return; + } + device->driver->devices[i] = NULL; + device->driver->devcount--; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->bus->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", + device->name); + return; + } + device->bus->devices[i] = NULL; + device->bus->devcount--; + + REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); + kfree(device); +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_bus(struct i2c_bus *bus) +{ + int i,ack; + LOCK_FLAGS; + + memset(bus->devices,0,sizeof(bus->devices)); + bus->devcount = 0; + + for (i = 0; i < I2C_BUS_MAX; i++) + if (NULL == busses[i]) + break; + if (I2C_BUS_MAX == i) + return -ENOMEM; + + busses[i] = bus; + bus_count++; + REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); + + MOD_INC_USE_COUNT; + + if (scan) + { + /* scan whole i2c bus */ + LOCK_I2C_BUS(bus); + for (i = 0; i < 256; i+=2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,i,0); + i2c_stop(bus); + if (!ack) + { + printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", + bus->name,i); + } + } + UNLOCK_I2C_BUS(bus); + } + + /* probe available drivers */ + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (drivers[i]) + i2c_attach_device(bus,drivers[i]); + return 0; +} + +int i2c_unregister_bus(struct i2c_bus *bus) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i]) + i2c_detach_device(bus->devices[i]); + + for (i = 0; i < I2C_BUS_MAX; i++) + if (bus == busses[i]) + break; + if (I2C_BUS_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", + bus->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + busses[i] = NULL; + bus_count--; + REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_driver(struct i2c_driver *driver) +{ + int i; + + memset(driver->devices,0,sizeof(driver->devices)); + driver->devcount = 0; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + return -ENOMEM; + + drivers[i] = driver; + driver_count++; + + MOD_INC_USE_COUNT; + + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); + + /* Probe available busses */ + for (i = 0; i < I2C_BUS_MAX; i++) + if (busses[i]) + i2c_attach_device(busses[i],driver); + + return 0; +} + +int i2c_unregister_driver(struct i2c_driver *driver) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (driver->devices[i]) + i2c_detach_device(driver->devices[i]); + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", + driver->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + drivers[i] = NULL; + driver_count--; + REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i] && bus->devices[i]->driver->id == id) + break; + if (i == I2C_DEVICE_MAX) + return -ENODEV; + if (NULL == bus->devices[i]->driver->command) + return -ENODEV; + return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); +} + +/* ----------------------------------------------------------------------- */ + +#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) +#define I2C_GET(bus) (bus->i2c_getdataline(bus)) + +void i2c_start(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); + I2C_DEBUG(printk("%s: < ",bus->name)); +} + +void i2c_stop(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,1,1); + I2C_DEBUG(printk(">\n")); +} + +void i2c_one(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,0,1); +} + +void i2c_zero(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); +} + +int i2c_ack(struct i2c_bus *bus) +{ + int ack; + + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + ack = I2C_GET(bus); + I2C_SET(bus,0,1); + return ack; +} + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) +{ + int i, ack; + + I2C_SET(bus,0,0); + for (i=7; i>=0; i--) + (data&(1<=0; i--) + { + I2C_SET(bus,1,1); + if (I2C_GET(bus)) + data |= (1<i2c_read) + return bus->i2c_read(bus, addr); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ret = i2c_readbyte(bus,1); + i2c_stop(bus); + return ret; +} + +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char data1, unsigned char data2, int both) +{ + int ack; + + if (bus->i2c_write) + return bus->i2c_write(bus, addr, data1, data2, both); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ack = i2c_sendbyte(bus,data1,0); + if (both) + ack = i2c_sendbyte(bus,data2,0); + i2c_stop(bus); + return ack ? -1 : 0 ; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 0x020100 +EXPORT_SYMBOL(i2c_register_bus); +EXPORT_SYMBOL(i2c_unregister_bus); +EXPORT_SYMBOL(i2c_register_driver); +EXPORT_SYMBOL(i2c_unregister_driver); +EXPORT_SYMBOL(i2c_control_device); +EXPORT_SYMBOL(i2c_start); +EXPORT_SYMBOL(i2c_stop); +EXPORT_SYMBOL(i2c_one); +EXPORT_SYMBOL(i2c_zero); +EXPORT_SYMBOL(i2c_ack); +EXPORT_SYMBOL(i2c_sendbyte); +EXPORT_SYMBOL(i2c_readbyte); +EXPORT_SYMBOL(i2c_read); +EXPORT_SYMBOL(i2c_write); +#endif + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif --- linux-old/drivers/char/i2c.c Sat Jan 15 20:08:52 CET 2000 +++ linux/drivers/char/i2c.c Sat Jan 15 20:08:52 CET 2000 @@ -1,460 +0,0 @@ -/* - * Generic i2c interface for linux - * - * (c) 1998 Gerd Knorr - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REGPRINT(x) if (verbose) (x) -#define I2C_DEBUG(x) if (i2c_debug) (x) - -static int scan = 0; -static int verbose = 0; -static int i2c_debug = 0; - -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(scan,"i"); -MODULE_PARM(verbose,"i"); -MODULE_PARM(i2c_debug,"i"); -#endif - -/* ----------------------------------------------------------------------- */ - -static struct i2c_bus *busses[I2C_BUS_MAX]; -static struct i2c_driver *drivers[I2C_DRIVER_MAX]; -static int bus_count = 0, driver_count = 0; - -#ifdef CONFIG_VIDEO_BT848 -extern int i2c_tuner_init(void); -extern int msp3400c_init(void); -#endif -#ifdef CONFIG_VIDEO_BUZ -extern int saa7111_init(void); -extern int saa7185_init(void); -#endif -#ifdef CONFIG_VIDEO_LML33 -extern int bt819_init(void); -extern int bt856_init(void); -#endif - -int i2c_init(void) -{ - printk(KERN_INFO "i2c: initialized%s\n", - scan ? " (i2c bus scan enabled)" : ""); - /* anything to do here ? */ -#ifdef CONFIG_VIDEO_BT848 - i2c_tuner_init(); -#endif -#ifdef CONFIG_VIDEO_MSP3400 - msp3400c_init(); -#endif -#ifdef CONFIG_VIDEO_BUZ - saa7111_init(); - saa7185_init(); -#endif -#ifdef CONFIG_VIDEO_LML33 - bt819_init(); - bt856_init(); -#endif - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) -{ - struct i2c_device *device; - int i,j,ack=1; - unsigned char addr; - LOCK_FLAGS; - - /* probe for device */ - LOCK_I2C_BUS(bus); - for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,addr,0); - i2c_stop(bus); - if (!ack) - break; - } - UNLOCK_I2C_BUS(bus); - if (ack) - return; - - /* got answer */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (NULL == driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - return; - - for (j = 0; j < I2C_DEVICE_MAX; j++) - if (NULL == bus->devices[j]) - break; - if (I2C_DEVICE_MAX == j) - return; - - if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) - return; - device->bus = bus; - device->driver = driver; - device->addr = addr; - - /* Attach */ - - if (driver->attach(device)!=0) - { - kfree(device); - return; - } - driver->devices[i] = device; - driver->devcount++; - bus->devices[j] = device; - bus->devcount++; - - if (bus->attach_inform) - bus->attach_inform(bus,driver->id); - REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); -} - -static void i2c_detach_device(struct i2c_device *device) -{ - int i; - - if (device->bus->detach_inform) - device->bus->detach_inform(device->bus,device->driver->id); - device->driver->detach(device); - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", - device->name); - return; - } - device->driver->devices[i] = NULL; - device->driver->devcount--; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->bus->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", - device->name); - return; - } - device->bus->devices[i] = NULL; - device->bus->devcount--; - - REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); - kfree(device); -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_bus(struct i2c_bus *bus) -{ - int i,ack; - LOCK_FLAGS; - - memset(bus->devices,0,sizeof(bus->devices)); - bus->devcount = 0; - - for (i = 0; i < I2C_BUS_MAX; i++) - if (NULL == busses[i]) - break; - if (I2C_BUS_MAX == i) - return -ENOMEM; - - busses[i] = bus; - bus_count++; - REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); - - MOD_INC_USE_COUNT; - - if (scan) - { - /* scan whole i2c bus */ - LOCK_I2C_BUS(bus); - for (i = 0; i < 256; i+=2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,i,0); - i2c_stop(bus); - if (!ack) - { - printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", - bus->name,i); - } - } - UNLOCK_I2C_BUS(bus); - } - - /* probe available drivers */ - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (drivers[i]) - i2c_attach_device(bus,drivers[i]); - return 0; -} - -int i2c_unregister_bus(struct i2c_bus *bus) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i]) - i2c_detach_device(bus->devices[i]); - - for (i = 0; i < I2C_BUS_MAX; i++) - if (bus == busses[i]) - break; - if (I2C_BUS_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", - bus->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - busses[i] = NULL; - bus_count--; - REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_driver(struct i2c_driver *driver) -{ - int i; - - memset(driver->devices,0,sizeof(driver->devices)); - driver->devcount = 0; - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (NULL == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - return -ENOMEM; - - drivers[i] = driver; - driver_count++; - - MOD_INC_USE_COUNT; - - REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); - - /* Probe available busses */ - for (i = 0; i < I2C_BUS_MAX; i++) - if (busses[i]) - i2c_attach_device(busses[i],driver); - - return 0; -} - -int i2c_unregister_driver(struct i2c_driver *driver) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (driver->devices[i]) - i2c_detach_device(driver->devices[i]); - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (driver == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", - driver->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - drivers[i] = NULL; - driver_count--; - REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg) -{ - int i; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i] && bus->devices[i]->driver->id == id) - break; - if (i == I2C_DEVICE_MAX) - return -ENODEV; - if (NULL == bus->devices[i]->driver->command) - return -ENODEV; - return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); -} - -/* ----------------------------------------------------------------------- */ - -#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) -#define I2C_GET(bus) (bus->i2c_getdataline(bus)) - -void i2c_start(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); - I2C_DEBUG(printk("%s: < ",bus->name)); -} - -void i2c_stop(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,1,1); - I2C_DEBUG(printk(">\n")); -} - -void i2c_one(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,0,1); -} - -void i2c_zero(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); -} - -int i2c_ack(struct i2c_bus *bus) -{ - int ack; - - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - ack = I2C_GET(bus); - I2C_SET(bus,0,1); - return ack; -} - -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) -{ - int i, ack; - - I2C_SET(bus,0,0); - for (i=7; i>=0; i--) - (data&(1<=0; i--) - { - I2C_SET(bus,1,1); - if (I2C_GET(bus)) - data |= (1<i2c_read) - return bus->i2c_read(bus, addr); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ret = i2c_readbyte(bus,1); - i2c_stop(bus); - return ret; -} - -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char data1, unsigned char data2, int both) -{ - int ack; - - if (bus->i2c_write) - return bus->i2c_write(bus, addr, data1, data2, both); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ack = i2c_sendbyte(bus,data1,0); - if (both) - ack = i2c_sendbyte(bus,data2,0); - i2c_stop(bus); - return ack ? -1 : 0 ; -} - -/* ----------------------------------------------------------------------- */ - -#ifdef MODULE - -#if LINUX_VERSION_CODE >= 0x020100 -EXPORT_SYMBOL(i2c_register_bus); -EXPORT_SYMBOL(i2c_unregister_bus); -EXPORT_SYMBOL(i2c_register_driver); -EXPORT_SYMBOL(i2c_unregister_driver); -EXPORT_SYMBOL(i2c_control_device); -EXPORT_SYMBOL(i2c_start); -EXPORT_SYMBOL(i2c_stop); -EXPORT_SYMBOL(i2c_one); -EXPORT_SYMBOL(i2c_zero); -EXPORT_SYMBOL(i2c_ack); -EXPORT_SYMBOL(i2c_sendbyte); -EXPORT_SYMBOL(i2c_readbyte); -EXPORT_SYMBOL(i2c_read); -EXPORT_SYMBOL(i2c_write); -#endif - -int init_module(void) -{ - return i2c_init(); -} - -void cleanup_module(void) -{ -} -#endif --- linux-old/drivers/char/Makefile Sat Jan 15 20:08:53 CET 2000 +++ linux/drivers/char/Makefile Sat Jan 15 20:08:53 CET 2000 @@ -570,8 +570,8 @@ ifeq ($(L_I2C),y) -LX_OBJS += i2c.o +LX_OBJS += i2c-old.o else ifeq ($(M_I2C),y) - MX_OBJS += i2c.o + MX_OBJS += i2c-old.o endif endif --- linux-old/Documentation/i2c/dev-interface Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/dev-interface Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,124 @@ +Usually, i2c devices are controlled by a kernel driver. But it is also +possible to access all devices on an adapter from userspace, through +the /dev interface. You need to load module i2c-dev for this. + +Each registered i2c adapter gets a number, counting from 0. You can +examine /proc/bus/i2c to see what number corresponds to which adapter. +I2C device files are character device files with major device number 89 +and a minor device number corresponding to the number assigned as +explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., +i2c-10, ...). All 256 minor device numbers are reserved for i2c. + + +C example +========= + +So let's say you want to access an i2c adapter from a C program. The +first thing to do is `#include " and "#include . +Yes, I know, you should never include kernel header files, but until glibc +knows about i2c, there is not much choice. + +Now, you have to decide which adapter you want to access. You should +inspect /proc/bus/i2c to decide this. Adapter numbers are assigned +somewhat dynamically, so you can not even assume /dev/i2c-0 is the +first adapter. + +Next thing, open the device file, as follows: + int file; + int adapter_nr = 2; /* probably dynamically determined */ + char filename[20]; + + sprintf(filename,"/dev/i2c-%d",adapter_nr); + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +When you have opened the device, you must specify with what device +address you want to communicate: + int addr = 0x40; /* The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. + __u8 register = 0x10; /* Device register to access */ + __s32 res; + char buf[10]; + /* Using SMBus commands */ + res = i2c_smbus_read_word_data(file,register); + if (res < 0) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* res contains the read word */ + } + /* Using I2C Write, equivalent of + i2c_smbus_write_word_data(file,register,0x6543) */ + buf[0] = register; + buf[1] = 0x43; + buf[2] = 0x65; + if ( write(file,buf,3) != 3) { + /* ERROR HANDLING: i2c transaction failed */ + } + /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */ + if (read(file,buf,1) != 1) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* buf[0] contains the read byte */ + } + + +Full interface description +========================== + +The following IOCTLs are defined and fully supported +(see also i2c-dev.h and i2c.h): + +ioctl(file,I2C_SLAVE,long addr) + Change slave address. The address is passed in the 7 lower bits of the + argument (except for 10 bit addresses, passed in the 10 lower bits in this + case). + +ioctl(file,I2C_TENBIT,long select) + Selects ten bit addresses if select not equals 0, selects normal 7 bit + addresses if select equals 0. + +ioctl(file,I2C_FUNCS,unsigned long *funcs) + Gets the adapter functionality and puts it in *funcs. + +Other values are NOT supported at this moment, except for I2C_SMBUS, +which you should never directly call; instead, use the access functions +below. + +You can do plain i2c transactions by using read(2) and write(2) calls. +Combined read/write transactions are not yet supported (they will in +the future, through an ioctl). You do not need to pass the address +byte; instead, set it through ioctl I2C_SLAVE before you try to +access the device. + +You can do SMBus level transactions (see documentation file smbus-protocol +for details) through the following functions: + __s32 i2c_smbus_write_quick(int file, __u8 value); + __s32 i2c_smbus_read_byte(int file); + __s32 i2c_smbus_write_byte(int file, __u8 value); + __s32 i2c_smbus_read_byte_data(int file, __u8 command); + __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value); + __s32 i2c_smbus_read_word_data(int file, __u8 command); + __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value); + __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value); + __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values); + __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length, + __u8 *values); +All these tranactions return -1 on failure; you can read errno to see +what happened. The 'write' transactions return 0 on success; the +'read' transactions return the read value, except for read_block, which +returns the number of values read. The block buffers need not be longer +than 32 bytes. + +The above functions are all macros, that resolve to calls to the +i2c_smbus_access function, that on its turn calls a specific ioctl +with the data in a specific format. Read the source code if you +want to know what happens behind the screens. --- linux-old/Documentation/i2c/i2c-protocol Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/i2c-protocol Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,68 @@ +This document describes the i2c protocol. Or will, when it is finished :-) + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +Simple send tranaction +====================== + +This corresponds to i2c_master_send. + + S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P + + +Simple receive transaction +=========================== + +This corresponds to i2c_master_recv + + S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P + + +Combined tranactions +==================== + +This corresponds to i2c_transfer + +They are just like the above transactions, but instead of a stop bit P +a start bit S is sent and the transaction continues. An example of +a byte read, followed by a byte write: + + S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P + + +Modified transactions +===================== + +We have found some I2C devices that needs the following modifications: + + Flag I2C_M_NOSTART: + In a combined transaction, no 'S Addr' is generated at some point. + For example, setting I2C_M_NOSTART on the second partial message + generateds something like: + S Addr Rd [A] [Data] NA Wr [A] Data [A] P + If you set the I2C_M_NOSTART variable for the first partial message, + we do not generate Addr, but we do generate the startbit S. This will + probably confuse all other clients on your bus, so don't try this. + + Flags I2C_M_REV_DIR_ADDR + This toggles the Rd/Wr flag. That is, if you want to do a write, but + need to emit an Rd instead of a Wr, or vice versa, you set this + flag. For example: + S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P + --- linux-old/Documentation/i2c/proc-interface Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/proc-interface Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,53 @@ +i2c-core is the core i2c module (surprise!) which offers general routines on +which other modules build. You will find that all i2c-related modules depend +on this module, so it will (need to) be loaded whenever another i2c-related +module is loaded. Seen from the outside, the most interesting is the /proc +interface. Note that there is no corresponding sysctl interface! + +/proc/bus/i2c +============= + +Whenever i2c-core is loaded, you will find a file /proc/bus/i2c, which lists +all currently registered I2C adapters. Each line contains exactly one +I2C adapter. Each line has the following format: "i2c-%d\t%9s\t%-32s't%-32s\n", +which works out to four columns separated by tabs. Note that the file +will be empty, if no adapters are registered at all. + +Adapters are numbered from 0 upwards. The first column contains the number +of the adapter, for example "i2c-4" for adapter 4. The name listed is also +the name of the /proc file which lists all devices attached to it, and +of the /dev file which corresponds to this adapter. + +The second column documents what kind of adapter this is. Some adapters +understand the full I2C protocol, others only a subset called SMBus, +and yet others are some kind of pseudo-adapters that do not understand +i2c at all. Possible values in here are "i2c", "smbus", "i2c/smbus" +and "dummy". Because the SMBus protocol can be fully emulated by i2c +adapters, if you see "i2c" here, SMBus is supported too. There may +be some future adapters which support both specific SMBus commands and +general I2C, and they will display "i2c/smbus". + +The third and fourth column are respectively the algorithm and adapter +name of this adapter. Each adapter is associated with an algorithm, +and several adapters can share the same algorithm. The combination of +algorithm name and adapter name should be unique for an adapter, but +you can't really count on that yet. + + +/proc/bus/i2c-* +=============== + +Each registered adapter gets its own file in /proc/bus/, which lists +the devices registered to the adapter. Each line in such a file contains +one registered device. Each line has the following format: +"%02x\t%-32s\t%-32s\n", which works out to three columns separated by +tabs. Note that this file can be empty, if no devices are found on +the adapter. + +The first column contains the (hexadecimal) address of the client. As +only 7-bit addresses are supported at this moment, two digits are +enough. + +The second and third column are respectively the client name and the +driver name of this client. Each client is associated with a driver, +and several clients can share the same driver. --- linux-old/Documentation/i2c/smbus-protocol Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/smbus-protocol Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,127 @@ +Some adapters understand only the SMBus (System Management Bus) protocol, +which is a subset from the I2C protocol. Fortunately, many devices use +only the same subset, which makes it possible to put them on an SMBus. +If you write a driver for some I2C device, please try to use the SMBus +commands if at all possible (if the device uses only that subset of the +I2C protocol). This makes it possible to use the device driver on both +SMBus adapters and I2C adapters (the SMBus command set is automatically +translated to I2C on I2C adapters, but plain I2C commands can not be +handled at all on a pure SMBus adapter). + +Below is a list of SMBus commands. + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +SMBus Write Quick +================= + +This sends a single byte to the device, at the place of the Rd/Wr bit. +There is no equivalent Read Quick command. + +A Addr Rd/Wr [A] P + + +SMBus Read Byte +=============== + +This reads a single byte from a device, without specifying a device +register. Some devices are so simple that this interface is enough; for +others, it is a shorthand if you want to read the same register as in +the previous SMBus command. + +S Addr Rd [A] [Data] NA P + + +SMBus Write Byte +================ + +This is the reverse of Read Byte: it sends a single byte to a device. +See Read Byte for more information. + +S Addr Wr [A] Data NA P + + +SMBus Read Byte Data +==================== + +This reads a single byte from a device, from a designated register. +The register is specified through the Comm byte. + +S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P + + +SMBus Read Word Data +==================== + +This command is very like Read Byte Data; again, data is read from a +device, from a designated register that is specified through the Comm +byte. But this time, the data is a complete word (16 bits). + +S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Write Byte Data +===================== + +This writes a single byte to a device, to a designated register. The +register is specified through the Comm byte. This is the opposite of +the Read Byte Data command. + +S Addr Wr [A] Comm [A] Data [A] P + + +SMBus Write Word Data +===================== + +This is the opposite operation of the Read Word Data command. 16 bits +of data is read from a device, from a designated register that is +specified through the Comm byte. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P + + +SMBus Process Call +================== + +This command selects a device register (through the Comm byte), sends +16 bits of data to it, and reads 16 bits of data in return. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] + S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Block Read +================ + +This command reads a block of upto 32 bytes from a device, from a +designated register that is specified through the Comm byte. The amount +of data is specified by the device in the Count byte. + +S Addr Wr [A] Comm [A] + S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P + + +SMBus Block Write +================= + +The opposite of the Block Read command, this writes upto 32 bytes to +a device, to a designated register that is specified through the +Comm byte. The amount of data is specified in the Count byte. + +S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P --- linux-old/Documentation/i2c/summary Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/summary Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,63 @@ +This is an explanation of what i2c is, and what is supported. + +I2C and SMBus +============= + +I2C (pronounce: I square C) is a protocol developed by Philips. It is a +slow two-wire protocol (10-100 kHz), but it suffices for many types of +devices. + +SMBus (System Management Bus) is a subset of the I2C protocol. Many +modern mainboards have a System Management Bus. There are a lot of +devices which can be connected to a SMBus; the most notable are modern +memory chips with EEPROM memories and chips for hardware monitoring. + +Because the SMBus is just a special case of the generalized I2C bus, we +can simulate the SMBus protocol on plain I2C busses. The reverse is +regretfully impossible. + + +Terminology +=========== + +When we talk about I2C, we use the following terms: + Bus -> Algorithm + Adapter + Device -> Driver + Client +An Algorithm driver contains general code that can be used for a whole class +of I2C adapters. Each specific adapter driver depends on one algorithm +driver. +A Driver driver (yes, this sounds ridiculous, sorry) contains the general +code to access some type of device. Each detected device gets its own +data in the Client structure. Usually, Driver and Client are more closely +integrated than Algorithm and Adapter. + +For a given configuration, you will need a driver for your I2C bus (usually +a separate Adapter and Algorithm driver), and drivers for your I2C devices +(usually one driver for each device). + + +Included Drivers +================ + +Base modules +------------ + +i2c-core: The basic I2C code, including the /proc interface +i2c-dev: The /dev interface + +Algorithm drivers +----------------- + +i2c-algo-bit: A bit-banging algorithm +i2c-algo-pcf: A PCF 8584 style algorithm + +Adapter drivers +--------------- + +i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) +i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) +i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) +i2c-velleman: Velleman K9000 parallel port adapter (uses i2c-algo-bit) + --- linux-old/Documentation/i2c/ten-bit-addresses Sat Jan 15 20:08:53 CET 2000 +++ linux/Documentation/i2c/ten-bit-addresses Sat Jan 15 20:08:53 CET 2000 @@ -0,0 +1,22 @@ +The I2C protocol knows about two kinds of device addresses: normal 7 bit +addresses, and an extended set of 10 bit addresses. The sets of addresses +do not intersect: the 7 bit address 0x10 is not the same as the 10 bit +address 0x10 (though a single device could respond to both of them). You +select a 10 bit address by adding an extra byte after the address +byte: + S Addr7 Rd/Wr .... +becomes + S 11110 Addr10 Rd/Wr +S is the start bit, Rd/Wr the read/write bit, and if you count the number +of bits, you will see the there are 8 after the S bit for 7 bit addresses, +and 16 after the S bit for 10 bit addresses. + +WARNING! The current 10 bit address support is EXPERIMENTAL. There are +several places in the code that will cause SEVERE PROBLEMS with 10 bit +addresses, even though there is some basic handling and hooks. Also, +almost no supported adapter handles the 10 bit addresses correctly. + +As soon as a real 10 bit address device is spotted 'in the wild', we +can and will add proper support. Right now, 10 bit address devices +are defined by the I2C protocol, but we have never seen a single device +which supports them. --- linux-old/Documentation/i2c/writing-clients Sat Jan 15 20:08:54 CET 2000 +++ linux/Documentation/i2c/writing-clients Sat Jan 15 20:08:54 CET 2000 @@ -0,0 +1,857 @@ +This is a small guide for those who want to write kernel drivers for I2C +or SMBus devices. + +To set up a driver, you need to do several things. Some are optional, and +some things can be done slightly or completely different. Use this as a +guide, not as a rule book! + + +General remarks +=============== + +Try to keep the kernel namespace as clean as possible. The best way to +do this is to use a unique prefix for all global symbols. This is +especially important for exported symbols, but it is a good idea to do +it for non-exported symbols too. We will use the prefix `foo_' in this +tutorial, and `FOO_' for preprocessor variables. + + +The driver structure +==================== + +Usually, you will implement a single driver structure, and instantiate +all clients from it. Remember, a driver structure contains general access +routines, a client structure specific information like the actual I2C +address. + + struct i2c_driver foo_driver + { + /* name */ "Foo version 2.3 and later driver", + /* id */ I2C_DRIVERID_FOO, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &foo_attach_adapter, + /* detach_client */ &foo_detach_client, + /* command */ &foo_command, /* May be NULL */ + /* inc_use */ &foo_inc_use, /* May be NULL */ + /* dec_use */ &foo_dev_use /* May be NULL */ + } + +The name can be choosen freely, and may be upto 40 characters long. Please +use something descriptive here. + +The id should be a unique ID. The range 0xf000 to 0xffff is reserved for +local use, and you can use one of those until you start distributing the +driver. Before you do that, contact the i2c authors to get your own ID(s). + +Don't worry about the flags field; just put I2C_DF_NOTIFY into it. This +means that your driver will be notified when new adapters are found. +This is almost always what you want. + +All other fields are for call-back functions which will be explained +below. + + +Module usage count +================== + +If your driver can also be compiled as a module, there are moments at +which the module can not be removed from memory. For example, when you +are doing a lengthy transaction, or when you create a /proc directory, +and some process has entered that directory (this last case is the +main reason why these call-backs were introduced). + +To increase or decrease the module usage count, you can use the +MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module +which needs to get its usage count changed; that is why each driver +module has to implement its own callback. + + void foo_inc_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_INC_USE_COUNT; + #endif + } + + void foo_dec_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_DEC_USE_COUNT; + #endif + } + +Do not call these call-back functions directly; instead, use one of the +following functions defined in i2c.h: + void i2c_inc_use_client(struct i2c_client *); + void i2c_dec_use_client(struct i2c_client *); + +You should *not* increase the module count just because a device is +detected and a client created. This would make it impossible to remove +an adapter driver! + + +Extra client data +================= + +The client structure has a special `data' field that can point to any +structure at all. You can use this to keep client-specific data. You +do not always need this, but especially for `sensors' drivers, it can +be very useful. + +An example structure is below. + + struct foo_data { + struct semaphore lock; /* For ISA access in `sensors' drivers. */ + int sysctl_id; /* To keep the /proc directory entry for + `sensors' drivers. */ + enum chips type; /* To keep the chips type for `sensors' drivers. */ + + /* Because the i2c bus is slow, it is often useful to cache the read + information of a chip for some time (for example, 1 or 2 seconds). + It depends of course on the device whether this is really worthwhile + or even sensible. */ + struct semaphore update_lock; /* When we are reading lots of information, + another process should not update the + below information */ + char valid; /* != 0 if the following fields are valid. */ + unsigned long last_updated; /* In jiffies */ + /* Add the read information here too */ + }; + + +Accessing the client +==================== + +Let's say we have a valid client structure. At some time, we will need +to gather information from the client, or write new information to the +client. How we will export this information to user-space is less +important at this moment (perhaps we do not need to do this at all for +some obscure clients). But we need generic reading and writing routines. + +I have found it useful to define foo_read and foo_write function for this. +For some cases, it will be easier to call the i2c functions directly, +but many chips have some kind of register-value idea that can easily +be encapsulated. Also, some chips have both ISA and I2C interfaces, and +it useful to abstract from this (only for `sensors' drivers). + +The below functions are simple examples, and should not be copied +literally. + + int foo_read_value(struct i2c_client *client, u8 reg) + { + if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_read_byte_data(client,reg); + else /* word-sized register */ + return i2c_smbus_read_word_data(client,reg); + } + + int foo_write_value(struct i2c_client *client, u8 reg, u16 value) + { + if (reg == 0x10) /* Impossible to write - driver error! */ { + return -1; + else if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_write_byte_data(client,reg,value); + else /* word-sized register */ + return i2c_smbus_write_word_data(client,reg,value); + } + +For sensors code, you may have to cope with ISA registers too. Something +like the below often works. Note the locking! + + int foo_read_value(struct i2c_client *client, u8 reg) + { + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct foo_data *) (client->data)) -> lock)); + outb_p(reg,client->addr + FOO_ADDR_REG_OFFSET); + res = inb_p(client->addr + FOO_DATA_REG_OFFSET); + up(&(((struct foo_data *) (client->data)) -> lock)); + return res; + } else + return i2c_smbus_read_byte_data(client,reg); + } + +Writing is done the same way. + + +Probing and attaching +===================== + +Most i2c devices can be present on several i2c addresses; for some this +is determined in hardware (by soldering some chip pins to Vcc or Ground), +for others this can be changed in software (by writing to specific client +registers). Some devices are usually on a specific address, but not always; +and some are even more tricky. So you will probably need to scan several +i2c addresses for your clients, and do some sort of detection to see +whether it is actually a device supported by your driver. + +To give the user a maximum of possibilities, some default module parameters +are defined to help determine what addresses are scanned. Several macros +are defined in i2c.h to help you support them, as well as a generic +detection algorithm. + +You do not have to use this parameter interface; but don't try to use +function i2c_probe() (or sensors_detect()) if you don't. + +NOTE: If you want to write a `sensors' driver, the interface is slightly + different! See below. + + + +Probing classes (i2c) +--------------------- + +All parameters are given as lists of unsigned 16-bit integers. Lists are +terminated by I2C_CLIENT_END. +The following lists are used internally: + + normal_i2c: filled in by the module writer. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + probe: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the address. These addresses are also probed, as if they + were in the 'normal' list. + probe_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + addresses that are also probed, as if they were in the 'normal' list. + ignore: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. These addresses are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + ignore_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + force: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. A device is blindly assumed to be on + the given address, no probing is done. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters. The complete declaration could look +like this: + + /* Scan 0x20 to 0x2f, 0x37, and 0x40 to 0x4f */ + static unsigned short normal_i2c[] = { 0x37,I2C_CLIENT_END }; + static unsigned short normal_i2c_range[] = { 0x20, 0x2f, 0x40, 0x4f, + I2C_CLIENT_END }; + + /* Magic definition of all other variables and things */ + I2C_CLIENT_INSMOD; + +Note that you *have* to call the two defined variables `normal_i2c' and +`normal_i2c_range', without any prefix! + + +Probing classes (sensors) +------------------------- + +If you write a `sensors' driver, you use a slightly different interface. +As well as I2C addresses, we have to cope with ISA addresses. Also, we +use a enum of chip types. Don't forget to include `sensors.h'. + +The following lists are used internally. They are all lists of integers. + + normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. Terminated by + SENSORS_I2C_END + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. + A list of ISA addresses which should normally be examined. + normal_isa_range: filled in by the module writer. Terminated by + SENSORS_ISA_END + A list of triples. The first two elements are ISA addresses, being an + range of addresses which should normally be examined. The third is the + modulo parameter: only addresses which are 0 module this value relative + to the first address of the range are actually considered. + probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the address. These + addresses are also probed, as if they were in the 'normal' list. + probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of addresses that are also probed, as + if they were in the 'normal' list. + ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the I2C address. These + addresses are never probed. This parameter overrules 'normal' and + 'probe', but not the 'force' lists. + ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + +Also used is a list of pointers to sensors_force_data structures: + force_data: insmod parameters. A list, ending with an element of which + the force field is NULL. + Each element contains the type of chip and a list of pairs. + The first value is a bus number (SENSORS_ISA_BUS for the ISA bus, + -1 for any I2C bus), the second is the address. + These are automatically translated to insmod variables of the form + force_foo. + +So we have a generic insmod variabled `force', and chip-specific variables +`force_CHIPNAME'. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters, and define what chip names are used. +The complete declaration could look like this: + /* Scan i2c addresses 0x20 to 0x2f, 0x37, and 0x40 to 0x4f + static unsigned short normal_i2c[] = {0x37,SENSORS_I2C_END}; + static unsigned short normal_i2c_range[] = {0x20,0x2f,0x40,0x4f, + SENSORS_I2C_END}; + /* Scan ISA address 0x290 */ + static unsigned int normal_isa[] = {0x0290,SENSORS_ISA_END}; + static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; + + /* Define chips foo and bar, as well as all module parameters and things */ + SENSORS_INSMOD_2(foo,bar); + +If you have one chip, you use macro SENSORS_INSMOD_1(chip), if you have 2 +you use macro SENSORS_INSMOD_2(chip1,chip2), etc. If you do not want to +bother with chip types, you can use SENSORS_INSMOD_0. + +A enum is automatically defined as follows: + enum chips { any_chip, chip1, chip2, ... } + + +Attaching to an adapter +----------------------- + +Whenever a new adapter is inserted, or for all adapters if the driver is +being registered, the callback attach_adapter() is called. Now is the +time to determine what devices are present on the adapter, and to register +a client for each of them. + +The attach_adapter callback is really easy: we just call the generic +detection function. This function will scan the bus for us, using the +information as defined in the lists explained above. If a device is +detected at a specific address, another callback is called. + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return i2c_probe(adapter,&addr_data,&foo_detect_client); + } + +For `sensors' drivers, use the sensors_detect function instead: + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return sensors_detect(adapter,&addr_data,&foo_detect_client); + } + +Remember, structure `addr_data' is defined by the macros explained above, +so you do not have to define it yourself. + +The i2c_probe or sensors_detect function will call the foo_detect_client +function only for those i2c addresses that actually have a device on +them (unless a `force' parameter was used). In addition, addresses that +are already in use (by some other registered client) are skipped. + + +The detect client function +-------------------------- + +The detect client function is called by i2c_probe or sensors_detect. +The `kind' parameter contains 0 if this call is due to a `force' +parameter, and 0 otherwise (for sensors_detect, it contains 0 if +this call is due to the generic `force' parameter, and the chip type +number if it is due to a specific `force' parameter). + +Below, some things are only needed if this is a `sensors' driver. Those +parts are between /* SENSORS ONLY START */ and /* SENSORS ONLY END */ +markers. + +This function should only return an error (any value != 0) if there is +some reason why no more detection should be done anymore. If the +detection just fails for this address, return 0. + +For now, you can ignore the `flags' parameter. It is there for future use. + + /* Unique ID allocation */ + static int foo_id = 0; + + int foo_detect_client(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) + { + int err = 0; + int i; + struct i2c_client *new_client; + struct foo_data *data; + const char *client_name = ""; /* For non-`sensors' drivers, put the real + name here! */ + + /* Let's see whether this adapter can support what we need. + Please substitute the things you need here! + For `sensors' drivers, add `! is_isa &&' to the if statement */ + if (i2c_check_functionality(adapter,I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE)) + goto ERROR0; + + /* SENSORS ONLY START */ + const char *type_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (is_isa) { + + /* If this client can't be on the ISA bus at all, we can stop now + (call `goto ERROR0'). But for kicks, we will assume it is all + right. */ + + /* Discard immediately if this ISA range is already used */ + if (check_region(address,FOO_EXTENT)) + goto ERROR0; + + /* Probe whether there is anything on this address. + Some example code is below, but you will have to adapt this + for your own driver */ + + if (kind < 0) /* Only if no force parameter was used */ { + /* We may need long timeouts at least for some chips. */ + #define REALLY_SLOW_IO + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; + #undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f,address+5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i,address+5); + return 0; + } + } + } + + /* SENSORS ONLY END */ + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access several i2c functions safely */ + + /* Note that we reserve some space for foo_data too. If you don't + need it, remove it. We do it here to help to lessen memory + fragmentation. */ + if (! (new_client = kmalloc(sizeof(struct i2c_client)) + + sizeof(struct foo_data), + GFP_KERNEL)) { + err = -ENOMEM; + goto ERROR0; + } + + /* This is tricky, but it will set the data to the right value. */ + client->data = new_client + 1; + data = (struct foo_data *) (client->data); + + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &foo_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. If no `force' parameter is used. */ + + /* First, the generic detection (if any), that is skipped if any force + parameter was used. */ + if (kind < 0) { + /* The below is of course bogus */ + if (foo_read(new_client,FOO_REG_GENERIC) != FOO_GENERIC_VALUE) + goto ERROR1; + } + + /* SENSORS ONLY START */ + + /* Next, specific detection. This is especially important for `sensors' + devices. */ + + /* Determine the chip type. Not needed if a `force_CHIPTYPE' parameter + was used. */ + if (kind <= 0) { + i = foo_read(new_client,FOO_REG_CHIPTYPE); + if (i == FOO_TYPE_1) + kind = chip1; /* As defined in the enum */ + else if (i == FOO_TYPE_2) + kind = chip2; + else { + printk("foo: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n",i2c_adapter_id(adapter),address); + goto ERROR1; + } + } + + /* Now set the type and chip names */ + if (kind == chip1) { + type_name = "chip1"; /* For /proc entry */ + client_name = "CHIP 1"; + } else if (kind == chip2) { + type_name = "chip2"; /* For /proc entry */ + client_name = "CHIP 2"; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address,FOO_EXTENT,type_name); + + /* SENSORS ONLY END */ + + /* Fill in the remaining client fields. */ + strcpy(new_client->name,client_name); + + /* SENSORS ONLY BEGIN */ + data->type = kind; + /* SENSORS ONLY END */ + + new_client->id = foo_id++; /* Automatically unique */ + data->valid = 0; /* Only if you use this field */ + init_MUTEX(&data->update_lock); /* Only if you use this field */ + + /* Any other initializations in data must be done here too. */ + + /* Tell the i2c layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* SENSORS ONLY BEGIN */ + /* Register a new directory entry with module sensors. See below for + the `template' structure. */ + if ((i = sensors_register_entry(new_client, type_name, + foo_dir_table_template,THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* SENSORS ONLY END */ + + /* This function can write default values to the client registers, if + needed. */ + foo_init_client(new_client); + return 0; + + /* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR2: + /* SENSORS ONLY START */ + if (is_isa) + release_region(address,FOO_EXTENT); + /* SENSORS ONLY END */ + ERROR1: + kfree(new_client); + ERROR0: + return err; + } + + +Removing the client +=================== + +The detach_client call back function is called when a client should be +removed. It may actually fail, but only when panicking. This code is +much simpler than the attachment code, fortunately! + + int foo_detach_client(struct i2c_client *client) + { + int err,i; + + /* SENSORS ONLY START */ + /* Deregister with the `sensors' module. */ + sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id); + /* SENSORS ONLY END */ + + /* Try to detach the client from i2c space */ + if ((err = i2c_detach_client(client))) { + printk("foo.o: Client deregistration failed, client not detached.\n"); + return err; + } + + /* SENSORS ONLY START */ + if i2c_is_isa_client(client) + release_region(client->addr,LM78_EXTENT); + /* SENSORS ONLY END */ + + kfree(client); /* Frees client data too, if allocated at the same time */ + return 0; + } + + +Initializing the module or kernel +================================= + +When the kernel is booted, or when your foo driver module is inserted, +you have to do some initializing. Fortunately, just attaching (registering) +the driver module is usually enough. + + /* Keep track of how far we got in the initialization process. If several + things have to initialized, and we fail halfway, only those things + have to be cleaned up! */ + static int __initdata foo_initialized = 0; + + int __init foo_init(void) + { + int res; + printk("foo version %s (%s)\n",FOO_VERSION,FOO_DATE); + + if ((res = i2c_add_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not inserted.\n"); + foo_cleanup(); + return res; + } + foo_initialized ++; + return 0; + } + + int __init foo_cleanup(void) + { + int res; + if (foo_initialized == 1) { + if ((res = i2c_del_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not removed.\n"); + return res; + } + foo_initialized --; + } + return 0; + } + + #ifdef MODULE + + /* Substitute your own name and email address */ + MODULE_AUTHOR("Frodo Looijaard " + MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); + + int init_module(void) + { + return foo_init(); + } + + int cleanup_module(void) + { + return foo_cleanup(); + } + + #endif /* def MODULE */ + +Note that some functions are marked by `__init', and some data structures +by `__init_data'. If this driver is compiled as part of the kernel (instead +of as a module), those functions and structures can be removed after +kernel booting is completed. + +Command function +================ + +A generic ioctl-like function call back is supported. You will seldomly +need this. You may even set it to NULL. + + /* No commands defined */ + int foo_command(struct i2c_client *client, unsigned int cmd, void *arg) + { + return 0; + } + + +Sending and receiving +===================== + +If you want to communicate with your device, there are several functions +to do this. You can find all of them in i2c.h. + +If you can choose between plain i2c communication and SMBus level +communication, please use the last. All adapters understand SMBus level +commands, but only some of them understand plain i2c! + + +Plain i2c communication +----------------------- + + extern int i2c_master_send(struct i2c_client *,const char* ,int); + extern int i2c_master_recv(struct i2c_client *,char* ,int); + +These routines read and write some bytes from/to a client. The client +contains the i2c address, so you do not have to include it. The second +parameter contains the bytes the read/write, the third the length of the +buffer. Returned is the actual number of bytes read/written. + + extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num); + +This sends a series of messages. Each message can be a read or write, +and they can be mixed in any way. The transactions are combined: no +stop bit is sent between transaction. The i2c_msg structure contains +for each message the client address, the number of bytes of the message +and the message data itself. + +You can read the file `i2c-protocol' for more information about the +actual i2c protocol. + + +SMBus communication +------------------- + + extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + + This is the generic SMBus function. All functions below are implemented + in terms of it. Never use this function directly! + + + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte(struct i2c_client * client); + extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); + extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); + extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); + +All these tranactions return -1 on failure. The 'write' transactions +return 0 on success; the 'read' transactions return the read value, except +for read_block, which returns the number of values read. The block buffers +need not be longer than 32 bytes. + +You can read the file `smbus-protocol' for more information about the +actual SMBus protocol. + + +General purpose routines +======================== + +Below all general purpose routines are listed, that were not mentioned +before. + + /* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ + extern int i2c_adapter_id(struct i2c_adapter *adap); + + +The sensors sysctl/proc interface +================================= + +This section only applies if you write `sensors' drivers. + +Each sensors driver creates a directory in /proc/sys/dev/sensors for each +registered client. The directory is called something like foo-i2c-4-65. +The sensors module helps you to do this as easily as possible. + +The template +------------ + +You will need to define a ctl_table template. This template will automatically +be copied to a newly allocated structure and filled in where necessary when +you call sensors_register_entry. + +First, I will give an example definition. + static ctl_table foo_dir_table_template[] = { + { FOO_SYSCTL_FUNC1, "func1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_FUNC2, "func2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_DATA, "data", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_data }, + { 0 } + }; + +In the above example, three entries are defined. They can either be +accessed through the /proc interface, in the /proc/sys/dev/sensors/* +directories, as files named func1, func2 and data, or alternatively +through the sysctl interface, in the appropriate table, with identifiers +FOO_SYSCTL_FUNC1, FOO_SYSCTL_FUNC2 and FOO_SYSCTL_DATA. + +The third, sixth and ninth parameters should always be NULL, and the +fourth should always be 0. The fifth is the mode of the /proc file; +0644 is safe, as the file will be owned by root:root. + +The seventh and eigth parameters should be &sensors_proc_real and +&sensors_sysctl_real if you want to export lists of reals (scaled +integers). You can also use your own function for them, as usual. +Finally, the last parameter is the call-back to gather the data +(see below) if you use the *_proc_real functions. + + +Gathering the data +------------------ + +The call back functions (foo_func and foo_data in the above example) +can be called in several ways; the operation parameter determines +what should be done: + + * If operation == SENSORS_PROC_REAL_INFO, you must return the + magnitude (scaling) in nrels_mag; + * If operation == SENSORS_PROC_REAL_READ, you must read information + from the chip and return it in results. The number of integers + to display should be put in nrels_mag; + * If operation == SENSORS_PROC_REAL_WRITE, you must write the + supplied information to the chip. nrels_mag will contain the number + of integers, results the integers themselves. + +The *_proc_real functions will display the elements as reals for the +/proc interface. If you set the magnitude to 2, and supply 345 for +SENSORS_PROC_REAL_READ, it would display 3.45; and if the user would +write 45.6 to the /proc file, it would be returned as 4560 for +SENSORS_PROC_REAL_WRITE. A magnitude may even be negative! + +An example function: + + /* FOO_FROM_REG and FOO_TO_REG translate between scaled values and + register values. Note the use of the read cache. */ + void foo_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + { + struct foo_data *data = client->data; + int nr = ctl_name - FOO_SYSCTL_FUNC1; /* reduce to 0 upwards */ + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + /* Update the readings cache (if necessary) */ + foo_update_client(client); + /* Get the readings from the cache */ + results[0] = FOO_FROM_REG(data->foo_func_base[nr]); + results[1] = FOO_FROM_REG(data->foo_func_more[nr]); + results[2] = FOO_FROM_REG(data->foo_func_readonly[nr]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + /* Update the cache */ + data->foo_base[nr] = FOO_TO_REG(results[0]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_BASE(nr),data->foo_base[nr]); + } + if (*nrels_mag >= 2) { + /* Update the cache */ + data->foo_more[nr] = FOO_TO_REG(results[1]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_MORE(nr),data->foo_more[nr]); + } + } + } --- linux-old/drivers/i2c/i2c-algo-bit.c Sat Jan 15 20:08:55 CET 2000 +++ linux/drivers/i2c/i2c-algo-bit.c Sat Jan 15 20:08:55 CET 2000 @@ -0,0 +1,660 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-bit.c,v 1.22 2000/01/09 16:00:48 frodo Exp $ */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=0; +static int bit_test=0; /* see if the line-setting functions work */ +static int bit_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap,val) adap->setsda(adap->data, val) +#define setscl(adap,val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap,0); + udelay(adap->udelay); +#ifdef SLO_IO + SLO_IO +#endif +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static inline int sclhi(struct i2c_algo_bit_data *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + + /* Not all adapters have scl sense line... */ + if (adap->getscl == NULL ) + return 0; + + while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ + setscl(adap,1); + if (start+adap->timeout <= jiffies) { + return -ETIMEDOUT; + } +#if LINUX_VERSION_CODE >= 0x02016e + if (current->need_resched) + schedule(); +#else + if (need_resched) + schedule(); +#endif + } + DEBSTAT(printk("needed %ld jiffies\n", jiffies-start)); +#ifdef SLO_IO + SLO_IO +#endif + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + DEBPROTO(printk("P\n")); + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + sdahi(adap); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occured (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk(" i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk("%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + return -ETIMEDOUT; + }; + /* do arbitration here: + * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + */ + setscl(adap, 0 ); + udelay(adap->udelay); + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(" i2c_outb: getsda() = 0x%2.2x\n", ~ack )); + + DEBPROTO( printk("[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(" A ");} else printk(" NA ") ); + scllo(adap); + return 0==ack; /* return 1 if device acked */ + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata=0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEBPROTO(printk(" %2.2x", indata & 0xff)); + return (int) (indata & 0xff); +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_bit_data *adap, char* name) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-bit.o: %s seems to be busy.\n",name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-bit.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static inline int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i,ret = -1; + for (i=0;i<=retries;i++) { + ret = i2c_outb(i2c_adap,addr); + if (ret==1) + break; /* success! */ + i2c_stop(adap); + udelay(5/*adap->udelay*/); + if (i==retries) /* no success */ + break; + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-bit.o: needed %d retries for %d\n",i,addr)); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c-algo-bit.o: %s i2c_write: writing %2.2X\n", + i2c_adap->name, c&0xff)); + retval = i2c_outb(i2c_adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c-algo-bit.o: %s i2c_write: error - bailout.\n", + i2c_adap->name); + i2c_stop(adap); + return (retval<0)? retval : -EFAULT; /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + while (count > 0) { + inval = i2c_inb(i2c_adap); +/*printk("%#02x ",inval); if ( ! (count % 16) ) printk("\n"); */ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c-algo-bit.o: i2c_read: i2c_inb timed out.\n"); + break; + } + + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); + } else { + sdahi(adap); /* neg. ack on last byte */ + DEBPROTO(printk(" NAm ")); + } + if (sclhi(adap)<0) { /* timeout */ + sdahi(adap); + printk("i2c-algo-bit.o: i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed + * -x an error occured (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + if (ret != 1) { + /* the chip did not ack / xmission error occured */ + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + int i,ret; + + i2c_start(adap); + for (i=0;iflags & I2C_M_NOSTART)) { + if (i) { + i2c_repstart(adap); + } + ret = bit_doAddress(i2c_adap,pmsg,i2c_adap->retries); + if (ret != 0) { + DEB2(printk("i2c-algo-bit.o: NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return (ret<0) ? ret : -EREMOTEIO; + } + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: read %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; + } + } + } + i2c_stop(adap); + return num; +} + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_bit_algo = { + "Bit-shift algorithm", + I2C_ALGO_BIT, + bit_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + bit_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + int i; + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + + if (bit_test) { + int ret = test_bus(bit_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-bit.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= i2c_bit_algo.id; + adap->algo = &i2c_bit_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + /* scan bus */ + if (bit_scan) { + int ack; + printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(bit_adap); + ack = i2c_outb(adap,i); + i2c_stop(bit_adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + i2c_add_adapter(adap); + + return 0; +} + + +int i2c_bit_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_bit_init (void) +{ + printk("i2c-algo-bit.o: i2c bit algorithm module\n"); + return 0; +} + + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + +MODULE_PARM(bit_test, "i"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + +int init_module(void) +{ + return i2c_algo_bit_init(); +} + +void cleanup_module(void) +{ +} +#endif --- linux-old/include/linux/i2c-algo-bit.h Sat Jan 15 20:08:56 CET 2000 +++ linux/include/linux/i2c-algo-bit.h Sat Jan 15 20:08:56 CET 2000 @@ -0,0 +1,55 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-bit.h,v 1.7 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_ALGO_BIT_H +#define I2C_ALGO_BIT_H 1 + +#include + +/* --- Defines for bit-adapters --------------------------------------- */ +/* + * This struct contains the hw-dependent functions of bit-style adapters to + * manipulate the line states, and to init any hw-specific features. This is + * only used if you have more than one hw-type of adapter running. + */ +struct i2c_algo_bit_data { + void *data; /* private data for lowlevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_BIT_ADAP_MAX 16 + +int i2c_bit_add_bus(struct i2c_adapter *); +int i2c_bit_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_BIT_H */ --- linux-old/drivers/i2c/i2c-algo-pcf.c Sat Jan 15 20:08:56 CET 2000 +++ linux/drivers/i2c/i2c-algo-pcf.c Sat Jan 15 20:08:56 CET 2000 @@ -0,0 +1,592 @@ + +/* ------------------------------------------------------------------------- */ +/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-pcf.c,v 1.16 2000/01/09 16:00:48 frodo Exp $ */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + + +#include +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include "i2c-pcf8584.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int pcf_test=0; /* see if the line-setting functions work */ +static int pcf_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + + +/* --- other auxiliary functions -------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("S ")); + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(" Sr ")); + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("P\n")); + set_pcf(adap, 1, I2C_PCF_STOP); +} + + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) { + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + while (timeout-- && !(status & I2C_PCF_BB)) { + udelay(1000); /* How much is this? */ + status = get_pcf(adap, 1); + } + if (timeout<=0) + printk("Timeout waiting for Bus Busy\n"); + /* + set_pcf(adap, 1, I2C_PCF_STOP); + */ + return(timeout<=0); +} + + +static inline void pcf_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + while (timeout-- && (*status & I2C_PCF_PIN)) { + adap->waitforpin(); + *status = get_pcf(adap, 1); + } + if (timeout <= 0) + return(-1); + else + return(0); +} + + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + DEB2(printk("i2c-algo-pcf.o: irq: Initialized 8584.\n")); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_pcf_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-pcf.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-pcf.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-pcf.o: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-pcf.o:1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-pcf.o:2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while SDA high!\n",adap->name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-pcf.o:3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-pcf.o:4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-pcf.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + +static inline int try_address(struct i2c_algo_pcf_data *adap, + unsigned char addr, int retries) +{ + int i, status, ret = -1; + for (i=0;i= 0) { + if ((status && I2C_PCF_LRB) == 0) { + i2c_stop(adap); + break; /* success! */ + } + } + i2c_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-pcf.o: needed %d retries for %d\n",i,addr)); + return ret; +} + + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcountname, buf[wrcount]&0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + printk("i2c-algo-pcf.o: %s i2c_write: error - timeout.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + printk("i2c-algo-pcf.o: %s i2c_write: error - no ack.\n", + i2c_adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } + } + return (wrcount); +} + + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + int rdcount=0, i, status, timeout, dummy=1; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + + for (i=0; iflags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + i2c_outb(adap,msg->addr & 0x7f); +/* Status check comes here */ + if (ret != 1) { + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + i2c_outb(adap, addr); + } + return 0; +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i, ret, timeout, status; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for BB in pcf_xfer\n");) + return -EIO; + } + i2c_start(adap); + + for (i=0; iflags & I2C_M_NOSTART)) { + if (i) { + i2c_repstart(adap); + } + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + timeout = wait_for_pin(adap, &status); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: Timeout waiting for PIN(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } + } + DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); + } else { + /* write bytes from buffer */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + } + i2c_stop(adap); + return (num); +} + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm pcf_algo = { + "PCF8584 algorithm", + I2C_ALGO_PCF, + pcf_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + pcf_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + int i, status; + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + + if (pcf_test) { + int ret = test_bus(pcf_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-pcf.o: hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= pcf_algo.id; + adap->algo = &pcf_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + pcf_init_8584(pcf_adap); + + /* scan bus */ + if (pcf_scan) { + printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_outb(pcf_adap, i); + i2c_start(pcf_adap); + if ((wait_for_pin(pcf_adap, &status) >= 0) && + ((status && I2C_PCF_LRB) == 0)) { + printk("(%02x)",i>>1); + } else { + printk("."); + } + i2c_stop(pcf_adap); + udelay(pcf_adap->udelay); + } + printk("\n"); + } + return 0; +} + + +int i2c_pcf_del_bus(struct i2c_adapter *adap) +{ + i2c_del_adapter(adap); + DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_pcf_init (void) +{ + printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); + return 0; +} + + +EXPORT_SYMBOL(i2c_pcf_add_bus); +EXPORT_SYMBOL(i2c_pcf_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + +MODULE_PARM(pcf_test, "i"); +MODULE_PARM(pcf_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(pcf_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); + + +int init_module(void) +{ + return i2c_algo_pcf_init(); +} + +void cleanup_module(void) +{ +} +#endif --- linux-old/include/linux/i2c-algo-pcf.h Sat Jan 15 20:08:57 CET 2000 +++ linux/include/linux/i2c-algo-pcf.h Sat Jan 15 20:08:57 CET 2000 @@ -0,0 +1,52 @@ +/* ------------------------------------------------------------------------- */ +/* adap-pcf.h i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-pcf.h,v 1.6 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_ALGO_PCF_H +#define I2C_AGLO_PCF_H 1 + +/* --- Defines for pcf-adapters --------------------------------------- */ +#include + +struct i2c_algo_pcf_data { + void *data; /* private data for lolevel routines */ + void (*setpcf) (void *data, int ctl, int val); + int (*getpcf) (void *data, int ctl); + int (*getown) (void *data); + int (*getclock) (void *data); + void (*waitforpin) (void); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_PCF_ADAP_MAX 16 + +int i2c_pcf_add_bus(struct i2c_adapter *); +int i2c_pcf_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_PCF_H */ --- linux-old/drivers/i2c/i2c-core.c Sat Jan 15 20:08:58 CET 2000 +++ linux/drivers/i2c/i2c-core.c Sat Jan 15 20:08:58 CET 2000 @@ -0,0 +1,1369 @@ +/* i2c-core.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki . + All SMBus-related things are written by Frodo Looijaard */ + +/* $Id: i2c-core.c,v 1.44 1999/12/21 23:45:58 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include + +#include + +/* ----- compatibility stuff ----------------------------------------------- */ + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53) +#include +#else +#define __init +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#else +#include +#endif + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) + +#define ADAP_LOCK() down(&adap_lock) +#define ADAP_UNLOCK() up(&adap_lock) + +#define DRV_LOCK() down(&driver_lock) +#define DRV_UNLOCK() up(&driver_lock) + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** lock for writing to global variables: the adapter & driver list */ +struct semaphore adap_lock; +struct semaphore driver_lock; + +/**** adapter list */ +static struct i2c_adapter *adapters[I2C_ADAP_MAX]; +static int adap_count; + +/**** drivers list */ +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int driver_count; + +/**** debug level */ +static int i2c_debug=1; +static void i2c_dummy_adapter(struct i2c_adapter *adapter); +static void i2c_dummy_client(struct i2c_client *client); + +/* --------------------------------------------------- + * /proc entry declarations + *---------------------------------------------------- + */ + +/* Note that quite some things changed within the 2.1 kernel series. + Some things below are somewhat difficult to read because of this. */ + +#ifdef CONFIG_PROC_FS + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +static void monitor_bus_i2c(struct inode *inode, int fill); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + +static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + +static int i2cproc_bus_read(struct inode * inode, struct file * file, + char * buf, int count); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int unused); + +static struct proc_dir_entry proc_bus_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "bus", + /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, + /* nlink */ 2, /* Corrected by proc_register[_dynamic] */ + /* uid */ 0, + /* gid */ 0, + /* size */ 0, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36)) + /* ops */ &proc_dir_inode_operations, +#endif + }; + +static struct proc_dir_entry proc_bus_i2c_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "i2c", + /* mode */ S_IRUGO | S_IFREG, + /* nlink */ 1, + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ NULL, + /* get_info */ &read_bus_i2c + }; + +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + +/* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ +static struct file_operations i2cproc_operations = { + NULL, + i2cproc_bus_read, +}; + +static struct inode_operations i2cproc_inode_operations = { + &i2cproc_operations +}; + +static int i2cproc_initialized = 0; + +#else /* undef CONFIG_PROC_FS */ + +#define i2cproc_init() +#define i2cproc_cleanup() + +#endif /* CONFIG_PROC_FS */ + + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * i2c_add_adapter is called from within the algorithm layer, + * when a new hw adapter registers. A new device is register to be + * available for clients. + */ +int i2c_add_adapter(struct i2c_adapter *adap) +{ + int i,j; + + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + ADAP_UNLOCK(); + return -ENOMEM; + } + + adapters[i] = adap; + adap_count++; + ADAP_UNLOCK(); + + /* init data types */ + init_MUTEX(&adap->lock); + + i2c_dummy_adapter(adap); /* actually i2c_dummy->add_adapter */ +#ifdef CONFIG_PROC_FS + + if (i2cproc_initialized) { + char name[8]; + struct proc_dir_entry *proc_entry; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) + int res; +#endif + + sprintf(name,"i2c-%d", i); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + proc_entry = create_proc_entry(name,0,proc_bus); + if (! proc_entry) { + printk("i2c-core.o: Could not create /proc/bus/%s\n", + name); + return -ENOENT; + } + proc_entry->ops = &i2cproc_inode_operations; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_entry->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_entry->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + adap->proc_entry = NULL; + if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+ + strlen(name)+1, GFP_KERNEL))) { + printk("i2c-core.o: Out of memory!\n"); + return -ENOMEM; + } + memset(proc_entry,0,sizeof(struct proc_dir_entry)); + proc_entry->namelen = strlen(name); + proc_entry->name = (char *) (proc_entry + 1); + proc_entry->mode = S_IRUGO | S_IFREG; + proc_entry->nlink = 1; + proc_entry->ops = &i2cproc_inode_operations; + + /* Nasty stuff to keep GCC satisfied */ + { + char *procname; + (const char *) procname = proc_entry->name; + strcpy (procname,name); + } + + if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) { + printk("i2c-core.o: Could not create %s.\n",name); + kfree(proc_entry); + return res; + } + + adap->proc_entry = proc_entry; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + adap->inode = proc_entry->low_ino; + } + +#endif /* def CONFIG_PROC_FS */ + + /* inform drivers of new adapters */ + DRV_LOCK(); + for (j=0;jflags&I2C_DF_NOTIFY) + drivers[j]->attach_adapter(adap); + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",adap->name,i)); + + return 0; +} + + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk( " i2c-core.o: unregister_adapter adap [%s] not found.\n", + adap->name); + ADAP_UNLOCK(); + return -ENODEV; + } + + i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ +#ifdef CONFIG_PROC_FS + if (i2cproc_initialized) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char name[8]; + sprintf(name,"i2c-%d", i); + remove_proc_entry(name,proc_bus); +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (adapters[i]->proc_entry) { + if ((res = proc_unregister(&proc_bus_dir, + adapters[i]->proc_entry->low_ino))) { + printk("i2c-core.o: Deregistration of /proc " + "entry failed\n"); + ADAP_UNLOCK(); + return res; + } + kfree(adapters[i]->proc_entry); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } +#endif /* def CONFIG_PROC_FS */ + + /* detach any active clients */ + for (j=0;jclients[j]; + if ( (client!=NULL) + /* && (client->driver->flags & I2C_DF_NOTIFY) */ ) + /* detaching devices is unconditional of the set notify + * flag, as _all_ clients that reside on the adapter + * must be deleted, as this would cause invalid states. + */ + client->driver->detach_client(client); + /* i2c_detach_client(client); --- frodo */ + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + ADAP_UNLOCK(); + DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); + return 0; +} + + +/* ----- + * What follows is the "upwards" interface: commands for talking to clients, + * which implement the functions to access the physical information of the + * chips. + */ + +int i2c_add_driver(struct i2c_driver *driver) +{ + int i,j; + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + DRV_UNLOCK(); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DRV_UNLOCK(); /* driver was successfully added */ + + DEB(printk("i2c-core.o: driver %s registered.\n",driver->name)); + + /* Notify all existing adapters and clients to dummy driver */ + ADAP_LOCK(); + if (driver->flags&I2C_DF_DUMMY) { + for (i=0; iattach_adapter(adapters[i]); + for (j=0; jclients[j]) + driver->detach_client(adapters[i]->clients[j]); + } + } + ADAP_UNLOCK(); + return 0; + } + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&I2C_DF_NOTIFY ) { + for (i=0;iattach_adapter(adapters[i]); + } + ADAP_UNLOCK(); + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_driver: [%s] not found\n", + driver->name); + DRV_UNLOCK(); + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still + * attached. If so, detach them to be able to kill the driver + * afterwards. + */ + DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); + /* removing clients does not depend on the notify flag, else + * invalid operation might (will!) result, when using stale client + * pointers. + */ + ADAP_LOCK(); /* should be moved inside the if statement... */ + if ((driver->flags&I2C_DF_DUMMY)==0) + for (k=0;kname)); + for (j=0;jclients[j]; + if (client != NULL && client->driver == driver) { + DEB2(printk("i2c-core.o: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + ADAP_UNLOCK(); + drivers[i] = NULL; + driver_count--; + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: driver unregistered: %s\n",driver->name)); + return 0; +} + +int i2c_check_addr (struct i2c_adapter *adapter, int addr) +{ + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; +} + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + i2c_dummy_client(client); + + if (adapter->client_register != NULL) + adapter->client_register(client); + DEB(printk("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", + client->name, adapter->name,i)); + return 0; +} + + +int i2c_detach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (client == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if (adapter->client_unregister != NULL) + adapter->client_unregister(client); + /* client->driver->detach_client(client);*/ + + adapter->clients[i] = NULL; + adapter->client_count--; + i2c_dummy_client(client); + + DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); + return 0; +} + +void i2c_inc_use_client(struct i2c_client *client) +{ + + if (client->driver->inc_use != NULL) + client->driver->inc_use(client); + + if (client->adapter->inc_use != NULL) + client->adapter->inc_use(client->adapter); +} + +void i2c_dec_use_client(struct i2c_client *client) +{ + + if (client->driver->dec_use != NULL) + client->driver->dec_use(client); + + if (client->adapter->dec_use != NULL) + client->adapter->dec_use(client->adapter); +} + +/* ---------------------------------------------------- + * The /proc functions + * ---------------------------------------------------- + */ + +#ifdef CONFIG_PROC_FS + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ + (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible + if some process still uses it or some file in it */ +void monitor_bus_i2c(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +/* This function generates the output for /proc/bus/i2c */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ +{ + int i; + int nr = 0; + /* Note that it is safe to write a `little' beyond len. Yes, really. */ + for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) + if (adapters[i]) { + nr += sprintf(buf+nr, "i2c-%d\t", i); + if (adapters[i]->algo->smbus_xfer) { + if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"smbus/i2c"); + else + nr += sprintf(buf+nr,"smbus "); + } else if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"i2c "); + else + nr += sprintf(buf+nr,"dummy "); + nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", + adapters[i]->name, + adapters[i]->algo->name); + } + return nr; +} + +/* This function generates the output for /proc/bus/i2c-? */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) +ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; +#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) +int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf, + int count) +{ +#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + char *kbuf; + struct i2c_client *client; + int i,j,len=0; + + if (count < 0) + return -EINVAL; + if (count > 4000) + count = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + for (j = 0; j < I2C_CLIENT_MAX; j++) + if ((client = adapters[i]->clients[j])) + /* Filter out dummy clients */ + if (client->driver->id != I2C_DRIVERID_I2CDEV) + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name,client->driver->name); + if (file->f_pos+len > count) + len = count - file->f_pos; + len = len - file->f_pos; + if (len < 0) + len = 0; + copy_to_user (buf,kbuf+file->f_pos,len); + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; +} + +int i2cproc_init(void) +{ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + struct proc_dir_entry *proc_bus_i2c; +#else + int res; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + + i2cproc_initialized = 0; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + if (! proc_bus) { + printk("i2c-core.o: /proc/bus/ does not exist"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk("i2c-core.o: Could not create /proc/bus/i2c"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c->read_proc = &read_bus_i2c; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_bus_i2c->owner = THIS_MODULE; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) + proc_bus_i2c->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + i2cproc_initialized += 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module + introduced it, or we are fucked. And 2.0.35 and earlier does not + export proc_dir_inode_operations, so we grab it from proc_net, + which also uses it. Not nice. */ + proc_bus_dir.ops = proc_net.ops; + if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { + printk("i2c-core.o: Could not create /proc/bus/"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { + printk("i2c-core.o: Could not create /proc/bus/i2c\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + return 0; +} + +int i2cproc_cleanup(void) +{ + + if (i2cproc_initialized >= 1) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) + remove_proc_entry("i2c",proc_bus); + i2cproc_initialized -= 2; +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ + int res; + if (i2cproc_initialized >= 2) { + if ((res = proc_unregister(&proc_bus_dir, + proc_bus_i2c_dir.low_ino))) { + printk("i2c-core.o: could not delete " + "/proc/bus/i2c, module not removed."); + return res; + } + i2cproc_initialized --; + } + if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { + printk("i2c-core.o: could not delete /proc/bus/, " + "module not removed."); + return res; + } + i2cproc_initialized --; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ + } + return 0; +} + + +#endif /* def CONFIG_PROC_FS */ + +/* --------------------------------------------------- + * dummy driver notification + * --------------------------------------------------- + */ + +static void i2c_dummy_adapter(struct i2c_adapter *adap) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); +} + +static void i2c_dummy_client(struct i2c_client *client) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + if (adap->algo->master_xfer) { + DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n",adap->name,num)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,msgs,num); + I2C_UNLOCK(adap); + + return ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + adap->id); + return -ENOSYS; + } +} + +int i2c_master_send(struct i2c_client *client,const char *buf ,int count) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + (const char *)msg.buf = buf; + + DEB2(printk("i2c-core.o: master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB2(printk("i2c-core.o: master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + DEB2(printk("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + + +int i2c_control(struct i2c_client *client, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct i2c_adapter *adap = client->adapter; + + DEB2(printk("i2c-core.o: i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_RETRIES: + adap->retries = arg; + break; + case I2C_TIMEOUT: + adap->timeout = arg; + break; + default: + if (adap->algo->algo_control!=NULL) + ret = adap->algo->algo_control(adap,cmd,arg); + } + return ret; +} + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ +int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc) +{ + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; + addr <= 0x7f; + addr++) { + + /* Skip if already in use */ + if (i2c_check_addr(adapter,addr)) + continue; + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; + !found && (address_data->force[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; +} + +/* +++ frodo + * return id number for a specific adapter + */ +int i2c_adapter_id(struct i2c_adapter *adap) +{ + int i; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + return i; + return -1; +} + +/* The SMBus parts */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + value,0,I2C_SMBUS_QUICK,NULL); +} + +extern s32 i2c_smbus_read_byte(struct i2c_client * client) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA,&data); +} + +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); +} + +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA,&data); +} + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[33]; + unsigned char msgbuf1[33]; + int num = read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; + + msgbuf0[0] = command; + switch(size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len=3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + printk("i2c-core.o: Block read not supported under " + "I2C emulation!\n"); + return -1; + } else { + msg[1].len = data->block[0] + 1; + if (msg[1].len > 32) { + printk("i2c-core.o: smbus_access called with " + "invalid block write size (%d)\n", + msg[1].len); + return -1; + } + for (i = 1; i <= msg[1].len; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + printk("i2c-core.o: smbus_access called with invalid size (%d)\n", + size); + return -1; + } + + if (i2c_transfer(adapter, msg, num) < 0) + return -1; + + if (read_write == I2C_SMBUS_READ) + switch(size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + } + return 0; +} + + +s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + s32 res; + flags = flags & I2C_M_TEN; + if (adapter->algo->smbus_xfer) { + I2C_LOCK(adapter); + res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, + command,size,data); + I2C_UNLOCK(adapter); + } else + res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, + command,size,data); + return res; +} + + +/* You should always define `functionality'; the 'else' is just for + backward compatibility. */ +u32 i2c_get_functionality (struct i2c_adapter *adap) +{ + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; +} + +int i2c_check_functionality (struct i2c_adapter *adap, u32 func) +{ + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; +} + + +static int __init i2c_init(void) +{ + printk("i2c-core.o: i2c core module\n"); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + adap_count=0; + driver_count=0; + + init_MUTEX(&adap_lock); + init_MUTEX(&driver_lock); + + i2cproc_init(); + + return 0; +} + +#ifndef MODULE +#ifdef CONFIG_I2C_CHARDEV + extern int i2c_dev_init(void); +#endif +#ifdef CONFIG_I2C_ALGOBIT + extern int algo_bit_init(void); +#endif +#ifdef CONFIG_I2C_BITLP + extern int bitlp_init(void); +#endif +#ifdef CONFIG_I2C_BITELV + extern int bitelv_init(void); +#endif +#ifdef CONFIG_I2C_BITVELLE + extern int bitvelle_init(void); +#endif +#ifdef CONFIG_I2C_BITVIA + extern int bitvia_init(void); +#endif + +#ifdef CONFIG_I2C_ALGOPCF + extern int algo_pcf_init(void); +#endif +#ifdef CONFIG_I2C_PCFISA + extern int pcfisa_init(void); +#endif + +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + +int __init i2c_init_all(void) +{ + /* --------------------- global ----- */ + i2c_init(); + +#ifdef CONFIG_I2C_CHARDEV + i2c_dev_init(); +#endif + /* --------------------- bit -------- */ +#ifdef CONFIG_I2C_ALGOBIT + i2c_algo_bit_init(); +#endif +#ifdef CONFIG_I2C_PHILIPSPAR + i2c_bitlp_init(); +#endif +#ifdef CONFIG_I2C_ELV + i2c_bitelv_init(); +#endif +#ifdef CONFIG_I2C_VELLEMAN + i2c_bitvelle_init(); +#endif + + /* --------------------- pcf -------- */ +#ifdef CONFIG_I2C_ALGOPCF + i2c_algo_pcf_init(); +#endif +#ifdef CONFIG_I2C_ELEKTOR + i2c_pcfisa_init(); +#endif +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + + return 0; +} + +#endif + + + +EXPORT_SYMBOL(i2c_add_adapter); +EXPORT_SYMBOL(i2c_del_adapter); +EXPORT_SYMBOL(i2c_add_driver); +EXPORT_SYMBOL(i2c_del_driver); +EXPORT_SYMBOL(i2c_attach_client); +EXPORT_SYMBOL(i2c_detach_client); +EXPORT_SYMBOL(i2c_inc_use_client); +EXPORT_SYMBOL(i2c_dec_use_client); +EXPORT_SYMBOL(i2c_check_addr); + + +EXPORT_SYMBOL(i2c_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); +EXPORT_SYMBOL(i2c_adapter_id); +EXPORT_SYMBOL(i2c_probe); + +EXPORT_SYMBOL(i2c_smbus_xfer); +EXPORT_SYMBOL(i2c_smbus_write_quick); +EXPORT_SYMBOL(i2c_smbus_read_byte); +EXPORT_SYMBOL(i2c_smbus_write_byte); +EXPORT_SYMBOL(i2c_smbus_read_byte_data); +EXPORT_SYMBOL(i2c_smbus_write_byte_data); +EXPORT_SYMBOL(i2c_smbus_read_word_data); +EXPORT_SYMBOL(i2c_smbus_write_word_data); +EXPORT_SYMBOL(i2c_smbus_process_call); +EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +EXPORT_SYMBOL(i2c_get_functionality); +EXPORT_SYMBOL(i2c_check_functionality); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_PARM(i2c_debug, "i"); +MODULE_PARM_DESC(i2c_debug,"debug level"); + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ + i2cproc_cleanup(); +} +#endif --- linux-old/drivers/i2c/i2c-dev.c Sat Jan 15 20:08:59 CET 2000 +++ linux/drivers/i2c/i2c-dev.c Sat Jan 15 20:08:59 CET 2000 @@ -0,0 +1,557 @@ +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* $Id: i2c-dev.c,v 1.18 1999/12/21 23:45:58 frodo Exp $ */ + +#include +#include +#include +#include +#include + +/* If you want debugging uncomment: */ +/* #define DEBUG */ + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51) +#include +#else +#define __init +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define get_user_data(to,from) ((to) = get_user(from),0) +#else +#include +#define get_user_data(to,from) get_user(to,from) +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* def MODULE */ + +/* struct file_operations changed too often in the 2.1 series for nice code */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +static long long i2cdev_lseek (struct file *file, long long offset, int origin); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin); +#else +static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset); +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count); +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset); +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count); +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count); +#endif + +static int i2cdev_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int i2cdev_open (struct inode *inode, struct file *file); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file); +#else +static void i2cdev_release (struct inode *inode, struct file *file); +#endif + + +static int i2cdev_attach_adapter(struct i2c_adapter *adap); +static int i2cdev_detach_client(struct i2c_client *client); +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +#ifdef MODULE +static +#else +extern +#endif + int __init i2c_dev_init(void); +static int i2cdev_cleanup(void); + +static struct file_operations i2cdev_fops = { + i2cdev_lseek, + i2cdev_read, + i2cdev_write, + NULL, /* i2cdev_readdir */ + NULL, /* i2cdev_select */ + i2cdev_ioctl, + NULL, /* i2cdev_mmap */ + i2cdev_open, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) + NULL, /* i2cdev_flush */ +#endif + i2cdev_release, +}; + +#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX +static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; + +static struct i2c_driver i2cdev_driver = { + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, +}; + +static struct i2c_client i2cdev_client_template = { + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL +}; + +static int i2cdev_initialized; + +/* Note that the lseek function is called llseek in 2.1 kernels. But things + are complicated enough as is. */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) +long long i2cdev_lseek (struct file *file, long long offset, int origin) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +long long i2cdev_llseek (struct inode *inode, struct file *file, + long long offset, int origin) +#else +int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, + int origin) +#endif +{ +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); +#endif /* DEBUG */ + return -ESPIPE; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_read (struct inode *inode, struct file *file, char *buf, + unsigned long count) +#else +static int i2cdev_read(struct inode *inode, struct file *file, char *buf, + int count) +#endif +{ + char *tmp; + int ret; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + struct i2c_client *client = (struct i2c_client *)file->private_data; + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + + ret = i2c_master_recv(client,tmp,count); + copy_to_user(buf,tmp,count); + kfree(tmp); + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +static long i2cdev_write (struct inode *inode, struct file *file, + const char *buf, unsigned long offset) +#else +static int i2cdev_write(struct inode *inode, struct file *file, + const char *buf, int count) +#endif +{ + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; + +#ifdef DEBUG +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) + struct inode *inode = file->f_dentry->d_inode; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ +#endif /* DEBUG */ + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + copy_from_user(tmp,buf,count); + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count); +#endif + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int ver,datasize,res; + unsigned long funcs; + +#ifdef DEBUG + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); +#endif /* DEBUG */ + + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_WRITE,(unsigned long *) arg, + sizeof(unsigned long))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + + funcs = i2c_get_functionality(client->adapter); + copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long)); + return 0; + case I2C_SMBUS: + if (! arg) { +#ifdef DEBUG + printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data))) { +#ifdef DEBUG + printk("i2c-dev.o: invalid argument pointer (%ld) " + "in IOCTL I2C_SMBUS.\n", arg); +#endif + return -EINVAL; + } + copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data)); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { +#ifdef DEBUG + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); +#endif + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { +#ifdef DEBUG + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); +#endif + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + data_arg.read_write, data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { +#ifdef DEBUG + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + + /* This seems unlogical but it is not: if the user wants to read a + value, we must write that value to user memory! */ + ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && + (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE; + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if (verify_area(ver,data_arg.data,datasize)) { +#ifdef DEBUG + printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n", + data_arg.data); +#endif + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user(&temp,data_arg.data,datasize); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user(data_arg.data,&temp,datasize); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; +} + +int i2cdev_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; + + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { +#ifdef DEBUG + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor); +#endif + return -ENODEV; + } + + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; + + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; + +#ifdef DEBUG + printk("i2c-dev.o: opened i2c-%d\n",minor); +#endif + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) +static int i2cdev_release (struct inode *inode, struct file *file) +#else +static void i2cdev_release (struct inode *inode, struct file *file) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; +#ifdef DEBUG + printk("i2c-dev.o: Closed: i2c-%d\n", minor); +#endif + MOD_DEC_USE_COUNT; + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) + return 0; +#endif +} + +int i2cdev_attach_adapter(struct i2c_adapter *adap) +{ + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; +#ifdef DEBUG + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); +#endif + } + + return 0; +} + +int i2cdev_detach_client(struct i2c_client *client) +{ + return 0; +} + +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +int __init i2c_dev_init(void) +{ + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; +} + +int i2cdev_cleanup(void) +{ + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); +MODULE_DESCRIPTION("I2C /dev entries driver"); + +int init_module(void) +{ + return i2c_dev_init(); +} + +int cleanup_module(void) +{ + return i2cdev_cleanup(); +} + +#endif /* def MODULE */ + --- linux-old/include/linux/i2c-dev.h Sat Jan 15 20:09:00 CET 2000 +++ linux/include/linux/i2c-dev.h Sat Jan 15 20:09:00 CET 2000 @@ -0,0 +1,156 @@ +/* + i2c-dev.h - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: i2c-dev.h,v 1.3 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_DEV_H +#define I2C_DEV_H + + +#include +#include + +/* Some IOCTL commands are defined in */ +/* Note: 10-bit addresses are NOT supported! */ + +/* This is the structure as used in the I2C_SMBUS ioctl call */ +struct i2c_smbus_ioctl_data { + char read_write; + __u8 command; + int size; + union i2c_smbus_data *data; +}; + +#ifndef __KERNEL__ + +#include + +extern inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + return ioctl(file,I2C_SMBUS,&args); +} + + +extern inline __s32 i2c_smbus_write_quick(int file, __u8 value) +{ + return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); +} + +extern inline __s32 i2c_smbus_read_byte(int file) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte(int file, __u8 value) +{ + return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, + __u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_BYTE_DATA, + &data); +} + +extern inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_WORD_DATA,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern inline __s32 i2c_smbus_write_word_data(int file, __u8 command, + __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_WORD_DATA, + &data); +} + +extern inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_PROC_CALL,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + + +/* Returns the number of read bytes */ +extern inline __s32 i2c_smbus_read_block_data(int file, __u8 command, + __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command,I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern inline __s32 i2c_smbus_write_block_data(int file, __u8 command, + __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,I2C_SMBUS_BLOCK_DATA, + &data); +} + +#endif /* ndef __KERNEL__ */ + +#endif --- linux-old/drivers/i2c/i2c-elektor.c Sat Jan 15 20:09:00 CET 2000 +++ linux/drivers/i2c/i2c-elektor.c Sat Jan 15 20:09:00 CET 2000 @@ -0,0 +1,327 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elektor.c,v 1.13 1999/12/21 23:45:58 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include +#include +#include "i2c-pcf8584.h" + +#define DEFAULT_BASE 0x300 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1c +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; +static int i2c_debug=0; +static struct i2c_pcf_isa gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *pcf_wait = NULL; +#else +static wait_queue_head_t pcf_wait; +#endif +static int pcf_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* --- Convenience defines for the i2c port: */ +#define BASE ((struct i2c_pcf_isa *)(data))->pi_base +#define DATA BASE /* Adapter data port */ +#define CTRL (BASE+1) /* Adapter control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val|I2C_PCF_ENI)); + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); + outb(val, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); + outb(val, DATA); + } +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); + } + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (gpi.pi_own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (gpi.pi_clock); +} + + + +#if LINUX_VERSION_CODE < 0x02017f +static void schedule_timeout(int j) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + j; + schedule(); +} +#endif + +#if 0 +static void pcf_isa_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +static void pcf_isa_waitforpin(void) { + + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = jiffies + timeout * HZ; + interruptible_sleep_on(&pcf_wait); +#else + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); +#endif + } + else + pcf_pending = 0; + sti(); +#if LINUX_VERSION_CODE < 0x02017f + current->timeout = 0; +#endif + } + else { + udelay(100); + } +} + + +static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); +} + + +static int pcf_isa_init(void) +{ + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } + else + enable_irq(gpi.pi_irq); + } + return 0; +} + + +static void pcf_isa_exit(void) +{ + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); +} + + +static int pcf_isa_reg(struct i2c_client *client) +{ + return 0; +} + + +static int pcf_isa_unreg(struct i2c_client *client) +{ + return 0; +} + +static void pcf_isa_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void pcf_isa_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + NULL, + pcf_isa_setbyte, + pcf_isa_getbyte, + pcf_isa_getown, + pcf_isa_getclock, + pcf_isa_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter pcf_isa_ops = { + "PCF8584 ISA adapter", + I2C_HW_P_ELEK, + NULL, + &pcf_isa_data, + pcf_isa_inc_use, + pcf_isa_dec_use, + pcf_isa_reg, + pcf_isa_unreg, +}; + +int __init i2c_pcfisa_init(void) +{ + + struct i2c_pcf_isa *pisa = &gpi; + + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; + + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; + + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; + + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; + + pcf_isa_data.data = (void *)pisa; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&pcf_wait); +#endif + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); + +int init_module(void) +{ + return i2c_pcfisa_init(); +} + +void cleanup_module(void) +{ + i2c_pcf_del_bus(&pcf_isa_ops); + pcf_isa_exit(); +} + +#endif + + --- linux-old/include/linux/i2c-elektor.h Sat Jan 15 20:09:01 CET 2000 +++ linux/include/linux/i2c-elektor.h Sat Jan 15 20:09:01 CET 2000 @@ -0,0 +1,43 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elektor.h,v 1.3 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_PCF_ELEKTOR_H +#define I2C_PCF_ELEKTOR_H 1 + +/* + * This struct contains the hw-dependent functions of PCF8584 adapters to + * manipulate the registers, and to init any hw-specific features. + */ + +struct i2c_pcf_isa { + int pi_base; + int pi_irq; + int pi_clock; + int pi_own; +}; + + +#endif /* PCF_ELEKTOR_H */ --- linux-old/drivers/i2c/i2c-elv.c Sat Jan 15 20:09:01 CET 2000 +++ linux/drivers/i2c/i2c-elv.c Sat Jan 15 20:09:01 CET 2000 @@ -0,0 +1,236 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elv.c,v 1.13 2000/01/09 16:00:48 frodo Exp $ */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +#include +#include +#include +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; +static unsigned char PortData = 0; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ +#define DEBINIT(x) x /* detection status messages */ + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + + +/* ----- local functions ---------------------------------------------- */ + + +static void bit_elv_setscl(void *data, int state) +{ + if (state) { + PortData &= 0xfe; + } else { + PortData |=1; + } + outb(PortData, DATA); +} + +static void bit_elv_setsda(void *data, int state) +{ + if (state) { + PortData &=0xfd; + } else { + PortData |=2; + } + outb(PortData, DATA); +} + +static int bit_elv_getscl(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +static int bit_elv_getsda(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +static int bit_elv_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(base+1) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c-elv.o: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x0c,base+2); /* SLCT auf low */ + udelay(400); + if ( !(inb(base+1) && 0x10) ) { + outb(0x04,base+2); + DEBINIT(printk("i2c-elv.o: Select was high.\n")); + return -ENODEV; + } + } + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + PortData = 0; + bit_elv_setsda((void*)base,1); + bit_elv_setscl((void*)base,1); + } + return 0; +} + +static void bit_elv_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_elv_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_elv_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_elv_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_elv_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_bit_data bit_elv_data = { + NULL, + bit_elv_setsda, + bit_elv_setscl, + bit_elv_getsda, + bit_elv_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_elv_ops = { + "ELV Parallel port adaptor", + I2C_HW_B_ELV, + NULL, + &bit_elv_data, + bit_elv_inc_use, + bit_elv_dec_use, + bit_elv_reg, + bit_elv_unreg, +}; + +int __init i2c_bitelv_init(void) +{ + printk("i2c-elv.o: i2c ELV parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_elv_data.data=(void*)DEFAULT_BASE; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_elv_ops.data=(void*)base; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-elv.o: found device at %#x.\n",base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter") +; + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitelv_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_elv_ops); + bit_elv_exit(); +} + +#endif --- linux-old/include/linux/i2c-id.h Sat Jan 15 20:09:01 CET 2000 +++ linux/include/linux/i2c-id.h Sat Jan 15 20:09:01 CET 2000 @@ -0,0 +1,137 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1999 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* $Id: i2c-id.h,v 1.7 1999/12/27 10:25:38 simon Exp $ */ + +#ifndef I2C_ID_H +#define I2C_ID_H +/* + * This file is part of the i2c-bus package and contains the identifier + * values for drivers, adapters and other folk populating these serial + * worlds. + * + * These will change often (i.e. additions) , therefore this has been + * separated from the functional interface definitions of the i2c api. + * + */ + +/* + * ---- Driver types ----------------------------------------------------- + * device id name + number function description, i2c address(es) + * + * Range 1000-1999 range is defined in sensors/sensors.h + * Range 0x100 - 0x1ff is for V4L2 Common Components + * Range 0xf000 - 0xffff is reserved for local experimentation, and should + * never be used in official drivers + */ + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 /* please rename */ +#define I2C_DRIVERID_TDA8425 4 /* stereo sound processor */ +#define I2C_DRIVERID_TEA6420 5 /* audio matrix switch */ +#define I2C_DRIVERID_TEA6415C 6 /* video matrix switch */ +#define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */ +#define I2C_DRIVERID_SAA7111A 8 /* video input processor */ +#define I2C_DRIVERID_SAA5281 9 /* videotext decoder */ +#define I2C_DRIVERID_SAA7112 10 /* video decoder, image scaler */ +#define I2C_DRIVERID_SAA7120 11 /* video encoder */ +#define I2C_DRIVERID_SAA7121 12 /* video encoder */ +#define I2C_DRIVERID_SAA7185B 13 /* video encoder */ +#define I2C_DRIVERID_CH7003 14 /* digital pc to tv encoder */ +#define I2C_DRIVERID_PCF8574A 15 /* i2c expander - 8 bit in/out */ +#define I2C_DRIVERID_PCF8582C 16 /* eeprom */ +#define I2C_DRIVERID_AT24Cxx 17 /* eeprom 1/2/4/8/16 K */ +#define I2C_DRIVERID_TEA6300 18 /* audio mixer */ +#define I2C_DRIVERID_BT829 19 /* pc to tv encoder */ +#define I2C_DRIVERID_TDA9850 20 /* audio mixer */ +#define I2C_DRIVERID_TDA9855 21 /* audio mixer */ +#define I2C_DRIVERID_SAA7110 22 /* */ +#define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ + +#define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */ +#define I2C_DRIVERID_EXP1 0xF1 +#define I2C_DRIVERID_EXP2 0xF2 +#define I2C_DRIVERID_EXP3 0xF3 + +#define I2C_DRIVERID_I2CDEV 900 +#define I2C_DRIVERID_I2CPROC 901 + +/* + * ---- Adapter types ---------------------------------------------------- + * + * First, we distinguish between several algorithms to access the hardware + * interface types, as a PCF 8584 needs other care than a bit adapter. + */ + +#define I2C_ALGO_NONE 0x000000 +#define I2C_ALGO_BIT 0x010000 /* bit style adapters */ +#define I2C_ALGO_PCF 0x020000 /* PCF 8584 style adapters */ +#define I2C_ALGO_ATI 0x030000 /* ATI video card */ +#define I2C_ALGO_SMBUS 0x040000 +#define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */ +#define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */ +#define I2C_ALGO_SAA7146A 0x060001 /* SAA 7146A - enhanced version */ + + +#define I2C_ALGO_EXP 0x800000 /* experimental */ + +#define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ +#define I2C_ALGO_SHIFT 0x10 /* right shift to get index values */ + +#define I2C_HW_ADAPS 0x10000 /* # adapter types */ +#define I2C_HW_MASK 0xffff + + +/* hw specific modules that are defined per algorithm layer + */ + +/* --- Bit algorithm adapters */ +#define I2C_HW_B_LP 0x00 /* Parallel port Philips style adapter */ +#define I2C_HW_B_LPC 0x01 /* Parallel port, over control reg. */ +#define I2C_HW_B_SER 0x02 /* Serial line interface */ +#define I2C_HW_B_ELV 0x03 /* ELV Card */ +#define I2C_HW_B_VELLE 0x04 /* Vellemann K8000 */ +#define I2C_HW_B_BT848 0x05 /* BT848 video boards */ +#define I2C_HW_B_WNV 0x06 /* Winnov Videums */ +#define I2C_HW_B_VIA 0x07 /* Via vt82c586b */ +#define I2C_HW_B_HYDRA 0x08 /* Apple Hydra Mac I/O */ +#define I2C_HW_B_G400 0x09 /* Matrox G400 */ + +/* --- PCF 8584 based algorithms */ +#define I2C_HW_P_LP 0x00 /* Parallel port interface */ +#define I2C_HW_P_ISA 0x01 /* generic ISA Bus inteface card */ +#define I2C_HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + +/* --- SMBus only adapters */ +#define I2C_HW_SMBUS_PIIX4 0x00 +#define I2C_HW_SMBUS_ALI15X3 0x01 +#define I2C_HW_SMBUS_VIA2 0x02 +#define I2C_HW_SMBUS_VOODOO3 0x03 +#define I2C_HW_SMBUS_I801 0x04 +#define I2C_HW_SMBUS_AMD756 0x05 +#define I2C_HW_SMBUS_SIS5595 0x06 + +/* --- ISA pseudo-adapter */ +#define I2C_HW_ISA 0x00 + +#endif /* I2C_ID_H */ --- linux-old/drivers/i2c/i2c-pcf8584.h Sat Jan 15 20:09:01 CET 2000 +++ linux/drivers/i2c/i2c-pcf8584.h Sat Jan 15 20:09:01 CET 2000 @@ -0,0 +1,78 @@ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard */ + +/* $Id: i2c-pcf8584.h,v 1.2 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK8 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif I2C_PCF8584_H --- linux-old/drivers/i2c/i2c-philips-par.c Sat Jan 15 20:09:02 CET 2000 +++ linux/drivers/i2c/i2c-philips-par.c Sat Jan 15 20:09:02 CET 2000 @@ -0,0 +1,232 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-philips-par.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-philips-par.c,v 1.13 2000/01/09 16:00:48 frodo Exp $ */ + +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* Note: all we need to know is the base address of the parallel port, so + * instead of having a dedicated struct to store this value, we store this + * int in the pointer field (=bit_lp_ops.data) itself. + */ + +/* Note2: as the hw that implements the i2c bus on the parallel port is + * incompatible with other epp stuff etc., we access the port exclusively + * and don't cooperate with parport functions. + */ + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void bit_lp_setscl(void *data, int state) +{ + /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + outb(inb(CTRL)|I2C_SCL, CTRL); + } else { + outb(inb(CTRL)&I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + /* reset hardware to sane state */ + bit_lp_setsda((void*)base,1); + bit_lp_setscl((void*)base,1); + } + return 0; +} + +static void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_lp_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_lp_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_lp_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_lp_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_lp_data = { + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_lp_ops = { + "Philips Parallel port adapter", + I2C_HW_B_LP, + NULL, + &bit_lp_data, + bit_lp_inc_use, + bit_lp_dec_use, + bit_lp_reg, + bit_lp_unreg, +}; + + +int __init i2c_bitlp_init(void) +{ + printk("i2c-philips-par.o: i2c Philips parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_data.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_lp_data.data=(void*)base; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-philips-par.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitlp_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + --- linux-old/drivers/i2c/i2c-velleman.c Sat Jan 15 20:09:02 CET 2000 +++ linux/drivers/i2c/i2c-velleman.c Sat Jan 15 20:09:02 CET 2000 @@ -0,0 +1,219 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-velleman.c i2c-hw access for Velleman K9000 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* $Id: i2c-velleman.c,v 1.15 2000/01/09 16:00:48 frodo Exp $ */ + +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020135 +#include +#else +#define __init +#endif +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +/* 2.0.0 kernel compatibility */ +#if LINUX_VERSION_CODE < 0x020100 +#define MODULE_AUTHOR(noone) +#define MODULE_DESCRIPTION(none) +#define MODULE_PARM(no,param) +#define MODULE_PARM_DESC(no,description) +#define EXPORT_SYMBOL(noexport) +#define EXPORT_NO_SYMBOLS +#endif + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + + /* Pin Port Inverted name */ +#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */ +#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */ + +#define I2C_SDAIN 0x10 /* stat bit 4 */ +#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv) (reads own output) */ + +#define I2C_DMASK 0xfd +#define I2C_CMASK 0xf7 + + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* ----- local functions --------------------------------------------------- */ + +static void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +static void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +static int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +static int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c-velleman.o: Port %#x already in use.\n", base)); + return -ENODEV; + } else { + request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); + } + return 0; +} + +static void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +static int bit_velle_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_velle_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_velle_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_velle_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_velle_data = { + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + 10, 10, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_velle_ops = { + "Velleman K8000", + I2C_HW_B_VELLE, + NULL, + &bit_velle_data, + bit_velle_inc_use, + bit_velle_dec_use, + bit_velle_reg, + bit_velle_unreg, +}; + +int __init i2c_bitvelle_init(void) +{ + printk("i2c-velleman.o: i2c Velleman K8000 adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_data.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_velle_data.data=(void*)base; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-velleman.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitvelle_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif --- linux-old/include/linux/i2c.h Sat Jan 15 20:09:03 CET 2000 +++ linux/include/linux/i2c.h Sat Jan 15 20:09:03 CET 2000 @@ -1,190 +1,525 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1999 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + +/* $Id: i2c.h,v 1.34 2000/01/10 15:32:39 frodo Exp $ */ + #ifndef I2C_H #define I2C_H -/* - * linux i2c interface. Works a little bit like the scsi subsystem. - * There are: - * - * i2c the basic control module (like scsi_mod) - * bus driver a driver with a i2c bus (hostadapter driver) - * chip driver a driver for a chip connected - * to a i2c bus (cdrom/hd driver) - * - * A device will be attached to one bus and one chip driver. Every chip - * driver gets a unique ID. - * - * A chip driver can provide a ioctl-like callback for the - * communication with other parts of the kernel (not every i2c chip is - * useful without other devices, a TV card tuner for example). - * - * "i2c internal" parts of the structs: only the i2c module is allowed to - * write to them, for others they are read-only. - * - */ - -#define I2C_BUS_MAX 4 /* max # of bus drivers */ -#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ -#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ - -struct i2c_bus; -struct i2c_driver; -struct i2c_device; +#include /* id values of adapters et. al. */ -#define I2C_DRIVERID_MSP3400 1 -#define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_VIDEOTEXT 3 -#define I2C_DRIVERID_VIDEODECODER 4 -#define I2C_DRIVERID_VIDEOENCODER 5 - -#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ - /* 2 is used in 2.3.x */ -#define I2C_BUSID_BUZ 3 /* I2C bus on a BUZ */ -#define I2C_BUSID_ZORAN 4 /* I2C bus on a Zoran */ -#define I2C_BUSID_SGIVWFB 5 /* Moved to be unique */ - -/* - * struct for a driver for a i2c chip (tuner, soundprocessor, - * videotext, ... ). - * - * a driver will register within the i2c module. The i2c module will - * callback the driver (i2c_attach) for every device it finds on a i2c - * bus at the specified address. If the driver decides to "accept" - * the, device, it must return a struct i2c_device, and NULL - * otherwise. - * - * i2c_detach = i2c_attach ** -1 - * - * i2c_command will be used to pass commands to the driver in a - * ioctl-line manner. - * - */ - -struct i2c_driver -{ - char name[32]; /* some useful label */ - int id; /* device type ID */ - unsigned char addr_l, addr_h; /* address range of the chip */ - - int (*attach)(struct i2c_device *device); - int (*detach)(struct i2c_device *device); - int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); - - /* i2c internal */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; -}; - - -/* - * this holds the informations about a i2c bus available in the system. - * - * a chip with a i2c bus interface (like bt848) registers the bus within - * the i2c module. This struct provides functions to access the i2c bus. - * - * One must hold the spinlock to access the i2c bus (XXX: is the irqsave - * required? Maybe better use a semaphore?). - * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing - * to bang their i2c bus from an interrupt. - * - * attach/detach_inform is a callback to inform the bus driver about - * attached chip drivers. - * - */ +#ifdef __KERNEL__ -/* needed: unsigned long flags */ +/* --- Includes and compatibility declarations ------------------------ */ #include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif -#if LINUX_VERSION_CODE >= 0x020100 -# if 0 -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); -# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); -# else -# define LOCK_FLAGS -# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); -# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); -# endif +#include /* for 2.2.xx */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,25) +#include #else -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } -# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#include #endif +#include +#include + +/* --- General options ------------------------------------------------ */ + +#define I2C_ALGO_MAX 4 /* control memory consumption */ +#define I2C_ADAP_MAX 16 +#define I2C_DRIVER_MAX 16 +#define I2C_CLIENT_MAX 32 +#define I2C_DUMMY_MAX 4 + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; +struct i2c_client_address_data; +union i2c_smbus_data; + + +/* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to + * transmit one message at a time, a more complex version can be used to + * transmit an arbitrary number of messages without interruption. + */ +extern int i2c_master_send(struct i2c_client *,const char* ,int); +extern int i2c_master_recv(struct i2c_client *,char* ,int); -struct i2c_bus -{ - char name[32]; /* some useful label */ - int id; - void *data; /* free for use by the bus driver */ +/* Transfer num messages. + */ +extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],int num); + +/* + * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. + * This is not tested/implemented yet and will change in the future. + */ +extern int i2c_slave_send(struct i2c_client *,char*,int); +extern int i2c_slave_recv(struct i2c_client *,char*,int); -#if LINUX_VERSION_CODE >= 0x020100 - spinlock_t bus_lock; + +/* + * I2C Message - could be used in the current interface to + */ +struct i2c_msg { + u16 addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_RD 0x01 +#define I2C_M_NOSTART 0x4000 +#define I2C_M_REV_DIR_ADDR 0x2000 +#if 0 +#define I2C_M_PROBE 0x20 #endif + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; - /* attach/detach inform callbacks */ - void (*attach_inform)(struct i2c_bus *bus, int id); - void (*detach_inform)(struct i2c_bus *bus, int id); - /* Software I2C */ - void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); - int (*i2c_getdataline)(struct i2c_bus *bus); +/* This is the very generalized SMBus access routine. You probably do not + want to use this, though; one of the functions below may be much easier, + and probably just as fast. + Note that we use i2c_adapter here, because you do not need a specific + smbus adapter to call this function. */ +extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + +/* Now follow the 'nice' access routines. These also document the calling + conventions of smbus_access. */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte(struct i2c_client * client); +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); - /* Hardware I2C */ - int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); - int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); - /* internal data for i2c module */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; +/* + * A driver is capable of handling one or more physical devices present on + * I2C adapters. This information is used to inform the driver of adapter + * events. + */ + +struct i2c_driver { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + + /* Notifies the driver that a new bus has appeared. This routine + * can be used by the driver to test if the bus meets its conditions + * & seek for the presence of the chip(s) it supports. If found, it + * registers the client(s) that are on the bus to the i2c admin. via + * i2c_attach_client. + */ + int (*attach_adapter)(struct i2c_adapter *); + + /* tells the driver that a client is about to be deleted & gives it + * the chance to remove its private data. Also, if the client struct + * has been dynamically allocated by the driver in the function above, + * it must be freed here. + */ + int (*detach_client)(struct i2c_client *); + + /* a ioctl like command that can be used to perform specific functions + * with the device. + */ + int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); + + /* These two are mainly used for bookkeeping & dynamic unloading of + * kernel modules. inc_use tells the driver that a client is being + * used by another module & that it should increase its ref. counter. + * dec_use is the inverse operation. + * NB: Make sure you have no circular dependencies, or else you get a + * deadlock when trying to unload the modules. + * You should use the i2c_{inc,dec}_use_client functions instead of + * calling this function directly. + */ + void (*inc_use)(struct i2c_client *client); + void (*dec_use)(struct i2c_client *client); +}; + +/* + * i2c_client identifies a single device (i.e. chip) that is connected to an + * i2c bus. The behaviour is defined by the routines of the driver. This + * function is mainly used for lookup & other admin. functions. + */ +struct i2c_client { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + unsigned int addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits of this char */ + /* addr: unsigned int to make lm_sensors i2c-isa adapter work + more cleanly. It does not take any more memory space, due to + alignment considerations */ + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct i2c_driver *driver; /* and our access routines */ + void *data; /* for the clients */ }; /* - * This holds per-device data for a i2c device + * The following structs are for those who like to implement new bus drivers: + * i2c_algorithm is the interface to a class of hardware solutions which can + * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 + * to name two of the most common. */ +struct i2c_algorithm { + char name[32]; /* textual description */ + unsigned int id; + + /* If a adapter algorithm can't to I2C-level access, set master_xfer + to NULL. If an adapter algorithm can do SMBus access, set + smbus_xfer. If set to NULL, the SMBus protocol is simulated + using common I2C messages */ + int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], + int num); + int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data); + + /* --- these optional/future use for some adapter types.*/ + int (*slave_send)(struct i2c_adapter *,char*,int); + int (*slave_recv)(struct i2c_adapter *,char*,int); -struct i2c_device -{ - char name[32]; /* some useful label */ - void *data; /* free for use by the chip driver */ - unsigned char addr; /* chip addr */ + /* --- ioctl like call to set div. parameters. */ + int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); - /* i2c internal */ - struct i2c_bus *bus; - struct i2c_driver *driver; + /* To determine what the adapter supports */ + u32 (*functionality) (struct i2c_adapter *); }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) +struct proc_dir_entry; +#endif + +/* + * i2c_adapter is the structure used to identify a physical i2c bus along + * with the access algorithms necessary to access it. + */ +struct i2c_adapter { + char name[32]; /* some useful name to identify the adapter */ + unsigned int id;/* == is algo->id | hwdep.struct->id, */ + /* for registered values see below */ + struct i2c_algorithm *algo;/* the algorithm to access the bus */ + void *algo_data; + + /* --- These may be NULL, but should increase the module use count */ + void (*inc_use)(struct i2c_adapter *); + void (*dec_use)(struct i2c_adapter *); + + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); + + void *data; /* private data for the adapter */ + /* some data fields that are used by all types */ + /* these data fields are readonly to the public */ + /* and can be set via the i2c_ioctl call */ + + /* data fields that are valid for all devices */ + struct semaphore lock; + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + + int timeout; + int retries; + +#ifdef CONFIG_PROC_FS + /* No need to set this when you initialize the adapter */ + int inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) + struct proc_dir_entry *proc_entry; +#endif +#endif /* def CONFIG_PROC_FS */ +}; + +/*flags for the driver struct: */ +#define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ +#define I2C_DF_DUMMY 0x02 /* do not connect any clients */ + +/* i2c_client_address_data is the struct for holding default client + * addresses for a driver and for the parameters supplied on the + * command line + */ +struct i2c_client_address_data { + unsigned short *normal_i2c; + unsigned short *normal_i2c_range; + unsigned short *probe; + unsigned short *probe_range; + unsigned short *ignore; + unsigned short *ignore_range; + unsigned short *force; +}; + +/* Internal numbers to terminate lists */ +#define I2C_CLIENT_END 0xfffe + +/* The numbers to use to set I2C bus address */ +#define ANY_I2C_BUS 0xffff + +/* The length of the option lists */ +#define I2C_CLIENT_MAX_OPTS 48 + + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +extern int i2c_add_adapter(struct i2c_adapter *); +extern int i2c_del_adapter(struct i2c_adapter *); + +extern int i2c_add_driver(struct i2c_driver *); +extern int i2c_del_driver(struct i2c_driver *); + +extern int i2c_attach_client(struct i2c_client *); +extern int i2c_detach_client(struct i2c_client *); + +/* Only call these if you grab a resource that makes unloading the + client and the adapter it is on completely impossible. Like when a + /proc directory is entered. */ +extern void i2c_inc_use_client(struct i2c_client *); +extern void i2c_dec_use_client(struct i2c_client *); + +/* returns -EBUSY if address has been taken, 0 if not. Note that the only + other place at which this is called is within i2c_attach_client; so + you can cheat by simply not registering. Not recommended, of course! */ +extern int i2c_check_addr (struct i2c_adapter *adapter, int addr); + +/* Detect function. It itterates over all possible addresses itself. + * It will only call found_proc if some client is connected at the + * specific address (unless a 'force' matched); + */ +typedef int i2c_client_found_addr_proc (struct i2c_adapter *adapter, + int addr, unsigned short flags,int kind); + +extern int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc); -/* ------------------------------------------------------------------- */ -/* i2c module functions */ +/* An ioctl like call to set div. parameters of the adapter. + */ +extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); -/* register/unregister a i2c bus */ -int i2c_register_bus(struct i2c_bus *bus); -int i2c_unregister_bus(struct i2c_bus *bus); +/* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ +extern int i2c_adapter_id(struct i2c_adapter *adap); -/* register/unregister a chip driver */ -int i2c_register_driver(struct i2c_driver *driver); -int i2c_unregister_driver(struct i2c_driver *driver); -/* send a command to a chip using the ioctl-like callback interface */ -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg); -/* i2c bus access functions */ -void i2c_start(struct i2c_bus *bus); -void i2c_stop(struct i2c_bus *bus); -void i2c_one(struct i2c_bus *bus); -void i2c_zero(struct i2c_bus *bus); -int i2c_ack(struct i2c_bus *bus); +/* Return the functionality mask */ +extern u32 i2c_get_functionality (struct i2c_adapter *adap); + +/* Return 1 if adapter supports everything we need, 0 if not. */ +extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func); + +#endif /* __KERNEL__ */ + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART} */ +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* New I2C-like block */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* transfers */ + +#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE +#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA +#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA +#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA +#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK + +#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ + I2C_FUNC_SMBUS_READ_BLOCK_DATA + +/* + * Data for SMBus Messages + */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[33]; /* block[0] is used for length */ +}; + +/* smbus_access read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 + + +/* ----- commands for the ioctl like i2c_command call: + * note that additional calls are defined in the algorithm and hw + * dependent layers - these can be listed here, or see the + * corresponding header files. + */ + /* -> bit-adapter specific ioctls */ +#define I2C_RETRIES 0x0701 /* number times a device adress should */ + /* be polled when not acknowledging */ +#define I2C_TIMEOUT 0x0702 /* set timeout - call with int */ + + +/* this is for i2c-dev.c */ +#define I2C_SLAVE 0x0703 /* Change slave address */ + /* Attn.: Slave address is 7 or 10 bits */ +#define I2C_SLAVE_FORCE 0x0706 /* Change slave address */ + /* Attn.: Slave address is 7 or 10 bits */ + /* This changes the address, even if it */ + /* is already taken! */ +#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */ + +#define I2C_FUNCS 0x0705 /* Get the adapter functionality */ +#if 0 +#define I2C_ACK_TEST 0x0710 /* See if a slave is at a specific adress */ +#endif -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); -unsigned char i2c_readbyte(struct i2c_bus *bus,int last); +#define I2C_SMBUS 0x0720 /* SMBus-level access */ -/* i2c (maybe) hardware functions */ -int i2c_read(struct i2c_bus *bus, unsigned char addr); -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); +/* ... algo-bit.c recognizes */ +#define I2C_UDELAY 0x0705 /* set delay in microsecs between each */ + /* written byte (except address) */ +#define I2C_MDELAY 0x0706 /* millisec delay between written bytes */ + + +/* ----- I2C-DEV: char device interface stuff ------------------------- */ + +#define I2C_MAJOR 89 /* Device major number */ + +#ifdef __KERNEL__ + +# ifndef NULL +# define NULL ( (void *) 0 ) +# endif + +# ifndef ENODEV +# include +# endif + +/* These defines are used for probing i2c client addresses */ +/* Default fill of many variables */ +#define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END} + +/* This is ugly. We need to evaluate I2C_CLIENT_MAX_OPTS before it is + stringified */ +#define I2C_CLIENT_MODPARM_AUX1(x) "1-" #x "h" +#define I2C_CLIENT_MODPARM_AUX(x) I2C_CLIENT_MODPARM_AUX1(x) +#define I2C_CLIENT_MODPARM I2C_CLIENT_MODPARM_AUX(I2C_CLIENT_MAX_OPTS) + +/* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the + module header */ + +#define I2C_CLIENT_MODULE_PARM(var,desc) \ + static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \ + MODULE_PARM(var,I2C_CLIENT_MODPARM); \ + MODULE_PARM_DESC(var,desc) + +/* This is the one you want to use in your own modules */ +#define I2C_CLIENT_INSMOD \ + I2C_CLIENT_MODULE_PARM(probe, \ + "List of adapter,address pairs to scan additionally"); \ + I2C_CLIENT_MODULE_PARM(probe_range, \ + "List of adapter,start-addr,end-addr triples to scan " \ + "additionally"); \ + I2C_CLIENT_MODULE_PARM(ignore, \ + "List of adapter,address pairs not to scan"); \ + I2C_CLIENT_MODULE_PARM(ignore_range, \ + "List of adapter,start-addr,end-addr triples not to " \ + "scan"); \ + I2C_CLIENT_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + static struct i2c_client_address_data addr_data = \ + {normal_i2c, normal_i2c_range, \ + probe, probe_range, \ + ignore, ignore_range, \ + force} -int i2c_init(void); +#endif /* def __KERNEL__ */ #endif /* I2C_H */ --- linux-old/drivers/i2c/Config.in Sat Jan 15 20:09:03 CET 2000 +++ linux/drivers/i2c/Config.in Sat Jan 15 20:09:03 CET 2000 @@ -0,0 +1,29 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'I2C support' + +tristate 'I2C support' CONFIG_I2C + +if [ "$CONFIG_I2C" != "n" ]; then + + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi + + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + +fi +endmenu --- linux-old/drivers/i2c/Makefile Sat Jan 15 20:09:03 CET 2000 +++ linux/drivers/i2c/Makefile Sat Jan 15 20:09:03 CET 2000 @@ -0,0 +1,98 @@ +# +# Makefile for the kernel i2c bus driver. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) +MOD_LIST_NAME := I2C_MODULES + +L_TARGET := i2c.a +MX_OBJS := +M_OBJS := +LX_OBJS := +L_OBJS := + +# ----- +# i2c core components +# ----- + +ifeq ($(CONFIG_I2C),y) + LX_OBJS += i2c-core.o +else + ifeq ($(CONFIG_I2C),m) + MX_OBJS += i2c-core.o + endif +endif + +ifeq ($(CONFIG_I2C_CHARDEV),y) + L_OBJS += i2c-dev.o +else + ifeq ($(CONFIG_I2C_CHARDEV),m) + M_OBJS += i2c-dev.o + endif +endif + +# ----- +# Bit banging adapters... +# ----- + +ifeq ($(CONFIG_I2C_ALGOBIT),y) + LX_OBJS += i2c-algo-bit.o +else + ifeq ($(CONFIG_I2C_ALGOBIT),m) + MX_OBJS += i2c-algo-bit.o + endif +endif + +ifeq ($(CONFIG_I2C_PHILIPSPAR),y) + L_OBJS += i2c-philips-par.o +else + ifeq ($(CONFIG_I2C_PHILIPSPAR),m) + M_OBJS += i2c-philips-par.o + endif +endif + +ifeq ($(CONFIG_I2C_ELV),y) + L_OBJS += i2c-elv.o +else + ifeq ($(CONFIG_I2C_ELV),m) + M_OBJS += i2c-elv.o + endif +endif + +ifeq ($(CONFIG_I2C_VELLEMAN),y) + L_OBJS += i2c-velleman.o +else + ifeq ($(CONFIG_I2C_VELLEMAN),m) + M_OBJS += i2c-velleman.o + endif +endif + + + +# ----- +# PCF components +# ----- + +ifeq ($(CONFIG_I2C_ALGOPCF),y) + LX_OBJS += i2c-algo-pcf.o +else + ifeq ($(CONFIG_I2C_ALGOPCF),m) + MX_OBJS += i2c-algo-pcf.o + endif +endif + +ifeq ($(CONFIG_I2C_ELEKTOR),y) + L_OBJS += i2c-elektor.o +else + ifeq ($(CONFIG_I2C_ELEKTOR),m) + M_OBJS += i2c-elektor.o + endif +endif + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + +include $(TOPDIR)/Rules.make + --- linux-old/Makefile Sat Jan 15 20:09:03 CET 2000 +++ linux/Makefile Sat Jan 15 20:09:03 CET 2000 @@ -203,4 +203,8 @@ endif +ifeq ($(CONFIG_I2C),y) +DRIVERS := $(DRIVERS) drivers/i2c/i2c.a +endif + ifeq ($(CONFIG_PHONE),y) DRIVERS := $(DRIVERS) drivers/telephony/telephony.a --- linux-old/drivers/Makefile Sat Jan 15 20:09:03 CET 2000 +++ linux/drivers/Makefile Sat Jan 15 20:09:03 CET 2000 @@ -11,5 +11,5 @@ MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp \ - macintosh video dio zorro fc4 usb telephony + macintosh video dio zorro fc4 usb telephony i2c ifdef CONFIG_DIO @@ -128,4 +128,13 @@ SUB_DIRS += net/hamradio MOD_SUB_DIRS += net/hamradio +endif + +ifeq ($(CONFIG_I2C),y) +SUB_DIRS += i2c +MOD_SUB_DIRS += i2c +else + ifeq ($(CONFIG_I2C),m) + MOD_SUB_DIRS += i2c + endif endif --- linux-old/drivers/char/Config.in Sat Jan 15 20:09:04 CET 2000 +++ linux/drivers/char/Config.in Sat Jan 15 20:09:04 CET 2000 @@ -113,4 +113,6 @@ tristate '/dev/nvram support' CONFIG_NVRAM bool 'Enhanced Real Time Clock Support' CONFIG_RTC +source drivers/i2c/Config.in + if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 --- linux-old/drivers/char/mem.c Sat Jan 15 20:09:04 CET 2000 +++ linux/drivers/char/mem.c Sat Jan 15 20:09:04 CET 2000 @@ -16,5 +16,4 @@ #include #include -#include #include @@ -23,4 +22,10 @@ #include +#ifdef CONFIG_VIDEO_BT848 +extern int i2c_init(void); +#endif +#ifdef CONFIG_I2C +extern int i2c_init_all(void); +#endif #ifdef CONFIG_SOUND void soundcore_init(void); @@ -631,4 +636,7 @@ ohci_hcd_init(); #endif +#endif +#ifdef CONFIG_I2C + i2c_init_all(); #endif #if defined (CONFIG_FB) --- linux-old/Documentation/Configure.help Sat Jan 15 20:09:04 CET 2000 +++ linux/Documentation/Configure.help Sat Jan 15 20:09:04 CET 2000 @@ -12042,4 +12042,56 @@ # +I2C support +CONFIG_I2C + I2C (pronounce: I-square-C) is a slow bus protocol developed by + Philips. SMBus, or System Management Bus is a sub-protocol of I2C. + + Both I2C and SMBus are supported here. You will need this for + hardware sensors support, and in the future for Video for Linux + support. + + Beside this option, you will also need to select specific drivers + for your bus adapter(s). + +I2C bit-banging interfaces +CONFIG_I2C_ALGOBIT + This allows you to use a range of I2C adapters called bit-banging + adapters. Why they are called so is rather technical and uninteresting; + but you need to select this if you own one of the adapters listed + under it. + +Philips style parallel port adapter +CONFIG_I2C_PHILIPSPAR + This supports parallel-port I2C adapters made by Philips. Unless you + own such an adapter, you do not need to select this. + +ELV adapter +CONFIG_I2C_ELV + This supports parallel-port I2C adapters called ELV. Unless you + own such an adapter, you do not need to select this. + +Velleman K9000 adapter +CONFIG_I2C_VELLEMAN + This supports the Velleman K9000 parallel-port I2C adapter. Unless + you own such an adapter, you do not need to select this. + +I2C PCF 8584 interfaces +CONFIG_I2C_ALGOPCF + This allows you to use a range of I2C adapters called PCF + adapters. Why they are called so is rather technical and uninteresting; + but you need to select this if you own one of the adapters listed + under it. + +Elektor ISA card +CONFIG_I2C_ELEKTOR + This supports the PCF8584 ISA bus I2C adapter. Unless you own such + an adapter, you do not need to select this. + +I2C device interface +CONFIG_I2C_CHARDEV + Here you find the drivers which allow you to use the i2c-* device + files, usually found in the /dev directory on your system. They + make it possible to have user-space programs use the I2C bus. + # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, --- linux-old/MAINTAINERS Sat Jan 15 20:09:04 CET 2000 +++ linux/MAINTAINERS Sat Jan 15 20:09:04 CET 2000 @@ -390,4 +390,13 @@ S: Maintained +I2C DRIVERS +P: Simon Vogl +M: simon@tk.uni-linz.ac.at +P: Frodo Looijaard +M: frodol@dds.nl +L: linux-i2c@pelican.tk.uni-linz.ac.at +W: http://www.tk.uni-linz.ac.at/~simon/private/i2c +S: Maintained + IBM MCA SCSI SUBSYSTEM DRIVER P: Michael Lang