Skip to contentSkip to conversation

PCA9539 - new driver

PATCH New [email protected] 3 participants 5 messages May 31, 2005 – Jun 3, 2005

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 PCA9539 - new driver

The PCA9539 is a 16 bit discrete I/O chip, where each of the 16 pins can be configured to be either an input or output. It is connected via the i2c bus.

It's probably not found on any standard motherboard, but it's pretty handy for custom embedded stuff.

The driver exposes the 8 registers (input/output/config/invert for channels 0 & 1) via sysfs.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: pca9539.patch
Type: application/octet-stream
Size: 7539 bytes
Desc: pca9539.patch
Url : http://lists.atrpms.net/pipermail/lm-sensors/attachments/20050531/a0730624/pca9539.obj

#1 lore raw

PATCH 1/1 PCA9539 - new driver

Hello,

I will add some coments into code.

drivers/i2c/chips/Kconfig

Quoted 3 lines

diff -uEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/Kconfig linux-2.6.12-rc4-pca9539/drivers/i2c/chips/Kconfig

drivers/i2c/chips/Kconfig +0 -0

I think we would like to have patch against -mm- series of kernel. Please note that sysfs interface changed a bit and you need to add one parameter

-static ssize_t show_xxx(struct device * dev, char * buf)
+static ssize_t show_xxx(struct device * dev, struct device_attribute *attr, char * buf)

Related info: http://lists.lm-sensors.org/pipermail/lm-sensors/2005-May/012251.html

Quoted 19 lines
drivers/i2c/chips/Kconfig +10 -0
@@ -440,4 +440,14 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called m41t00.
 
+config SENSORS_PCA9539
+	tristate "PCA9539 16-bit digital IO"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for the PCA9539
+	  low power digital I/O with 16 bit.
+          
+	  This driver can also be built as a module.  If so, the module
+	  will be called pca9539.
+
 endmenu

diff -uEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/Makefile linux-2.6.12-rc4-pca9539/drivers/i2c/chips/Makefile

drivers/i2c/chips/Makefile line 44

Quoted 4 lines
drivers/i2c/chips/Makefile +1 -0
@@ -41,6 +41,7 @@
 obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
 obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
+obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o

why not alphabetically? I think PCF8574 also is. Any other here around some other oppinion?

Quoted 67 lines
drivers/i2c/chips/Makefile +0 -0
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG

diff -uEbBN linux-2.6.12-rc4-clean/drivers/i2c/chips/pca9539.c linux-2.6.12-rc4-pca9539/drivers/i2c/chips/pca9539.c

drivers/i2c/chips/pca9539.c +61 -0
@@ -0,0 +1,203 @@
+/*
+    pca9539.c - 16 port digital I/O with interrupt and reset
+    
+    Copyright (C) 2005 Ben Gardner <[email protected]>
+
+    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; version 2 of the License.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x74, 0x75, 0x76, 0x77, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(pca9539);
+
+enum pca9539_cmd
+{
+	PCA9539_INPUT_0 = 0,
+	PCA9539_INPUT_1 = 1,
+	PCA9539_OUTPUT_0 = 2,
+	PCA9539_OUTPUT_1 = 3,
+	PCA9539_INVERT_0 = 4,
+	PCA9539_INVERT_1 = 5,
+	PCA9539_CONFIG_0 = 6,
+	PCA9539_CONFIG_1 = 7
+};
+
+struct pca9539_data {
+	struct i2c_client client;
+	struct semaphore  update_lock;
+};
+
+static int pca9539_attach_adapter(struct i2c_adapter *adapter);
+static int pca9539_detect(struct i2c_adapter *adapter, int address, int kind);
+static int pca9539_detach_client(struct i2c_client *client);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pca9539_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "pca9539",
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= pca9539_attach_adapter,
+	.detach_client	= pca9539_detach_client,
+};
+
+static void pca9539_store_reg(struct device *dev, const char *buf, 
+			      enum pca9539_cmd cmd)
+{
+	struct pca9539_data *data = i2c_get_clientdata(to_i2c_client(dev));
+	u8 val = simple_strtoul(buf, NULL, 0);
+	i2c_smbus_write_byte_data(&data->client, cmd, val);
+}

drivers/i2c/chips/pca9539.c line 71

Quoted 10 lines
drivers/i2c/chips/pca9539.c +10 -0
+
+static ssize_t pca9539_show_reg(struct device *dev, char *buf, enum pca9539_cmd cmd)
+{
+	struct pca9539_data *data = i2c_get_clientdata(to_i2c_client(dev));
+	return sprintf(buf, "%d\n", i2c_smbus_read_byte_data(&data->client, cmd));
+}
+
+/* following are the sysfs callback functions */
+#define show_data(enum_name)						\
+static ssize_t show_##enum_name(struct device *dev, char *buf)		\

