blob: e3b2137f22bff7652e9d9234f17707d4dd7a33ce [file] [log] [blame]
/*
* Temperature Monitor Driver
*
* Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* drivers/hwmon/tmon-tmp411.c
*
*/
/* Note: Copied temperature conversion code from tmp401 driver */
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/sysfs.h>
#include <linux/platform_data/tmon_tmp411.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/spinlock.h>
#define DEFAULT_TMON_POLLING_TIME 2000 /* Time in ms */
#define DEFAULT_TMON_DELTA_TEMP 4000 /* Temp. change to execute
platform callback */
#define TMON_ERR INT_MAX
#define TMON_NOCHANGE (INT_MAX - 1)
/*
* The TMP411/NVT210 registers, note some registers have different addresses for
* reading and writing
*/
#define REG_STATUS 0x02
#define REG_CFG_READ 0x03
#define REG_CFG_WRITE 0x09
#define REG_CON_RATE_READ 0x04
#define REG_CONV_RATE_WRITE 0x0A
#define REG_MAN_ID 0xFE
/* Flags */
#define CFG_EXTEND (0x1<<2)
#define CFG_ALERT_MASK (0x1<<7)
#define CFG_STANDBY (0x1<<6)
#define CFG_ALERT_DISABLE (0x1<<5)
#define STATUS_LOCAL_CRIT 0x01
#define STATUS_REMOTE_CRIT 0x02
#define STATUS_REMOTE_OPEN 0x04
#define STATUS_REMOTE_LOW 0x08
#define STATUS_REMOTE_HIGH 0x10
#define STATUS_LOCAL_LOW 0x20
#define STATUS_LOCAL_HIGH 0x40
#define STATUS_RTHRM 0x02
#define STATUS_LTHRM 0x01
/* Based on POR values of CONVERSION RATE REGISTER (125ms) +
RESOLUTION REGISTER(conversion time: 12.5ms) */
#define CONVER_TIME 140
#define TMON_RD(clnt, reg, val) \
do { \
err = tmon_read(clnt, reg, val); \
if (err) \
return err; \
} while (0)
#define TMON_WRT(clnt, reg, val) \
do { \
err = i2c_smbus_write_byte_data(clnt, reg, val); \
if (err < 0) \
return err; \
} while (0)
enum alert_type {
NO_ALERT = 0,
REMOTE_HIGH_ALERT,
REMOTE_LOW_ALERT,
};
enum tmp_sensor {
TMP411 = 0,
NVT210,
};
enum temp_type {
LOCAL = 0,
REMOTE,
};
static enum tmp_sensor tmon_device;
/* TMP411/NVT210 registers
Note: Local temperature low bytes not applicable in case of NVT210
index 0 for local temperatures and 1 for remote temperatures */
static const u8 REG_TEMP_MSB[2] = { 0x00, 0x01 };
static const u8 REG_TEMP_LSB[2] = { 0x15, 0x10 };
static const u8 REG_TEMP_LOW_LIMIT_MSB_RD[2] = { 0x06, 0x08 };
static const u8 REG_TEMP_LOW_LIMIT_MSB_WRT[2] = { 0x0C, 0x0E };
static const u8 REG_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 };
static const u8 REG_TEMP_HIGH_LIMIT_MSB_RD[2] = { 0x05, 0x07 };
static const u8 REG_TEMP_HIGH_LIMIT_MSB_WRT[2] = { 0x0B, 0x0D };
static const u8 REG_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
static const u8 REG_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
#ifdef CONFIG_PM
/* index 0 for local temperatures and 1 for remote temperatures */
static u16 temp_low_limit[2];
static u16 temp_high_limit[2];
static u8 temp_crit_limit[2];
static u8 conv_rate;
static u8 config;
#endif
struct tmon_info {
int mode;
struct i2c_client *client;
struct i2c_client *ara; /*Alert Response Address for clearing alert */
struct delayed_work tmon_work;
struct tmon_plat_data *pdata;
struct mutex update_lock; /* For TMP411 registers read/writes */
struct mutex sysfs_alert_lock; /* For block alert sysfs protection */
spinlock_t alert_spinlock; /* Used for protection between ISR
and user process */
struct i2c_smbus_alert_setup *alert_info;
wait_queue_head_t alert_wait_queue;
char therm_alert;
char disable_intr_reqr;
int irq_num;
long sysfs_therm_alert_timeout;
unsigned long last_updated; /* in jiffies */
};
/* Return Both High byte and Low byte */
static u16 temp_to_reg(long temp, u8 config)
{
if (config & CFG_EXTEND) {
temp = clamp_val(temp, -64000, 191000);
temp += 64000;
} else
temp = clamp_val(temp, 0, 127000);
return (temp * 160 + 312) / 625; /* Copied from TMP411 driver */
}
static s32 tmon_read(struct i2c_client *client, u8 reg, u8 *value)
{
s32 tmp;
tmp = i2c_smbus_read_byte_data(client, reg);
if (tmp < 0)
return -EINVAL;
*value = tmp;
return 0;
}
/* Expects both High byte and Low byte, If not Low bytes then
make Low byte as 0 */
static s32 reg_to_temp(u16 reg, u8 config)
{
s32 temp = reg;
if (config & CFG_EXTEND)
temp -= 64 * 256;
return (temp * 625 + 80) / 160; /* Copied from TMP411 driver */
}
static s32 tmon_read_remote_temp(struct i2c_client *client,
s32 *ptemp)
{
u8 config;
u8 tmp;
int err;
u16 temperature = 0;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
TMON_RD(client, REG_TEMP_MSB[1], &tmp);
temperature = ((u16)tmp) << 8;
TMON_RD(client, REG_TEMP_LSB[1], &tmp);
temperature |= tmp;
*ptemp = reg_to_temp(temperature, config) +
data->pdata->remote_offset;
return 0;
}
static s32 tmon_read_local_temp(struct i2c_client *client,
int *ptemp)
{
u8 config;
u8 tmp;
int err;
u16 temperature = 0;
TMON_RD(client, REG_CFG_READ, &config);
TMON_RD(client, REG_TEMP_MSB[0], &tmp);
temperature = ((u16)tmp) << 8;
if (tmon_device == TMP411) {
TMON_RD(client, REG_TEMP_LSB[0], &tmp);
temperature |= tmp;
}
*ptemp = reg_to_temp(temperature, config);
return 0;
}
static s32 tmon_read_low_limit(struct i2c_client *client,
s32 *ptemp, enum temp_type typ)
{
u8 config;
u8 tmp;
int err;
u16 temperature = 0;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
TMON_RD(client, REG_TEMP_LOW_LIMIT_MSB_RD[typ], &tmp);
temperature = ((u16)tmp) << 8;
if ((typ == REMOTE) || (typ == LOCAL && tmon_device == TMP411)) {
TMON_RD(client, REG_TEMP_LOW_LIMIT_LSB[typ], &tmp);
temperature |= tmp;
}
*ptemp = reg_to_temp(temperature, config);
if (typ == REMOTE)
*ptemp = *ptemp + data->pdata->remote_offset;
return 0;
}
static s32 tmon_read_high_limit(struct i2c_client *client,
s32 *ptemp, enum temp_type typ)
{
u8 config;
u8 tmp;
int err;
u16 temperature = 0;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
TMON_RD(client, REG_TEMP_HIGH_LIMIT_MSB_RD[typ], &tmp);
temperature = ((u16)tmp) << 8;
if ((typ == REMOTE) || (typ == LOCAL && tmon_device == TMP411)) {
TMON_RD(client, REG_TEMP_HIGH_LIMIT_LSB[typ], &tmp);
temperature |= tmp;
}
*ptemp = reg_to_temp(temperature, config);
if (typ == REMOTE)
*ptemp = *ptemp + data->pdata->remote_offset;
return 0;
}
static s32 tmon_read_critical_limit(struct i2c_client *client,
s32 *ptemp, enum temp_type typ)
{
u8 config;
u8 tmp;
int err;
u16 temperature = 0;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
TMON_RD(client, REG_TEMP_CRIT_LIMIT[typ], &tmp);
temperature = ((u16)tmp) << 8;
*ptemp = reg_to_temp(temperature, config);
if (typ == REMOTE)
*ptemp = *ptemp + data->pdata->remote_offset;
return 0;
}
static int tmon_write_high_limit(struct i2c_client *client,
s32 temp, enum temp_type typ)
{
u8 config;
u8 reg8_val;
int err;
u16 reg16_val;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
if (typ == REMOTE)
temp = temp - data->pdata->remote_offset;
reg16_val = temp_to_reg(temp, config);
reg8_val = reg16_val >> 8;
mutex_lock(&data->update_lock);
TMON_WRT(client, REG_TEMP_HIGH_LIMIT_MSB_WRT[typ], reg8_val);
if (tmon_device == TMP411)
TMON_WRT(client, REG_TEMP_HIGH_LIMIT_LSB[typ],
(reg16_val & 0xFF));
mutex_unlock(&data->update_lock);
return 0;
}
static int tmon_write_low_limit(struct i2c_client *client,
s32 temp, enum temp_type typ)
{
u8 config;
u8 reg8_val;
int err;
u16 reg16_val;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
if (typ == REMOTE)
temp = temp - data->pdata->remote_offset;
reg16_val = temp_to_reg(temp, config);
reg8_val = reg16_val >> 8;
mutex_lock(&data->update_lock);
TMON_WRT(client, REG_TEMP_LOW_LIMIT_MSB_WRT[typ], reg8_val);
if (tmon_device == TMP411)
TMON_WRT(client, REG_TEMP_LOW_LIMIT_LSB[typ],
(reg16_val & 0xFF));
mutex_unlock(&data->update_lock);
return 0;
}
#if FUTURE_USE
static int tmon_write_critical_limit(struct i2c_client *client,
s32 temp, enum temp_type typ)
{
u8 config;
u16 reg16_val;
u8 reg8_val;
int err;
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &config);
if (typ == REMOTE)
temp = temp - data->pdata->remote_offset;
reg16_val = temp_to_reg(temp, config);
reg8_val = reg16_val >> 8;
mutex_lock(&data->update_lock);
TMON_WRT(client, REG_TEMP_CRIT_LIMIT[typ], reg8_val);
mutex_unlock(&data->update_lock);
return 0;
}
#endif
static ssize_t show_temp_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
int temperature = 0;
if (index == 0) /* local temperature */
tmon_read_local_temp(to_i2c_client(dev), &temperature);
else
tmon_read_remote_temp(to_i2c_client(dev), &temperature);
return sprintf(buf, "%d\n", temperature);
}
static ssize_t show_remote_low_limit(struct device *dev,
struct device_attribute *devattr, char *buf)
{
s32 low_limit = 0;
tmon_read_low_limit(to_i2c_client(dev), &low_limit, REMOTE);
return sprintf(buf, "%d\n", low_limit);
}
static ssize_t show_remote_high_limit(struct device *dev,
struct device_attribute *devattr, char *buf)
{
s32 high_limit = 0;
tmon_read_high_limit(to_i2c_client(dev), &high_limit, REMOTE);
return sprintf(buf, "%d\n", high_limit);
}
static int clear_alert(struct tmon_info *data)
{
struct i2c_client *ara = data->ara;
int alrt_stat;
alrt_stat = i2c_smbus_read_byte(ara);
return 0;
}
static u8 is_alert_present(u8 status)
{
if ((status & STATUS_REMOTE_LOW) || (status & STATUS_REMOTE_HIGH) ||
(status & STATUS_LOCAL_LOW) || (status & STATUS_LOCAL_HIGH))
return 1;
else
return 0;
}
static u8 manage_alert(struct i2c_client *client)
{
u8 status;
int err;
struct tmon_info *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
/* If gpio line is asserted then try to clear the alert */
if (data->pdata->alert_gpio != -1)
if (!gpio_get_value(data->pdata->alert_gpio))
clear_alert(data);
/* Read the status register twice to get rid of stale alert */
TMON_RD(client, REG_STATUS, &status);
if (is_alert_present(status)) {
msleep(CONVER_TIME); /* Wait for another conversion to happen */
TMON_RD(client, REG_STATUS, &status);
}
mutex_unlock(&data->update_lock);
return status;
}
static ssize_t return_status(char *buf, u8 status)
{
enum alert_type alert = NO_ALERT;
if (status & STATUS_REMOTE_HIGH)
alert = REMOTE_HIGH_ALERT;
else if (status & STATUS_REMOTE_LOW)
alert = REMOTE_LOW_ALERT;
return sprintf(buf, "%d\n", alert);
}
static ssize_t show_alert(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u8 status;
status = manage_alert(to_i2c_client(dev));
return return_status(buf, status);
}
static ssize_t store_alert_block_timeout(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
struct tmon_info *data = i2c_get_clientdata(to_i2c_client(dev));
long val;
if (kstrtol(buf, 10, &val))
return -EINVAL;
if (!mutex_trylock(&data->sysfs_alert_lock))
return -EAGAIN;
data->sysfs_therm_alert_timeout = val;
mutex_unlock(&data->sysfs_alert_lock);
return count;
}
/* This function blocks until alert occurs or
timeout occurs(If any timeout value > 0 is set) */
static ssize_t show_alert_blocking(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct tmon_info *data = i2c_get_clientdata(to_i2c_client(dev));
u8 status;
int err;
unsigned long intr_flags;
mutex_lock(&data->sysfs_alert_lock);
status = manage_alert(to_i2c_client(dev));
if ((status & STATUS_REMOTE_LOW) || (status & STATUS_REMOTE_HIGH)) {
mutex_unlock(&data->sysfs_alert_lock);
return return_status(buf, status);
}
/* Even though data->therm_alert is shared between ISR and process,
protection is not required here. Because before any process access
this varaible here, alert interrupt would have been disabled */
data->therm_alert = 0;
data->disable_intr_reqr = 1;
/* Enable Interrupt for thermal alert */
enable_irq(data->irq_num);
/* Wait event on therm alert */
if (data->sysfs_therm_alert_timeout > 0) {
wait_event_interruptible_timeout(data->alert_wait_queue,
(data->therm_alert & 0x1),
msecs_to_jiffies(data->sysfs_therm_alert_timeout));
} else {
wait_event_interruptible(
data->alert_wait_queue,
(data->therm_alert & 0x1));
}
/* data->therm_alert is shared between ISR and process.
So, spinlock used for protection */
spin_lock_irqsave(&data->alert_spinlock, intr_flags);
if (data->disable_intr_reqr) {
disable_irq_nosync(data->irq_num);
data->disable_intr_reqr = 0;
}
spin_unlock_irqrestore(&data->alert_spinlock, intr_flags);
mutex_lock(&data->update_lock);
TMON_RD(to_i2c_client(dev), REG_STATUS, &status);
mutex_unlock(&data->update_lock);
mutex_unlock(&data->sysfs_alert_lock);
return return_status(buf, status);
}
static ssize_t show_remote_shutdown_limit(struct device *dev,
struct device_attribute *devattr, char *buf)
{
s32 temp;
s32 err;
err = tmon_read_critical_limit(to_i2c_client(dev), &temp, REMOTE);
if (err)
return err;
return sprintf(buf, "%d\n", temp);
}
static ssize_t store_remote_low_limit(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
long val;
s32 err;
if (kstrtol(buf, 10, &val))
return -EINVAL;
err = tmon_write_low_limit(to_i2c_client(dev), val, REMOTE);
if (err)
return err;
return count;
}
static ssize_t store_remote_high_limit(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
long val;
int err;
if (kstrtol(buf, 10, &val))
return -EINVAL;
err = tmon_write_high_limit(to_i2c_client(dev), val, REMOTE);
if (err)
return err;
return count;
}
static struct sensor_device_attribute tmp411_attr[] = {
SENSOR_ATTR(local_temp, S_IRUGO, show_temp_value, NULL, 0),
SENSOR_ATTR(remote_temp, S_IRUGO, show_temp_value, NULL, 1),
SENSOR_ATTR(remote_temp_low_limit, S_IWUSR | S_IRUGO,
show_remote_low_limit, store_remote_low_limit, 1),
SENSOR_ATTR(remote_temp_high_limit, S_IWUSR | S_IRUGO,
show_remote_high_limit, store_remote_high_limit, 1),
SENSOR_ATTR(remote_temp_alert, S_IRUGO, show_alert, NULL,
STATUS_REMOTE_LOW | STATUS_REMOTE_HIGH),
SENSOR_ATTR(remote_temp_alert_blocking, S_IWUSR | S_IRUGO,
show_alert_blocking, store_alert_block_timeout,
STATUS_REMOTE_LOW | STATUS_REMOTE_HIGH),
SENSOR_ATTR(remote_shutdown_limit, S_IRUGO, show_remote_shutdown_limit,
NULL, 1),
};
static int tmon_check_local_temp(struct i2c_client *client,
u32 delta_temp)
{
static int last_temp;
int err;
int curr_temp = 0;
err = tmon_read_local_temp(client, &curr_temp);
if (err)
return TMON_ERR;
if (abs(curr_temp - last_temp) >= delta_temp) {
last_temp = curr_temp;
return curr_temp;
}
return TMON_NOCHANGE;
}
static int tmon_check_remote_temp(struct i2c_client *client,
u32 delta_temp)
{
static int last_temp;
int err;
int curr_temp = 0;
err = tmon_read_remote_temp(client, &curr_temp);
if (err)
return TMON_ERR;
if (abs(curr_temp - last_temp) >= delta_temp) {
last_temp = curr_temp;
return curr_temp;
}
return TMON_NOCHANGE;
}
static void tmon_update(struct work_struct *work)
{
int ret;
struct tmon_info *tmon_data =
container_of(to_delayed_work(work),
struct tmon_info,
tmon_work);
struct tmon_plat_data *pdata = tmon_data->pdata;
if (pdata->delta_time <= 0)
pdata->delta_time = DEFAULT_TMON_POLLING_TIME;
if (pdata->delta_temp <= 0)
pdata->delta_temp = DEFAULT_TMON_DELTA_TEMP;
ret =
tmon_check_local_temp(tmon_data->client,
pdata->delta_temp);
if (ret != TMON_ERR && ret != TMON_NOCHANGE &&
pdata->ltemp_dependent_reg_update)
pdata->ltemp_dependent_reg_update(ret);
ret = tmon_check_remote_temp(tmon_data->client,
pdata->delta_temp);
if (ret != TMON_ERR && ret != TMON_NOCHANGE &&
pdata->utmip_temp_dep_update)
pdata->utmip_temp_dep_update(ret, pdata->utmip_temp_bound);
schedule_delayed_work(&tmon_data->tmon_work,
msecs_to_jiffies(pdata->delta_time));
}
static irqreturn_t tmon_alert_isr(int irq, void *d)
{
struct tmon_info *data = d;
unsigned long int intr_flags;
spin_lock_irqsave(&data->alert_spinlock, intr_flags);
data->therm_alert = 1;
/* Disable the interrupt */
if (data->disable_intr_reqr) {
disable_irq_nosync(irq);
data->disable_intr_reqr = 0;
}
spin_unlock_irqrestore(&data->alert_spinlock, intr_flags);
/* Wake up the process waiting on therm alert */
if (waitqueue_active(&data->alert_wait_queue))
wake_up_all(&data->alert_wait_queue);
return IRQ_HANDLED;
}
static int tmon_tmp411_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tmon_plat_data *tmon_pdata =
client->dev.platform_data;
struct tmon_info *data;
int err;
int i;
u8 man_id;
u8 config;
if (tmon_pdata == NULL) {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "insufficient functionality!\n");
return -ENODEV;
}
data = devm_kzalloc(&client->dev, sizeof(struct tmon_info), GFP_KERNEL);
if (!data)
return -ENOMEM;
TMON_RD(client, REG_MAN_ID, &man_id);
if (man_id == 0x41) {
tmon_device = NVT210;
dev_info(&client->dev, "detected NVT210\n");
} else if (man_id == 0x55) {
tmon_device = TMP411;
dev_info(&client->dev, "detected TMP411\n");
} else {
dev_warn(&client->dev,
"unsuported t-sensor with manufacturer-id:0x%x\n",
man_id);
return -EINVAL;
}
TMON_RD(client, REG_CFG_READ, &config);
/* Enable Alert, Extended mode and disable stand by */
config |= CFG_EXTEND;
config &= ~CFG_ALERT_MASK;
config &= ~CFG_STANDBY;
config &= ~CFG_ALERT_DISABLE;
err = i2c_smbus_write_byte_data(client, REG_CFG_WRITE, config);
/*FIXME: Is it required to wait for one temperature conversion? */
if (err < 0) {
dev_warn(&client->dev,
"\n Failed to write config for temperature sensor\n");
return err;
}
data->client = client;
i2c_set_clientdata(client, data);
data->pdata = tmon_pdata;
mutex_init(&data->update_lock);
mutex_init(&data->sysfs_alert_lock);
spin_lock_init(&data->alert_spinlock);
data->sysfs_therm_alert_timeout = 0;
data->therm_alert = 0;
/*
* Suppose, if we get alert interrupts immediately after irq
* registeration and before disable_irq_nosync, then once entered
* in to Isr, if the below variable is set then irq will be disabled,
* otherwise it's likely that continious interrupts comes.
*/
data->disable_intr_reqr = 1;
init_waitqueue_head(&data->alert_wait_queue);
if (data->pdata->alert_gpio != -1) {
/* IRQ for therm alert */
data->irq_num = gpio_to_irq(data->pdata->alert_gpio);
err = request_irq(data->irq_num,
tmon_alert_isr,
IRQF_TRIGGER_LOW,
"tmon alert",
data);
dev_info(&client->dev, "\n Tmon IRQ registered for gpio:%d\n",
data->pdata->alert_gpio);
if (!err) {
/*Disable now and enable only when sysfs
alert is opened */
disable_irq_nosync(data->irq_num);
data->alert_info =
devm_kzalloc(&client->dev,
sizeof(struct i2c_smbus_alert_setup),
GFP_KERNEL);
if (!data->alert_info) {
err = -ENOMEM;
goto err_irq;
}
data->alert_info->alert_edge_triggered = 1;
data->ara = i2c_setup_smbus_alert(client->adapter,
data->alert_info);
} else {
dev_warn(&client->dev,
"failed to get irq for alert gpio:%d\n",
data->pdata->alert_gpio);
return err;
}
}
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
err = device_create_file(&client->dev,
&tmp411_attr[i].dev_attr);
if (err)
goto err_exit;
}
INIT_DELAYED_WORK(&data->tmon_work, tmon_update);
schedule_delayed_work(&data->tmon_work,
msecs_to_jiffies(data->pdata->delta_time));
dev_info(&client->dev, "Temperature Monitor enabled\n");
return 0;
err_exit:
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
device_remove_file(&client->dev, &tmp411_attr[i].dev_attr);
i2c_unregister_device(data->ara);
err_irq:
free_irq(data->irq_num, data);
return err;
}
static int tmon_tmp411_remove(struct i2c_client *client)
{
struct tmon_info *data = i2c_get_clientdata(client);
int i;
cancel_delayed_work(&data->tmon_work);
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
device_remove_file(&client->dev, &tmp411_attr[i].dev_attr);
free_irq(data->irq_num, data);
i2c_unregister_device(data->ara);
kfree(data->alert_info);
data->alert_info = NULL;
kfree(data);
data = NULL;
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tmon_tmp411_suspend(struct device *dev)
{
int i;
struct i2c_client *client = to_i2c_client(dev);
struct tmon_info *data = i2c_get_clientdata(client);
u8 tmp;
int err;
/* save temperature limits for restore during resume */
for (i = 0; i < 2; i++) {
/*
* High byte must be read first immediately followed
* by the low byte
*/
TMON_RD(client, REG_TEMP_LOW_LIMIT_MSB_RD[i], &tmp);
temp_low_limit[i] = ((u16)tmp) << 8;
if ((i == REMOTE) || (i == LOCAL && tmon_device == TMP411)) {
TMON_RD(client, REG_TEMP_LOW_LIMIT_LSB[i], &tmp);
temp_low_limit[i] |= tmp;
}
TMON_RD(client, REG_TEMP_HIGH_LIMIT_MSB_RD[i], &tmp);
temp_high_limit[i] = ((u16)tmp) << 8;
if ((i == REMOTE) || (i == LOCAL && tmon_device == TMP411)) {
TMON_RD(client, REG_TEMP_HIGH_LIMIT_LSB[i], &tmp);
temp_high_limit[i] |= tmp;
}
TMON_RD(client, REG_TEMP_CRIT_LIMIT[i], &temp_crit_limit[i]);
}
TMON_RD(client, REG_CON_RATE_READ, &conv_rate);
TMON_RD(client, REG_CFG_READ, &config);
cancel_delayed_work_sync(&data->tmon_work);
return 0;
}
static int tmon_tmp411_resume(struct device *dev)
{
int i;
int err;
u8 curr_cnfg;
u8 limit_correction = 0;
struct i2c_client *client = to_i2c_client(dev);
struct tmon_info *data = i2c_get_clientdata(client);
TMON_RD(client, REG_CFG_READ, &curr_cnfg);
/* Stop the temperature conversions */
curr_cnfg = (curr_cnfg & (~CFG_STANDBY));
TMON_WRT(client, REG_CFG_WRITE, curr_cnfg);
/* Restore temperature limits */
for (i = 0; i < 2; i++) {
TMON_WRT(client,
REG_TEMP_HIGH_LIMIT_MSB_WRT[i],
(temp_high_limit[i] >> 8) + limit_correction);
if ((i == REMOTE) || (i == LOCAL && tmon_device == TMP411)) {
TMON_WRT(client,
REG_TEMP_HIGH_LIMIT_LSB[i],
(temp_high_limit[i] & 0xFF));
}
TMON_WRT(client,
REG_TEMP_LOW_LIMIT_MSB_WRT[i],
(temp_low_limit[i] >> 8) + limit_correction);
if ((i == REMOTE) || (i == LOCAL && tmon_device == TMP411)) {
TMON_WRT(client,
REG_TEMP_LOW_LIMIT_LSB[i],
(temp_low_limit[i] & 0xFF));
}
TMON_WRT(client,
REG_TEMP_CRIT_LIMIT[i],
temp_crit_limit[i] + limit_correction);
}
TMON_WRT(client, REG_CONV_RATE_WRITE, conv_rate);
/* Start the temperature conversion and restore config */
TMON_WRT(client, REG_CFG_WRITE, config);
/* FIXME: Is it required to wait for one temperature conversion? */
schedule_delayed_work(&data->tmon_work,
msecs_to_jiffies(data->pdata->delta_time));
return 0;
}
#endif
static const struct dev_pm_ops tegra_tmp411_dev_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend = tmon_tmp411_suspend,
.resume = tmon_tmp411_resume,
#endif
};
/* tmon-tmp411 driver struct */
static const struct i2c_device_id tmon_tmp411_id[] = {
{"tmon-tmp411-sensor", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, tmon_tmp411_id);
static struct i2c_driver tmon_tmp411_driver = {
.driver = {
.name = "tmon-tmp411-sensor",
#ifdef CONFIG_PM_SLEEP
.pm = &tegra_tmp411_dev_pm_ops,
#endif
},
.probe = tmon_tmp411_probe,
.remove = tmon_tmp411_remove,
.id_table = tmon_tmp411_id,
};
static int __init tmon_tmp411_module_init(void)
{
return i2c_add_driver(&tmon_tmp411_driver);
}
static void __exit tmon_tmp411_module_exit(void)
{
i2c_del_driver(&tmon_tmp411_driver);
}
module_init(tmon_tmp411_module_init);
module_exit(tmon_tmp411_module_exit);
MODULE_AUTHOR("Manoj Chourasia <mchourasia@nvidia.com>");
MODULE_DESCRIPTION("Temperature Monitor module");
MODULE_LICENSE("GPL");