diff --git a/README.directories b/README.directories new file mode 100644 index 00000000..0654fad0 --- /dev/null +++ b/README.directories @@ -0,0 +1,14 @@ +The directories within this package: + +* CVS + The CVS directories. Do not touch these! +* archive + Packages from other sources. Currently, it contains Simon Vogl's i2c code + (mod-????????.tar.gz). +* doc + Documentation about the modules within this package +* i2c + Simon Vogl's i2c module, including any necessary patches, excluding any + files which are removed by 'make clean'. +* src + Our own code. diff --git a/archive/mod-19981006.tar.gz b/archive/mod-19981006.tar.gz new file mode 100644 index 00000000..33152d7f Binary files /dev/null and b/archive/mod-19981006.tar.gz differ diff --git a/doc/design b/doc/design new file mode 100644 index 00000000..89f2cd3d --- /dev/null +++ b/doc/design @@ -0,0 +1,497 @@ +This is a design for version 2 of our smbus and lm_sensors module. This +document is Copyright (c) 1998 by Frodo Looijaard. You may freely copy and +distribute it, as long as you recognize me as the author, and mark any +changes you make as being your own, and distribute this notice with it. + +Document version 1.0, 19981101. + + +Object oriented approach +======================== + +The i2c module structs contain callback functions and data fields. In the +i2c module, these structures are only referenced by pointers. This makes +is easy to extend these structs to contain additional information or +callback functions. For those familiar with object oriented languages, +you can see the smbus structures as an object extension of the i2c +structures, and the sensors structures are an extenstion of the smbus +structures. + +To make this clearer, I will show in an example how this is done. Note +that I have simplified some things here, so this example does not +correspond to an actual struct found in one of the modules. + +In the i2c module, you can find a struct somewhat like this: + +struct i2c_adapter { + char name[32]; + int (*detach_client) (struct i2c_client *); + struct i2c_adapter *next; +} + +We have a plain data field (name), a call-back function which needs one +parameter (a pointer to a i2c_client struct), and a data field which is +a pointer to the next adapater. + +Now we want to extend this structure. We need another data field, +containing a number of flags. We will call this new structure smbus_adapter. +A few other things change too: + +struct smbus_adapter { + char name[32]; + int (*detach_client) (struct smbus_client *); + struct smbus_adapter *next; + unsigned int flags; +} + +So we copy all existing fields. The next field still points to the next +adapter - but it is now of type smbus_adapter, not i2c_adapter. And the +call-back function takes now a parameter of type pointer to smbus_client. +And there is a new data field, called flags. + +Now examine this function definition: + +int fully_detach_i2c_client (struct i2c_client * client) +{ + res = 0; + struct i2c_adapter *current_adapter = first_adapter; /* a global var. */ + while (current_adapter) { + res |= (current_adapter -> detach_client) (client); + current_adapter = current_adapter -> next; + } + return res; +} + +This function detaches a client from all adapters. Nice. But now comes the +Big Trick(tm): we can still use this function for smbus_adapters! + +int fully_detach_smbus_client (struct smbus_client * client) +{ + return fully_detach_i2c_client( (struct i2c_client *) client); +} + +All we needed here was a simple typecast. Datapointers remain datapointers, +so this can safely be done. And because we use call-back functions, the +actual function called to detach a client from a single adapter might +be the same, or different, depending on what function was put in +client->detach_client! + +It gets even better. The i2c module stores internally all registered +adapters. But it stores pointers to the i2c_adapter struct, not the +structs themselves! So there is an array: + +#define I2C_ADAPTER_MAX 32 +struct i2c_adapter *adapters[I2C_ADAPTER_MAX]; +/* this is an array of pointers to structs, not vice versa! */ + +So, an element of this array might in fact point to a smbus_adapter, instead +of an i2c_adapter! If you know this for sure, you might use a typecast and +access the additional fields. In the meantime, the i2c internal +administration remains valid. + +We have to thank Simon Vogl and Gerd Knorr for the way they implemented +their i2c module. Any other way would have made this approach impossible, +and basing anything upon their module much more difficult. + + +Module overview +=============== + +All in all, lots of modules will be stacked on each other. Too bad, but +that is the only way to cleanly implement everything. Note that in a +specific situation, only a few modules may need to be loaded. sensor.o, +for example, does not depend on smbus.o (in the sense that you can load +sensor.o without loading smbus.o). A specific bus driver, though, will +depend on many of them. + +Generally: + sensor.o depends on nothing + smbus.o depends on nothing + i2c.o depends on nothing. + + isa.o depends only on sensor.o + A non-i2c SMBus bus driver depends only on smbus.o + A i2c bus driver depends only on i2c.o + + A sensor chip driver depends at least on sensor.o, and possibly on smbus.o + A SMBus chip driver depends only on smbus.o + A I2C chip driver depends only on i2c.o + + +sensor.o +Main sensors handling +Unites SMBus adapters and ISA adapters. + + isa.o + Implements the ISA adapter driver for sensor.o. This may prove to be so + closely integrated that it can better be made part of sensor.o + + +smbus.o +Main SMBus handling +Unites I2C adapters and SMBus hosts (like the PIIX4) + + piix4.o + SMBus adapter driver for the PIIX4 SMBus host. + + ????.o + Adapter driver for another SMBus host + + +i2c.o +Main i2c handling + + ????.o + I2C adapter driver + Implementing a class of I2C busses + + +A chip driver (typically defined in its own module) can be hooked on all +these levels: + * If it is a sensor chip, it should be hooked to sensor.o + * An SMBus chip should be hooked to smbus.o + * An I2C chip should be hooked to i2c.o +It can be difficult to decide whether a specific chip should be hoooked to +smbus.o or i2c.o. A good deciding question is, 'could it be connected to +a PIIX4?' + + +Module i2c.o +============ + +This is Simon Vogl's i2c module (this one is different from the i2c module +included in kernels around 1.2.120!). + +A driver +-------- + +struct sensor_driver { + char name[32]; + int id; + unsigned int flags; + int (* attach_adapter) (struct i2c_adapter *); + int (* detach_client) (struct i2c_client *); + int (* command) (struct i2c_client *, unsigned int cmd, void *arg); + void (* inc_use) (struct i2c_client *); + void (* dec_use) (struct i2c_client *); +} + +A driver tells us how we should handle a specific type of i2c chip. An +instance of such a chip is called a 'client'. An example would be the LM75 +module: it would contain only one driver, which tells us how to handle the +LM75; but each detected LM75 would be a separate client (which would be +dynamically allocated). + + +A description of the above struct: + name: The name of this driver + id: A unique driver identifier + flags: Flags to set certain kinds of behaviour (not used right now) + attach_adapter: A call-back function which is called if a new adapter (bus) + is found. This allows us to do our detection stuff on the new adapter, + and register new clients. + detach_client: A call-back function which is called if a specific client + which uses this driver should be disallocated. If a specific sensor module + is removed, for instance, this function would be called for each registered + client. + command: A generic call-back function to communicate in a driver-dependent + way with a specific client. This should only be seldom needed. + inc_use: Can be called to add one to an internal 'use' counter. This can be + used to control when it is safe to remove this module, for example. + + +A client +-------- + +struct i2c_client { + char name[32]; + int id; + unsigned int flags; + unsigned char addr; + struct i2c_adapter *adapter; + struct i2c_driver *driver; + void *data; +} + +A client is a specific sensor chip. Its operation is controlled by a driver +(which describes a type of sensor chip), and it is connected to an adapter +(a bus). + +A description of the above struct: + name: The name of this client + id: A unique client id + flags: Flags to set certain kinds of behaviour (not very important) + addr: The client address. 10-bit addresses are a bit of a kludge right now. + adapter: A pointer to the adapter this client is on. + driver: A pointer to the driver this client uses. + data: Additional, client-dependent data + + +An algorithm +------------ + +struct i2c_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct i2c_adapter *adap, struct i2c_msg msgs[], + int num); + int (* slave_send) (struct i2c_adapter *,char *, int); + int (* slave_recv) (struct i2c_adapter *,char *, int); + int (* algo_control) (struct i2c_adapter *, unsigned int, unsigned long); + int (* client_register) (struct i2c_client *); + int (* client_unregister) (struct i2c_client *); +} + +An algorithm describes how a certain class of i2c busses can be accessed. + +A description of the above struct: + name: The name of this algorithm + id: A unique algorithm id + master_xfer: Transfer a bunch of messages through a specific i2c bus. + slave_send: Send a message as if we are a slave device + slave_recv: Receive a message as if we are a slave device + client_register: Register a new client + client_unregister: Unregister a client + + +An adapter +---------- + +struct i2c_adapter { + char name[32]; + unsigned int id; + struct i2c_algorithm *algo; + void *data; +#ifdef SPINLOCK + spinlock_t lock; + unsigned long lockflags; +#else + struct semaphore lock; +#endif + unsigned int flags; + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + int timeout; + int retries; +} + +An adapter is a specific i2c bus. How to access it is defined in the +algorithm associated with it. + +A description of the above struct: + name: The name of this adapter + id: A unique adapter id + algo: The algorithm through which this bus must be accessed + data: Adapter specific data + lock, lockflags: To lock out simultaneous adapter access + flags: Modifiers for adapter operation + clients: All currently connected clients + client_count: The number of currently connected clients + timeout, retries: Internal variables (unimportant here). + + +Access functions +---------------- + +All these functions are defined extern. + +int i2c_master_send(struct i2c_client *,const char *, int); +int i2c_master_recv(struct i2c_client *,char *, int); +int i2c_transfer(struct i2c_adapter *,struct i2c_msg [], int num); + +These function respectively send one message to a client, receive one message +from a client, or transmit a bunch of messages. struct i2c_msg contains +an i2c address to communicate with, and can both read from and write to this +address. + + +int i2c_slave_send(struct i2c_client *, char *, int); +int i2c_slave_recv(struct i2c_client *, char *, int); + +Communicate with another master as if the normal master is a common slave +device. + + +There are several other functions, to register things for example, that +are less important to us. + + +Module smbus.o +============== + +This module offers support for SMBus operation. An SMBus adapter can either +accept SMBus commands (like the PIIX4), or be in fact an I2C driver. In +the last case, all SMBus commands will be expressed through I2C primitives. +This means that any I2C adapter driver will automatically be a SMBus +driver. + +At this point, it should be noted that there can only be one System +Management Bus in a given system (is this really true, by the way?). This +means there must be some way of selecting which of the many possible adapters +is in fact *the* SMBus. For now, I will ignore this problem. Later on, +we can add a hook somewhere in the i2c module to help us decide this. + + +A driver, client and adapter +------------------- + +We will not need to extend i2c_driver, i2c_client or i2c_adapter. This means +that struct smbus_driver is exactly the same as struct i2c_driver, and +struct smbus_client is the same as struct i2c_client, and smbus_adapter is +the same as struct i2c_adapter. We *will* define the smbus_* variants, and +use them within this module, so it should be easy to extend them after all. + +Note that at this moment, 10 bit SMBus addresses seem to be only +partially supported by the i2c module. We will ignore this issue for +now. + + +An algorithm +------------ + +struct smbus_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct smbus_adapter *adap, struct smbus_msg msgs[], + int num); + int (* slave_send) (struct smbus_adapter *,char *, int); + int (* slave_recv) (struct smbus_adapter *,char *, int); + int (* algo_control) (struct smbus_adapter *, unsigned int, unsigned long); + int (* client_register) (struct smbus_client *); + int (* client_unregister) (struct smbus_client *); + + int (* smbus_access) (struct smbus_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); +} + + +A description of the above struct: + smbus_access: A function to access the SMBus. If the algorithm describes + a i2c bus, it will use master_xfer to simulate this operation. In + case of a PIIX4, master_xfer, slave_send and slave_recv will not be + implemented. + + +Access functions +---------------- + +All these functions are defined extern. + +The i2c access function should not be used within SMBus chip drivers, as +they might not be defined (for the PIIX4, for example). Instead, use the +following general access function, or one of the easier functions based +on it: + +int smbus_access (struct smbus_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); + +There will be specific SMBus registration functions too, like the i2c +ones. + + +Module sensors.o +================ + +This module acts as a coordinations point between specific sensor modules +(which each support a certain kind of sensor). We need this module to unite +SMBus access and ISA access. + + +A driver or adapter +------------------- + +We will not need to extend smbus_driver or smbus_adapter. This means that +struct sensor_driver is exactly the same as struct smbus_driver, and struct +sensor_adapter is the same as struct smbus_adapter. We *will* define the +sensor_* variants, and use them within this module, so it should be easy to +extend them after all. + +Note that a driver can be for a chip on either the ISA bus or the +I2C/SMBus. If a specific chip can be on both, you must check variable +client->adapter->algorithm->on_isa to determine which bus you need to access. + + +A client +-------- + +struct sensor_client { + char name[32]; + int id; + unsigned int flags; + unsigned char addr; + struct sensor_adapter *adapter; + struct sensor_driver *driver; + void *data; + + unsigned int full_address; +} + +A client is a specific sensor chip. Its operation is controlled by a driver +(which describes a type of sensor chip), and it is connected to an adapter +(a bus, either a I2C/SMBus or the ISA bus). + +A description of the above struct: + full_address: The full client address. ISA addresses and 10-bit SMBus + addresses do not fit in the i2c-compatible addr field, so we needed + a new field. + + + +An algorithm +------------ + +struct sensor_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct sensor_adapter *adap, struct smbus_msg msgs[], + int num); + int (* slave_send) (struct sensor_adapter *,char *, int); + int (* slave_recv) (struct sensor_adapter *,char *, int); + int (* algo_control) (struct sensor_adapter *, unsigned int, unsigned long); + int (* client_register) (struct sensor_client *); + int (* client_unregister) (struct sensor_client *); + + int (* smbus_access) (struct sensor_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); + int isa_bus; +} + +A description of the above struct: + isa_bus: 0 if this structure describes SMBus access, 1 if it describes + ISA access. + +In case of the ISA bus, the master_xfer, slave_send, slave_recv and +smbus_access hooks will be NULL, because these functions make no sense. +It is regrettably not easy to create an access abstraction in which both +ISA bus access and SMBus access are united. See below for examples how +you can solve this problem. + + +Access functions +---------------- + +All these functions are defined extern. + +The most imporant additional access function: + + int is_on_isa (struct sensor_client *); + +This function tells us whether a specific client is connected to the ISA +bus or to the SMBus. This is important, because it determines whether we +can use the SMBus access routines. + +As an example, I will here implement our old LM78 access function: + +u8 lm78_read_value(struct sensor_client *client, u8 register) +{ + if (is_on_isa(client)) { + /* Ignore the check for LM78_BUSY to keep things simple here; the best + place to put this semaphore struct would be in client->data */ + outb_p(register,client->address + LM78_ADDR_REG_OFFSET); + return inb_p(client->address + LM78_DATA_REG_OFFSET); + } else + return smbus_read_byte_data(client,register); + /* This is a standard function based on smbus_access */ +} + + diff --git a/doc/useful_addresses.html b/doc/useful_addresses.html new file mode 100644 index 00000000..ec85655f --- /dev/null +++ b/doc/useful_addresses.html @@ -0,0 +1,60 @@ + + + +I²C, SMBus and hardware monitoring references + + +