I think this place needs to change, see above.

Quoted 5 lines
drivers/i2c/chips/pca9539.c +5 -0
+{									\
+	return pca9539_show_reg(dev, buf, enum_name);			\
+}
+
+show_data(PCA9539_INPUT_0);

drivers/i2c/chips/pca9539.c line 86

Quoted 10 lines
drivers/i2c/chips/pca9539.c +10 -0
+show_data(PCA9539_INPUT_1);
+show_data(PCA9539_OUTPUT_0);
+show_data(PCA9539_OUTPUT_1);
+show_data(PCA9539_INVERT_0);
+show_data(PCA9539_INVERT_1);
+show_data(PCA9539_CONFIG_0);
+show_data(PCA9539_CONFIG_1);
+
+#define store_data(enum_name)						\
+static ssize_t store_##enum_name(struct device *dev, const char *buf, size_t count) \

and here too.

Quoted 17 lines
drivers/i2c/chips/pca9539.c +17 -0
+{									\
+	pca9539_store_reg(dev, buf, enum_name);				\
+	return count;							\
+}
+
+store_data(PCA9539_OUTPUT_0);
+store_data(PCA9539_OUTPUT_1);
+store_data(PCA9539_INVERT_0);
+store_data(PCA9539_INVERT_1);
+store_data(PCA9539_CONFIG_0);
+store_data(PCA9539_CONFIG_1);
+
+/* Define the device attributes */
+#define my_rw_attr(name,enum_name) \
+static DEVICE_ATTR(name,S_IRUGO|S_IWUGO,show_##enum_name,store_##enum_name)
+
+#define my_ro_attr(name,enum_name) \

drivers/i2c/chips/pca9539.c line 113

Quoted 10 lines
drivers/i2c/chips/pca9539.c +10 -0
+static DEVICE_ATTR(name,S_IRUGO,show_##enum_name,NULL)
+
+my_ro_attr(input0,  PCA9539_INPUT_0);
+my_ro_attr(input1,  PCA9539_INPUT_1);
+my_rw_attr(output0, PCA9539_OUTPUT_0);
+my_rw_attr(output1, PCA9539_OUTPUT_1);
+my_rw_attr(invert0, PCA9539_INVERT_0);
+my_rw_attr(invert1, PCA9539_INVERT_1);
+my_rw_attr(config0, PCA9539_CONFIG_0);
+my_rw_attr(config1, PCA9539_CONFIG_1);

Question is if the name chosen are OK? We have PCF8574 driver and it has "read" and "write" names instead of input output. Please can other members say something to this?

Quoted 26 lines
drivers/i2c/chips/pca9539.c +26 -0
+static int pca9539_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, pca9539_detect);
+}
+
+/* This function is called by i2c_detect */
+static int pca9539_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	struct pca9539_data *data;
+	int err = 0;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE
+				     | I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+				     | I2C_FUNC_SMBUS_READ_BYTE_DATA))
+		goto exit;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet. */
+	if (!(data = kmalloc(sizeof(struct pca9539_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(data, 0, sizeof(struct pca9539_data));
+	
+	new_client = &data->client;

drivers/i2c/chips/pca9539.c line 149

Quoted 10 lines
drivers/i2c/chips/pca9539.c +10 -0
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &pca9539_driver;
+	new_client->flags = 0;
+
+	/* Detection: not sure how to do that... 
+	 * possibility: toggle the value read from invert0 and see if input0
+	 * changes polarity.
+	 */

I can try to help you with this a bit. Please can you use i2cdump to dump chip registers?

We can try to look what are the values of non-existing registers like:
1) last valid register read
2) zero
3) ff
4) aliased so reg0 is same value as reg8 etc
5) good would be to check what is read from 0x3e 0x3f 0xfe 0xff regs
because this are often used for manufacturers id and if there is 0x0 or 0xff it will help.

Quoted 6 lines
drivers/i2c/chips/pca9539.c +6 -0
+	strlcpy(new_client->name, "pca9539", I2C_NAME_SIZE);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		goto exit_kfree;
+

drivers/i2c/chips/pca9539.c line 165

