/* * 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 . * * drivers/hwmon/tmon-tmp411.c * */ /* Note: Copied temperature conversion code from tmp401 driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 "); MODULE_DESCRIPTION("Temperature Monitor module"); MODULE_LICENSE("GPL");