From dddc66ffa048fbc842f5de385031f1826649ffc2 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 6 Dec 2009 17:06:16 +0100 Subject: i2c/chips: Move ds1682 to drivers/misc As i2c/chips is deprecated, move ds1682 to a more apropriate location. Build tested. Signed-off-by: Wolfram Sang Acked-by: Grant Likely Signed-off-by: Jean Delvare diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f9618f4..ae4539d 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -6,16 +6,6 @@ menu "Miscellaneous I2C Chip support" -config DS1682 - tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" - depends on EXPERIMENTAL - help - If you say yes here you get support for Dallas Semiconductor - DS1682 Total Elapsed Time Recorder. - - This driver can also be built as a module. If so, the module - will be called ds1682. - config SENSORS_TSL2550 tristate "Taos TSL2550 ambient light sensor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 749cf36..fe0af0f 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,7 +10,6 @@ # * I/O expander drivers go to drivers/gpio # -obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/ds1682.c b/drivers/i2c/chips/ds1682.c deleted file mode 100644 index f3ee4a1..0000000 --- a/drivers/i2c/chips/ds1682.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver - * - * Written by: Grant Likely - * - * Copyright (C) 2007 Secret Lab Technologies Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * The DS1682 elapsed timer recorder is a simple device that implements - * one elapsed time counter, one event counter, an alarm signal and 10 - * bytes of general purpose EEPROM. - * - * This driver provides access to the DS1682 counters and user data via - * the sysfs. The following attributes are added to the device node: - * elapsed_time (u32): Total elapsed event time in ms resolution - * alarm_time (u32): When elapsed time exceeds the value in alarm_time, - * then the alarm pin is asserted. - * event_count (u16): number of times the event pin has gone low. - * eeprom (u8[10]): general purpose EEPROM - * - * Counter registers and user data are both read/write unless the device - * has been write protected. This driver does not support turning off write - * protection. Once write protection is turned on, it is impossible to - * turn it off again, so I have left the feature out of this driver to avoid - * accidental enabling, but it is trivial to add write protect support. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Device registers */ -#define DS1682_REG_CONFIG 0x00 -#define DS1682_REG_ALARM 0x01 -#define DS1682_REG_ELAPSED 0x05 -#define DS1682_REG_EVT_CNTR 0x09 -#define DS1682_REG_EEPROM 0x0b -#define DS1682_REG_RESET 0x1d -#define DS1682_REG_WRITE_DISABLE 0x1e -#define DS1682_REG_WRITE_MEM_DISABLE 0x1f - -#define DS1682_EEPROM_SIZE 10 - -/* - * Generic counter attributes - */ -static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - __le32 val = 0; - int rc; - - dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name); - - /* Read the register */ - rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr, - (u8 *) & val); - if (rc < 0) - return -EIO; - - /* Special case: the 32 bit regs are time values with 1/4s - * resolution, scale them up to milliseconds */ - if (sattr->nr == 4) - return sprintf(buf, "%llu\n", - ((unsigned long long)le32_to_cpu(val)) * 250); - - /* Format the output string and return # of bytes */ - return sprintf(buf, "%li\n", (long)le32_to_cpu(val)); -} - -static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - char *endp; - u64 val; - __le32 val_le; - int rc; - - dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name); - - /* Decode input */ - val = simple_strtoull(buf, &endp, 0); - if (buf == endp) { - dev_dbg(dev, "input string not a number\n"); - return -EINVAL; - } - - /* Special case: the 32 bit regs are time values with 1/4s - * resolution, scale input down to quarter-seconds */ - if (sattr->nr == 4) - do_div(val, 250); - - /* write out the value */ - val_le = cpu_to_le32(val); - rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr, - (u8 *) & val_le); - if (rc < 0) { - dev_err(dev, "register write failed; reg=0x%x, size=%i\n", - sattr->index, sattr->nr); - return -EIO; - } - - return count; -} - -/* - * Simple register attributes - */ -static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show, - ds1682_store, 4, DS1682_REG_ELAPSED); -static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show, - ds1682_store, 4, DS1682_REG_ALARM); -static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show, - ds1682_store, 2, DS1682_REG_EVT_CNTR); - -static const struct attribute_group ds1682_group = { - .attrs = (struct attribute *[]) { - &sensor_dev_attr_elapsed_time.dev_attr.attr, - &sensor_dev_attr_alarm_time.dev_attr.attr, - &sensor_dev_attr_event_count.dev_attr.attr, - NULL, - }, -}; - -/* - * User data attribute - */ -static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - struct i2c_client *client = kobj_to_i2c_client(kobj); - int rc; - - dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n", - buf, off, count); - - if (off >= DS1682_EEPROM_SIZE) - return 0; - - if (off + count > DS1682_EEPROM_SIZE) - count = DS1682_EEPROM_SIZE - off; - - rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off, - count, buf); - if (rc < 0) - return -EIO; - - return count; -} - -static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - struct i2c_client *client = kobj_to_i2c_client(kobj); - - dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n", - buf, off, count); - - if (off >= DS1682_EEPROM_SIZE) - return -ENOSPC; - - if (off + count > DS1682_EEPROM_SIZE) - count = DS1682_EEPROM_SIZE - off; - - /* Write out to the device */ - if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off, - count, buf) < 0) - return -EIO; - - return count; -} - -static struct bin_attribute ds1682_eeprom_attr = { - .attr = { - .name = "eeprom", - .mode = S_IRUGO | S_IWUSR, - }, - .size = DS1682_EEPROM_SIZE, - .read = ds1682_eeprom_read, - .write = ds1682_eeprom_write, -}; - -/* - * Called when a ds1682 device is matched with this driver - */ -static int ds1682_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int rc; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_I2C_BLOCK)) { - dev_err(&client->dev, "i2c bus does not support the ds1682\n"); - rc = -ENODEV; - goto exit; - } - - rc = sysfs_create_group(&client->dev.kobj, &ds1682_group); - if (rc) - goto exit; - - rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); - if (rc) - goto exit_bin_attr; - - return 0; - - exit_bin_attr: - sysfs_remove_group(&client->dev.kobj, &ds1682_group); - exit: - return rc; -} - -static int ds1682_remove(struct i2c_client *client) -{ - sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); - sysfs_remove_group(&client->dev.kobj, &ds1682_group); - return 0; -} - -static const struct i2c_device_id ds1682_id[] = { - { "ds1682", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ds1682_id); - -static struct i2c_driver ds1682_driver = { - .driver = { - .name = "ds1682", - }, - .probe = ds1682_probe, - .remove = ds1682_remove, - .id_table = ds1682_id, -}; - -static int __init ds1682_init(void) -{ - return i2c_add_driver(&ds1682_driver); -} - -static void __exit ds1682_exit(void) -{ - i2c_del_driver(&ds1682_driver); -} - -MODULE_AUTHOR("Grant Likely "); -MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver"); -MODULE_LICENSE("GPL"); - -module_init(ds1682_init); -module_exit(ds1682_exit); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index df1f86b..498ab3e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -246,6 +246,16 @@ config EP93XX_PWM To compile this driver as a module, choose M here: the module will be called ep93xx_pwm. +config DS1682 + tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Dallas Semiconductor + DS1682 Total Elapsed Time Recorder. + + This driver can also be built as a module. If so, the module + will be called ds1682. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f982d2e..b6245e4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o +obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ obj-y += cb710/ diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c new file mode 100644 index 0000000..f3ee4a1 --- /dev/null +++ b/drivers/misc/ds1682.c @@ -0,0 +1,267 @@ +/* + * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver + * + * Written by: Grant Likely + * + * Copyright (C) 2007 Secret Lab Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * The DS1682 elapsed timer recorder is a simple device that implements + * one elapsed time counter, one event counter, an alarm signal and 10 + * bytes of general purpose EEPROM. + * + * This driver provides access to the DS1682 counters and user data via + * the sysfs. The following attributes are added to the device node: + * elapsed_time (u32): Total elapsed event time in ms resolution + * alarm_time (u32): When elapsed time exceeds the value in alarm_time, + * then the alarm pin is asserted. + * event_count (u16): number of times the event pin has gone low. + * eeprom (u8[10]): general purpose EEPROM + * + * Counter registers and user data are both read/write unless the device + * has been write protected. This driver does not support turning off write + * protection. Once write protection is turned on, it is impossible to + * turn it off again, so I have left the feature out of this driver to avoid + * accidental enabling, but it is trivial to add write protect support. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Device registers */ +#define DS1682_REG_CONFIG 0x00 +#define DS1682_REG_ALARM 0x01 +#define DS1682_REG_ELAPSED 0x05 +#define DS1682_REG_EVT_CNTR 0x09 +#define DS1682_REG_EEPROM 0x0b +#define DS1682_REG_RESET 0x1d +#define DS1682_REG_WRITE_DISABLE 0x1e +#define DS1682_REG_WRITE_MEM_DISABLE 0x1f + +#define DS1682_EEPROM_SIZE 10 + +/* + * Generic counter attributes + */ +static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + __le32 val = 0; + int rc; + + dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name); + + /* Read the register */ + rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr, + (u8 *) & val); + if (rc < 0) + return -EIO; + + /* Special case: the 32 bit regs are time values with 1/4s + * resolution, scale them up to milliseconds */ + if (sattr->nr == 4) + return sprintf(buf, "%llu\n", + ((unsigned long long)le32_to_cpu(val)) * 250); + + /* Format the output string and return # of bytes */ + return sprintf(buf, "%li\n", (long)le32_to_cpu(val)); +} + +static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + char *endp; + u64 val; + __le32 val_le; + int rc; + + dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name); + + /* Decode input */ + val = simple_strtoull(buf, &endp, 0); + if (buf == endp) { + dev_dbg(dev, "input string not a number\n"); + return -EINVAL; + } + + /* Special case: the 32 bit regs are time values with 1/4s + * resolution, scale input down to quarter-seconds */ + if (sattr->nr == 4) + do_div(val, 250); + + /* write out the value */ + val_le = cpu_to_le32(val); + rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr, + (u8 *) & val_le); + if (rc < 0) { + dev_err(dev, "register write failed; reg=0x%x, size=%i\n", + sattr->index, sattr->nr); + return -EIO; + } + + return count; +} + +/* + * Simple register attributes + */ +static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show, + ds1682_store, 4, DS1682_REG_ELAPSED); +static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show, + ds1682_store, 4, DS1682_REG_ALARM); +static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show, + ds1682_store, 2, DS1682_REG_EVT_CNTR); + +static const struct attribute_group ds1682_group = { + .attrs = (struct attribute *[]) { + &sensor_dev_attr_elapsed_time.dev_attr.attr, + &sensor_dev_attr_alarm_time.dev_attr.attr, + &sensor_dev_attr_event_count.dev_attr.attr, + NULL, + }, +}; + +/* + * User data attribute + */ +static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + int rc; + + dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n", + buf, off, count); + + if (off >= DS1682_EEPROM_SIZE) + return 0; + + if (off + count > DS1682_EEPROM_SIZE) + count = DS1682_EEPROM_SIZE - off; + + rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off, + count, buf); + if (rc < 0) + return -EIO; + + return count; +} + +static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = kobj_to_i2c_client(kobj); + + dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n", + buf, off, count); + + if (off >= DS1682_EEPROM_SIZE) + return -ENOSPC; + + if (off + count > DS1682_EEPROM_SIZE) + count = DS1682_EEPROM_SIZE - off; + + /* Write out to the device */ + if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off, + count, buf) < 0) + return -EIO; + + return count; +} + +static struct bin_attribute ds1682_eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS1682_EEPROM_SIZE, + .read = ds1682_eeprom_read, + .write = ds1682_eeprom_write, +}; + +/* + * Called when a ds1682 device is matched with this driver + */ +static int ds1682_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&client->dev, "i2c bus does not support the ds1682\n"); + rc = -ENODEV; + goto exit; + } + + rc = sysfs_create_group(&client->dev.kobj, &ds1682_group); + if (rc) + goto exit; + + rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); + if (rc) + goto exit_bin_attr; + + return 0; + + exit_bin_attr: + sysfs_remove_group(&client->dev.kobj, &ds1682_group); + exit: + return rc; +} + +static int ds1682_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); + sysfs_remove_group(&client->dev.kobj, &ds1682_group); + return 0; +} + +static const struct i2c_device_id ds1682_id[] = { + { "ds1682", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1682_id); + +static struct i2c_driver ds1682_driver = { + .driver = { + .name = "ds1682", + }, + .probe = ds1682_probe, + .remove = ds1682_remove, + .id_table = ds1682_id, +}; + +static int __init ds1682_init(void) +{ + return i2c_add_driver(&ds1682_driver); +} + +static void __exit ds1682_exit(void) +{ + i2c_del_driver(&ds1682_driver); +} + +MODULE_AUTHOR("Grant Likely "); +MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver"); +MODULE_LICENSE("GPL"); + +module_init(ds1682_init); +module_exit(ds1682_exit); -- cgit v0.10.2 From 6f7e549f873e9ff10a4a27241db568bf7988ee0c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:17 +0100 Subject: i2c-powermac: Reject unsupported I2C transactions The i2c-powermac driver doesn't support arbitrary multi-message I2C transactions, only SMBus ones. Make it clear by returning an error if a multi-message I2C transaction is attempted. This is better than only processing the first message, because most callers won't recover from the short transaction. Anyone wishing to issue multi-message transactions should use the SMBus API instead of the raw I2C API. Signed-off-by: Jean Delvare Tested-by: Michel Daenzer Acked-by: Benjamin Herrenschmidt Cc: Paul Mackerras diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 3c9d71f..a74f3ba 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -146,6 +146,12 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, int read; int addrdir; + if (num != 1) { + dev_err(&adap->dev, + "Multi-message I2C transactions not supported\n"); + return -EOPNOTSUPP; + } + if (msgs->flags & I2C_M_TEN) return -EINVAL; read = (msgs->flags & I2C_M_RD) != 0; -- cgit v0.10.2 From 02864d58ce102804b148c92e44726d9571d65d1e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:17 +0100 Subject: i2c-powermac: Refactor i2c_powermac_smbus_xfer I wanted to add some error logging to the i2c-powermac driver, but found that it was very difficult due to the way the i2c_powermac_smbus_xfer function is organized. Refactor the code in this function so that each low-level function is only called once. Signed-off-by: Jean Delvare Tested-by: Michel Daenzer Cc: Benjamin Herrenschmidt Cc: Paul Mackerras diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index a74f3ba..dba8e38 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -49,48 +49,38 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, int rc = 0; int read = (read_write == I2C_SMBUS_READ); int addrdir = (addr << 1) | read; + int mode, subsize, len; + u32 subaddr; + u8 *buf; u8 local[2]; - rc = pmac_i2c_open(bus, 0); - if (rc) - return rc; + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) { + mode = pmac_i2c_mode_std; + subsize = 0; + subaddr = 0; + } else { + mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub; + subsize = 1; + subaddr = command; + } switch (size) { case I2C_SMBUS_QUICK: - rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0); + buf = NULL; + len = 0; break; case I2C_SMBUS_BYTE: - rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1); - break; case I2C_SMBUS_BYTE_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1); + buf = &data->byte; + len = 1; break; case I2C_SMBUS_WORD_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; if (!read) { local[0] = data->word & 0xff; local[1] = (data->word >> 8) & 0xff; } - rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2); - if (rc == 0 && read) { - data->word = ((u16)local[1]) << 8; - data->word |= local[0]; - } + buf = local; + len = 2; break; /* Note that these are broken vs. the expected smbus API where @@ -105,28 +95,35 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, * a repeat start/addr phase (but not stop in between) */ case I2C_SMBUS_BLOCK_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block, - data->block[0] + 1); - + buf = data->block; + len = data->block[0] + 1; break; case I2C_SMBUS_I2C_BLOCK_DATA: - rc = pmac_i2c_setmode(bus, read ? - pmac_i2c_mode_combined : - pmac_i2c_mode_stdsub); - if (rc) - goto bail; - rc = pmac_i2c_xfer(bus, addrdir, 1, command, - &data->block[1], data->block[0]); + buf = &data->block[1]; + len = data->block[0]; break; default: - rc = -EINVAL; + return -EINVAL; } + + rc = pmac_i2c_open(bus, 0); + if (rc) + return rc; + + rc = pmac_i2c_setmode(bus, mode); + if (rc) + goto bail; + + rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len); + if (rc) + goto bail; + + if (size == I2C_SMBUS_WORD_DATA && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + bail: pmac_i2c_close(bus); return rc; -- cgit v0.10.2 From d7d838a6ebbab6e786148133b6cf9617144603b4 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:18 +0100 Subject: i2c-powermac: Log errors Log errors when they happen, otherwise we have no idea what went wrong. Signed-off-by: Jean Delvare Tested-by: Michel Daenzer Cc: Benjamin Herrenschmidt Cc: Paul Mackerras diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index dba8e38..5c22d9f 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -108,16 +108,25 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, } rc = pmac_i2c_open(bus, 0); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); return rc; + } rc = pmac_i2c_setmode(bus, mode); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + mode, rc); goto bail; + } rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len); - if (rc) + if (rc) { + dev_err(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, err %d\n", + addrdir >> 1, size, rc); goto bail; + } if (size == I2C_SMBUS_WORD_DATA && read) { data->word = ((u16)local[1]) << 8; @@ -157,12 +166,21 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, addrdir ^= 1; rc = pmac_i2c_open(bus, 0); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); return rc; + } rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); - if (rc) + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + pmac_i2c_mode_std, rc); goto bail; + } rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + if (rc < 0) + dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", addrdir >> 1, + rc); bail: pmac_i2c_close(bus); return rc < 0 ? rc : 1; -- cgit v0.10.2 From 6dfa5ca3c9a35cb395ab1f51e0e500106c85dff4 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:19 +0100 Subject: i2c-powermac: Include the i2c_adapter in struct pmac_i2c_bus Include the i2c_adapter in struct pmac_i2c_bus. This avoids memory fragmentation and allows for several code cleanups. Signed-off-by: Jean Delvare Tested-by: Michel Daenzer Cc: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/pmac_low_i2c.h b/arch/powerpc/include/asm/pmac_low_i2c.h index 131011b..01d7182 100644 --- a/arch/powerpc/include/asm/pmac_low_i2c.h +++ b/arch/powerpc/include/asm/pmac_low_i2c.h @@ -72,11 +72,7 @@ extern int pmac_i2c_get_type(struct pmac_i2c_bus *bus); extern int pmac_i2c_get_flags(struct pmac_i2c_bus *bus); extern int pmac_i2c_get_channel(struct pmac_i2c_bus *bus); -/* i2c layer adapter attach/detach */ -extern void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, - struct i2c_adapter *adapter); -extern void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus, - struct i2c_adapter *adapter); +/* i2c layer adapter helpers */ extern struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus); extern struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 414ca98..345e2da 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,7 @@ struct pmac_i2c_bus struct device_node *busnode; int type; int flags; - struct i2c_adapter *adapter; + struct i2c_adapter adapter; void *hostdata; int channel; /* some hosts have multiple */ int mode; /* current mode */ @@ -1014,25 +1015,9 @@ int pmac_i2c_get_channel(struct pmac_i2c_bus *bus) EXPORT_SYMBOL_GPL(pmac_i2c_get_channel); -void pmac_i2c_attach_adapter(struct pmac_i2c_bus *bus, - struct i2c_adapter *adapter) -{ - WARN_ON(bus->adapter != NULL); - bus->adapter = adapter; -} -EXPORT_SYMBOL_GPL(pmac_i2c_attach_adapter); - -void pmac_i2c_detach_adapter(struct pmac_i2c_bus *bus, - struct i2c_adapter *adapter) -{ - WARN_ON(bus->adapter != adapter); - bus->adapter = NULL; -} -EXPORT_SYMBOL_GPL(pmac_i2c_detach_adapter); - struct i2c_adapter *pmac_i2c_get_adapter(struct pmac_i2c_bus *bus) { - return bus->adapter; + return &bus->adapter; } EXPORT_SYMBOL_GPL(pmac_i2c_get_adapter); @@ -1041,7 +1026,7 @@ struct pmac_i2c_bus *pmac_i2c_adapter_to_bus(struct i2c_adapter *adapter) struct pmac_i2c_bus *bus; list_for_each_entry(bus, &pmac_i2c_busses, link) - if (bus->adapter == adapter) + if (&bus->adapter == adapter) return bus; return NULL; } @@ -1053,7 +1038,7 @@ int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter) if (bus == NULL) return 0; - return (bus->adapter == adapter); + return (&bus->adapter == adapter); } EXPORT_SYMBOL_GPL(pmac_i2c_match_adapter); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 5c22d9f..b58fb70 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -204,19 +204,16 @@ static const struct i2c_algorithm i2c_powermac_algorithm = { static int __devexit i2c_powermac_remove(struct platform_device *dev) { struct i2c_adapter *adapter = platform_get_drvdata(dev); - struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter); int rc; rc = i2c_del_adapter(adapter); - pmac_i2c_detach_adapter(bus, adapter); - i2c_set_adapdata(adapter, NULL); /* We aren't that prepared to deal with this... */ if (rc) printk(KERN_WARNING "i2c-powermac.c: Failed to remove bus %s !\n", adapter->name); platform_set_drvdata(dev, NULL); - kfree(adapter); + memset(adapter, 0, sizeof(*adapter)); return 0; } @@ -261,23 +258,17 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus)); of_node_put(parent); - adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); - if (adapter == NULL) { - printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n"); - return -ENOMEM; - } + adapter = pmac_i2c_get_adapter(bus); platform_set_drvdata(dev, adapter); strcpy(adapter->name, name); adapter->algo = &i2c_powermac_algorithm; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; - pmac_i2c_attach_adapter(bus, adapter); rc = i2c_add_adapter(adapter); if (rc) { printk(KERN_ERR "i2c-powermac: Adapter %s registration " "failed\n", name); - i2c_set_adapdata(adapter, NULL); - pmac_i2c_detach_adapter(bus, adapter); + memset(adapter, 0, sizeof(*adapter)); } printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); -- cgit v0.10.2 From bc6286e50b4a34d8af52132d6e22f85c6c8853a7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:20 +0100 Subject: i2c-powermac: Drop temporary name buffer We no longer need to write the adapter name to a temporary buffer. We can write it directly to the i2c_adapter's name field. This is more efficient. Signed-off-by: Jean Delvare Tested-by: Michel Daenzer Cc: Benjamin Herrenschmidt diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index b58fb70..02b0241 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -224,12 +224,12 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) struct pmac_i2c_bus *bus = dev->dev.platform_data; struct device_node *parent = NULL; struct i2c_adapter *adapter; - char name[32]; const char *basename; int rc; if (bus == NULL) return -EINVAL; + adapter = pmac_i2c_get_adapter(bus); /* Ok, now we need to make up a name for the interface that will * match what we used to do in the past, that is basically the @@ -255,23 +255,22 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) default: return -EINVAL; } - snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus)); + snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename, + pmac_i2c_get_channel(bus)); of_node_put(parent); - adapter = pmac_i2c_get_adapter(bus); platform_set_drvdata(dev, adapter); - strcpy(adapter->name, name); adapter->algo = &i2c_powermac_algorithm; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; rc = i2c_add_adapter(adapter); if (rc) { printk(KERN_ERR "i2c-powermac: Adapter %s registration " - "failed\n", name); + "failed\n", adapter->name); memset(adapter, 0, sizeof(*adapter)); } - printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); if (!strncmp(basename, "uni-n", 5)) { struct device_node *np; -- cgit v0.10.2 From a0c11cdd6a1975fd8d6d186f2e2865a82f3e9bbf Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:21 +0100 Subject: i2c-voodoo3: Delete Superseded by tdfxfb. I2C/DDC support used to live in a separate driver but this caused driver conflicts. Signed-off-by: Jean Delvare Cc: Krzysztof Helt diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index f613df8..5516941 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -419,15 +419,6 @@ Who: Alex Chiang --------------------------- -What: i2c-voodoo3 driver -When: October 2009 -Why: Superseded by tdfxfb. I2C/DDC support used to live in a separate - driver but this caused driver conflicts. -Who: Jean Delvare - Krzysztof Helt - ---------------------------- - What: CONFIG_RFKILL_INPUT When: 2.6.33 Why: Should be implemented in userspace, policy daemon. diff --git a/Documentation/i2c/busses/i2c-voodoo3 b/Documentation/i2c/busses/i2c-voodoo3 deleted file mode 100644 index 62d90a4..0000000 --- a/Documentation/i2c/busses/i2c-voodoo3 +++ /dev/null @@ -1,62 +0,0 @@ -Kernel driver i2c-voodoo3 - -Supported adapters: - * 3dfx Voodoo3 based cards - * Voodoo Banshee based cards - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Ralph Metzler , - Mark D. Studebaker - -Main contact: Philip Edelbrock - -The code is based upon Ralph's test code (he did the hard stuff ;') - -Description ------------ - -The 3dfx Voodoo3 chip contains two I2C interfaces (aka a I2C 'master' or -'host'). - -The first interface is used for DDC (Data Display Channel) which is a -serial channel through the VGA monitor connector to a DDC-compliant -monitor. This interface is defined by the Video Electronics Standards -Association (VESA). The standards are available for purchase at -http://www.vesa.org . - -The second interface is a general-purpose I2C bus. The intent by 3dfx was -to allow manufacturers to add extra chips to the video card such as a -TV-out chip such as the BT869 or possibly even I2C based temperature -sensors like the ADM1021 or LM75. - -Stability ---------- - -Seems to be stable on the test machine, but needs more testing on other -machines. Simultaneous accesses of the DDC and I2C busses may cause errors. - -Supported Devices ------------------ - -Specifically, this driver was written and tested on the '3dfx Voodoo3 AGP -3000' which has a tv-out feature (s-video or composite). According to the -docs and discussions, this code should work for any Voodoo3 based cards as -well as Voodoo Banshee based cards. The DDC interface has been tested on a -Voodoo Banshee card. - -Issues ------- - -Probably many, but it seems to work OK on my system. :') - - -External Device Connection --------------------------- - -The digital video input jumpers give availability to the I2C bus. -Specifically, pins 13 and 25 (bottom row middle, and bottom right-end) are -the I2C clock and I2C data lines, respectively. +5V and GND are probably -also easily available making the addition of extra I2C/SMBus devices easy -to implement. diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e8fe7f1..5f318ce 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -640,22 +640,6 @@ config I2C_TINY_USB This driver can also be built as a module. If so, the module will be called i2c-tiny-usb. -comment "Graphics adapter I2C/DDC channel drivers" - depends on PCI - -config I2C_VOODOO3 - tristate "Voodoo 3 (DEPRECATED)" - depends on PCI - select I2C_ALGOBIT - help - If you say yes to this option, support will be included for the - Voodoo 3 I2C interface. This driver is deprecated and you should - use the tdfxfb driver instead, which additionally provides - framebuffer support. - - This driver can also be built as a module. If so, the module - will be called i2c-voodoo3. - comment "Other I2C/SMBus bus drivers" config I2C_ACORN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ff937ac..302c551 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -61,9 +61,6 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o -# Graphics adapter I2C/DDC channel drivers -obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o - # Other I2C/SMBus bus drivers obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c deleted file mode 100644 index 7663d57..0000000 --- a/drivers/i2c/busses/i2c-voodoo3.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - Copyright (c) 1998, 1999 Frodo Looijaard , - Philip Edelbrock , - Ralph Metzler , and - Mark D. Studebaker - - Based on code written by Ralph Metzler and - Simon 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. -*/ - -/* This interfaces to the I2C bus of the Voodoo3 to gain access to - the BT869 and possibly other I2C devices. */ - -#include -#include -#include -#include -#include -#include -#include - -/* the only registers we use */ -#define REG 0x78 -#define REG2 0x70 - -/* bit locations in the register */ -#define DDC_ENAB 0x00040000 -#define DDC_SCL_OUT 0x00080000 -#define DDC_SDA_OUT 0x00100000 -#define DDC_SCL_IN 0x00200000 -#define DDC_SDA_IN 0x00400000 -#define I2C_ENAB 0x00800000 -#define I2C_SCL_OUT 0x01000000 -#define I2C_SDA_OUT 0x02000000 -#define I2C_SCL_IN 0x04000000 -#define I2C_SDA_IN 0x08000000 - -/* initialization states */ -#define INIT2 0x2 -#define INIT3 0x4 - -/* delays */ -#define CYCLE_DELAY 10 -#define TIMEOUT (HZ / 2) - - -static void __iomem *ioaddr; - -/* The voo GPIO registers don't have individual masks for each bit - so we always have to read before writing. */ - -static void bit_vooi2c_setscl(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= I2C_SCL_OUT; - else - r &= ~I2C_SCL_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static void bit_vooi2c_setsda(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= I2C_SDA_OUT; - else - r &= ~I2C_SDA_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -/* The GPIO pins are open drain, so the pins always remain outputs. - We rely on the i2c-algo-bit routines to set the pins high before - reading the input from other chips. */ - -static int bit_vooi2c_getscl(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); -} - -static int bit_vooi2c_getsda(void *data) -{ - return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); -} - -static void bit_vooddc_setscl(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= DDC_SCL_OUT; - else - r &= ~DDC_SCL_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static void bit_vooddc_setsda(void *data, int val) -{ - unsigned int r; - r = readl(ioaddr + REG); - if (val) - r |= DDC_SDA_OUT; - else - r &= ~DDC_SDA_OUT; - writel(r, ioaddr + REG); - readl(ioaddr + REG); /* flush posted write */ -} - -static int bit_vooddc_getscl(void *data) -{ - return (0 != (readl(ioaddr + REG) & DDC_SCL_IN)); -} - -static int bit_vooddc_getsda(void *data) -{ - return (0 != (readl(ioaddr + REG) & DDC_SDA_IN)); -} - -static int config_v3(struct pci_dev *dev) -{ - unsigned long cadr; - - /* map Voodoo3 memory */ - cadr = dev->resource[0].start; - cadr &= PCI_BASE_ADDRESS_MEM_MASK; - ioaddr = ioremap_nocache(cadr, 0x1000); - if (ioaddr) { - writel(0x8160, ioaddr + REG2); - writel(0xcffc0020, ioaddr + REG); - dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr); - return 0; - } - return -ENODEV; -} - -static struct i2c_algo_bit_data voo_i2c_bit_data = { - .setsda = bit_vooi2c_setsda, - .setscl = bit_vooi2c_setscl, - .getsda = bit_vooi2c_getsda, - .getscl = bit_vooi2c_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT -}; - -static struct i2c_adapter voodoo3_i2c_adapter = { - .owner = THIS_MODULE, - .name = "I2C Voodoo3/Banshee adapter", - .algo_data = &voo_i2c_bit_data, -}; - -static struct i2c_algo_bit_data voo_ddc_bit_data = { - .setsda = bit_vooddc_setsda, - .setscl = bit_vooddc_setscl, - .getsda = bit_vooddc_getsda, - .getscl = bit_vooddc_getscl, - .udelay = CYCLE_DELAY, - .timeout = TIMEOUT -}; - -static struct i2c_adapter voodoo3_ddc_adapter = { - .owner = THIS_MODULE, - .class = I2C_CLASS_DDC, - .name = "DDC Voodoo3/Banshee adapter", - .algo_data = &voo_ddc_bit_data, -}; - -static struct pci_device_id voodoo3_ids[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) }, - { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) }, - { 0, } -}; - -MODULE_DEVICE_TABLE (pci, voodoo3_ids); - -static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int retval; - - retval = config_v3(dev); - if (retval) - return retval; - - /* set up the sysfs linkage to our parent device */ - voodoo3_i2c_adapter.dev.parent = &dev->dev; - voodoo3_ddc_adapter.dev.parent = &dev->dev; - - retval = i2c_bit_add_bus(&voodoo3_i2c_adapter); - if (retval) - return retval; - retval = i2c_bit_add_bus(&voodoo3_ddc_adapter); - if (retval) - i2c_del_adapter(&voodoo3_i2c_adapter); - return retval; -} - -static void __devexit voodoo3_remove(struct pci_dev *dev) -{ - i2c_del_adapter(&voodoo3_i2c_adapter); - i2c_del_adapter(&voodoo3_ddc_adapter); - iounmap(ioaddr); -} - -static struct pci_driver voodoo3_driver = { - .name = "voodoo3_smbus", - .id_table = voodoo3_ids, - .probe = voodoo3_probe, - .remove = __devexit_p(voodoo3_remove), -}; - -static int __init i2c_voodoo3_init(void) -{ - return pci_register_driver(&voodoo3_driver); -} - -static void __exit i2c_voodoo3_exit(void) -{ - pci_unregister_driver(&voodoo3_driver); -} - - -MODULE_AUTHOR("Frodo Looijaard , " - "Philip Edelbrock , " - "Ralph Metzler , " - "and Mark D. Studebaker "); -MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); -MODULE_LICENSE("GPL"); - -module_init(i2c_voodoo3_init); -module_exit(i2c_voodoo3_exit); -- cgit v0.10.2 From 194684e596af4bdaebb424166d94a8aa528edfda Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Sun, 6 Dec 2009 17:06:22 +0100 Subject: i2c: Prevent priority inversion on top of bus lock Low priority thread holding the i2c bus mutex could block higher priority threads to access the bus resulting in unacceptable latencies. Change the mutex type to rt_mutex preventing priority inversion. Tested-by: Peter Ujfalusi Signed-off-by: Mika Kuoppala Signed-off-by: Jean Delvare diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index d7ece13..8d8a00e 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -5,6 +5,7 @@ menuconfig I2C tristate "I2C support" depends on HAS_IOMEM + select RT_MUTEXES ---help--- I2C (pronounce: I-square-C) is a slow serial bus protocol used in many micro controller applications and developed by Philips. SMBus, diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 2965043..d664b4a 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -584,7 +584,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } - mutex_init(&adap->bus_lock); + rt_mutex_init(&adap->bus_lock); /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) @@ -1092,12 +1092,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = mutex_trylock(&adap->bus_lock); + ret = rt_mutex_trylock(&adap->bus_lock); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - mutex_lock_nested(&adap->bus_lock, adap->level); + rt_mutex_lock(&adap->bus_lock); } /* Retry automatically on arbitration loss */ @@ -1109,7 +1109,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } - mutex_unlock(&adap->bus_lock); + rt_mutex_unlock(&adap->bus_lock); return ret; } else { @@ -1913,7 +1913,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC; if (adapter->algo->smbus_xfer) { - mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; @@ -1927,7 +1927,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout)) break; } - mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, command, protocol, data); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 7b40cda..52317fb 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -338,8 +338,7 @@ struct i2c_adapter { void *algo_data; /* data fields that are valid for all devices */ - u8 level; /* nesting level for lockdep */ - struct mutex bus_lock; + struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; @@ -367,7 +366,7 @@ static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data) */ static inline void i2c_lock_adapter(struct i2c_adapter *adapter) { - mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); } /** @@ -376,7 +375,7 @@ static inline void i2c_lock_adapter(struct i2c_adapter *adapter) */ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) { - mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } /*flags for the client struct: */ -- cgit v0.10.2 From da44bdeb95ea75eec263f42f7703bbf14f004f6a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:22 +0100 Subject: i2c: Bus drivers don't have to support I2C_M_REV_DIR_ADDR I2C bus drivers don't have to support I2C_M_REV_DIR_ADDR. It is a deviation from the I2C specification, which only makes sense to implement when really needed. Signed-off-by: Jean Delvare Cc: Ben Dooks diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index a75c75e..5901707 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -56,12 +56,6 @@ iic_cook_addr(struct i2c_msg *msg) if (msg->flags & I2C_M_RD) addr |= 1; - /* - * Read or Write? - */ - if (msg->flags & I2C_M_REV_DIR_ADDR) - addr ^= 1; - return addr; } diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index bbab0e1..ed387ff 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -338,9 +338,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, if (msg->flags & I2C_M_RD) dir = 1; - if (msg->flags & I2C_M_REV_DIR_ADDR) - dir ^= 1; - if (msg->flags & I2C_M_TEN) { drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; drv_data->addr2 = (u32)msg->addr & 0xff; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 02b0241..1c440a7 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -162,8 +162,6 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, return -EINVAL; read = (msgs->flags & I2C_M_RD) != 0; addrdir = (msgs->addr << 1) | read; - if (msgs->flags & I2C_M_REV_DIR_ADDR) - addrdir ^= 1; rc = pmac_i2c_open(bus, 0); if (rc) { -- cgit v0.10.2 From abe38388e50f4d89726fd0c0cceea61563c7026b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:23 +0100 Subject: i2c: Add missing __devinit markers to old i2c adapter drivers These _setup functions are called from _probe so they can be marked __devinit. Signed-off-by: Jean Delvare diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index d108450..8de7d7b 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -138,7 +138,7 @@ static unsigned short ali1535_smba; Note the differences between kernels with the old PCI BIOS interface and newer kernels with the real PCI interface. In compat.h some things are defined to make the transition easier. */ -static int ali1535_setup(struct pci_dev *dev) +static int __devinit ali1535_setup(struct pci_dev *dev) { int retval = -ENODEV; unsigned char temp; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index d627fce..e7e3205 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -131,7 +131,7 @@ MODULE_PARM_DESC(force_addr, static struct pci_driver ali15x3_driver; static unsigned short ali15x3_smba; -static int ali15x3_setup(struct pci_dev *ALI15X3_dev) +static int __devinit ali15x3_setup(struct pci_dev *ALI15X3_dev) { u16 a; unsigned char temp; diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 139f0c7..844569f 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -142,7 +142,7 @@ static void sis5595_write(u8 reg, u8 data) outb(data, sis5595_base + SMB_DAT); } -static int sis5595_setup(struct pci_dev *SIS5595_dev) +static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev) { u16 a; u8 val; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 70ca41e..68cff7a 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -389,7 +389,7 @@ static u32 sis630_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA; } -static int sis630_setup(struct pci_dev *sis630_dev) +static int __devinit sis630_setup(struct pci_dev *sis630_dev) { unsigned char b; struct pci_dev *dummy = NULL; -- cgit v0.10.2 From c7b25a9e96dc89954ae8d8f473f56fae62030f84 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:24 +0100 Subject: i2c: Drop probe, ignore and force module parameters The legacy probe and force module parameters are obsolete now, the same can be achieved using the new_device sysfs interface, which is both more flexible and cheaper (it is implemented by i2c-core rather than replicated in every driver module.) The legacy ignore module parameters can be dropped as well. Ignoring can be done by instantiating a "dummy" device at the problematic address. This is the first step of a huge cleanup to i2c-core's i2c_detect function, i2c.h's I2C_CLIENT_INSMOD* macros, and all drivers that made use of them. Signed-off-by: Jean Delvare diff --git a/Documentation/i2c/old-module-parameters b/Documentation/i2c/old-module-parameters new file mode 100644 index 0000000..8e2b629 --- /dev/null +++ b/Documentation/i2c/old-module-parameters @@ -0,0 +1,44 @@ +I2C device driver binding control from user-space +================================================= + +Up to kernel 2.6.32, many i2c drivers used helper macros provided by + which created standard module parameters to let the user +control how the driver would probe i2c buses and attach to devices. These +parameters were known as "probe" (to let the driver probe for an extra +address), "force" (to forcibly attach the driver to a given device) and +"ignore" (to prevent a driver from probing a given address). + +With the conversion of the i2c subsystem to the standard device driver +binding model, it became clear that these per-module parameters were no +longer needed, and that a centralized implementation was possible. The new, +sysfs-based interface is described in the documentation file +"instantiating-devices", section "Method 4: Instantiate from user-space". + +Below is a mapping from the old module parameters to the new interface. + +Attaching a driver to an I2C device +----------------------------------- + +Old method (module parameters): +# modprobe probe=1,0x2d +# modprobe force=1,0x2d +# modprobe force_=1,0x2d + +New method (sysfs interface): +# echo 0x2d > /sys/bus/i2c/devices/i2c-1/new_device + +Preventing a driver from attaching to an I2C device +--------------------------------------------------- + +Old method (module parameters): +# modprobe ignore=1,0x2f + +New method (sysfs interface): +# echo dummy 0x2f > /sys/bus/i2c/devices/i2c-1/new_device +# modprobe + +Of course, it is important to instantiate the "dummy" device before loading +the driver. The dummy device will be handled by i2c-core itself, preventing +other drivers from binding to it later on. If there is a real device at the +problematic address, and you want another driver to bind to it, then simply +pass the name of the device in question instead of "dummy". diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d664b4a..fdfaebd 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1259,40 +1259,13 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) return -ENOMEM; temp_client->adapter = adapter; - /* Force entries are done first, and are not affected by ignore - entries */ - if (address_data->forces) { - const unsigned short * const *forces = address_data->forces; - int kind; - - for (kind = 0; forces[kind]; kind++) { - for (i = 0; forces[kind][i] != I2C_CLIENT_END; - i += 2) { - if (forces[kind][i] == adap_id - || forces[kind][i] == ANY_I2C_BUS) { - dev_dbg(&adapter->dev, "found force " - "parameter for adapter %d, " - "addr 0x%02x, kind %d\n", - adap_id, forces[kind][i + 1], - kind); - temp_client->addr = forces[kind][i + 1]; - err = i2c_detect_address(temp_client, - kind, driver); - if (err) - goto exit_free; - } - } - } - } - /* Stop here if the classes do not match */ if (!(adapter->class & driver->class)) goto exit_free; /* Stop here if we can't use SMBUS_QUICK */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) { - if (address_data->probe[0] == I2C_CLIENT_END - && address_data->normal_i2c[0] == I2C_CLIENT_END) + if (address_data->normal_i2c[0] == I2C_CLIENT_END) goto exit_free; dev_warn(&adapter->dev, "SMBus Quick command not supported, " @@ -1301,43 +1274,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) goto exit_free; } - /* Probe entries are done second, and are not affected by ignore - entries either */ - for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) { - if (address_data->probe[i] == adap_id - || address_data->probe[i] == ANY_I2C_BUS) { - dev_dbg(&adapter->dev, "found probe parameter for " - "adapter %d, addr 0x%02x\n", adap_id, - address_data->probe[i + 1]); - temp_client->addr = address_data->probe[i + 1]; - err = i2c_detect_address(temp_client, -1, driver); - if (err) - goto exit_free; - } - } - - /* Normal entries are done last, unless shadowed by an ignore entry */ for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) { - int j, ignore; - - ignore = 0; - for (j = 0; address_data->ignore[j] != I2C_CLIENT_END; - j += 2) { - if ((address_data->ignore[j] == adap_id || - address_data->ignore[j] == ANY_I2C_BUS) - && address_data->ignore[j + 1] - == address_data->normal_i2c[i]) { - dev_dbg(&adapter->dev, "found ignore " - "parameter for adapter %d, " - "addr 0x%02x\n", adap_id, - address_data->ignore[j + 1]); - ignore = 1; - break; - } - } - if (ignore) - continue; - dev_dbg(&adapter->dev, "found normal entry for adapter %d, " "addr 0x%02x\n", adap_id, address_data->normal_i2c[i]); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 52317fb..419ab54 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -110,7 +110,7 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, * @driver: Device driver model driver * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection - * @address_data: The I2C addresses to probe, ignore or force (for detect) + * @address_data: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) * * The driver.owner field should be set to the module owner of this driver. @@ -397,9 +397,6 @@ static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) */ struct i2c_client_address_data { const unsigned short *normal_i2c; - const unsigned short *probe; - const unsigned short *ignore; - const unsigned short * const *forces; }; /* Internal numbers to terminate lists */ @@ -613,134 +610,48 @@ union i2c_smbus_data { module_param_array(var, short, &var##_num, 0); \ MODULE_PARM_DESC(var, desc) -#define I2C_CLIENT_MODULE_PARM_FORCE(name) \ -I2C_CLIENT_MODULE_PARM(force_##name, \ - "List of adapter,address pairs which are " \ - "unquestionably assumed to contain a `" \ - # name "' chip") - - #define I2C_CLIENT_INSMOD_COMMON \ -I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan " \ - "additionally"); \ -I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to " \ - "scan"); \ static const struct i2c_client_address_data addr_data = { \ .normal_i2c = normal_i2c, \ - .probe = probe, \ - .ignore = ignore, \ - .forces = forces, \ } -#define I2C_CLIENT_FORCE_TEXT \ - "List of adapter,address pairs to boldly assume to be present" - /* These are the ones you want to use in your own drivers. Pick the one which matches the number of devices the driver differenciates between. */ #define I2C_CLIENT_INSMOD \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -static const unsigned short * const forces[] = { force, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_1(chip1) \ enum chips { any_chip, chip1 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_2(chip1, chip2) \ enum chips { any_chip, chip1, chip2 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_3(chip1, chip2, chip3) \ enum chips { any_chip, chip1, chip2, chip3 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_4(chip1, chip2, chip3, chip4) \ enum chips { any_chip, chip1, chip2, chip3, chip4 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip4); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, \ - force_##chip4, NULL}; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_5(chip1, chip2, chip3, chip4, chip5) \ enum chips { any_chip, chip1, chip2, chip3, chip4, chip5 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip4); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip5); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, \ - force_##chip4, force_##chip5, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_6(chip1, chip2, chip3, chip4, chip5, chip6) \ enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip4); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip5); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip6); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, \ - force_##chip4, force_##chip5, force_##chip6, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_7(chip1, chip2, chip3, chip4, chip5, chip6, chip7) \ enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, \ chip7 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip4); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip5); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip6); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip7); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, \ - force_##chip4, force_##chip5, force_##chip6, \ - force_##chip7, NULL }; \ I2C_CLIENT_INSMOD_COMMON #define I2C_CLIENT_INSMOD_8(chip1, chip2, chip3, chip4, chip5, chip6, chip7, chip8) \ enum chips { any_chip, chip1, chip2, chip3, chip4, chip5, chip6, \ chip7, chip8 }; \ -I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip1); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip2); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip3); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip4); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip5); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip6); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip7); \ -I2C_CLIENT_MODULE_PARM_FORCE(chip8); \ -static const unsigned short * const forces[] = { force, \ - force_##chip1, force_##chip2, force_##chip3, \ - force_##chip4, force_##chip5, force_##chip6, \ - force_##chip7, force_##chip8, NULL }; \ I2C_CLIENT_INSMOD_COMMON #endif /* __KERNEL__ */ #endif /* _LINUX_I2C_H */ -- cgit v0.10.2 From ccfbbd082a1c71667bead7124591ccd09f08ac90 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:25 +0100 Subject: i2c: Simplify i2c_detect_address The kind parameter of i2c_detect_address() always has value -1, so we can get rid of it. Next step is to update all i2c detect callback functions to get rid of this now useless parameter. Signed-off-by: Jean Delvare diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index fdfaebd..37df0f1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1180,7 +1180,7 @@ EXPORT_SYMBOL(i2c_master_recv); * ---------------------------------------------------- */ -static int i2c_detect_address(struct i2c_client *temp_client, int kind, +static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver) { struct i2c_board_info info; @@ -1199,22 +1199,18 @@ static int i2c_detect_address(struct i2c_client *temp_client, int kind, if (i2c_check_addr(adapter, addr)) return 0; - /* Make sure there is something at this address, unless forced */ - if (kind < 0) { - if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, - I2C_SMBUS_QUICK, NULL) < 0) - return 0; + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) + return 0; - /* prevent 24RF08 corruption */ - if ((addr & ~0x0f) == 0x50) - i2c_smbus_xfer(adapter, addr, 0, 0, 0, - I2C_SMBUS_QUICK, NULL); - } + /* Prevent 24RF08 corruption */ + if ((addr & ~0x0f) == 0x50) + i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL); /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); info.addr = addr; - err = driver->detect(temp_client, kind, &info); + err = driver->detect(temp_client, -1, &info); if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ @@ -1279,7 +1275,7 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) "addr 0x%02x\n", adap_id, address_data->normal_i2c[i]); temp_client->addr = address_data->normal_i2c[i]; - err = i2c_detect_address(temp_client, -1, driver); + err = i2c_detect_address(temp_client, driver); if (err) goto exit_free; } -- cgit v0.10.2 From c2e90e9b6835d71cbadd42fe5b38704acaccf8aa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:26 +0100 Subject: ics932s401: Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare Acked-by: "Darrick J. Wong" diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index 6e43ab4..4bb7a3a 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -417,32 +417,25 @@ static int ics932s401_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_word_data(client, - ICS932S401_REG_VENDOR_REV); - vendor >>= 8; - revision = vendor >> ICS932S401_REV_SHIFT; - vendor &= ICS932S401_VENDOR_MASK; - if (vendor != ICS932S401_VENDOR) - return -ENODEV; - - device = i2c_smbus_read_word_data(client, - ICS932S401_REG_DEVICE); - device >>= 8; - if (device != ICS932S401_DEVICE) - return -ENODEV; - - if (revision != ICS932S401_REV) - dev_info(&adapter->dev, "Unknown revision %d\n", - revision); - } else - dev_dbg(&adapter->dev, "detection forced\n"); + vendor = i2c_smbus_read_word_data(client, ICS932S401_REG_VENDOR_REV); + vendor >>= 8; + revision = vendor >> ICS932S401_REV_SHIFT; + vendor &= ICS932S401_VENDOR_MASK; + if (vendor != ICS932S401_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_word_data(client, ICS932S401_REG_DEVICE); + device >>= 8; + if (device != ICS932S401_DEVICE) + return -ENODEV; + + if (revision != ICS932S401_REV) + dev_info(&adapter->dev, "Unknown revision %d\n", revision); strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); -- cgit v0.10.2 From 9669f54194b4df34c96478d696d9ba2b977545f5 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sun, 6 Dec 2009 17:06:26 +0100 Subject: i2c: Remove big kernel lock from i2cdev_open The BKL is held over a kmalloc so cannot protect anything beyond that. The two calls before the kmalloc have their own locking. Improve device open function by removing the now unnecessary ret variable Signed-off-by: Vincent Sanders Signed-off-by: Thomas Gleixner Signed-off-by: Jean Delvare diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 7e13d2d..f4110aa 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -445,20 +444,14 @@ static int i2cdev_open(struct inode *inode, struct file *file) struct i2c_client *client; struct i2c_adapter *adap; struct i2c_dev *i2c_dev; - int ret = 0; - lock_kernel(); i2c_dev = i2c_dev_get_by_minor(minor); - if (!i2c_dev) { - ret = -ENODEV; - goto out; - } + if (!i2c_dev) + return -ENODEV; adap = i2c_get_adapter(i2c_dev->adap->nr); - if (!adap) { - ret = -ENODEV; - goto out; - } + if (!adap) + return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. @@ -470,8 +463,7 @@ static int i2cdev_open(struct inode *inode, struct file *file) client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); - ret = -ENOMEM; - goto out; + return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->driver = &i2cdev_driver; @@ -479,9 +471,7 @@ static int i2cdev_open(struct inode *inode, struct file *file) client->adapter = adap; file->private_data = client; -out: - unlock_kernel(); - return ret; + return 0; } static int i2cdev_release(struct inode *inode, struct file *file) -- cgit v0.10.2 From 7e2193a8f942a80eef4ae87e3850615450966d0a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:27 +0100 Subject: i2c-i801: Retry on lost arbitration The Intel 82801 is sometimes used on systems with a BMC connected. The BMC can access the SMBus, resulting in lost arbitration for the 82801. We should let i2c-core retry transactions for us in this case. Signed-off-by: Jean Delvare diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 55edcfe..df6ab553 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -767,6 +767,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id /* set up the sysfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; + /* Retry up to 3 times on lost arbitration */ + i801_adapter.retries = 3; + snprintf(i801_adapter.name, sizeof(i801_adapter.name), "SMBus I801 adapter at %04lx", i801_smba); err = i2c_add_adapter(&i801_adapter); -- cgit v0.10.2 From 69b0089a6750a0435570df3ba8456c77b352af55 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:27 +0100 Subject: i2c: Refactor for_each callbacks Functions i2c_do_add_adapter() and __attach_adapter() do essentially the same thing, differing only in how the parameters are passed. Same for i2c_do_add_adapter() and __detach_adapter(). Introduce wrappers to normalize the parameters, so that we do not have to duplicate the code. Signed-off-by: Jean Delvare Cc: David Brownell diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 37df0f1..4f34823 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -558,11 +558,9 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) up_read(&__i2c_board_lock); } -static int i2c_do_add_adapter(struct device_driver *d, void *data) +static int i2c_do_add_adapter(struct i2c_driver *driver, + struct i2c_adapter *adap) { - struct i2c_driver *driver = to_i2c_driver(d); - struct i2c_adapter *adap = data; - /* Detect supported devices on that bus, and instantiate them */ i2c_detect(adap, driver); @@ -574,6 +572,11 @@ static int i2c_do_add_adapter(struct device_driver *d, void *data) return 0; } +static int __process_new_adapter(struct device_driver *d, void *data) +{ + return i2c_do_add_adapter(to_i2c_driver(d), data); +} + static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0, dummy; @@ -614,7 +617,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) /* Notify drivers */ mutex_lock(&core_lock); dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, - i2c_do_add_adapter); + __process_new_adapter); mutex_unlock(&core_lock); return 0; @@ -715,10 +718,9 @@ retry: } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); -static int i2c_do_del_adapter(struct device_driver *d, void *data) +static int i2c_do_del_adapter(struct i2c_driver *driver, + struct i2c_adapter *adapter) { - struct i2c_driver *driver = to_i2c_driver(d); - struct i2c_adapter *adapter = data; struct i2c_client *client, *_n; int res; @@ -750,6 +752,11 @@ static int __unregister_client(struct device *dev, void *dummy) return 0; } +static int __process_removed_adapter(struct device_driver *d, void *data) +{ + return i2c_do_del_adapter(to_i2c_driver(d), data); +} + /** * i2c_del_adapter - unregister I2C adapter * @adap: the adapter being unregistered @@ -777,7 +784,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) /* Tell drivers about this removal */ mutex_lock(&core_lock); res = bus_for_each_drv(&i2c_bus_type, NULL, adap, - i2c_do_del_adapter); + __process_removed_adapter); mutex_unlock(&core_lock); if (res) return res; @@ -826,22 +833,11 @@ EXPORT_SYMBOL(i2c_del_adapter); /* ------------------------------------------------------------------------- */ -static int __attach_adapter(struct device *dev, void *data) +static int __process_new_driver(struct device *dev, void *data) { - struct i2c_adapter *adapter; - struct i2c_driver *driver = data; - if (dev->type != &i2c_adapter_type) return 0; - adapter = to_i2c_adapter(dev); - - i2c_detect(adapter, driver); - - /* Legacy drivers scan i2c busses directly */ - if (driver->attach_adapter) - driver->attach_adapter(adapter); - - return 0; + return i2c_do_add_adapter(data, to_i2c_adapter(dev)); } /* @@ -873,40 +869,18 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ mutex_lock(&core_lock); - bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver); mutex_unlock(&core_lock); return 0; } EXPORT_SYMBOL(i2c_register_driver); -static int __detach_adapter(struct device *dev, void *data) +static int __process_removed_driver(struct device *dev, void *data) { - struct i2c_adapter *adapter; - struct i2c_driver *driver = data; - struct i2c_client *client, *_n; - if (dev->type != &i2c_adapter_type) return 0; - adapter = to_i2c_adapter(dev); - - /* Remove the devices we created ourselves as the result of hardware - * probing (using a driver's detect method) */ - list_for_each_entry_safe(client, _n, &driver->clients, detected) { - dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", - client->name, client->addr); - list_del(&client->detected); - i2c_unregister_device(client); - } - - if (driver->detach_adapter) { - if (driver->detach_adapter(adapter)) - dev_err(&adapter->dev, - "detach_adapter failed for driver [%s]\n", - driver->driver.name); - } - - return 0; + return i2c_do_del_adapter(data, to_i2c_adapter(dev)); } /** @@ -917,7 +891,7 @@ static int __detach_adapter(struct device *dev, void *data) void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_removed_driver); mutex_unlock(&core_lock); driver_unregister(&driver->driver); -- cgit v0.10.2 From 4710317891e4824ce1510a6b5066abbd3e917750 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:28 +0100 Subject: i2c-stub: Implement I2C block support This is required to test some drivers, for example at24. Signed-off-by: Jean Delvare diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub index 0d8be1c..d9c383b 100644 --- a/Documentation/i2c/i2c-stub +++ b/Documentation/i2c/i2c-stub @@ -2,9 +2,9 @@ MODULE: i2c-stub DESCRIPTION: -This module is a very simple fake I2C/SMBus driver. It implements four -types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, and -(r/w) word data. +This module is a very simple fake I2C/SMBus driver. It implements five +types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, (r/w) +word data, and (r/w) I2C block data. You need to provide chip addresses as a module parameter when loading this driver, which will then only react to SMBus commands to these addresses. diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index 1b7b2af..52ebeda 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -48,7 +48,7 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data) { s32 ret; - int i; + int i, len; struct stub_chip *chip = NULL; /* Search for the right chip */ @@ -118,6 +118,29 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, ret = 0; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + default: dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); ret = -EOPNOTSUPP; @@ -130,7 +153,8 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, static u32 stub_func(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; } static const struct i2c_algorithm smbus_algorithm = { -- cgit v0.10.2 From 38f41f282f1f88b4038f019de51cb95984e569d5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:29 +0100 Subject: i2c-stub: Allow user to disable some commands Add a module parameter to override the functionality bitfield. This lets the user disable some commands. This can be used to force a chip driver to take different code paths. Signed-off-by: Jean Delvare diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub index d9c383b..326d1ee 100644 --- a/Documentation/i2c/i2c-stub +++ b/Documentation/i2c/i2c-stub @@ -33,6 +33,12 @@ PARAMETERS: int chip_addr[10]: The SMBus addresses to emulate chips at. +unsigned long functionality: + Functionality override, to disable some commands. See I2C_FUNC_* + constants in for the suitable values. For example, + value 0x1f0000 would only enable the quick, byte and byte data + commands. + CAVEATS: If your target driver polls some byte or word waiting for it to change, the diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c index 52ebeda..0c770ea 100644 --- a/drivers/i2c/busses/i2c-stub.c +++ b/drivers/i2c/busses/i2c-stub.c @@ -35,6 +35,10 @@ module_param_array(chip_addr, ushort, NULL, S_IRUGO); MODULE_PARM_DESC(chip_addr, "Chip addresses (up to 10, between 0x03 and 0x77)"); +static unsigned long functionality = ~0UL; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + struct stub_chip { u8 pointer; u16 words[256]; /* Byte operations use the LSB as per SMBus @@ -152,9 +156,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, static u32 stub_func(struct i2c_adapter *adapter) { - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK; + I2C_FUNC_SMBUS_I2C_BLOCK) & functionality; } static const struct i2c_algorithm smbus_algorithm = { -- cgit v0.10.2 From 6471b68982d3bb1a593c3e183c804ecf830125d3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 6 Dec 2009 17:06:30 +0100 Subject: i2c-stub: Documentation update There is nothing sensors-specific to i2c-stub. Signed-off-by: Jean Delvare diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub index 326d1ee..fa4b669 100644 --- a/Documentation/i2c/i2c-stub +++ b/Documentation/i2c/i2c-stub @@ -21,8 +21,8 @@ EEPROMs, among others. The typical use-case is like this: 1. load this module - 2. use i2cset (from lm_sensors project) to pre-load some data - 3. load the target sensors chip driver module + 2. use i2cset (from the i2c-tools project) to pre-load some data + 3. load the target chip driver module 4. observe its behavior in the kernel log There's a script named i2c-stub-from-dump in the i2c-tools package which -- cgit v0.10.2