From patchwork Fri May 21 12:17:46 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shubhrajyoti Datta X-Patchwork-Id: 101399 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o4LCIR6D016394 for ; Fri, 21 May 2010 12:18:27 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754804Ab0EUMRw (ORCPT ); Fri, 21 May 2010 08:17:52 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:56289 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754608Ab0EUMRv convert rfc822-to-8bit (ORCPT ); Fri, 21 May 2010 08:17:51 -0400 Received: from dbdp20.itg.ti.com ([172.24.170.38]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id o4LCHl1D029455 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 21 May 2010 07:17:50 -0500 Received: from dbde71.ent.ti.com (localhost [127.0.0.1]) by dbdp20.itg.ti.com (8.13.8/8.13.8) with ESMTP id o4LCHk6g009045; Fri, 21 May 2010 17:47:47 +0530 (IST) Received: from dbde02.ent.ti.com ([172.24.170.145]) by dbde71.ent.ti.com ([172.24.170.149]) with mapi; Fri, 21 May 2010 17:47:46 +0530 From: "Datta, Shubhrajyoti" To: "linux-kernel@vger.kernel.org" CC: "linux-omap@vger.kernel.org" , "linux-input@vger.kernel.org" Date: Fri, 21 May 2010 17:47:46 +0530 Subject: [RFC] [PATCH] TMP105 : Driver support for the temperature sensor Thread-Topic: [RFC] [PATCH] TMP105 : Driver support for the temperature sensor Thread-Index: Acr43Hz0vOw7mFCpTrWmDQZ9lsENfAAADBMg Message-ID: <0680EC522D0CC943BC586913CF3768C003B32B3AC4@dbde02.ent.ti.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 21 May 2010 12:18:27 +0000 (UTC) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 68cf877..a4a5352 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1076,6 +1076,16 @@ config SENSORS_MC13783_ADC help Support for the A/D converter on MC13783 PMIC. +config SENSORS_TMP105 + tristate "Texas Instruments TMP421 and compatible" + depends on I2C + help + If you say yes here you get support for Texas Instruments TMP105 + temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp105. + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4bc215c..2c4e7a5 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_SENSORS_TMP105) += tmp105.o ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/hwmon/tmp105.c b/drivers/hwmon/tmp105.c new file mode 100644 index 0000000..8765b11 --- /dev/null +++ b/drivers/hwmon/tmp105.c @@ -0,0 +1,326 @@ +/* + * tmp105.c + * + * TMP105 temperature sensor driver + * + * Copyright (C) 2010 Texas Instruments + * + * Author: Shubhrajyoti Datta + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define TMP105_TEMP_REG 0x00 +#define TMP105_CONF_REG 0x01 +#define TMP105_TLOW_REG 0x02 +#define TMP105_THIGH_REG 0x03 + +/* Configuration register parameters */ +#define TMP105_CONF_SD 0x01 +#define TMP105_CONF_TM 0x02 +#define TMP105_CONF_POL 0x04 +#define TMP105_CONF_F0 0x08 +#define TMP105_CONF_F1 0x10 +#define TMP105_CONF_R0 0x20 +#define TMP105_CONF_R1 0x40 +#define TMP105_CONF_OS 0x80 + +#define TMP105_I2C_ADDRESS 0x48 + +#define MAX_TEMP 128 +#define MIN_TEMP -55 + +/* Each client has this additional data */ +struct tmp105_data { + struct i2c_client *client; + /* mutex for sysfs operations */ + struct mutex lock; + struct device *hwmon_dev; + s16 temp[3]; + unsigned long last_updated; + u8 configuration_setting; +}; + +static const u8 tmp105_reg[] = { + TMP105_TEMP_REG, + TMP105_TLOW_REG, + TMP105_THIGH_REG, +}; + +static void tmp105_init_client(struct i2c_client *client); + +static signed long tmp105_reg_to_mC(s16 val) +{ + signed long temp_mC; + if (val & 0x800) + val = val - 0x1000 ; + temp_mC = (val * 64000) / 1024; + return temp_mC; +} + +static u16 tmp105_C_to_reg(signed long val) +{ + val = (val * 1024) / 64000; + if (val < 0) + val = val + 0x1000; + return (u16)val; +} + +static s16 *tmp105_update_device(struct i2c_client *client, + int index) +{ + struct tmp105_data *data = i2c_get_clientdata(client); + u8 tmp[2]; + + mutex_lock(&data->lock); + + if (time_after(jiffies, data->last_updated + HZ/4)) { + i2c_smbus_read_i2c_block_data(client, + tmp105_reg[index], 2, tmp); + data->temp[index] = ((tmp[0] << 4) | ((tmp[1] & 0xF0) >> 4)); + printk(KERN_INFO "Raw temperature: %u\n", data->temp[index]); + data->last_updated = jiffies; + } + + mutex_unlock(&data->lock); + return data->temp[index] ; +} + +static ssize_t show_temp_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + s16 temperature = tmp105_update_device(client , sda->index); + signed long temp_in_mC; + + temp_in_mC = tmp105_reg_to_mC(temperature); + + return sprintf(buf, "%d\n", temp_in_mC); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL , 0); + +static ssize_t tmp105_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct tmp105_data *tmp105 = i2c_get_clientdata(client); + signed long val; + int status = 0; + u16 temp; + + if ((strict_strtol(buf, 10, &val) < 0)) + return -EINVAL; + + SENSORS_LIMIT(val , MIN_TEMP , MAX_TEMP); + + mutex_lock(&tmp105->lock); + + temp = tmp105_C_to_reg(val); + temp = ((temp & 0xFF0) >> 4) | ((temp & 0xF)<<12); + + status = i2c_smbus_write_word_data(client, tmp105_reg[sda->index], + temp); + + tmp105->temp[sda->index] = temp; + mutex_unlock(&tmp105->lock); + return status ? : count; +} + +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_value, + tmp105_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_value, + tmp105_set_temp, 2); + +/* sysfs call */ +static ssize_t set_configuration(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + s32 status; + struct i2c_client *client = to_i2c_client(dev); + struct tmp105_data *data = i2c_get_clientdata(client); + data->configuration_setting = simple_strtoul(buf, NULL, 10); + /* I2C write to the configuration register */ + status = i2c_smbus_write_byte_data(client, TMP105_CONF_REG, + data->configuration_setting); + return count; +} + +static ssize_t show_configuration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tmp105_data *data = i2c_get_clientdata(client); + u8 tmp; + i2c_smbus_read_i2c_block_data(client, TMP105_CONF_REG, 1, &tmp); + data->configuration_setting = tmp; + return sprintf(buf, "%u\n", data->configuration_setting); +} +static DEVICE_ATTR(configuration, S_IWUSR | S_IRUGO, show_configuration, + set_configuration); + + +static struct attribute *tmp105_attributes[] = { + &dev_attr_configuration.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp105_attr_group = { + .attrs = tmp105_attributes, +}; + +static int tmp105_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tmp105_data *tmp105_data; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_dbg(&client->dev, "adapter doesn't support I2C\n"); + return -ENODEV; + } + + tmp105_data = kzalloc(sizeof(struct tmp105_data), GFP_KERNEL); + if (!tmp105_data) { + err = -ENOMEM; + goto exit; + } + tmp105_data->client = client; + + i2c_set_clientdata(client, tmp105_data); + mutex_init(&tmp105_data->lock); + + /* Initialize the TMP105 chip */ + tmp105_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &tmp105_attr_group); + if (err) + goto exit_free; + tmp105_data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(tmp105_data->hwmon_dev)) { + err = PTR_ERR(tmp105_data->hwmon_dev); + tmp105_data->hwmon_dev = NULL; + goto exit_remove; + } + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &tmp105_attr_group); +exit_free: + i2c_set_clientdata(client, NULL); + kfree(tmp105_data); +exit: + return err; +} + +static int tmp105_remove(struct i2c_client *client) +{ + struct tmp105_data *tmp105 = i2c_get_clientdata(client); + hwmon_device_unregister(tmp105->hwmon_dev); + + sysfs_remove_group(&client->dev.kobj, &tmp105_attr_group); + i2c_set_clientdata(client, NULL); + kfree(tmp105); + return 0; +} + +/* Called when we have found a new TMP105. */ +static void tmp105_init_client(struct i2c_client *client) +{ + struct tmp105_data *data = i2c_get_clientdata(client); + data->last_updated = jiffies - HZ; + mutex_init(&data->lock); +} + +static const struct i2c_device_id tmp105_id[] = { + { "tmp105", 0 }, + { } +}; + +#ifdef CONFIG_PM +static int tmp105_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 config_reg; + i2c_smbus_read_i2c_block_data(client, TMP105_CONF_REG, 1, &config_reg); + config_reg = config_reg | TMP102_CONF_SD; + i2c_smbus_write_byte_data(client, TMP105_CONF_REG, TMP102_CONF_SD); + return 0; +} + +static int tmp105_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + i2c_smbus_read_i2c_block_data(client, TMP105_CONF_REG, 1, &config_reg); + config_reg = config_reg & ~TMP102_CONF_SD; + i2c_smbus_write_byte_data(client, TMP105_CONF_REG, TMP102_CONF_SD); +} + +static struct dev_pm_ops tmp105_dev_pm_ops = { + .suspend = tmp105_suspend, + .resume = tmp105_resume, +}; + +#define TMP105_DEV_PM_OPS (&tmp105_dev_pm_ops) +#else +#define TMP105_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + + +static struct i2c_driver tmp105_driver = { + .driver = { + .name = "tmp105", + .owner = THIS_MODULE, + .pm = TMP105_DEV_PM_OPS, + }, + .probe = tmp105_probe, + .remove = tmp105_remove, + .id_table = tmp105_id, + .class = I2C_CLASS_HWMON, +}; + +static int __init tmp105_init(void) +{ + return i2c_add_driver(&tmp105_driver); +} + +static void __exit tmp105_exit(void) +{ + i2c_del_driver(&tmp105_driver); +} + +MODULE_DESCRIPTION("TMP105 driver"); +MODULE_LICENSE("GPL"); + +module_init(tmp105_init); +module_exit(tmp105_exit); +