Fixed documentation thanks to David Hubbard ============ Added support for the MAX7311 i2c io port expanders. This driver is based on the pcf8574 driver which supports the same kind of chip. Signed off by Walter Goossens <waltergoossens at home.nl> diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/Kconfig linux-2.6.17-mpe04/drivers/i2c/chips/Kconfig --- linux-2.6.17/drivers/i2c/chips/Kconfig 2006-06-18 01:49:35.000000000 +0000 +++ linux-2.6.17-mpe04/drivers/i2c/chips/Kconfig 2006-10-18 11:00:05.000000000 +0000 @@ -117,4 +117,23 @@ This driver can also be built as a module. If so, the module will be called max6875. +config SENSORS_MAX7311 + tristate "Maxim MAX7311 16 port io expander" + depends on I2C + help + If you say yes here you will get support for the max7311 port + expander chip. This chip has 16 ioports each configurable for + input or output. + + This driver can also be built as a module. If so, the module + will be called max7311. +config SENSORS_MAX7311_NUMCLIENTS + int "Maximum number of chips" + default "4" + depends on SENSORS_MAX7311 +config SENSORS_MAX7311_ADDRESSES + string "Addresses to scan" + default "0x20 0x24" + depends on SENSORS_MAX7311 + endmenu diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/Makefile linux-2.6.17-mpe04/drivers/i2c/chips/Makefile --- linux-2.6.17/drivers/i2c/chips/Makefile 2006-06-18 01:49:35.000000000 +0000 +++ linux-2.6.17-mpe04/drivers/i2c/chips/Makefile 2006-10-18 10:00:29.000000000 +0000 @@ -6,6 +6,7 @@ obj-$(CONFIG_SENSORS_DS1374) += ds1374.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o +obj-$(CONFIG_SENSORS_MAX7311) += max7311.o obj-$(CONFIG_SENSORS_M41T00) += m41t00.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o diff -uarN -x '*.o' -x '*.ko' linux-2.6.17/drivers/i2c/chips/max7311.c linux-2.6.17-mpe04/drivers/i2c/chips/max7311.c --- linux-2.6.17/drivers/i2c/chips/max7311.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-mpe04/drivers/i2c/chips/max7311.c 2006-10-18 11:27:59.000000000 +0000 @@ -0,0 +1,352 @@ +/* + max7311.c + Copyright (c) 2006 Walter Goossens <walter.goossens at axon.tv> + + Based on: + + pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2000 Frodo Looijaard <frodol at dds.nl>, + Philip Edelbrock <phil at netroedge.com>, + Dan Eaton <dan.eaton at rocketlogix.com> + Ported to Linux 2.6 by Aurelien Jarno <aurel32 at debian.org> with + the help of Jean Delvare <khali at linux-fr.org> + + 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 <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/list.h> + +/* Addresses to scan */ +static unsigned short normal_i2c[CONFIG_SENSORS_MAX7311_NUMCLIENTS]; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD; + +/* Initial values */ +#define MAX7311_INIT 0xFFFF /* All ports in input */ +/* Chip registers */ +#define REG_INPUT 0x00 +#define REG_OUTPUT 0x02 +#define REG_POLARITY 0x04 +#define REG_CONFIG 0x06 +#define REG_TIMEOUT 0x08 + +/* Keep a list of all registered clients */ +static LIST_HEAD(max7311_clients); + +/* Each client has this additional data */ +struct max7311_data { + struct i2c_client client; + + uint16_t output; + uint16_t config; + uint16_t polarity; + struct list_head list; +}; + + +static int max7311_attach_adapter(struct i2c_adapter *adapter); +static int max7311_detect(struct i2c_adapter *adapter, int address, int kind); +static int max7311_detach_client(struct i2c_client *client); +static void max7311_init_client(struct i2c_client *client); + +/* This is the driver that will be inserted */ +static struct i2c_driver max7311_driver = { + .driver = { + .name = "max7311", + }, + .id = I2C_DRIVERID_MAX7311, + .attach_adapter = max7311_attach_adapter, + .detach_client = max7311_detach_client, +}; + +/* following are the sysfs callback functions */ +static ssize_t show_read(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%u\n", i2c_smbus_read_word_data(client,REG_INPUT)); +} + +static DEVICE_ATTR(read, S_IRUGO, show_read, NULL); + +static ssize_t show_write(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%u\n", data->output); +} + +static ssize_t set_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max7311_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + if (val > 0xFFFF) + { + return -EINVAL; + } + data->output = val; + i2c_smbus_write_word_data(client, REG_OUTPUT,data->output); + return count; +} + +static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write); + +static ssize_t show_config(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%u\n", data->config); +} + +static ssize_t set_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max7311_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + if (val > 0xFFFF) + { + return -EINVAL; + } + data->config = val; + i2c_smbus_write_word_data(client, REG_CONFIG,data->config); + return count; +} + +static DEVICE_ATTR(config, S_IWUSR | S_IRUGO, show_config, set_config); + +static ssize_t show_polarity(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct max7311_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%u\n", data->polarity); +} + +static ssize_t set_polarity(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max7311_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + if (val > 0xFFFF) + { + return -EINVAL; + } + data->polarity = val; + i2c_smbus_write_word_data(client, REG_CONFIG,data->polarity); + return count; +} + +static DEVICE_ATTR(polarity, S_IWUSR | S_IRUGO, show_polarity, set_polarity); + +/* + * Real code + */ + +static int max7311_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, max7311_detect); +} + +/* This function is called by i2c_probe */ +static int max7311_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct max7311_data *data; + int err = 0; + /* Can out adapter suply everything we need ? */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE|I2C_FUNC_SMBUS_BYTE_DATA)) + { + return -1; + } + + /* 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 = kzalloc(sizeof(struct max7311_data), GFP_KERNEL))) { + err = -ENOMEM; + return err; + } + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &max7311_driver; + new_client->flags = 0; + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, "max7311", I2C_NAME_SIZE); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + { + kfree(data); + return err; + } + /* Initialize the PCF8574 chip */ + max7311_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_read); + device_create_file(&new_client->dev, &dev_attr_write); + device_create_file(&new_client->dev, &dev_attr_config); + device_create_file(&new_client->dev, &dev_attr_polarity); + return 0; +} + +static int max7311_detach_client(struct i2c_client *client) +{ + int err; + struct max7311_data *data = i2c_get_clientdata(client); + + if ((err = i2c_detach_client(client))) + { + return err; + } + list_del(&data->list); + kfree(data); + return 0; +} + +/* Called when we have found a new MAX7311. */ +static void max7311_init_client(struct i2c_client *client) +{ + struct max7311_data *data = i2c_get_clientdata(client); + //Default all ports to inputs! + data->config = i2c_smbus_read_word_data(client,REG_CONFIG); + data->output = i2c_smbus_read_word_data(client,REG_OUTPUT); + data->polarity = i2c_smbus_read_word_data(client,REG_POLARITY); + //Enable bus timeout + i2c_smbus_write_byte_data(client,REG_TIMEOUT,0x01); + //Register with our list + INIT_LIST_HEAD(&data->list); + list_add_tail(&data->list, &max7311_clients); +} + +struct max7311_data * get_max7311_client(int bus, int address) +{ + struct list_head *walk; + struct list_head *tmp; + struct max7311_data *data; + + list_for_each_safe(walk, tmp, &max7311_clients) { + data = list_entry(walk, struct max7311_data, list); + if ((data->client.adapter->nr == bus)&&(data->client.addr==address)) + return data; + } + return NULL; +} + +static int do_max7311_write(int bus, int address, int chipReg, int newData) +{ + struct max7311_data *data; + if((data=get_max7311_client(bus,address))) + { + switch(chipReg) { + case REG_OUTPUT: + { + data->output = newData; + return i2c_smbus_write_word_data(&data->client,REG_OUTPUT,newData); + } break; + case REG_POLARITY: + { + data->polarity = newData; + return i2c_smbus_write_word_data(&data->client,REG_POLARITY,newData); + } break; + case REG_CONFIG: + { + data->config = newData; + return i2c_smbus_write_word_data(&data->client,REG_CONFIG,newData); + } + default: + { + return -1; + } + } + } else { + return -ENODEV; + } +} +static int do_max7311_read(int bus, int address, int chipReg) +{ + struct max7311_data *data; + if((data=get_max7311_client(bus,address))) + { + switch(chipReg) { + case REG_INPUT: + { + return i2c_smbus_read_word_data(&data->client,REG_INPUT); + } break; + case REG_OUTPUT: + { + return data->output; + } break; + case REG_POLARITY: + { + return data->polarity; + } break; + case REG_CONFIG: + { + return data->config; + } + default: + { + return -1; + } + } + } else { + return -ENODEV; + } +} + +static int __init max7311_init(void) +{ + int i; + unsigned short addr; + char *all_addr = CONFIG_SENSORS_MAX7311_ADDRESSES; + for(i=0; i<CONFIG_SENSORS_MAX7311_NUMCLIENTS; i++) { + addr = simple_strtoul(all_addr,&all_addr,16); + if(all_addr[0]!='\0') { + all_addr++; + } + if(addr) + { + normal_i2c[i] = addr; + } else { + normal_i2c[i] = I2C_CLIENT_END; + } + } + return i2c_add_driver(&max7311_driver); +} + +static void __exit max7311_exit(void) +{ + i2c_del_driver(&max7311_driver); +} + + +MODULE_AUTHOR("Walter Goossens <walter.goossens at axon.tv>"); +MODULE_DESCRIPTION("MAX7311 driver"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(do_max7311_read); +EXPORT_SYMBOL(do_max7311_write); + +module_init(max7311_init); +module_exit(max7311_exit);