diff options
author | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-01-10 14:50:08 +0530 |
---|---|---|
committer | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-01-10 14:50:08 +0530 |
commit | bf5a56deb588e201d95f21c00257e88ac721f90b (patch) | |
tree | d418c01fea425ea8063a6c0fb6f7119b301e135a /drivers/misc | |
parent | 3cfc0c8843cbc3f2c859d9eea1003834af6119e7 (diff) | |
parent | b99662a653ac3d90bfada1bfc326d3138229860c (diff) |
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Conflicts:
arch/arm/mach-tegra/fuse.c
drivers/misc/Makefile
Change-Id: I300b925d78b31efe00c342190d8dbd50e2e81230
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rwxr-xr-x | drivers/misc/nct1008.c | 291 |
3 files changed, 300 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8a3b4579dd2d..c37ebfaad5c4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -340,6 +340,14 @@ config SENSORS_AK8975 If you say yes here you get support for Asahi Kasei's orientation sensor AK8975. +config SENSORS_NCT1008 + tristate "ON Semiconductor Temperature Sensor" + default n + depends on I2C + help + Say yes here if you wish to include the ON Semiconductor + NCT1008 Temperature sensor. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index eb16069b545e..c23222e921ec 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o obj-$(CONFIG_SENSORS_MPU3050) += mpu3050/ +obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o diff --git a/drivers/misc/nct1008.c b/drivers/misc/nct1008.c new file mode 100755 index 000000000000..4ae48e4003aa --- /dev/null +++ b/drivers/misc/nct1008.c @@ -0,0 +1,291 @@ +/* + * drivers/misc/nct1008.c + * + * Driver for NCT1008, temperature monitoring device from ON Semiconductors + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/gpio.h> + +#include <linux/nct1008.h> + +#define DRIVER_NAME "nct1008" + +/* Register Addresses */ +#define LOCAL_TEMP_RD 0x00 +#define STATUS_RD 0x02 +#define CONFIG_RD 0x03 + +#define CONFIG_WR 0x09 +#define CONV_RATE_WR 0x0A +#define LOCAL_TEMP_HI_LIMIT_WR 0x0B +#define EXT_TEMP_HI_LIMIT_HI_BYTE 0x0D +#define OFFSET_WR 0x11 +#define EXT_THERM_LIMIT_WR 0x19 +#define LOCAL_THERM_LIMIT_WR 0x20 +#define THERM_HYSTERESIS_WR 0x21 + +/* Configuration Register Bits */ +#define EXTENDED_RANGE_BIT (0x1 << 2) +#define THERM2_BIT (0x1 << 5) +#define STANDBY_BIT (0x1 << 6) + +/* Max Temperature Measurements */ +#define EXTENDED_RANGE_OFFSET 64U +#define STANDARD_RANGE_MAX 127U +#define EXTENDED_RANGE_MAX (150U + EXTENDED_RANGE_OFFSET) + +struct nct1008_data { + struct work_struct work; + struct i2c_client *client; + struct mutex mutex; + u8 config; + void (*alarm_fn)(bool raised); +}; + +static void nct1008_enable(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, CONFIG_WR, + data->config & ~STANDBY_BIT); +} + +static void nct1008_disable(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, CONFIG_WR, + data->config | STANDBY_BIT); +} + + +static void nct1008_work_func(struct work_struct *work) +{ + struct nct1008_data *data = container_of(work, struct nct1008_data, work); + int irq = data->client->irq; + + mutex_lock(&data->mutex); + + if (data->alarm_fn) { + /* Therm2 line is active low */ + data->alarm_fn(!gpio_get_value(irq_to_gpio(irq))); + } + + mutex_unlock(&data->mutex); +} + +static irqreturn_t nct1008_irq(int irq, void *dev_id) +{ + struct nct1008_data *data = dev_id; + schedule_work(&data->work); + + return IRQ_HANDLED; +} + +static inline u8 value_to_temperature(bool extended, u8 value) +{ + return (extended ? (u8)(value - EXTENDED_RANGE_OFFSET) : value); +} + +static inline u8 temperature_to_value(bool extended, u8 temp) +{ + return (extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : temp); +} + +static int __devinit nct1008_configure_sensor(struct nct1008_data* data) +{ + struct i2c_client *client = data->client; + struct nct1008_platform_data *pdata = client->dev.platform_data; + u8 value; + int err; + + if (!pdata || !pdata->supported_hwrev) + return -ENODEV; + + /* + * Initial Configuration - device is placed in standby and + * ALERT/THERM2 pin is configured as THERM2 + */ + data->config = value = pdata->ext_range ? + (STANDBY_BIT | THERM2_BIT | EXTENDED_RANGE_BIT) : + (STANDBY_BIT | THERM2_BIT); + + err = i2c_smbus_write_byte_data(client, CONFIG_WR, value); + if (err < 0) + goto error; + + /* Temperature conversion rate */ + err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate); + if (err < 0) + goto error; + + /* External temperature h/w shutdown limit */ + value = temperature_to_value(pdata->ext_range, pdata->shutdown_ext_limit); + err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value); + if (err < 0) + goto error; + + /* Local temperature h/w shutdown limit */ + value = temperature_to_value(pdata->ext_range, pdata->shutdown_local_limit); + err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value); + if (err < 0) + goto error; + + /* External Temperature Throttling limit */ + value = temperature_to_value(pdata->ext_range, pdata->throttling_ext_limit); + err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE, value); + if (err < 0) + goto error; + + /* Local Temperature Throttling limit */ + value = pdata->ext_range ? EXTENDED_RANGE_MAX : STANDARD_RANGE_MAX; + err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR, value); + if (err < 0) + goto error; + + /* Remote channel offset */ + err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset); + if (err < 0) + goto error; + + /* THERM hysteresis */ + err = i2c_smbus_write_byte_data(client, THERM_HYSTERESIS_WR, pdata->hysteresis); + if (err < 0) + goto error; + + data->alarm_fn = pdata->alarm_fn; + return 0; +error: + return err; +} + +static int __devinit nct1008_configure_irq(struct nct1008_data *data) +{ + INIT_WORK(&data->work, nct1008_work_func); + + return request_irq(data->client->irq, nct1008_irq, IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, DRIVER_NAME, data); +} + +static int __devinit nct1008_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct nct1008_data *data; + int err; + + data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL); + + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->mutex); + + err = nct1008_configure_sensor(data); /* sensor is in standby */ + if (err < 0) + goto error; + + err = nct1008_configure_irq(data); + if (err < 0) + goto error; + + nct1008_enable(client); /* sensor is running */ + + schedule_work(&data->work); /* check initial state */ + + return 0; + +error: + kfree(data); + return err; +} + +static int __devexit nct1008_remove(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + free_irq(data->client->irq, data); + cancel_work_sync(&data->work); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int nct1008_suspend(struct i2c_client *client, pm_message_t state) +{ + disable_irq(client->irq); + nct1008_disable(client); + + return 0; +} + +static int nct1008_resume(struct i2c_client *client) +{ + struct nct1008_data *data = i2c_get_clientdata(client); + + nct1008_enable(client); + enable_irq(client->irq); + schedule_work(&data->work); + + return 0; +} +#endif + +static const struct i2c_device_id nct1008_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct1008_id); + +static struct i2c_driver nct1008_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = nct1008_probe, + .remove = __devexit_p(nct1008_remove), + .id_table = nct1008_id, +#ifdef CONFIG_PM + .suspend = nct1008_suspend, + .resume = nct1008_resume, +#endif +}; + +static int __init nct1008_init(void) +{ + return i2c_add_driver(&nct1008_driver); +} + +static void __exit nct1008_exit(void) +{ + i2c_del_driver(&nct1008_driver); +} + +MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008"); +MODULE_LICENSE("GPL"); + +module_init (nct1008_init); +module_exit (nct1008_exit); |