From:        Aurelien Jarno <aurelien@aurel32.net>

Hello,

I have to use the pcf8574 chip with my I2C interface, however, the pcf8574
drivers in lm-sensors is a bit basic. It supposes the pin P1 is used as an
input and the others pin as outputs.

Attached is a patch which corrects that, providing to files in /proc
interface, one for writting, the other for reading. It also modifies a bit
the driver to support the pcf8574a (it is a pcf8574 with a different address
range), and add a documentation file in doc/chips.

The patch also includes some cosmetic changes in the pcf8591 driver I wrote
previously, that is to say it modify the chip name in some places I had
forgotten to change when I made a copy/paste from another driver.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1318 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Mark D. Studebaker committed 2002-02-10 23:15:12 +00:00
1 parent 877bf15087
commit 0442f73da9
6 files changed
+161 -29

No files matched your search

+4
View File
@@ -18,6 +18,7 @@ ask CVS about it:
-----------------------------------------------------------------------------
2.6.3 (2002????)
Chip modules (most): Allow THIS_MODULE definition for kernels 2.2.18+
File doc/chips/it87: add more temp_type help
File sensors.conf.eg: Un-ignore it87 in8, temp3, fan3;
add adm1021 section; add lm87 AIN[1,2] template;
@@ -34,11 +35,14 @@ ask CVS about it:
Module i2c-amd756: Add support for AMD768
Module i2c-i810: Add support for 810E
Module i2c-piix4: Check for uninitialized base address
Module i2c-sis5595: Blacklist 735
Module i2c-viapro: Check for uninitialized base address
Module gl518sm: Standardize /proc entries in0-3
Module lm87: Fix in0, in1, in5 initial limits; ain[1,2] -> in[6,7];
fan -> fan1; fix temp2 limit writes
Module pcf8574: Add support for PCF8574A; /proc interface changed
Module pcf8591: new
Module sis5595: Blacklist 735
Module w83781d: Add W83697HF support; allow force_subclients parameter
for Tyan 2460
Program decode-vaio.pl: new
+1 -1
View File
@@ -162,7 +162,7 @@ ltc1710
matorb
Matrix Orbital LCD displays
pcf8574
pcf8574 & pcf8574a
Simple eight-bit parallel I/O
pcf8591
+97
View File
@@ -0,0 +1,97 @@
Kernel driver `pcf8574.o'
========================
Status: Status: Complete;
Supported chips:
* Philips PCF8574
Prefix: `pcf8574'
Addresses scanned: I2C 0x20 - 0x27 (inclusive)
Datasheet: Publicly available at the Philips Semiconductor website
http://semiconductors.philips.com/
* Philips PCF8574A
Prefix: `pcf8574a'
Addresses scanned: I2C 0x38 - 0x3f (inclusive)
Datasheet: Publicly available at the Philips Semiconductor website
http://semiconductors.philips.com/
Authors: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>,
and Dan Eaton <dan.eaton@rocketlogix.com>
Module Parameters
-----------------
* force: short array (min = 1, max = 48)
List of adapter,address pairs to boldly assume to be present
* force_pcf8574: short array (min = 1, max = 48)
List of adapter,address pairs which are unquestionably assumed to
contain a `pcf8574' chip
* ignore: short array (min = 1, max = 48)
List of adapter,address pairs not to scan
* ignore_range: short array (min = 1, max = 48)
List of adapter,start-addr,end-addr triples not to scan
* probe: short array (min = 1, max = 48)
List of adapter,address pairs to scan additionally
* probe_range: short array (min = 1, max = 48)
List of adapter,start-addr,end-addr triples to scan additionally
Description
-----------
The PCF8574(A) is an 8-bit I/O expander for the I2C bus produced by Philips
Semiconductors. It is designed to provide a byte I2C interface to up to 16
separate devices (8 x PCF8574 and 8 x PCF8574A).
This device consists of a quasi-bidirectionnal port. Each of the eight I/Os
can be independently used as an input or output. To setup an I/O as an
input, you have to write a 1 to the corresponding output.
For more informations see the datasheet.
Accessing PCF8574(A) via /proc interface
-------------------------------------
! Be careful !
The PCF8574(A) is plainly impossible to detect ! Stupid chip.
So every chip with adress in the interval [20..27] and (38..3f) are
detected as PCF8574(A). If you have other chips in this address
range, the workaround is to load this module after the one
for your others chips.
On detection (i.e. insmod, modprobe et al.), directories are being
created for each detected PCF8574(A):
/proc/sys/dev/sensors/pcf8574-<0>-<1>/
where <0> is the bus the chip was detected on (e. g. i2c-0)
and <1> the chip address ([20..27] or [38..3f]):
./pcf8574-i2c-0-27/
Inside these directories, there are two files each:
read and write.
The read file is read-only. Reading gives you the current I/O input
if the corresponding output is set as 1, otherwise the current output
value, that is to say 0.
The write file is read/write. Writing a value outputs it on the I/O
port. Reading return the last written value.
On module initialization the chip is configured as eight I/Os input (all
outputs to 1), so you can connect any circuit to the PCF8574(A) without
to be afraid of short-circuit.
Chip Features
-------------
Chip `pcf8574'
LABEL LABEL CLASS COMPUTE CLASS ACCESS MAGNITUDE
read NONE NONE R 0
write NONE NONE RW 0
LABEL FEATURE SYMBOL SYSCTL FILE:OFFSET
read SENSORS_PCF8591_READ read:1
write SENSORS_PCF8591_WRITE write:1
+55 -25
View File
@@ -61,19 +61,19 @@ MODULE_LICENSE("GPL");
/* Addresses to scan */
static unsigned short normal_i2c[] = { SENSORS_I2C_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x27, SENSORS_I2C_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x27, 0x38, 0x3f, SENSORS_I2C_END };
static unsigned int normal_isa[] = { SENSORS_ISA_END };
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(pcf8574);
SENSORS_INSMOD_2(pcf8574, pcf8574a);
/* The PCF8574 registers */
/* (No registers. [Wow! This thing is SIMPLE!] ) */
/* Initial values */
#define PCF8574_INIT 0 /* Both off */
#define PCF8574_INIT 255 /* All outputs on (input mode) */
/* Each client has this additional data */
struct pcf8574_data {
@@ -83,7 +83,7 @@ struct pcf8574_data {
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 status; /* Register values */
u8 read, write; /* Register values */
};
#ifdef MODULE
@@ -106,10 +106,12 @@ static int pcf8574_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void pcf8574_inc_use(struct i2c_client *client);
static void pcf8574_dec_use(struct i2c_client *client);
static void pcf8574_status(struct i2c_client *client, int operation,
static void pcf8574_read(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pcf8574_write(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pcf8574_update_client(struct i2c_client *client);
static void pcf8574_init_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver pcf8574_driver = {
@@ -129,8 +131,10 @@ static struct i2c_driver pcf8574_driver = {
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table pcf8574_dir_table_template[] = {
{PCF8574_SYSCTL_STAT, "status", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &pcf8574_status},
{PCF8574_SYSCTL_READ, "read", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &pcf8574_read},
{PCF8574_SYSCTL_WRITE, "write", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &pcf8574_write},
{0}
};
@@ -190,13 +194,22 @@ int pcf8574_detect(struct i2c_adapter *adapter, int address,
/* Now, we would do the remaining detection. But the PCF8574 is plainly
impossible to detect! Stupid chip. */
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = pcf8574;
/* Determine the chip type */
if (kind <= 0) {
if (address >= 0x20 && address <= 0x27)
kind = pcf8574;
else if (address >= 0x38 && address <= 0x3f)
kind = pcf8574a;
else goto ERROR1;
}
if (kind == pcf8574) {
type_name = "pcf8574";
client_name = "PCF8574 chip";
} else
if (kind == pcf8574a) {
type_name = "pcf8574a";
client_name = "PCF8574A chip";
} else {
#ifdef DEBUG
printk("pcf8574.o: Internal error: unknown kind (%d)?!?",
@@ -225,6 +238,8 @@ int pcf8574_detect(struct i2c_adapter *adapter, int address,
}
data->sysctl_id = i;
/* Initialize the PCF8574 chip */
pcf8574_init_client(new_client);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
@@ -281,6 +296,14 @@ void pcf8574_dec_use(struct i2c_client *client)
#endif
}
/* Called when we have found a new PCF8574. */
void pcf8574_init_client(struct i2c_client *client)
{
struct pcf8574_data *data = client->data;
data->write = PCF8574_INIT;
i2c_smbus_write_byte(client, data->write);
}
void pcf8574_update_client(struct i2c_client *client)
{
@@ -295,7 +318,7 @@ void pcf8574_update_client(struct i2c_client *client)
printk("Starting pcf8574 update\n");
#endif
data->status = i2c_smbus_read_byte(client);
data->read = i2c_smbus_read_byte(client);
data->last_updated = jiffies;
data->valid = 1;
}
@@ -304,7 +327,19 @@ void pcf8574_update_client(struct i2c_client *client)
}
void pcf8574_status(struct i2c_client *client, int operation,
void pcf8574_read(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pcf8574_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
pcf8574_update_client(client);
results[0] = data->read;
*nrels_mag = 1;
}
}
void pcf8574_write(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
u8 tmpstatus;
@@ -313,22 +348,17 @@ void pcf8574_status(struct i2c_client *client, int operation,
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
pcf8574_update_client(client);
tmpstatus = data->status;
tmpstatus = (tmpstatus & 0xf0) >> 4;
results[0] = tmpstatus;
tmpstatus = data->status;
tmpstatus &= 0x0f;
results[1] = tmpstatus;
*nrels_mag = 2;
results[0] = data->write;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->status = (data->status & 2) | results[0];
i2c_smbus_write_byte(client, data->status);
if (*nrels_mag = 1) {
data->write = results[0];
i2c_smbus_write_byte(client, data->write);
}
}
}
int __init sensors_pcf8574_init(void)
{
int res;
@@ -366,7 +396,7 @@ EXPORT_NO_SYMBOLS;
#ifdef MODULE
MODULE_AUTHOR
("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com and Dan Eaton <dan.eaton@rocketlogix.com>");
("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Dan Eaton <dan.eaton@rocketlogix.com> and Aurelien Jarno <aurelien@aurel32.net>");
MODULE_DESCRIPTION("PCF8574 driver");
int init_module(void)
+2 -2
View File
@@ -200,7 +200,7 @@ int pcf8591_detect(struct i2c_adapter *adapter, int address,
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
printk
(KERN_ERR "pcf8574.o: pcf8574_detect called for an ISA bus adapter?!?\n");
(KERN_ERR "pcf8591.o: pcf8591_detect called for an ISA bus adapter?!?\n");
return 0;
}
#endif
@@ -224,7 +224,7 @@ int pcf8591_detect(struct i2c_adapter *adapter, int address,
new_client->driver = &pcf8591_driver;
new_client->flags = 0;
/* Now, we would do the remaining detection. But the PCF8574 is plainly
/* Now, we would do the remaining detection. But the PCF8591 is plainly
impossible to detect! Stupid chip. */
/* Determine the chip type - only one kind supported! */
+2 -1
View File
@@ -431,7 +431,8 @@
#define LM87_ALARM_TEMP2_FAULT 0x4000
#define LM87_ALARM_TEMP3_FAULT 0x08000
#define PCF8574_SYSCTL_STAT 1000
#define PCF8574_SYSCTL_READ 1000
#define PCF8574_SYSCTL_WRITE 1001
#define MTP008_SYSCTL_IN0 1000 /* Volts * 100 */
#define MTP008_SYSCTL_IN1 1001