[PATCH] i2c driver fixes for 2.6.0

From: Greg KH (greg_at_kroah.com)
Date: 12/30/03

  • Next message: Greg KH: "Re: [PATCH] i2c driver fixes for 2.6.0"
    Date:	Tue, 30 Dec 2003 14:06:09 -0800
    To: linux-kernel@vger.kernel.org, sensors@stimpy.netroedge.com
    
    

    ChangeSet 1.1337.6.1, 2003/10/09 13:40:07-07:00, khali@linux-fr.org

    [PATCH] I2C: Add lm83 chip driver

     drivers/i2c/chips/Kconfig | 11 +
     drivers/i2c/chips/Makefile | 1
     drivers/i2c/chips/lm83.c | 413 +++++++++++++++++++++++++++++++++++++++++++++
     include/linux/i2c-id.h | 1
     4 files changed, 426 insertions(+)

    diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
    --- a/drivers/i2c/chips/Kconfig Tue Dec 30 12:33:21 2003
    +++ b/drivers/i2c/chips/Kconfig Tue Dec 30 12:33:21 2003
    @@ -68,6 +68,17 @@
               This driver can also be built as a module. If so, the module
               will be called lm78.
     
    +config SENSORS_LM83
    + tristate "National Semiconductor LM83"
    + depends on I2C && EXPERIMENTAL
    + select I2C_SENSOR
    + help
    + If you say yes here you get support for National Semiconductor
    + LM83 sensor chips.
    +
    + This driver can also be built as a module. If so, the module
    + will be called lm83.
    +
     config SENSORS_LM85
             tristate "National Semiconductors LM85 and compatibles"
             depends on I2C && EXPERIMENTAL
    diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
    --- a/drivers/i2c/chips/Makefile Tue Dec 30 12:33:21 2003
    +++ b/drivers/i2c/chips/Makefile Tue Dec 30 12:33:21 2003
    @@ -10,5 +10,6 @@
     obj-$(CONFIG_SENSORS_IT87) += it87.o
     obj-$(CONFIG_SENSORS_LM75) += lm75.o
     obj-$(CONFIG_SENSORS_LM78) += lm78.o
    +obj-$(CONFIG_SENSORS_LM83) += lm83.o
     obj-$(CONFIG_SENSORS_LM85) += lm85.o
     obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
    diff -Nru a/drivers/i2c/chips/lm83.c b/drivers/i2c/chips/lm83.c
    --- /dev/null Wed Dec 31 16:00:00 1969
    +++ b/drivers/i2c/chips/lm83.c Tue Dec 30 12:33:21 2003
    @@ -0,0 +1,413 @@
    +/*
    + * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
    + * monitoring
    + * Copyright (C) 2003 Jean Delvare <khali@linux-fr.org>
    + *
    + * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
    + * a sensor chip made by National Semiconductor. It reports up to four
    + * temperatures (its own plus up to three external ones) with a 1 deg
    + * resolution and a 3-4 deg accuracy. Complete data*** can be obtained
    + * from National's website at:
    + * http://www.national.com/pf/LM/LM83.html
    + * Since the data*** omits to give the chip stepping code, I give it
    + * here: 0x03 (at register 0xff).
    + *
    + * 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/i2c-sensor.h>
    +
    +/*
    + * Addresses to scan
    + * Address is selected using 2 three-level pins, resulting in 9 possible
    + * addresses.
    + */
    +
    +static unsigned short normal_i2c[] = { I2C_CLIENT_END };
    +static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
    + 0x4c, 0x4e, I2C_CLIENT_END };
    +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
    +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
    +
    +/*
    + * Insmod parameters
    + */
    +
    +SENSORS_INSMOD_1(lm83);
    +
    +/*
    + * The LM83 registers
    + * Manufacturer ID is 0x01 for National Semiconductor.
    + */
    +
    +#define LM83_REG_R_MAN_ID 0xFE
    +#define LM83_REG_R_CHIP_ID 0xFF
    +#define LM83_REG_R_CONFIG 0x03
    +#define LM83_REG_W_CONFIG 0x09
    +#define LM83_REG_R_STATUS1 0x02
    +#define LM83_REG_R_STATUS2 0x35
    +#define LM83_REG_R_LOCAL_TEMP 0x00
    +#define LM83_REG_R_LOCAL_HIGH 0x05
    +#define LM83_REG_W_LOCAL_HIGH 0x0B
    +#define LM83_REG_R_REMOTE1_TEMP 0x30
    +#define LM83_REG_R_REMOTE1_HIGH 0x38
    +#define LM83_REG_W_REMOTE1_HIGH 0x50
    +#define LM83_REG_R_REMOTE2_TEMP 0x01
    +#define LM83_REG_R_REMOTE2_HIGH 0x07
    +#define LM83_REG_W_REMOTE2_HIGH 0x0D
    +#define LM83_REG_R_REMOTE3_TEMP 0x31
    +#define LM83_REG_R_REMOTE3_HIGH 0x3A
    +#define LM83_REG_W_REMOTE3_HIGH 0x52
    +#define LM83_REG_R_TCRIT 0x42
    +#define LM83_REG_W_TCRIT 0x5A
    +
    +/*
    + * Conversions, initial values and various macros
    + * The LM83 uses signed 8-bit values.
    + */
    +
    +#define TEMP_FROM_REG(val) ((val > 127 ? val-256 : val) * 1000)
    +#define TEMP_TO_REG(val) ((val < 0 ? val+256 : val) / 1000)
    +
    +#define LM83_INIT_HIGH 100
    +#define LM83_INIT_CRIT 120
    +
    +static const u8 LM83_REG_R_TEMP[] = {
    + LM83_REG_R_LOCAL_TEMP,
    + LM83_REG_R_REMOTE1_TEMP,
    + LM83_REG_R_REMOTE2_TEMP,
    + LM83_REG_R_REMOTE3_TEMP
    +};
    +
    +static const u8 LM83_REG_R_HIGH[] = {
    + LM83_REG_R_LOCAL_HIGH,
    + LM83_REG_R_REMOTE1_HIGH,
    + LM83_REG_R_REMOTE2_HIGH,
    + LM83_REG_R_REMOTE3_HIGH
    +};
    +
    +static const u8 LM83_REG_W_HIGH[] = {
    + LM83_REG_W_LOCAL_HIGH,
    + LM83_REG_W_REMOTE1_HIGH,
    + LM83_REG_W_REMOTE2_HIGH,
    + LM83_REG_W_REMOTE3_HIGH
    +};
    +
    +/*
    + * Functions declaration
    + */
    +
    +static int lm83_attach_adapter(struct i2c_adapter *adapter);
    +static int lm83_detect(struct i2c_adapter *adapter, int address,
    + int kind);
    +static void lm83_init_client(struct i2c_client *client);
    +static int lm83_detach_client(struct i2c_client *client);
    +static void lm83_update_client(struct i2c_client *client);
    +
    +/*
    + * Driver data (common to all clients)
    + */
    +
    +static struct i2c_driver lm83_driver = {
    + .owner = THIS_MODULE,
    + .name = "lm83",
    + .id = I2C_DRIVERID_LM83,
    + .flags = I2C_DF_NOTIFY,
    + .attach_adapter = lm83_attach_adapter,
    + .detach_client = lm83_detach_client,
    +};
    +
    +/*
    + * Client data (each client gets its own)
    + */
    +
    +struct lm83_data
    +{
    + struct semaphore update_lock;
    + char valid; /* zero until following fields are valid */
    + unsigned long last_updated; /* in jiffies */
    +
    + /* registers values */
    + u8 temp_input[4];
    + u8 temp_high[4];
    + u8 temp_crit;
    + u16 alarms; /* bitvector, combined */
    +};
    +
    +/*
    + * Internal variables
    + */
    +
    +static int lm83_id = 0;
    +
    +/*
    + * Sysfs stuff
    + */
    +
    +#define show_temp(suffix, value) \
    +static ssize_t show_temp_##suffix(struct device *dev, char *buf) \
    +{ \
    + struct i2c_client *client = to_i2c_client(dev); \
    + struct lm83_data *data = i2c_get_clientdata(client); \
    + lm83_update_client(client); \
    + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
    +}
    +show_temp(input1, temp_input[0]);
    +show_temp(input2, temp_input[1]);
    +show_temp(input3, temp_input[2]);
    +show_temp(input4, temp_input[3]);
    +show_temp(high1, temp_high[0]);
    +show_temp(high2, temp_high[1]);
    +show_temp(high3, temp_high[2]);
    +show_temp(high4, temp_high[3]);
    +show_temp(crit, temp_crit);
    +
    +#define set_temp(suffix, value, reg) \
    +static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
    + size_t count) \
    +{ \
    + struct i2c_client *client = to_i2c_client(dev); \
    + struct lm83_data *data = i2c_get_clientdata(client); \
    + data->value = TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
    + i2c_smbus_write_byte_data(client, reg, data->value); \
    + return count; \
    +}
    +set_temp(high1, temp_high[0], LM83_REG_W_LOCAL_HIGH);
    +set_temp(high2, temp_high[1], LM83_REG_W_REMOTE1_HIGH);
    +set_temp(high3, temp_high[2], LM83_REG_W_REMOTE2_HIGH);
    +set_temp(high4, temp_high[3], LM83_REG_W_REMOTE3_HIGH);
    +set_temp(crit, temp_crit, LM83_REG_W_TCRIT);
    +
    +static ssize_t show_alarms(struct device *dev, char *buf)
    +{
    + struct i2c_client *client = to_i2c_client(dev);
    + struct lm83_data *data = i2c_get_clientdata(client);
    + lm83_update_client(client);
    + return sprintf(buf, "%d\n", data->alarms);
    +}
    +
    +static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input1, NULL);
    +static DEVICE_ATTR(temp_input2, S_IRUGO, show_temp_input2, NULL);
    +static DEVICE_ATTR(temp_input3, S_IRUGO, show_temp_input3, NULL);
    +static DEVICE_ATTR(temp_input4, S_IRUGO, show_temp_input4, NULL);
    +static DEVICE_ATTR(temp_max1, S_IWUSR | S_IRUGO, show_temp_high1,
    + set_temp_high1);
    +static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_temp_high2,
    + set_temp_high2);
    +static DEVICE_ATTR(temp_max3, S_IWUSR | S_IRUGO, show_temp_high3,
    + set_temp_high3);
    +static DEVICE_ATTR(temp_max4, S_IWUSR | S_IRUGO, show_temp_high4,
    + set_temp_high4);
    +static DEVICE_ATTR(temp_crit, S_IWUSR | S_IRUGO, show_temp_crit,
    + set_temp_crit);
    +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
    +
    +/*
    + * Real code
    + */
    +
    +static int lm83_attach_adapter(struct i2c_adapter *adapter)
    +{
    + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
    + return 0;
    + return i2c_detect(adapter, &addr_data, lm83_detect);
    +}
    +
    +/*
    + * The following function does more than just detection. If detection
    + * succeeds, it also registers the new chip.
    + */
    +static int lm83_detect(struct i2c_adapter *adapter, int address,
    + int kind)
    +{
    + struct i2c_client *new_client;
    + struct lm83_data *data;
    + int err = 0;
    + const char *name = "";
    +
    + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    + goto exit;
    +
    + if (!(new_client = kmalloc(sizeof(struct i2c_client) +
    + sizeof(struct lm83_data), GFP_KERNEL))) {
    + err = -ENOMEM;
    + goto exit;
    + }
    + memset(new_client, 0x00, sizeof(struct i2c_client) +
    + sizeof(struct lm83_data));
    +
    + /* The LM83-specific data is placed right after the common I2C
    + * client data. */
    + data = (struct lm83_data *) (new_client + 1);
    + i2c_set_clientdata(new_client, data);
    + new_client->addr = address;
    + new_client->adapter = adapter;
    + new_client->driver = &lm83_driver;
    + new_client->flags = 0;
    +
    + /* Now we do the detection and identification. A negative kind
    + * means that the driver was loaded with no force parameter
    + * (default), so we must both detect and identify the chip
    + * (actually there is only one possible kind of chip for now, LM83).
    + * A zero kind means that the driver was loaded with the force
    + * parameter, the detection step shall be skipped. A positive kind
    + * means that the driver was loaded with the force parameter and a
    + * given kind of chip is requested, so both the detection and the
    + * identification steps are skipped. */
    + if (kind < 0) { /* detection */
    + if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
    + & 0xA8) != 0x00) ||
    + ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
    + & 0x48) != 0x00) ||
    + ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
    + & 0x41) != 0x00)) {
    + dev_dbg(&client->dev,
    + "LM83 detection failed at 0x%02x.\n", address);
    + goto exit_free;
    + }
    + }
    +
    + if (kind <= 0) { /* identification */
    + u8 man_id, chip_id;
    +
    + man_id = i2c_smbus_read_byte_data(new_client,
    + LM83_REG_R_MAN_ID);
    + chip_id = i2c_smbus_read_byte_data(new_client,
    + LM83_REG_R_CHIP_ID);
    + if (man_id == 0x01) { /* National Semiconductor */
    + if (chip_id == 0x03) {
    + kind = lm83;
    + name = "lm83";
    + }
    + }
    +
    + if (kind <= 0) { /* identification failed */
    + dev_info(&adapter->dev,
    + "Unsupported chip (man_id=0x%02X, "
    + "chip_id=0x%02X).\n", man_id, chip_id);
    + goto exit_free;
    + }
    + }
    +
    + /* We can fill in the remaining client fields */
    + strlcpy(new_client->name, name, I2C_NAME_SIZE);
    + new_client->id = lm83_id++;
    + data->valid = 0;
    + init_MUTEX(&data->update_lock);
    +
    + /* Tell the I2C layer a new client has arrived */
    + if ((err = i2c_attach_client(new_client)))
    + goto exit_free;
    +
    + /* Initialize the LM83 chip */
    + lm83_init_client(new_client);
    +
    + /* Register sysfs hooks */
    + device_create_file(&new_client->dev, &dev_attr_temp_input1);
    + device_create_file(&new_client->dev, &dev_attr_temp_input2);
    + device_create_file(&new_client->dev, &dev_attr_temp_input3);
    + device_create_file(&new_client->dev, &dev_attr_temp_input4);
    + device_create_file(&new_client->dev, &dev_attr_temp_max1);
    + device_create_file(&new_client->dev, &dev_attr_temp_max2);
    + device_create_file(&new_client->dev, &dev_attr_temp_max3);
    + device_create_file(&new_client->dev, &dev_attr_temp_max4);
    + device_create_file(&new_client->dev, &dev_attr_temp_crit);
    + device_create_file(&new_client->dev, &dev_attr_alarms);
    +
    + return 0;
    +
    +exit_free:
    + kfree(new_client);
    +exit:
    + return err;
    +}
    +
    +static void lm83_init_client(struct i2c_client *client)
    +{
    + int nr;
    +
    + for (nr = 0; nr < 4; nr++)
    + i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr],
    + TEMP_TO_REG(LM83_INIT_HIGH));
    + i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT,
    + TEMP_TO_REG(LM83_INIT_CRIT));
    +}
    +
    +static int lm83_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(client);
    + return 0;
    +}
    +
    +static void lm83_update_client(struct i2c_client *client)
    +{
    + struct lm83_data *data = i2c_get_clientdata(client);
    +
    + down(&data->update_lock);
    +
    + if ((jiffies - data->last_updated > HZ * 2) ||
    + (jiffies < data->last_updated) ||
    + !data->valid) {
    + int nr;
    + dev_dbg(&client->dev, "Updating lm83 data.\n");
    + for (nr = 0; nr < 4 ; nr++) {
    + data->temp_input[nr] =
    + i2c_smbus_read_byte_data(client,
    + LM83_REG_R_TEMP[nr]);
    + data->temp_high[nr] =
    + i2c_smbus_read_byte_data(client,
    + LM83_REG_R_HIGH[nr]);
    + }
    + data->temp_crit =
    + i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
    + data->alarms =
    + i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
    + + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
    + << 8);
    + data->last_updated = jiffies;
    + data->valid = 1;
    + }
    +
    + up(&data->update_lock);
    +}
    +
    +static int __init sensors_lm83_init(void)
    +{
    + return i2c_add_driver(&lm83_driver);
    +}
    +
    +static void __exit sensors_lm83_exit(void)
    +{
    + i2c_del_driver(&lm83_driver);
    +}
    +
    +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
    +MODULE_DESCRIPTION("LM83 driver");
    +MODULE_LICENSE("GPL");
    +
    +module_init(sensors_lm83_init);
    +module_exit(sensors_lm83_exit);
    diff -Nru a/include/linux/i2c-id.h b/include/linux/i2c-id.h
    --- a/include/linux/i2c-id.h Tue Dec 30 12:33:21 2003
    +++ b/include/linux/i2c-id.h Tue Dec 30 12:33:21 2003
    @@ -153,6 +153,7 @@
     #define I2C_DRIVERID_FS451 1037
     #define I2C_DRIVERID_W83627HF 1038
     #define I2C_DRIVERID_LM85 1039
    +#define I2C_DRIVERID_LM83 1040
     
     /*
      * ---- Adapter types ----------------------------------------------------

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Greg KH: "Re: [PATCH] i2c driver fixes for 2.6.0"