I²C, SMBus and hardware monitoring references

+This document contains all kinds of useful references to I²C and SMBus +documents and implementations, and to hardware sensors documentation and +implementations. Please report any other references you have found! + + + diff --git a/i2c/Makefile b/i2c/Makefile new file mode 100644 index 00000000..ed1a7663 --- /dev/null +++ b/i2c/Makefile @@ -0,0 +1,40 @@ +################################################# +# config +KERNEL_LOCATION=/usr/src/linux +CURRENT=$(shell uname -r) + +################################################# +# some magic for using linux kernel settings +# when compiling module(s) + +MOD_SUB_DIRS = drivers +#MX_OBJS = i2c-core.o algo-bit.o i2c-dev.o bit-lp.o bit-velle.o algo-pcf.o +MX_OBJS = i2c-core.o algo-bit.o i2c-dev.o + +M_OBJS = bit-lp.o bit-velle.o bit-mb.o +EXTRA_CFLAGS = -Wno-unused + +here: + DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules) + +install: + su -c "cp -v $(M_OBJS) $(MX_OBJS) /lib/modules/$(CURRENT)/misc" + +clean: + -rm -f $(M_OBJS) $(MX_OBJS) .*.o.flags *~ + +up: + insmod i2c-core.o i2c_debug=0 + insmod algo-bit.o bit_test=1 i2c_debug=1 + insmod bit-lp.o base=0x278 + insmod i2c-dev.o + insmod drivers/eeprom.o test=1 + +down: + -rmmod eeprom + -rmmod i2c-dev + -rmmod bit-lp + -rmmod algo-bit + -rmmod i2c-core + +include $(KERNEL_LOCATION)/Rules.make diff --git a/i2c/README b/i2c/README new file mode 100644 index 00000000..b87e646a --- /dev/null +++ b/i2c/README @@ -0,0 +1,22 @@ +Okay, a new version is born... +There are still many files from the old version lying around that +need to be ported - anyone have spare time ? + +Currently, the following modules are running in the new mode: + i2c.c - main admin module & common interface + i2c-dev.c - character device interface for the bus + algo-bit.c - Algorithms for bit banging + bit-lp.c - hw part for parallel port adapter, Philips interface + bit-velle.c - hw part for parallel port adapter, Vellemann K9000 + bit-mb - VIA Chipset motherboards' SMBus -> see README.bitmb + +The other modules, esp. algo-pcf.c still need to be converted. For the +hw specific mods, this should be relatively easy & quick. + +A simple driver example is in drivers -- a piece of code to access EEPROMs. + +Simon (simon@tk.uni-linz.ac.at) + +For more detailed documentation have a look at +http://www.tk.uni-linz.ac.at/~simon/private/i2c + diff --git a/i2c/README.bitmb b/i2c/README.bitmb new file mode 100644 index 00000000..d739e86b --- /dev/null +++ b/i2c/README.bitmb @@ -0,0 +1,36 @@ + + README for bit-mb.c and hw-monitor.c + September 23, 1998. Kyösti Mälkki + + The questions you would probably want to ask + about bit-mb and hw-monitor, here are the answers. + +What are bit-mb.c and hw-monitor.c? + + Both are new modules for Simon G. Vogl's i2c-driver for Linux: + + Bit-mb gives you access to motherboard's SMBus (or i2c -bus). + You should have VIA VT82C586B South Bridge, and this is verified on + module-load. On some motherboards, you must enable USB -support in BIOS-setup + to make it active, check this with pciutils or in /proc/pci. + NOTE: Accessing wrong registers on PCI-chipset is likely to lock your + computer. If your hardware goes BOOOMMM, don't blame me. + + Hw-monitor is an i2c-driver for GL518SM (by Genesys Logic) chip, used in my + cpu fan (FAN78, EISCA-compatible). This gives you /proc/temperature to show + your cpu temperature. It is not tested with FAN79 (GL520SM), I will get the + datasheet for that in a few days. + + +Problems? + + This is not near alpha-level, but it works with my FIC-VA503+. + There is no quarantee your motherboard manufacturer has used the VIA + recommended pins for i2c-lines... With my FIC, they have. + + For error reports, turn i2c-debugging on. Check Simon's sourcecode + files for how to do that. + + Please report any success or failure with possible logfiles to me: + + Kyösti Mälkki kmalkki@cc.hut.fi diff --git a/i2c/algo-bit.c b/i2c/algo-bit.c new file mode 100644 index 00000000..bea9d109 --- /dev/null +++ b/i2c/algo-bit.c @@ -0,0 +1,665 @@ +/* ------------------------------------------------------------------------- */ +/* adap-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +static char alg_rcsid[] = "$Id: algo-bit.c,v 1.7 1998/09/28 06:45:38 i2c Exp i2c $"; + +#include +#include +#include +#include +#include + + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + + +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* ----- 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: + */ +int i2c_debug=1; +int bit_test=0; /* see if the line-setting functions work */ +int bit_scan=0; /* have a look at what's hanging 'round */ + +/* + * This array contains the hw-specific functions for + * each port (hardware) type. + */ +struct bit_adapter *bit_adaps[BIT_ADAP_MAX]; +int adap_count; +struct i2c_adapter *i2c_adaps[BIT_ADAP_MAX]; + +/* --- 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) + +inline void sdalo(struct bit_adapter *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +inline void sdahi(struct bit_adapter *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +inline void scllo(struct bit_adapter *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. + */ +inline int sclhi(struct bit_adapter *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + 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 -------------------------------------- */ +void i2c_start(struct bit_adapter *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +void i2c_repstart(struct bit_adapter *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +void i2c_stop(struct bit_adapter *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 */ +/* return 1 if ok */ +int i2c_outb(struct bit_adapter *adap, char c) +{ + int i; + int sb; + int ack; + + /* 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) */ +} + + + +int i2c_inb(struct bit_adapter *adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + char indata; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + indata=0; + 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. + */ +int test_bus(struct bit_adapter *adap) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c(bit): Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c(bit): Adapter: %s scl: %d sda: %d -- testing...\n", + adap->name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c(bit): %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c(bit):1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c(bit): %s SDA stuck high!\n",adap->name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL unexpected low while pulling SDA low!\n", + adap->name); + goto bailout; + } + sdahi(adap); + printk("i2c(bit):2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA stuck low!\n",adap->name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL unexpected low while SDA high!\n",adap->name); + goto bailout; + } + scllo(adap); + printk("i2c(bit):3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c(bit): %s SCL stuck high!\n",adap->name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA unexpected low while pulling SCL low!\n", + adap->name); + goto bailout; + } + sclhi(adap); + printk("i2c(bit):4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL stuck low!\n",adap->name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA unexpected low while SCL high!\n", + adap->name); + goto bailout; + } + printk("i2c(bit): %s passed test.\n",adap->name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +inline int try_address(struct bit_adapter *adap, + unsigned char addr, int retries) +{ + int i,ret = -1; + for (i=0;iudelay*/); + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c(bit): needed %d retries for %d\n",i,addr)); + return ret; +} + +int sendbytes(struct bit_adapter *adap,const char *buf, int count) +{ + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c(bit): %s i2c_write: writing %2.2X\n", + adap->name, c&0xff)); + retval = i2c_outb(adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c(bit): %s i2c_write: error - bailout.\n", + adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +inline int readbytes(struct bit_adapter *adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + + while (count > 0) { + inval = i2c_inb(adap); +/*printk("%#02x ",inval); +if ( ! (count % 16) ) +printk("\n"); +*/ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c(bit): 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(bit): i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +inline int bit_doAddress(struct bit_adapter *adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | ( flags & I2C_M_TENMASK ); + 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 */ + ret = i2c_outb(adap,msg->addr); + 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; + ret = try_address(adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +int bit_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + struct i2c_msg *pmsg; + int i,ret; + + i2c_start(adap); + for (i=0;iretries); + if (ret != 0) { + DEB2(printk("i2c(bit): NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return -EREMOTEIO; + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c(bit): read %d bytes.\n",ret)); + } else { + /* write bytes from buffer */ + ret = sendbytes(adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c(bit): wrote %d bytes.\n",ret)); + } + if (iadapter; + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + + if (adap->client_register != NULL) + return adap->client_register(client); + return 0; +} + +int client_unregister(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + + if (adap->client_unregister != NULL) + return adap->client_unregister(client); + return 0; +} + +/* -----exported algorithm data: ------------------------------------- */ + +struct i2c_algorithm bit_algo = { + "Bit-shift algorithm", + ALGO_BIT, + bit_xfer, +#if 0 + bit_send, /* master_xmit */ + bit_recv, /* master_recv */ + bit_comb, /* master_comb */ +#endif + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + client_register, + client_unregister, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct bit_adapter *adap) +{ + int i,ack; + struct i2c_adapter *i2c_adap; + + for (i = 0; i < BIT_ADAP_MAX; i++) + if (NULL == bit_adaps[i]) + break; + if (BIT_ADAP_MAX == i) + return -ENOMEM; + + if (bit_test) { + int ret = test_bus(adap); + if (ret<0) + return -ENODEV; + } + i2c_adap = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + return -ENOMEM; + + bit_adaps[i] = adap; + adap_count++; + DEB2(printk("i2c(bit): hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + memset(i2c_adap,0,sizeof(struct i2c_adapter)); + strcpy(i2c_adap->name,adap->name); + i2c_adap->id = bit_algo.id | adap->id; + i2c_adap->algo = &bit_algo; + i2c_adap->data = adap; + i2c_adap->timeout = 100; /* default values, should */ + i2c_adap->retries = 3; /* be replaced by defines */ + i2c_adaps[i] = i2c_adap; + i2c_add_adapter(i2c_adap); + + /* scan bus */ + if (bit_scan) { + printk(KERN_INFO " i2c(bit): scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(adap); + ack = i2c_outb(adap,i); + i2c_stop(adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + return 0; +} + + +int i2c_bit_del_bus(struct bit_adapter *adap) +{ + int i; + + for (i = 0; i < BIT_ADAP_MAX; i++) + if ( adap == bit_adaps[i]) + break; + if ( BIT_ADAP_MAX == i) { + printk(KERN_WARNING " i2c(bit): could not unregister bus: %s\n", + adap->name); + return -ENODEV; + } + + bit_adaps[i] = NULL; + i2c_del_adapter(i2c_adaps[i]); + kfree(i2c_adaps[i]); + i2c_adaps[i] = NULL; + adap_count--; + DEB2(printk("i2c(bit): adapter unregistered: %s\n",adap->name)); + + return 0; +} + +int algo_bit_init (void) +{ + int i; + + for (i=0;i"); +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"); + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + + +int init_module(void) +{ + return algo_bit_init(); +} + +void cleanup_module(void) +{ + i2c_del_algorithm(&bit_algo); +} +#endif + + + + + + + + + + diff --git a/i2c/algo-bit.h b/i2c/algo-bit.h new file mode 100644 index 00000000..f37ce492 --- /dev/null +++ b/i2c/algo-bit.h @@ -0,0 +1,38 @@ +#ifndef ALGO_BIT_H +#define AGLO_BIT_H 1 + +/* --- Defines for bit-adapters --------------------------------------- */ +#include "i2c.h" +/* + * 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 bit_adapter { + char name[32]; /* give it a nice name */ + unsigned int id; /* not used yet, maybe later */ + void *data; /* private data for lolevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + + /* administrative calls */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); + + /* local settings */ + int udelay; + int mdelay; + int timeout; + +}; + +extern struct bit_adapter *bit_adaps[]; + +#define BIT_ADAP_MAX 16 + +int i2c_bit_add_bus(struct bit_adapter *); +int i2c_bit_del_bus(struct bit_adapter *); + +#endif /* ALGO_BIT_H */ diff --git a/i2c/bit-elv.c b/i2c/bit-elv.c new file mode 100644 index 00000000..ea6af0ba --- /dev/null +++ b/i2c/bit-elv.c @@ -0,0 +1,146 @@ +/* ------------------------------------------------------------------------- */ +/* bit-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-elv.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: bit-elv.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.3 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.2 1997/06/15 14:21:37 i2c + * *** empty log message *** + * + * + */ + +#include +#include +#include +#include +#include +#include "i2c.h" +#include "i2c-priv.h" + + +/* ----- 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 */ +/* ----- local functions ---------------------------------------------- */ +#if (BITADAPS) == 1 +#define Local inline +#define bit_elv_getscl getscl +#define bit_elv_setscl setscl +#define bit_elv_getsda getsda +#define bit_elv_setsda setsda +#define bit_elv_bit_init bit_init +#define bit_elv_bit_exit bit_exit +#else +#define Local static +#endif + +#define portData(minor) ((char)((int)i2c_table[minor].minor_data)) + +Local void bit_elv_setscl(int minor, int state) +{ + if (state) { + portData(minor)&=0xfe; + } else { + portData(minor)|=1; + } + outb(portData(minor), DATA); +} + +Local void bit_elv_setsda(int minor, int state) +{ + if (state) { + portData(minor)&=0xfd; + } else { + portData(minor)|=2; + } + outb(portData(minor), DATA); +} + +Local int bit_elv_getscl(int minor) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +Local int bit_elv_getsda(int minor) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +Local int bit_elv_bit_init(int minor) +{ + if (check_region(i2c_table[minor].base, + (i2c_table[minor].base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(STAT) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c ELV: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x04,CTRL); /* SLCT auf low */ + udelay(40); + if ( inb(STAT) && 0x10 ) { + outb(0x0c,CTRL); + DEBINIT(printk("i2c ELV: Select was high.\n")); + return -ENODEV; + } + } + request_region(i2c_table[minor].base, + (i2c_table[minor].base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + portData(minor) = 0; + outb(0,DATA); + } + DEBINIT(printk("i2c%d: scl: %d sda %d \n",minor, + bit_elv_getscl(minor),bit_elv_getsda(minor)) ); + return 0; +} + +Local void bit_elv_bit_exit(int minor) +{ + outb(0,DATA); + outb(0x0c,CTRL); + release_region( i2c_table[minor].base , + (i2c_table[minor].base == 0x3bc)? 3 : 8 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (BITADAPS) > 1 +struct i2c_bit_opns bit_elv_ops = { + bit_elv_setscl, + bit_elv_setsda, + bit_elv_getscl, + bit_elv_getsda, + bit_elv_bit_init, + bit_elv_bit_exit +}; +#endif diff --git a/i2c/bit-lp.c b/i2c/bit-lp.c new file mode 100644 index 00000000..55c438a5 --- /dev/null +++ b/i2c/bit-lp.c @@ -0,0 +1,188 @@ +/* ------------------------------------------------------------------------- */ +/* bit-lp.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-lp.c,v 1.5 1998/09/28 06:45:38 i2c Exp i2c $"; +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +#define DEFAULT_BASE 0x378 +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 ---------------------------------------------- */ + +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); + } +} + +void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +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; +} + +void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +int bit_lp_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int bit_lp_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_lp_ops = { + "Philips Parallel port adapter", + HW_B_LP, + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + bit_lp_reg, + bit_lp_unreg, + 80, 80, 100, /* waits, timeout */ +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_ops.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + i2c_bit_add_bus(&bit_lp_ops); + } else { + return -ENODEV; + } + } else { + bit_lp_ops.data=(void*)base; + if (bit_lp_init()==0) { + i2c_bit_add_bus(&bit_lp_ops); + } else { + return -ENODEV; + } + } + printk("bit_lp: found device at %#x.\n",base); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + diff --git a/i2c/bit-mb.c b/i2c/bit-mb.c new file mode 100644 index 00000000..4f3371b7 --- /dev/null +++ b/i2c/bit-mb.c @@ -0,0 +1,163 @@ +/* + Motherboard SMBus (or i2c) Support for VIA VT82C586B South Bridge + Based on bit-lp.c by Simon Vogl + + Copyright (c) 1998 Kyösti Mälkki + + 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. +*/ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* Power management registers */ +#define PM_IO_BASE pm_io_base +#define PM_IO_BASE_REGISTER 0x48 +#define I2C_DIR (PM_IO_BASE+0x40) +#define I2C_OUT (PM_IO_BASE+0x42) +#define I2C_IN (PM_IO_BASE+0x44) +#define I2C_SCL 0x02 +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 +#define IOTEXT "Via VT82C586B i2c" + +/* ----- 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 */ + +/* ----- local functions ---------------------------------------------- */ + +u32 pm_io_base; + +static void bit_mb_setscl(void *data, int state) +{ + outb(state ? inb(I2C_OUT)|I2C_SCL : inb(I2C_OUT)&~I2C_SCL, I2C_OUT); +} + +static void bit_mb_setsda(void *data, int state) +{ + outb(state ? inb(I2C_OUT)|I2C_SDA : inb(I2C_OUT)&~I2C_SDA, I2C_OUT); +} + +static int bit_mb_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL) ); +} + +static int bit_mb_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA) ); +} + +static int bit_mb_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int bit_mb_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ */ + +struct bit_adapter bit_mb_ops = { + "VT82C586B i2c", + HW_B_MB, + NULL, + bit_mb_setsda, + bit_mb_setscl, + bit_mb_getsda, + bit_mb_getscl, + bit_mb_reg, + bit_mb_unreg, + 10, 10, 100, /*waits, timeout */ +}; + +u32 pm_io_base=0; + +static int bit_mb_init(void) +{ + if (check_region(I2C_DIR, IOSPACE) < 0 ) { + return -ENODEV; + } else { + request_region(I2C_DIR, IOSPACE, IOTEXT); + + /* Set lines to output and make them high */ + outb(inb(I2C_DIR) | I2C_SCL | I2C_SDA, I2C_DIR); + bit_mb_setsda(NULL, 1); + bit_mb_setscl(NULL, 1); + } + return 0; +} + +static void bit_mb_exit(void) +{ + release_region(I2C_DIR, IOSPACE); +} + +static u32 find_i2c(void) +{ + struct pci_dev *s_bridge; + u32 val; + + if (! pci_present()) + return(0); + + s_bridge = pci_find_device( + PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, NULL); + if (! s_bridge) return 0; + + if ( PCIBIOS_SUCCESSFUL != + pci_read_config_dword(s_bridge, PM_IO_BASE_REGISTER, &val) ) + return 0; + + return (val & (0xff<<8)); +} + +#ifdef MODULE +MODULE_AUTHOR("Kyosti Malkki "); +MODULE_DESCRIPTION("SMBus for VT82C586"); + +int init_module(void) +{ + pm_io_base = find_i2c(); + if (pm_io_base == 0) return -ENODEV; + if (bit_mb_init()==0) { + i2c_bit_add_bus(&bit_mb_ops); + } else { + return -ENODEV; + } + printk("Using i2c bus on motherboard\n"); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_mb_ops); + bit_mb_exit(); +} +#endif diff --git a/i2c/bit-velle.c b/i2c/bit-velle.c new file mode 100644 index 00000000..ffababcb --- /dev/null +++ b/i2c/bit-velle.c @@ -0,0 +1,195 @@ +/* ------------------------------------------------------------------------- */ +/* bit-velle.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. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-velle.c,v 1.3 1998/09/15 18:50:04 i2c Exp $"; +/* + * $Log: bit-velle.c,v $ + * Revision 1.3 1998/09/15 18:50:04 i2c + * *** empty log message *** + * + * Revision 1.2 1998/09/13 16:54:55 i2c + * *** empty log message *** + * + * Revision 1.1 1998/07/29 08:09:35 i2c + * Initial revision + * + * Revision 1.1 1998/01/20 10:01:29 i2c + * Initial revision + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* ----- 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 +int base=0; + +/* ----- local functions --------------------------------------------------- */ + +void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c_init: 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; +} + +void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +int bit_velle_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int bit_velle_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_velle_ops = { + "Philips Parallel port adapter", + HW_B_LP, + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + bit_velle_reg, + bit_velle_unreg, + 10, 10, 100, /* waits, timeout */ +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K9000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_ops.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + i2c_bit_add_bus(&bit_velle_ops); + } else { + return -ENODEV; + } + } else { + bit_velle_ops.data=(void*)base; + if (bit_velle_init()==0) { + i2c_bit_add_bus(&bit_velle_ops); + } else { + return -ENODEV; + } + } + printk("bit_velle: found device at %#x.\n",base); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif + + + diff --git a/i2c/detect/Makefile b/i2c/detect/Makefile new file mode 100644 index 00000000..8c3e33c3 --- /dev/null +++ b/i2c/detect/Makefile @@ -0,0 +1,32 @@ +# $Id: Makefile,v 1.1 1996/12/04 20:40:10 i2c Exp $ +# $Log: Makefile,v $ +# Revision 1.1 1996/12/04 20:40:10 i2c +# Initial revision +# +# + + +I2C_HOME = .. + +#---------------------------------------------------------------------------- +I2C_OPT = -DVERIFY_HEADERS -DSLOW_STARTER +KERNEL_OPT = -D__KERNEL__ -DKERNEL -DLINUX -DMODULE +WARNINGS = -Wall -Wstrict-prototypes +COMPILE = -pipe -fomit-frame-pointer -m486 -O2 \ + -I$(I2C_HOME) -I. +DEBUG = + +SRCS = i2c_main.c alg-illeg.c $(HW_SRCS) +INCLUDES = i2c_priv.h +OBJS = $(SRCS:.c=.o) +HDRS = i2c_priv.h +MOD = i2c + +CFLAGS = $(DEBUG) $(WARNINGS) $(COMPILE) $(I2C_OPT) $(KERNEL_OPT) +SFLAGS = -fverbose-asm $(WARNINGS) $(COMPILE) $(PRT_OPT) $(KERNEL_OPT) + +all: detect + +clean: + -rm -f detect + diff --git a/i2c/detect/detect.c b/i2c/detect/detect.c new file mode 100644 index 00000000..d3863058 --- /dev/null +++ b/i2c/detect/detect.c @@ -0,0 +1,68 @@ +/* ------------------------------------------------------------------------- */ +/* detect -- look who's there. Gets address acks from all devices on the bus.*/ +/* It should not change any values in the peripherals. */ +/* ------------------------------------------------------------------------- */ +static char *rcsid="$Id: detect.c,v 2.2 1996/11/17 22:03:06 i2c Exp $"; + +#include +#include +#include +#include +#include +#include +#include "i2c.h" + +#define Write(a,b,c) err(1,write(a,b,c)) +#define Read(a,b,c) err(0,read(a,b,c)) + +void err(int wr, int d){ + if (d==-1){ + printf("%s error: %s !\n",(wr)?"Write":"Read",strerror(errno)); + } +} +#define ERR(x) printf("return value: %d\n", (x) ); + +int main(int argc, char *argv[]){ + int i,f; + char b[40],c; + char *device = "/dev/i2c0"; + + /* parse options */ + opterr=1; + while ( (c=getopt(argc,argv,"hH?d:")) != -1) { + switch (c){ + case 'd': + if (optarg) + device = optarg; + break; + case 'h': + case 'H': + case '?': + default: + printf("%s tries to detect devices on the i2c-bus. Usage:\n",argv[0]); + printf("-d device device to use\n"); + return 0; + } + } + f=open(device,O_RDWR); + if (f<0){ + perror("detect"); + exit(1); + } + ERR( ioctl(f,I2C_UDELAY,10) ); + ERR( ioctl(f,I2C_MDELAY,0) ); +// ERR( ioctl(f,I2C_RETRIES,3) ); + + for (i=0;i<128;i++){ + ioctl(f,I2C_SLAVE,i); + if (0>read (f,b,0) ) { + putchar('.'); + } else { + printf("\n%d worked! \n",i); + } + usleep(100); + fflush(stdout); + } + putchar('\n'); + return close(f); +} diff --git a/i2c/drivers/Makefile b/i2c/drivers/Makefile new file mode 100644 index 00000000..f074ca3e --- /dev/null +++ b/i2c/drivers/Makefile @@ -0,0 +1,23 @@ +################################################# +# config +KERNEL_LOCATION=/usr/src/linux +CURRENT=$(shell uname -r) + +################################################# +# some magic for using linux kernel settings +# when compiling module(s) + +M_OBJS = eeprom.o hw-monitor.c +MX_OBJS = +EXTRA_CFLAGS = -DVERIFY_HEADERS -DSLOW_STARTER -I.. + +here: + DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules) + +install: + su -c "cp -v $(M_OBJS) $(MX_OBJS) /lib/modules/$(CURRENT)/misc" + +clean: + -rm -f $(M_OBJS) $(MX_OBJS) .*.o.flags *~ + +include $(KERNEL_LOCATION)/Rules.make diff --git a/i2c/drivers/eeprom.c b/i2c/drivers/eeprom.c new file mode 100644 index 00000000..2b037817 --- /dev/null +++ b/i2c/drivers/eeprom.c @@ -0,0 +1,172 @@ +/* experimental eeprom driver + */ +#include +#include +#include +#include + +#include "i2c.h" /* +++ simon */ +#include "algo-bit.h" + + +int test=0; +/* + * Read the configuration EEPROM + */ +#define EEPROM_ADDR 0x50 + +#define EEPROM_FROM 0x50 +#define EEPROM_TO 0x58 /*exclusive.*/ + +#define MEMSIZE 128 /* default eeprom memory size */ +/* i2c_client identifies a single device that is connected to an + * i2c bus. + */ +struct i2c_driver eepromDriver; + +struct { + int size; /* eeprom memory size*/ + unsigned int flags; + unsigned char *memory; +} eepromData; + +struct i2c_client eepromTmpl = { + "Winnov EEPROM", + 0, + 0, + EEPROM_ADDR, + NULL, /* the adapter we sit on */ + &eepromDriver, /* and our access routines */ + NULL, /* for the clients */ +}; + + +int eeprom_attach(struct i2c_adapter *adap) +{ + struct i2c_client *eeprom; + unsigned char buf[256]; + struct i2c_msg msgs[] = { + {EEPROM_ADDR, 0, 1, "\0", }, + {EEPROM_ADDR, I2C_M_RD, 256, buf, }, + }; + __u8 c=0; + int i; + int ret=0; + + printk("wnv-eeprom: Trying to attach to adapter %s \n",adap->name); + printk("Looking for EEPROMs\n"); + eepromTmpl.adapter = adap; + for (i=EEPROM_FROM;iname); + + kfree(client); + + return 0; +} + +#define LOAD_EEPROM 1 +#if 0 +#define SET_SIZE 0xee01 /* set memory size */ +#define GET_SIZE 0xee02 /* get memory size */ +#define GET_MEM 0xee03 /* load data from eeprom & pass memory pointer */ +#define SET_MEM 0xee04 /* dump memory to eeprom */ +#define OPT +#endif +int eeprom_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + switch(cmd) { + case LOAD_EEPROM: { +#if 0 + __u8 c=0; + int i, ret,d; + i2c_master_send(client,(unsigned char*)&c,1); + i2c_master_recv(client, (char*)&c,1); + i2c_master_send(client,(unsigned char*)&c,1); + ret = i2c_master_recv(client, (char*)& , + sizeof(struct EEPROM)); + if (ret != sizeof(struct EEPROM)) { + err_msg("wnv-eeprom: could only read %d bytes!\n" + ,ret); + return -1; + } + for (d=0,i=0;ieeprom))[i]; + } + if ((__u8)d != 0xff) { + err_msg("wnv-eeprom: Checksum error in configuration EEPROM\n"); + return -1; + } + info_msg("wnv-eeprom: Identified: %s\n", dev->eeprom.szProduct); +#endif + } break; + default: + printk("eeprom: unknown command %x!\n",cmd); + } + return 0; +} + +struct i2c_driver eepromDriver = { + "Videum EEPROM", + -1, + DF_NOTIFY, + eeprom_attach, + eeprom_detach, + eeprom_command, +}; + +#ifdef MODULE +MODULE_PARM(test,"i"); + +/*EXPORT_NO_SYMBOLS;*/ + +int init_module(void) +{ + /* register drivers to the i2c bus admin. system. + * These get notified upon attachment of adapters & + * in turn create new clients that are connected to + * these adapters... + */ + i2c_add_driver(&eepromDriver); + return 0; +} + +void cleanup_module(void) +{ + i2c_del_driver(&eepromDriver); +} +#endif /* MODULE*/ diff --git a/i2c/eeprom/Makefile b/i2c/eeprom/Makefile new file mode 100644 index 00000000..3d724608 --- /dev/null +++ b/i2c/eeprom/Makefile @@ -0,0 +1,41 @@ +# $Id: Makefile,v 1.1 1996/07/23 20:31:56 i2c Exp $ +# $Log: Makefile,v $ +# Revision 1.1 1996/07/23 20:31:56 i2c +# Initial revision +# + +I2C_HOME = .. + +#---------------------------------------------------------------------------- +I2C_OPT = -DVERIFY_HEADERS -DSLOW_STARTER +WARNINGS = -Wall -Wstrict-prototypes +COMPILE = -pipe -fomit-frame-pointer -m486 -O2 \ + -I$(I2C_HOME) -I. +DEBUG = + +SRCS = i2c_main.c adap-illeg.c adap-phil.c adap-lppcf.c adap-isapcf.c \ +# adap-lpbit.c +OBJS = $(SRCS:.c=.o) +HDRS = i2c_priv.h +MAIN = eeprom + +CFLAGS = $(DEBUG) $(WARNINGS) $(COMPILE) $(I2C_OPT) $(KERNEL_OPT) +SFLAGS = -fverbose-asm $(WARNINGS) $(COMPILE) $(PRT_OPT) $(KERNEL_OPT) + +all: $(MAIN) + +install: $(MAIN) + -@mkdir $(I2C_HOME)/bin + cp $(MAIN) $(I2C_HOME)/bin + +clean: + -rm -f a.out $(OBJS) $(MAIN) core .depend detect + +dep depend .depend: $(HDRS) $(SRCS) + gcc $(CFLAGS) $(SRCS) -M >.depend +# +# include dependency files if they exist +# +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif diff --git a/i2c/eeprom/README b/i2c/eeprom/README new file mode 100644 index 00000000..dac71e61 --- /dev/null +++ b/i2c/eeprom/README @@ -0,0 +1,4 @@ +This is a simple demo program that prints out the +first 256 bytes of an EEPROM memory. Adittionally, +you can write a test pattern to the chip. +With -[aA] the chip address may be specified. diff --git a/i2c/eeprom/a b/i2c/eeprom/a new file mode 100644 index 00000000..6b3aa0b0 --- /dev/null +++ b/i2c/eeprom/a @@ -0,0 +1,16 @@ +0000 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +0020 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +0030 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +0040 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +0050 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +0060 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +0070 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m +0080 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00a0 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +00b0 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +00c0 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +00d0 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +00e0 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +00f0 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m diff --git a/i2c/eeprom/b b/i2c/eeprom/b new file mode 100644 index 00000000..6b3aa0b0 --- /dev/null +++ b/i2c/eeprom/b @@ -0,0 +1,16 @@ +0000 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +0020 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +0030 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +0040 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +0050 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +0060 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +0070 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m +0080 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00a0 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +00b0 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +00c0 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +00d0 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +00e0 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +00f0 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m diff --git a/i2c/eeprom/eeprom.c b/i2c/eeprom/eeprom.c new file mode 100644 index 00000000..9dd77ec2 --- /dev/null +++ b/i2c/eeprom/eeprom.c @@ -0,0 +1,187 @@ +/* ------------------------------------------------------------------------- */ +/* eeprom -- read eeprom contents */ +/* revamped by Gerd Knorr */ +/* ------------------------------------------------------------------------- */ +static char *rcsid="$Id: eeprom.c,v 1.1 1996/07/23 20:32:12 i2c Exp $"; +/* + * $Log: eeprom.c,v $ + * Revision 1.1 1996/07/23 20:32:12 i2c + * Initial revision + * + * Revision 1.2 1996/07/06 17:20:38 i2c + * rewrote loop + * + * Revision 1.1 1996/03/15 08:55:53 root + * Initial revision + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "i2c.h" + +char *dev="/dev/i2c0"; +int wr=0, adr=0xa0; + + + +void usage(char *name) +{ + fprintf(stderr,"This is a i2c EEPROM tool\n"); + fprintf(stderr," read data: %s [ options ] > file\n",name); + fprintf(stderr," write data: %s [ options ] -w < file\n",name); + fprintf(stderr,"\n"); + fprintf(stderr,"File format is a hex dump. Without \"-w\" switch\n"); + fprintf(stderr,"only parsing (and printing) of the data is done,\n"); + fprintf(stderr,"writing is skipped.\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"options:\n"); + fprintf(stderr," -d device device to use [%s]\n",dev); + fprintf(stderr," -a addr set slave address [0x%02x]\n",adr); + fprintf(stderr,"\n"); + exit(1); +} + +void dump_buf(unsigned char *buf) +{ + int i,j; + + for (i = 0; i < 256; i += 16) { + printf("%04x ",i); + for (j = i; j < i+16; j++) { + if (!(j%4)) + printf(" "); + printf("%02x ",buf[j]); + } + printf(" "); + for (j = i; j < i+16; j++) + printf("%c",isalnum(buf[j]) ? buf[j] : '.'); + printf("\n"); + } +} + +int parse_buf(unsigned char *buf) +{ + int i,j,n,pos,count; + char line[100]; + + for (i = 0; i < 256; i += 16) { + if (NULL == fgets(line,99,stdin)) { + fprintf(stderr,"unexpected EOF\n"); + return -1; + } + if (1 != sscanf(line,"%x%n",&n,&pos)) { + fprintf(stderr,"addr parse error (%d)\n",i>>4); + return -1; + } + if (n != i) { + fprintf(stderr,"addr mismatch\n"); + return -1; + } + for (j = i; j < i+16; j++) { + if (1 != sscanf(line+pos,"%x%n",&n,&count)) { + fprintf(stderr,"value parse error\n"); + return -1; + } + buf[j] = n; + pos += count; + } + } + return 0; +} + +int +read_buf(int fd, unsigned char *buf) +{ + int i,n=8; + unsigned char addr; + + for (i = 0; i < 256; i += n) { + addr = i; + if (-1 == (write(fd,&addr,1))) { + fprintf(stderr,"write addr %s: %s\n",dev,strerror(errno)); + exit(1); + } + if (-1 == (read(fd,buf+i,n))) { + fprintf(stderr,"read data %s: %s\n",dev,strerror(errno)); + exit(1); + } + } +} +int +write_buf(int fd, unsigned char *buf) +{ + int i,j,n = 8; + unsigned char tmp[17]; + + for (i = 0; i < 256; i += n) { + tmp[0] = i; + for (j = 0; j < n; j++) + tmp[j+1] = buf[i+j]; + if (-1 == (write(fd,tmp,n+1))) { + fprintf(stderr," write data %s: %s\n",dev,strerror(errno)); + exit(1); + } + fprintf(stderr,"*"); + usleep(100000); /* 0.1 sec */ + } +} + +int main(int argc, char *argv[]) +{ + int f,c,rescue; + unsigned char buf[256]; + + /* parse options */ + opterr=1; + while ( (c=getopt(argc,argv,"hrwa:d:")) != -1) { + switch (c){ + case 'r': + rescue=1; + break; + case 'w': + wr=1; + break; + case 'd': + dev = optarg; + break; + case 'a': + adr = strtol(optarg,NULL,0); + break; + case 'h': + default: + usage(argv[0]); + } + } + + if (-1 == (f = open(dev,O_RDWR))) { + fprintf(stderr,"open %s: %s\n",dev,strerror(errno)); + exit(1); + } + ioctl(f,I2C_SLAVE,adr>>1); + memset(buf,0,256); + + if (isatty(fileno(stdin))) { + /* read */ + read_buf(f,buf); + dump_buf(buf); + } else { + /* write */ + if (-1 == parse_buf(buf)) + exit(1); + dump_buf(buf); + if (wr) { + fprintf(stderr,"writing to eeprom now... "); + write_buf(f,buf); + fprintf(stderr," ok\n"); + } + } + + close(f); + exit(0); +} diff --git a/i2c/hw-monitor.c b/i2c/hw-monitor.c new file mode 100644 index 00000000..8ff86ef7 --- /dev/null +++ b/i2c/hw-monitor.c @@ -0,0 +1,182 @@ + /* + EISCA Fan SMBus (or i2c) driver + Based on various char-device sources. + + Copyright (c) 1998 Kyösti Mälkki + + 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* Fan defines */ +#define I2C_GL518SM 0x2d /* 7-bit i2c slave address */ +#define REGMASK 0x1f +#define PROCNAME "temperature" + +/* i2c */ +static struct i2c_driver driver; +static struct i2c_client client_template; +static struct i2c_client *client = &client_template; + +/* ---------------------------------------------------------------------- */ + +static unsigned int gl_sysmon_read(int reg, int count) +{ + struct i2c_msg msgs[2]; + char write[1]={0}; + char read[2]={0,0}; + int ret; + unsigned int val; + + msgs[0].addr = msgs[1].addr = client->addr; + msgs[0].flags = msgs[1].flags = + (client->flags & (I2C_M_TEN|I2C_M_TENMASK)); + + msgs[0].len = 1; + msgs[0].buf = write; + write[0] = reg & REGMASK; + + msgs[1].flags |= I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = read; + + ret = i2c_transfer(client->adapter, msgs, 2); + + if ( 2 != ret ) return ret; + if (count==1) + val = read[0] & 0xff; + else + val = ( (read[0]<<8)|(read[1] & 0xff) ) & 0xffff; + + return val; +} + +static int gl_sysmon_write(int reg, unsigned int val, int count) +{ + unsigned char buffer[5]; + + buffer[0] = reg & REGMASK; + if (count==2) { + buffer[1] = (val >> 8) & 0xff; + buffer[2] = val & 0xff; + count = 3; + } else { + buffer[1] = val & 0xff; + count = 2; + } + + if (count != i2c_master_send(client, buffer, count)) + return -1; + return 0; +} + +static int gl_sysmon_info(char *buf, char **start, off_t fpos, int length, int dummy) +{ + char * p=buf; + int i; + unsigned int regs[32]; + + for (i=0; i<32; i++) { + regs[i] = 0; + regs[i] = gl_sysmon_read(i, (i>6 && i<13) ? 2 : 1); + } + + p += sprintf(buf, "CPU temperature %d Celsius\n", (regs[4]-119)); + + return p - buf; +} + +/* ----------------------------------------------------------------------- */ + +static struct proc_dir_entry *ent; + +static int gl_sysmon_attach(struct i2c_adapter *adap) +{ + client->adapter = adap; + + i2c_attach_client(client); + gl_sysmon_write(3, 64, 1); + + ent = create_proc_entry(PROCNAME, 0, 0); + ent->get_info = gl_sysmon_info; + + printk("EISCA Fan connected\n"); + return 0; +} + +static int gl_sysmon_detach(struct i2c_client *client) +{ + gl_sysmon_write(3, 128, 1); + i2c_detach_client(client); + remove_proc_entry(PROCNAME, 0); + + printk("EISCA Fan disconnected\n"); + return 0; +} + +static int +gl_sysmon_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return -1; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + "GL518SM", + -1, + DF_NOTIFY, + gl_sysmon_attach, + gl_sysmon_detach, + gl_sysmon_command, +}; + +static struct i2c_client client_template = { + "EISCA Cooler", + I2C_DRIVERID_GL518SM, + 0, + I2C_GL518SM, + NULL, + &driver +}; + +#ifdef MODULE +MODULE_AUTHOR("Kyosti Malkki "); +MODULE_DESCRIPTION("i2c driver for GL518SM"); + +int init_module(void) +{ + i2c_add_driver(&driver); + return 0; +} + +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif diff --git a/i2c/i2c-core.c b/i2c/i2c-core.c new file mode 100644 index 00000000..03ae5532 --- /dev/null +++ b/i2c/i2c-core.c @@ -0,0 +1,467 @@ +/* ------------------------------------------------------------------------- */ +/* i2c.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-98 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. */ +/* ------------------------------------------------------------------------- */ +#define RCSID "$Id: i2c.c,v 1.7 1998/09/28 06:45:38 i2c Exp i2c $" +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include "i2c.h" + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +/*#define SPINLOCK*/ +#ifdef SPINLOCK +#define I2C_LOCK(adap) spin_lock_irqsave(&adap->lock,adap->lockflags) +#define I2C_UNLOCK(adap) spin_unlock_irqrestore(&adap->lock,adap->lockflags) +#else +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) +#endif + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** algorithm list */ +struct i2c_algorithm *algorithms[I2C_ALGO_MAX]; +int algo_count; + +/**** adapter list */ +struct i2c_adapter *adapters[I2C_ADAP_MAX]; +int adap_count; + +/**** drivers list */ +struct i2c_driver *drivers[I2C_DRIVER_MAX]; +int driver_count; + +/**** debug level */ +int i2c_debug=1; + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * Algorithms - used to access groups of similar hw adapters or + * specific interfaces like the PCF8584 et al. + */ +int i2c_add_algorithm(struct i2c_algorithm *algo) +{ + int i; + + for (i = 0; i < I2C_ALGO_MAX; i++) + if (NULL == algorithms[i]) + break; + if (I2C_ALGO_MAX == i) { + printk(KERN_WARNING + " i2c: register_algorithm(%s) - enlarge I2C_ALGO_MAX.\n", + algo->name); + return -ENOMEM; + } + + algorithms[i] = algo; + algo_count++; + + DEB(printk("i2c: algorithm %s registered.\n",algo->name)); + return 0; +} + + +int i2c_del_algorithm(struct i2c_algorithm *algo) +{ + int i; + + for (i = 0; i < I2C_ALGO_MAX; i++) + if (algo == algorithms[i]) + break; + if (I2C_ALGO_MAX == i) { + printk(KERN_WARNING + " i2c: unregister_algorithm: [%s] not found.\n", + algo->name); + return -ENODEV; + } + algorithms[i] = NULL; + algo_count--; + + DEB(printk("i2c: algorithm unregistered: %s\n",algo->name)); + return 0; +} + + +/* ----- + * 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; + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + return -ENOMEM; + } + + + adapters[i] = adap; + adap_count++; + + /* init data types */ +#ifdef SPINLOCK + adap->lock = (spinlock_t)SPIN_LOCK_UNLOCKED; +#else + adap->lock = MUTEX; +#endif + + /* inform drivers of new adapters */ + for (i=0;iflags&DF_NOTIFY) + drivers[i]->attach_adapter(adap); + + DEB(printk("i2c: adapter %s registered.\n",adap->name)); + return 0; +} + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c: unregister_adapter adap [%s] not found.\n", + adap->name); + return -ENODEV; + } + + /* detach any active clients */ + for (j=0;jclients[j]; + if ( (client!=NULL) + /* && (client->driver->flags & 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. + */ + i2c_detach_client(client); + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + DEB(printk("i2c: 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; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DEB(printk("i2c: driver %s registered.\n",driver->name)); + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&DF_NOTIFY ) + for (i=0;iattach_adapter(adapters[i]); + + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c: unregister_driver: [%s] not found\n", + driver->name); + 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: 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. + */ + for (k=0;kname)); + for (j=0;jclients[j]; + if (client != NULL && client->driver == driver) { + DEB2(printk("i2c: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + drivers[i] = NULL; + driver_count--; + + DEB(printk("i2c: driver unregistered: %s\n",driver->name)); + return 0; +} + + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct i2c_algorithm *algo = adapter->algo; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + if (algo->client_register != NULL) + algo->client_register(client); + DEB(printk("i2c: 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; + struct i2c_algorithm *algo = adapter->algo; + 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: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if (algo->client_unregister != NULL) + algo->client_unregister(client); + /* client->driver->detach_client(client);*/ + adapter->clients[i] = NULL; + adapter->client_count--; + DEB(printk("i2c: client [%s] unregistered.\n",client->name)); + return 0; +} + +int i2c_init(void) +{ + /* clear algorithms */ + memset(algorithms,0,sizeof(algorithms)); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + algo_count=0; + adap_count=0; + driver_count=0; + + printk("i2c module initialized.\n"); + return 0; +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + DEB(printk("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; +} + +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; + msg.addr = client->addr; + msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK ); + msg.len = count; + (const char *)msg.buf = buf; + + DEB(printk("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; +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + msg.addr = client->addr; + msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK ); + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB(printk("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); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; +} + + +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 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; +} + +int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr) +{ + int i; + struct i2c_msg msg; + struct i2c_msg *pmsg = &msg; + msg.flags=client->flags & (I2C_M_TENMASK | I2C_M_TEN ); + msg.buf = NULL; + msg.len = 0; + I2C_LOCK(client->adapter); + for (i = low_addr; i <= hi_addr; i++) { + client->addr=i; + /* TODO: implement a control statement in the algo layer + * that does address lookup only. + */ + if (1 == client->adapter-> + algo->master_xfer(client->adapter,pmsg,1)) + break; + } + I2C_UNLOCK(client->adapter); + return (i <= hi_addr) ? i : -1; +} + +#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"); + +EXPORT_SYMBOL(i2c_add_algorithm); +EXPORT_SYMBOL(i2c_del_algorithm); +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_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); + + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif diff --git a/i2c/i2c-dev.c b/i2c/i2c-dev.c new file mode 100644 index 00000000..44d76d6d --- /dev/null +++ b/i2c/i2c-dev.c @@ -0,0 +1,364 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-dev.c - i2c-bus driver, char device interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +#define RCSID "$Id: i2c-dev.c,v 1.6 1998/09/28 06:45:38 i2c Exp i2c $" +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +#include "i2c.h" + +/* ----- global defines ---------------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEBE(x) x /* error messages ~ */ +#define DEBI(x) /* ioctl and its arguments */ + +/* ----- global variables -------------------------------------------------- */ + +/* save the registered char devices. + * there is one device per i2c adapter. + */ +#define I2C_DEV_MAX I2C_ADAP_MAX +struct i2c_client *devs[I2C_DEV_MAX]; +int dev_count; + +struct i2c_driver driver; +struct i2c_client dev_template = { + "I2C char device", /* name */ + -1, /* id */ + 0, /* flags */ + 0, /* address */ + NULL, /* adapter */ + &driver, /* driver */ +}; + + +/* ----- local functions --------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x020100 +long long i2c_lseek(struct file * file, long long offset, int origin) +#else +int i2c_lseek (struct inode * inode, struct file *file, off_t offset, int origin) +#endif +{ + return -ESPIPE; +} + + +int i2c_open(struct inode * inode, struct file * file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + if (minor >= dev_count ) { + DEBE(printk("i2c: minor exceeded ports\n")); + return -ENODEV; + } + file->private_data = devs[minor]; + MOD_INC_USE_COUNT; + i2c_attach_client(devs[minor]); /* tell adapter it is in use. */ + + DEB(printk("i2c_open: i2c%d\n",minor)); + return 0; +} + +#if LINUX_VERSION_CODE >= 0x020100 +int i2c_release (struct inode * inode, struct file * file) +#else +void i2c_release (struct inode * inode, struct file * file) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + + file->private_data=NULL; + DEB(printk("i2c_close: i2c%d\n",minor)); + i2c_detach_client(devs[minor]); + MOD_DEC_USE_COUNT; +#if LINUX_VERSION_CODE >= 0x020100 + return 0; +#endif +} + +#if LINUX_VERSION_CODE >= 0x020140 /* file operations changed again...*/ +ssize_t i2c_write(struct file * file, + const char * buf, size_t count, loff_t *ppos) +#else +# if LINUX_VERSION_CODE >= 0x020100 +long i2c_write(struct inode * inode, struct file * file, + const char * buf, unsigned long count) +# else +int i2c_write (struct inode *inode, struct file *file, + const char *buf, int count) +# endif +#endif +{ +#if LINUX_VERSION_CODE >= 0x020140 + struct inode *inode = file->f_dentry->d_inode; +#endif + int ret; + char *tmp; + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + + if (minor>=dev_count){ + DEBE(printk("i2c_write: minor %d invalid\n",minor)); + return -EINVAL; + } + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; +#if LINUX_VERSION_CODE >= 0x020100 + copy_from_user(tmp,buf,count); +#else + memcpy_fromfs(tmp,buf,count); +#endif + + DEB(printk("i2c_write: i2c%d writing %d bytes.\n",minor,count)); + + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +#if LINUX_VERSION_CODE >= 0x020140 /* file operations changed again...*/ +ssize_t i2c_read(struct file * file,char * buf, size_t count, loff_t *ppos) +#else +#if LINUX_VERSION_CODE >= 0x020100 +long i2c_read(struct inode * inode, struct file * file, + char * buf, unsigned long count) +#else +int i2c_read (struct inode *inode, struct file *file, char *buf, int count) +#endif +#endif +{ +#if LINUX_VERSION_CODE >= 0x020140 + struct inode *inode = file->f_dentry->d_inode; +#endif + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + char *tmp; + int ret; + + DEB(printk("i2c_read: i2c%d reading %d bytes from %d.\n",minor,count,client->addr)); + if (minor>=dev_count) { + DEBE(printk("i2c_write: minor %d invalid\n",minor)); + return -EINVAL; + } + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + ret = i2c_master_recv(client,tmp,count); +#if LINUX_VERSION_CODE >= 0x020100 + copy_to_user(buf,tmp,count); +#else + memcpy_tofs(buf,tmp,count); +#endif + + return ret; +} + + +int i2c_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + int ret = 0; + + DEBI(printk("i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_SLAVE: + client->addr = arg; + break; + case I2C_TENBIT: + client->flags &= !I2C_M_TENMASK; + if (arg<0) + return 0; + switch(arg) { + case 0: + client->flags|=I2C_M_TEN0; + break; + case 1: + client->flags|=I2C_M_TEN1; + break; + case 2: + client->flags|=I2C_M_TEN2; + break; + case 3: + client->flags|=I2C_M_TEN3; + break; + default: + printk("i2c-dev(%d): illegal arg to I2C_TEN: %ld\n" + ,minor,arg); + }; + break; +#if 0 + case I2C_WRITE_SIZE: + if ( arg >= I2C_BUFFER_SIZE ) { + printk("i2c%d: write size too big (%ld)",minor,arg); + return -E2BIG; + } + data->writelength = arg; + if ( arg > 0 ) + i2c_table[minor].flags|= P_REG; + else + i2c_table[minor].flags&=!P_REG; + break; + case I2C_WRITE_BUF: +#if LINUX_VERSION_CODE >= 0x020100 + copy_from_user(data->buf,(char*)arg,data->writelength); +#else + memcpy_fromfs(data->buf,(char*)arg,data->writelength); +#endif + break; +#endif + default: + ret = i2c_control(client,cmd,arg); + } + DEB(printk("i2c(dev): handled ioctl (%d,%ld)\n",cmd,arg)); + return ret; +} + + +/* ----- module functions --------------------------------------------- */ +struct file_operations i2c_fops = { + i2c_lseek, + i2c_read, + i2c_write, + NULL, /* i2c_readdir */ + NULL, /* i2c_select */ + i2c_ioctl, + NULL, /* i2c_mmap */ + i2c_open, +#if LINUX_VERSION_CODE >= 0x020178 + NULL, /* i2c_flush */ +#endif + i2c_release, +}; + +int attach_adapter(struct i2c_adapter *adap) +{ + int i; + struct i2c_client *client; + + for (i = 0; i < I2C_DEV_MAX; i++) + if (NULL == devs[i]) + break; + if (I2C_DEV_MAX == i) + return -ENOMEM; + + client = (struct i2c_client *)kmalloc(sizeof(struct i2c_client), + GFP_KERNEL); + if (client==NULL) + return -ENOMEM; + memcpy(client,&dev_template,sizeof(struct i2c_client)); + client->adapter = adap; +/* for combined r/w: + client->data = kmalloc(sizeof(char)*I2C_BUFFER_SIZE,GFP_KERNEL); +*/ + devs[i] = client; + dev_count++; + + DEB(printk("i2c(char): registered '%s' as minor %d\n",adap->name,i)); + return 0; +} + +int detach_adapter(struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + int i; + for (i = 0; i < I2C_DEV_MAX; i++) + if (devs[i]->adapter == adap) + break; + if (I2C_DEV_MAX == i) + { + printk(KERN_WARNING "i2c(char): detach adapter %s not found\n", + adap->name); + return -ENODEV; + } + if (devs[i]->data) + kfree(devs[i]->data); + kfree(devs[i]); + devs[i] = NULL; + dev_count--; + + DEB(printk("i2c(char): adapter unregistered: %s\n",adap->name)); + return 0; +} + +int command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + return -1; +} + + + +struct i2c_driver driver = { + "i2c character device", + 2, /* id */ + DF_NOTIFY, /* flags */ + attach_adapter, + detach_adapter, + command, +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus character device interface"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (register_chrdev(I2C_MAJOR,"i2c",&i2c_fops)) { + printk("i2c: unable to get major %d for i2c bus\n",I2C_MAJOR); + return -EIO; + } + + memset(&devs,0,sizeof(devs)); + dev_count = 0; + + i2c_add_driver(&driver); + printk("i2c char device interface initialized.\n"); + return 0; +} + + +void cleanup_module(void) +{ + i2c_del_driver(&driver); + unregister_chrdev(I2C_MAJOR,"i2c"); +} + +#endif diff --git a/i2c/i2c.h b/i2c/i2c.h new file mode 100644 index 00000000..bb8a1b5a --- /dev/null +++ b/i2c/i2c.h @@ -0,0 +1,371 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the \iic-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995 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. */ +/* ------------------------------------------------------------------------- */ +#ifndef _I2C_H +#define _I2C_H + +#ifdef __KERNEL__ + +/* define spinlock to use spinlocks for sync., else use semaphores*/ +/*#define SPINLOCK*/ + +#ifdef SPINLOCK +#include /* for spinlock_t */ +#else +#include +#endif +/* --- 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 + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; + + +/* + * 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); + +/* 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); + + + +/* + * I2C Message - could be used in the current interface to + */ +struct i2c_msg { + unsigned char addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_TEN0 0x10 /* herein lie the first 2 bits */ +#define I2C_M_TEN1 0x12 +#define I2C_M_TEN2 0x14 +#define I2C_M_TEN3 0x16 +#define I2C_M_TENMASK 0x06 +#define I2C_M_RD 0x01 + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; + +/* + * 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. + */ + 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 char addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits of this char */ + /* 10 bit addresses use the full*/ + /* 8 bits & the flags like in */ + /* i2c_msg */ + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct i2c_driver *driver; /* and our access routines */ + void *data; /* for the clients */ +}; + + +/* + * 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; + /* + int (*master_send)(struct i2c_client *,const char*,int); + int (*master_recv)(struct i2c_client *,char*,int); + int (*master_comb)(struct i2c_client *,char*,const char*,int,int,int); + */ + int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], int num); + + /* --- these optional/future use for some adapter types.*/ + int (*slave_send)(struct i2c_adapter *,char*,int); + int (*slave_recv)(struct i2c_adapter *,char*,int); + + /* --- ioctl like call to set div. parameters. */ + int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); + + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); +}; + + +/* + * 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 *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 */ +#ifdef SPINLOCK + spinlock_t lock;/* used to access the adapter exclusively */ + unsigned long lockflags; +#else + struct semaphore lock; +#endif + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + + int timeout; + int retries; +}; + + +/*flags for the driver struct: + */ +#define DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ + + +#if 0 /* deprecate! */ +/*flags for the client struct: + */ +#define CF_TEN 0x100000 /* we have a ten bit chip address */ +#define CF_TEN0 0x100000 /* herein lie the first 2 bits */ +#define CF_TEN1 0x110000 +#define CF_TEN2 0x120000 +#define CF_TEN3 0x130000 +#define TENMASK 0x130000 +#endif + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +extern int i2c_add_algorithm(struct i2c_algorithm *); +extern int i2c_del_algorithm(struct i2c_algorithm *); + +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 *); + + +/* + * A utility function used in the attach-phase of drivers. Returns at the first address + * that acks in the given range. + */ +extern int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr); + +/* An ioctl like call to set div. parameters of the adapter. + */ +extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); + + +#endif /* __KERNEL__ */ + + +/* ----- 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 bits long, */ + /* these are to be passed as the */ + /* lowest 7 bits in the arg. */ + /* for 10-bit addresses pass lower 8bits*/ +#define I2C_TENBIT 0x0704 /* with 0-3 as arg to this call */ + /* a value <0 resets to 7 bits */ +/* ... 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 */ + +#if 0 +#define I2C_ADDR 0x0707 /* Change adapter's \iic address */ + /* ...not supported by all adap's */ + +#define I2C_RESET 0x07fd /* reset adapter */ +#define I2C_CLEAR 0x07fe /* when lost, use to clear stale info */ +#define I2C_V_SLOW 0x07ff /* set jiffies delay call with int */ + +#define I2C_INTR 0x0708 /* Pass interrupt number - 2be impl. */ + +#endif + +/* + * ---- Driver types ----------------------------------------------------- + */ + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 +#define I2C_DRIVERID_GL518SM 4 + +/* + * ---- 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 ALGO_NONE 0x00000 +#define ALGO_BIT 0x10000 /* bit style adapters */ +#define ALGO_PCF 0x20000 /* PCF 8584 style adapters */ + +#define ALGO_MASK 0xf0000 /* Mask for algorithms */ +#define ALGO_SHIFT 0x10 /* right shift to get index values */ + +#define I2C_HW_ADAPS 0x10000 /* number of different hw implements per*/ + /* algorithm layer module */ +#define I2C_HW_MASK 0xffff /* space for indiv. hw implmentations */ + + +/* hw specific modules that are defined per algorithm layer + */ + +/* --- Bit algorithm adapters */ +#define HW_B_LP 0x00 /* Parallel port Philips style adapter */ +#define HW_B_LPC 0x01 /* Parallel port, over control reg. */ +#define HW_B_SER 0x02 /* Serial line interface */ +#define HW_B_ELV 0x03 /* ELV Card */ +#define HW_B_VELLE 0x04 /* Vellemann K8000 */ +#define HW_B_BT848 0x05 /* BT848 video boards */ +#define HW_B_WNV 0x06 /* Winnov Videums */ +#define HW_B_MB 0x07 /* Via vt82c586b */ + +/* --- PCF 8584 based algorithms */ +#define HW_P_LP 0x00 /* Parallel port interface */ +#define HW_P_ISA 0x01 /* generic ISA Bus inteface card */ +#define HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + + + + +/* ----- I2C-DEV: char device interface stuff ------------------------- */ + +#define I2C_MAJOR 89 /* Device major number */ + + +# if LINUX_VERSION_CODE < 0x020100 +/* Hack to make this thing compile under 2.0.xx kernels + */ + +# ifdef MODULE +# 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 + +# ifndef NULL +# define NULL ( (void *) 0 ) +# endif +#endif + +# ifndef ENODEV +# include +# endif +#endif diff --git a/i2c/old-code/algo-pcf.c b/i2c/old-code/algo-pcf.c new file mode 100644 index 00000000..a8e36236 --- /dev/null +++ b/i2c/old-code/algo-pcf.c @@ -0,0 +1,291 @@ +/* ------------------------------------------------------------------------- */ +/* adap-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +static char alg_rcsid[] = "$Id: algo-pcf.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; + +#include +#include +#include +#include +#include + + +#if LINUX_VERSION_CODE >= 0x020100 + +# include +#else +# include +#endif + + +#include +#include +#include + +#include "i2c.h" +#include "algo-pcf.h" + +/* ----- global defines ---------------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) /* error messages */ +#define DEBI(x) /* ioctl and its arguments */ +#define DEBACK(x) x /* ack failed message */ +#define DEBSTAT(x) /* print several statistical values */ + +#define DEBPROTO(x) /* debug the protocol by showing transferred bytes*/ + + +/* ----- global variables --------------------------------------------- */ + +/* module parameters: + */ +static int test=0; /* see if the line-setting functions work */ +static int scan=0; /* have a look at what's hanging 'round */ + +/* + * This array contains the hw-specific functions for + * each port (hardware) type. + */ +static struct pcf_adapter *pcf_adaps[PCF_ADAP_MAX]; +static int adap_count; +static struct i2c_adapter *i2c_adaps[PCF_ADAP_MAX]; + +/* --- setting states on the bus with the right timing: --------------- */ + +/* + * 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 pcf_adapter *adap) +{ +} + +/* ----- Utility functions + */ + + +/* send a message to a client. + */ +static int pcf_send(struct i2c_client *client,const char *buf, int count) +{ + struct i2c_adapter *adapter=client->adapter; + struct pcf_adapter *adap=(struct pcf_adapter*)adapter->data; + int ret,i; + + DEB2(printk(" i2c_write: wrote %d bytes.\n",wrcount)); + return wrcount; +} + + +static int pcf_recv(struct i2c_client *client,char *buf,int count) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + unsigned int flags = client->flags; + char addr; + int ret=0,i,rdcount; + + DEB(printk("i2c(bit): i2c_read: %d byte(s) read.\n", rdcount )); + return rdcount; +} + + +/* + * alpha version of combined transmit + */ +static int pcf_comb(struct i2c_client *client, char *readbuf,const char *writebuf, + int nread, int nwrite, int dir) +{ + /* struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + unsigned int flags = client->flags; + char addr; + int ret=0,i,rdcount=0,wrcount=0; + + + DEB(printk("i2c(bit): i2c_read: %d byte(s) read.\n", rdcount )); + return wrcount+rdcount; + */ + return 0; +} + + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static int client_register(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + + if (adap->client_register != NULL) + return adap->client_register(client); + return 0; +} + +int client_unregister(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + + if (adap->client_unregister != NULL) + return adap->client_unregister(client); + return 0; +} + +/* -----exported algorithm data: ------------------------------------- */ + +struct i2c_algorithm pcf_algo = { + "PCF 8584 algorithm", + ALGO_BIT, + pcf_send, /* master_xmit */ + pcf_recv, /* master_recv */ + pcf_comb, /* master_comb */ + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + client_register, + client_unregister, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct pcf_adapter *adap) +{ + int i,ack; + struct i2c_adapter *i2c_adap; + + for (i = 0; i < PCF_ADAP_MAX; i++) + if (NULL == pcf_adaps[i]) + break; + if (PCF_ADAP_MAX == i) + return -ENOMEM; + + if (test) { + int ret = test_bus(adap); + if (ret<0) + return -ENODEV; + } + i2c_adap = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + return -ENOMEM; + + pcf_adaps[i] = adap; + adap_count++; + DEB(printk("i2c(bit): algorithm %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + memset(i2c_adap,0,sizeof(struct i2c_adapter)); + strcpy(i2c_adap->name,adap->name); + i2c_adap->id = pcf_algo.id | adap->id; + i2c_adap->algo = &pcf_algo; + i2c_adap->data = adap; + i2c_adap->timeout = 100; /* default values, should */ + i2c_adap->retries = 3; /* be replaced by defines */ + i2c_adaps[i] = i2c_adap; + i2c_add_adapter(i2c_adap); + + /* scan bus */ + if (scan) { +#if 0 + printk(KERN_INFO "i2c(bit): scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(adap); + ack = i2c_outb(adap,i); + i2c_stop(adap); + if (ack>0) { + printk(KERN_INFO + "i2c(bit): found chip at addr=0x%2x\n",i>>1); + } + } +#endif + } + return 0; +} + + +int i2c_pcf_del_bus(struct pcf_adapter *adap) +{ + int i; + + for (i = 0; i < PCF_ADAP_MAX; i++) + if ( adap == pcf_adaps[i]) + break; + if ( PCF_ADAP_MAX == i) { + printk(KERN_WARNING "i2c(bit): could not unregister bus: %s\n", + adap->name); + return -ENODEV; + } + + pcf_adaps[i] = NULL; + i2c_del_adapter(i2c_adaps[i]); + kfree(i2c_adaps[i]); + i2c_adaps[i] = NULL; + adap_count--; + DEB(printk("i2c(bit): adapter unregistered: %s\n",adap->name)); + + return 0; +} + +int algo_pcf_init (void) +{ + int i; + + for (i=0;i +#include +#include +#include +#include +#include + + +#include "i2c.h" +#include "algo-bit.h" + + +#define BIT_LP_MAX 3 /* max. number of addresses to probe */ + +struct bit_lp_data { + unsigned int base; /* base address */ +}; + +static struct bit_lp_data bit_lp_adaps[BIT_LP_MAX] = { + {0,}, + {0,}, + {0,}, + }; +static int adap_count=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 */ + +/* ----- 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 bit_lp_adaps[minor].base +#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(int minor, int state) +{ + if (state) { + outb(I2C_SCL, CTRL); + } else { + outb(I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(int minor, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(int minor) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(int minor) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(int minor) +{ + if (check_region(bit_lp_adaps[minor].base, + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(bit_lp_adaps[minor].base, + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + bit_lp_setsda(minor,1); + bit_lp_setscl(minor,1); + } + printk("i2c%d: scl: %d sda %d \n",minor,bit_lp_getscl(minor),bit_lp_getsda(minor)); + return 0; +} + +static void bit_lp_exit(int minor) +{ + release_region( bit_lp_adaps[minor].base , + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8 ); + release_region( bit_lp_adaps[minor].base + 0x0400, 8 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_lp_ops = { + "Philips Parallel port adapter", + bit_lp_setscl, + bit_lp_setsda, + bit_lp_getscl, + bit_lp_getsda, + bit_lp_init, + bit_lp_exit +}; + +#ifdef MODULE +static int base[BIT_LP_MAX+1] = { [0 ... BIT_LP_MAX] = 0 }; +MODULE_PARM(base, "1-" __MODULE_STRING(BIT_LP_MAX) "i"); + +/* +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); +*/ +int init_module(void) +{ + /* Work out how many ports we have, then get parport_share to parse + the irq values. */ + unsigned int i; + for (i = 0; i < BIT_LP_MAX && base[i]; i++) { + bit_lp_adaps[adap_count].base = base[i]; + if (bit_lp_init(adap_count)==0) { +/* i2c_bit_add_bus(struct bit_adapter *algo);*/ +/* bit_add_adapter();*/ + adap_count++; + } + } + if (i==0) { /* no values specified -> probe */ + } + +/* count += probe_one_port(0x3bc, irq[0], dma[0]); + count += probe_one_port(0x378, irq[0], dma[0]); + count += probe_one_port(0x278, irq[0], dma[0]); +*/ + printk("bit_lp: found %d devices.\n",adap_count); + + if (adap_count==0) + return -ENODEV; + else + return 0; +} + +void cleanup_module(void) +{ + int i; + for (i=0;i +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- 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 DEBHW(x) x /* lowest level hardware debug -- log changes of*/ + /* the bit lines... */ + +#define basePort(port) (i2c_table[minor].base+(port)) + +/* ----- local functions ---------------------------------------------- */ + +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif + + + +Local int pcf_read (int minor, int adr) +{ + int ret; + udelay(10); /* Give the card some time */ + ret = inb(basePort( (adr!=0) ));/* force max. increment of 1 */ + return ret; +} + + +Local void pcf_write (int minor, int adr,char data) +{ + udelay(10); /* Give the card some time */ + outb(data,basePort( (adr!=0) )); +} + + +Local int pcf_init (int minor) +{ + if (check_region(i2c_table[minor].base, 4) < 0 ) { + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 4, "i2c (Elektor)"); + } + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base, 4 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_lp_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif diff --git a/i2c/old-code/pcf-isa.c b/i2c/old-code/pcf-isa.c new file mode 100644 index 00000000..c1292541 --- /dev/null +++ b/i2c/old-code/pcf-isa.c @@ -0,0 +1,110 @@ +/* ------------------------------------------------------------------------- */ +/* pcf-isa.c i2c-hw access for PCF 8584 on ISA boards */ +/* ------------------------------------------------------------------------- */ +/* 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. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: pcf-isa.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: pcf-isa.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.3 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.2 1996/11/20 20:20:46 i2c + * first version for ISA bus. + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- 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 */ + +/* ----- local functions --------------------------------------------------- */ + +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif + + +Local int pcf_read (int minor, int adr) +{ + if (adr) + return inb(BASE + 1); /* read status port */ + else + return inb(BASE); /* read data port */ +} + + +Local void pcf_write (int minor, int adr,char data) +{ + if (adr) + outb(data, BASE + 1); + else + outb(data, BASE); +} + + +Local int pcf_init (int minor) +{ + if (check_region(i2c_table[minor].base, 2) < 0 ) { + DEBE(printk("i2c_init: Port %#x already in use.\n", + i2c_table[minor].base)); + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 2, + "i2c (ISA bus)"); + } + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base , 2 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_isa_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif \ No newline at end of file diff --git a/i2c/old-code/pcf-lp.c b/i2c/old-code/pcf-lp.c new file mode 100644 index 00000000..cbe80875 --- /dev/null +++ b/i2c/old-code/pcf-lp.c @@ -0,0 +1,228 @@ +/* ------------------------------------------------------------------------- */ +/* pcf-lp.c i2c-hw access for PCF 8584 on bidir. parallel ports */ +/* ------------------------------------------------------------------------- */ +/* +-----.-.---....------..-.-.---.... Oh bugger! Hope it works this time...... + + Copyright (C) 1995-97 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. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: pcf-lp.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: pcf-lp.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.4 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.3 1997/06/15 14:21:37 i2c + * removed debugging messages + * + * Revision 1.2 1997/06/03 06:00:10 i2c + * first version that works + * + * Revision 1.2 1996/11/20 20:20:46 i2c + * first version for ISA bus. + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- 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 DEBHW(x) x /* lowest level hardware debug -- log changes of*/ + /* the bit lines... */ + +#define lpPort(port) (i2c_table[minor].base+(port)) + + +/* ----- local variables ---------------------------------------------- */ + +static unsigned char ctrl; /* remember line states */ + + /* (re)set line states */ +#define Strobe(arg) if (!arg) ctrl|= 0x01; else ctrl &=!0x01;\ + outb_p(ctrl,lpPort(2)) /* Strobe -- pin 1 */ +#define AutoLF(arg) if (!arg) ctrl|= 0x02; else ctrl &=!0x02;\ + outb_p(ctrl,lpPort(2)) /* AutoLF -- pin 14 */ +#define Init(arg) if ( arg) ctrl|= 0x04; else ctrl &=!0x04;\ + outb(ctrl,lpPort(2)) /* Init -- pin 16 */ +#define Select(arg) if (!arg) ctrl|= 0x08; else ctrl &=!0x08;\ + outb_p(ctrl,lpPort(2)) /* Sel. in - pin 17 */ + /* 0x10 -- Int Enable */ +#define ReadBit(arg) if(arg) ctrl|= 0x20; else ctrl &=!0x20;\ + outb_p(ctrl,lpPort(2)) /* direction bit */ + +#define inStr() (! (0x01 & inb(lpPort(2)) ) ) /* Strobe -- pin 1 */ +#define inALF() (! (0x02 & inb(lpPort(2)) ) ) /* AutoLF -- pin 14 */ +#define inIni() ( (0x04 & inb(lpPort(2)) ) ) /* Init -- pin 16 */ +#define inSel() (! (0x08 & inb(lpPort(2)) ) ) /* Sel. in - pin 17 */ + /* 0x10 -- Int Enable */ +/* +#define Argl() printk("(wr-%d cs-%d a0-%d (%#2x, %#2x))\n", \ + inStr(),inALF(),inIni(),inb(lpPort(0)),inb(lpPort(1)) ) +*/ +#define Argl() /**/ + +#define wr(arg) Strobe(arg); Argl() /* write line */ +#define cs(arg) AutoLF(arg); Argl() /* chip select */ +#define a0(arg) Init (arg); Argl() /* address line */ + +/* ----- local functions ---------------------------------------------- */ +/* +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif +*/ +#define Local /**/ + +/* --------- okay, I borrowed it. Stays here for debugging. + * check the epp status. After a EPP transfer, it should be true that + * 1) the TIMEOUT bit (SPP_STR.0) is clear + * 2) the READY bit (SPP_STR.7) is set + * returns 1 if okay + */ +static int ex_ppa_check_epp_status(int minor) +{ + char r; + inb(STAT); + r = inb(STAT); + + if (r & 1) { + outb(r, STAT); + outb(r&0xfe, STAT); + printk("timed out on port 0x%04x\n", BASE); + return 0; + } + if (!(r & 0x80)) { + return 0; + } + return 1; +} + + +Local int pcf_read (int minor, int adr) +{ + int ret; + a0(adr); + ex_ppa_check_epp_status(minor); + ret = inb(lpPort(4)); + return ret; +} + + +Local void pcf_write (int minor, int adr,char data) +{ + a0(adr); + ex_ppa_check_epp_status(minor); + outb(data,lpPort(4)); +} + + +Local int pcf_init (int minor) +{ + char ctrl,ecr; /* parallel port registers */ + + if (check_region(i2c_table[minor].base, 8) < 0 ) { + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 8, "i2c (pcf-lp)"); + } + + /* This is the standard detection algorithm for ECP ports */ + /* now detect, what kind of parallel port we have: */ + ecr = inb(i2c_table[minor].base+0x402); + ctrl= inb(i2c_table[minor].base+0x002); + if (ecr!=ctrl) { /* Okay, we seem to have an extended port :) */ + if (check_region(i2c_table[minor].base + 0x400, 8) < 0 ) { + DEBE(printk( "i2c_init, port %#x: ext. ports already in use " + "using only standard driver.\n", + i2c_table[minor].base)); + return -ENODEV; + } else { + if ( (ecr & 0x03) != 0x01 ) { /* FIFO empty? */ + printk("FIFO not empty\n"); + return -ENODEV; + } + + outb(0x34,i2c_table[minor].base+0x402); + if (inb(i2c_table[minor].base+0x402) != 0x35 ){ /* bits 0,1 readonly */ + printk("bits 0,1 not rd/only"); + return -ENODEV; + } + /* Okay, now we should have an ECP-capable printer port */ + printk("i2c%d: ECP parallel port.\n",minor); + request_region(i2c_table[minor].base+0x0400, 3 , + "i2c (Extended Parallel port adapter)"); + outb(0x94 ,lpPort(0x402)); /* EPP mode */ + ctrl = inb(CTRL); + + if (i2c_table[minor].irq > 0){ /* enable int. */ + outb(inb(lpPort(0x402)|0x10), lpPort(0x402)); + outb(ctrl|0x10,CTRL); + } + + ex_ppa_check_epp_status(minor); + printk("status reads %2x\n",pcf_read(minor,1)); + ex_ppa_check_epp_status(minor); + printk("data reads %2x\n",pcf_read(minor,0)); + } + } else { + printk("i2c%d: Need EPP port. Sorry.\n",minor); + return -ENODEV; + } + /* Okay, now it's time to init my data structures */ + + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base, 8 ); + release_region( i2c_table[minor].base + 0x0400, 3 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_lp_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif diff --git a/i2c/old-code/pcf8584.h b/i2c/old-code/pcf8584.h new file mode 100644 index 00000000..b3db0403 --- /dev/null +++ b/i2c/old-code/pcf8584.h @@ -0,0 +1,72 @@ +/* -------------------------------------------------------------------- */ +/* PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 19996 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: pcf8584.h,v 1.1 1998/07/29 08:09:51 i2c Exp $ + * $Log: pcf8584.h,v $ + * Revision 1.1 1998/07/29 08:09:51 i2c + * Initial revision + * + */ + + +/* ----- Control register bits ---------------------------------------- */ +#define PCF_PIN 0x80 +#define PCF_ESO 0x40 +#define PCF_ES1 0x20 +#define PCF_ES2 0x10 +#define PCF_ENI 0x08 +#define PCF_STA 0x04 +#define PCF_STO 0x02 +#define PCF_ACK 0x01 + +/* ----- Status register bits ----------------------------------------- */ +/*#define PCF_PIN 0x80 as above*/ + +#define PCF_INI 0x40 /* 1 if not initialized */ +#define PCF_STS 0x20 +#define PCF_BER 0x10 +#define PCF_AD0 0x08 +#define PCF_LRB 0x08 +#define PCF_AAS 0x04 +#define PCF_LAB 0x02 +#define PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define PCF_CLK3 0x00 +#define PCF_CLK443 0x10 +#define PCF_CLK6 0x14 +#define PCF_CLK8 0x18 +#define PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define PCF_TRNS90 0x00 /* 90 kHz */ +#define PCF_TRNS45 0x01 /* 45 kHz */ +#define PCF_TRNS11 0x02 /* 11 kHz */ +#define 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 PCF_OWNADR 0 +#define PCF_INTREG PCF_ES2 +#define PCF_CLKREG PCF_ES1 + +