Quoted 10 lines
drivers/i2c/chips/pca9539.c +10 -0
+	/* Register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_input0);
+	device_create_file(&new_client->dev, &dev_attr_input1);
+	device_create_file(&new_client->dev, &dev_attr_output0);
+	device_create_file(&new_client->dev, &dev_attr_output1);
+	device_create_file(&new_client->dev, &dev_attr_invert0);
+	device_create_file(&new_client->dev, &dev_attr_invert1);
+	device_create_file(&new_client->dev, &dev_attr_config0);
+	device_create_file(&new_client->dev, &dev_attr_config1);
+	return 0;

If you would read more about sysfs changes newer "size sparing" method of sysfs entries exists.
http://lists.lm-sensors.org/pipermail/lm-sensors/2005-May/012265.html
So if you want to save some bytes of kernel memory you can change it.

Quoted 37 lines
drivers/i2c/chips/pca9539.c +37 -0
+
+exit_kfree:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int pca9539_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	if ((err = i2c_detach_client(client))) {
+		dev_err(&client->dev,
+			"Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static int __init pca9539_init(void)
+{
+	return i2c_add_driver(&pca9539_driver);
+}
+
+static void __exit pca9539_exit(void)
+{
+	i2c_del_driver(&pca9539_driver);
+}
+
+MODULE_AUTHOR("Ben Gardner <[email protected]>");
+MODULE_DESCRIPTION("PCA9539 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pca9539_init);
+module_exit(pca9539_exit);
#2 lore raw

PATCH 1/1 PCA9539 - new driver

In reply to [email protected]

Rudolf,

Thank you for your feedback.

<clip>

Quoted 2 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	/* Detection: not sure how to do that... 
+	 * possibility: toggle the value read from invert0 and see if

input0

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	 * changes polarity.
+	 */

I can try to help you with this a bit. Please can you use i2cdump to dump chip registers?

<clip>

Here's the i2cdump output.
The device only supports 8 registers (0-7), so the output isn't too surprising.

AFAIK, this device isn't likely to be on any common motherboard. Perhaps detection isn't necessary?

Thanks,
Ben

~ # i2cdump
Error: No i2c-bus specified!
Syntax: i2cdump [-y] I2CBUS ADDRESS [MODE] [BANK [BANKREG]]
i2cdump -V
MODE is 'b[yte]', 'w[ord]', 's[mbusblock], 'i[2cblock]',
or 'c[onsecutive byte address mode]' (default b)
Append MODE with 'p' for PEC checking
I2CBUS is an integer
ADDRESS is an integer 0x00 - 0x7f
BANK and BANKREG are for byte and word accesses (default bank 0, reg
0x4e)
BANK is the command for smbusblock accesses (default 0) Installed I2C busses:
i2c-0 unknown CS5535 SMB0
Algorithm unavailable
~ # ls /sys/devices/platform/i2c-0/0-0074/
bus config1 driver input1 invert1
output0
config0 detach_state input0 invert0 name
output1
~ # i2cdump 0 0x74
No size specified (using byte-data access)
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c/0, address 0x74, mode byte Continue? [Y/n] y

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 00 ff ff ff 00 00 ff ff XX XX XX XX XX XX XX XX    ........XXXXXXXX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX    XXXXXXXXXXXXXXXX

~ #

#3 lore raw

PATCH 1/1 PCA9539 - new driver

In reply to [email protected]

Hello,

Quoted 3 lines

Here's the i2cdump output.
The device only supports 8 registers (0-7), so the output isn't too surprising.

I was waiting if "undefined" registers will alias or something like that but it seems that answer is "no".

Quoted 2 lines

AFAIK, this device isn't likely to be on any common motherboard. Perhaps detection isn't necessary?

Maybe Jean can suggest something? We can add it later...

Please fix the driver so it works with -mm- kernels and maybe also the alphabetical issue. Then I think it is quite ready. Please next time include [email protected] in your post, he is the official kernel I2C subsystem maintainer.

If you want you can create some documentation for the driver. Here is the guide:

Common formatting can be found for example here:
http://ftp.sh.cvut.cz/MIRRORS/kernel/linux/kernel/people/gregkh/gregkh-2.6/gregkh-02-i2c/i2c-docs-update-2.patch
We can add documentation in separate patch.
The documentation should reflect the use of syfs files. You can inspire from PCF8574 chip.

Thanks,
regards

Rudolf

#4 lore raw

PATCH 1/1 PCA9539 - new driver

In reply to [email protected]

Hi Ben

Quoted 1 line

Please fix the driver so it works with -mm- kernels and maybe also the alphabetical issue.

Well (and this goes for your all your other -mm submissions too), don't just fix it so it works with mm, fix it to take advantage of why the changes (specifically dynamic sysfs callbacks) are there in the first place.

Review

Quoted 2 lines
(quoted diff) +2 -0
+#define store_data(enum_name)                                                \
+static ssize_t store_##enum_name(struct device *dev, const char *buf, size_t count) \

and here too.

Review

Quoted 4 lines
(quoted diff) +4 -0
+{                                                                    \
+     pca9539_store_reg(dev, buf, enum_name);                         \
+     return count;                                                   \
+}

Things like this are exactly what the change is supposed to rid us of, please see the previous discussion and example patches in this thread:

http://lkml.org/lkml/2005/5/17/75

Thanks,
Yani