drivers: make tegra-throughput driver always post fps
[linux-2.6.git] / drivers / misc / nct1008.c
index 53b1766..61cbdca 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Driver for NCT1008, temperature monitoring device from ON Semiconductors
  *
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2013, NVIDIA CORPORATION. All rights reserved.
  *
  * 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
@@ -22,7 +22,6 @@
 
 
 #include <linux/interrupt.h>
-#include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
@@ -31,8 +30,8 @@
 #include <linux/device.h>
 #include <linux/nct1008.h>
 #include <linux/delay.h>
-
-#define DRIVER_NAME "nct1008"
+#include <linux/thermal.h>
+#include <linux/regulator/consumer.h>
 
 /* Register Addresses */
 #define LOCAL_TEMP_RD                  0x00
@@ -45,6 +44,7 @@
 #define LOCAL_TEMP_LO_LIMIT_RD         0x06
 
 #define EXT_TEMP_HI_LIMIT_HI_BYTE_RD   0x07
+#define EXT_TEMP_LO_LIMIT_HI_BYTE_RD   0x08
 
 #define CONFIG_WR                      0x09
 #define CONV_RATE_WR                   0x0A
@@ -52,8 +52,9 @@
 #define LOCAL_TEMP_LO_LIMIT_WR         0x0C
 #define EXT_TEMP_HI_LIMIT_HI_BYTE_WR   0x0D
 #define EXT_TEMP_LO_LIMIT_HI_BYTE_WR   0x0E
-#define EXT_TEMP_RD_LOW                        0x10
+#define ONE_SHOT                       0x0F
 #define OFFSET_WR                      0x11
+#define OFFSET_QUARTER_WR              0x12
 #define EXT_THERM_LIMIT_WR             0x19
 #define LOCAL_THERM_LIMIT_WR           0x20
 #define THERM_HYSTERESIS_WR            0x21
 
 #define MAX_STR_PRINT 50
 
+#define MAX_CONV_TIME_ONESHOT_MS (52)
+#define CELSIUS_TO_MILLICELSIUS(x) ((x)*1000)
+#define MILLICELSIUS_TO_CELSIUS(x) ((x)/1000)
+
+#define POWER_ON_DELAY 20 /*ms*/
+
 struct nct1008_data {
+       struct workqueue_struct *workqueue;
        struct work_struct work;
        struct i2c_client *client;
        struct nct1008_platform_data plat_data;
        struct mutex mutex;
+       struct dentry *dent;
        u8 config;
-       u8 *limits;
-       u8 limits_sz;
-       void (*alarm_fn)(bool raised);
+       enum nct1008_chip chip;
+       struct regulator *nct_reg;
+       long current_lo_limit;
+       long current_hi_limit;
+       int conv_period_ms;
+       long etemp;
+       int shutdown_complete;
+
+       struct thermal_zone_device *nct_int;
+       struct thermal_zone_device *nct_ext;
 };
 
-static inline u8 value_to_temperature(bool extended, u8 value)
+static int conv_period_ms_table[] =
+       {16000, 8000, 4000, 2000, 1000, 500, 250, 125, 63, 32, 16};
+
+static inline s16 value_to_temperature(bool extended, u8 value)
 {
-       return extended ? (u8)(value - EXTENDED_RANGE_OFFSET) : value;
+       return extended ? (s16)(value - EXTENDED_RANGE_OFFSET) : (s16)value;
 }
 
-static inline u8 temperature_to_value(bool extended, u8 temp)
+static inline u8 temperature_to_value(bool extended, s16 temp)
 {
-       return extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : temp;
+       return extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : (u8)temp;
+}
+
+static int nct1008_write_reg(struct i2c_client *client, u8 reg, u16 value)
+{
+       int ret = 0;
+       struct nct1008_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->mutex);
+       if (data && data->shutdown_complete) {
+               mutex_unlock(&data->mutex);
+               return -ENODEV;
+       }
+
+       ret = i2c_smbus_write_byte_data(client, reg, value);
+       mutex_unlock(&data->mutex);
+
+       if (ret < 0)
+               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+       return ret;
 }
 
-static int nct1008_get_temp(struct device *dev, u8 *pTemp)
+static int nct1008_read_reg(struct i2c_client *client, u8 reg)
+{
+       int ret = 0;
+       struct nct1008_data *data = i2c_get_clientdata(client);
+       mutex_lock(&data->mutex);
+       if (data && data->shutdown_complete) {
+               mutex_unlock(&data->mutex);
+               return -ENODEV;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+       mutex_unlock(&data->mutex);
+
+       if (ret < 0)
+               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int nct1008_get_temp(struct device *dev, long *etemp, long *itemp)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
-       u8 temp1, temp2, temp;
+       s16 temp_local;
+       u8 temp_ext_lo;
+       s16 temp_ext_hi;
+       long temp_ext_milli;
+       long temp_local_milli;
        u8 value;
-       value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
-       if (value < 0)
-               goto error;
-       temp1 = value_to_temperature(pdata->ext_range, value);
 
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LOW);
-       if (value < 0)
-               goto error;
-       temp2 = (value >> 6);
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
-       if (value < 0)
-               goto error;
-       temp = value_to_temperature(pdata->ext_range, value);
-       if (temp2 > 0)
-               *pTemp = max((int)temp1, (int)temp + 1);
-       else
-               *pTemp = max(temp1, temp);
+       /* Read Local Temp */
+       if (itemp) {
+               value = nct1008_read_reg(client, LOCAL_TEMP_RD);
+               if (value < 0)
+                       goto error;
+               temp_local = value_to_temperature(pdata->ext_range, value);
+               temp_local_milli = CELSIUS_TO_MILLICELSIUS(temp_local);
+
+               *itemp = temp_local_milli;
+       }
+
+       /* Read External Temp */
+       if (etemp) {
+               value = nct1008_read_reg(client, EXT_TEMP_RD_LO);
+               if (value < 0)
+                       goto error;
+               temp_ext_lo = (value >> 6);
+
+               value = nct1008_read_reg(client, EXT_TEMP_RD_HI);
+               if (value < 0)
+                       goto error;
+               temp_ext_hi = value_to_temperature(pdata->ext_range, value);
+
+               temp_ext_milli = CELSIUS_TO_MILLICELSIUS(temp_ext_hi) +
+                                       temp_ext_lo * 250;
+
+               *etemp = temp_ext_milli;
+       }
 
        return 0;
 error:
@@ -131,24 +205,24 @@ static ssize_t nct1008_show_temp(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
-       u8 temp1 = 0;
-       u8 temp = 0;
+       s16 temp1 = 0;
+       s16 temp = 0;
        u8 temp2 = 0;
-       u8 value;
+       int value = 0;
 
        if (!dev || !buf || !attr)
                return -EINVAL;
 
-       value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
+       value = nct1008_read_reg(client, LOCAL_TEMP_RD);
        if (value < 0)
                goto error;
        temp1 = value_to_temperature(pdata->ext_range, value);
 
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LOW);
+       value = nct1008_read_reg(client, EXT_TEMP_RD_LO);
        if (value < 0)
                goto error;
        temp2 = (value >> 6);
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
+       value = nct1008_read_reg(client, EXT_TEMP_RD_HI);
        if (value < 0)
                goto error;
        temp = value_to_temperature(pdata->ext_range, value);
@@ -157,8 +231,8 @@ static ssize_t nct1008_show_temp(struct device *dev,
                temp1, temp, temp2 * 25);
 
 error:
-       snprintf(buf, MAX_STR_PRINT, " Rd Error\n");
-       return value;
+       return snprintf(buf, MAX_STR_PRINT,
+               "Error read local/ext temperature\n");
 }
 
 static ssize_t nct1008_show_temp_overheat(struct device *dev,
@@ -167,27 +241,26 @@ static ssize_t nct1008_show_temp_overheat(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
-       u8 value;
-       u8 temp, temp2;
+       int value;
+       s16 temp, temp2;
 
        /* Local temperature h/w shutdown limit */
-       value = i2c_smbus_read_byte_data(client, LOCAL_THERM_LIMIT_WR);
+       value = nct1008_read_reg(client, LOCAL_THERM_LIMIT_WR);
        if (value < 0)
                goto error;
        temp = value_to_temperature(pdata->ext_range, value);
 
        /* External temperature h/w shutdown limit */
-       value = i2c_smbus_read_byte_data(client, EXT_THERM_LIMIT_WR);
+       value = nct1008_read_reg(client, EXT_THERM_LIMIT_WR);
        if (value < 0)
                goto error;
        temp2 = value_to_temperature(pdata->ext_range, value);
 
        return snprintf(buf, MAX_STR_PRINT, "%d %d\n", temp, temp2);
 error:
-       snprintf(buf, MAX_STR_PRINT, " Rd overheat Error\n");
        dev_err(dev, "%s: failed to read temperature-overheat "
                "\n", __func__);
-       return value;
+       return snprintf(buf, MAX_STR_PRINT, " Rd overheat Error\n");
 }
 
 static ssize_t nct1008_set_temp_overheat(struct device *dev,
@@ -197,14 +270,14 @@ static ssize_t nct1008_set_temp_overheat(struct device *dev,
        long int num;
        int err;
        u8 temp;
-       u8 currTemp;
+       long currTemp;
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
        char bufTemp[MAX_STR_PRINT];
        char bufOverheat[MAX_STR_PRINT];
        unsigned int ret;
 
-       if (strict_strtoul(buf, 0, &num)) {
+       if (strict_strtol(buf, 0, &num)) {
                dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
                        __LINE__, __func__);
                return -EINVAL;
@@ -215,28 +288,30 @@ static ssize_t nct1008_set_temp_overheat(struct device *dev,
                return -EINVAL;
        }
        /* check for system power down */
-       err = nct1008_get_temp(dev, &currTemp);
-       if (err < 0)
+       err = nct1008_get_temp(dev, &currTemp, NULL);
+       if (err)
                goto error;
 
+       currTemp = MILLICELSIUS_TO_CELSIUS(currTemp);
+
        if (currTemp >= (int)num) {
                ret = nct1008_show_temp(dev, attr, bufTemp);
                ret = nct1008_show_temp_overheat(dev, attr, bufOverheat);
                dev_err(dev, "\nCurrent temp: %s ", bufTemp);
                dev_err(dev, "\nOld overheat limit: %s ", bufOverheat);
-               dev_err(dev, "\nReset from overheat: curr temp=%d, "
+               dev_err(dev, "\nReset from overheat: curr temp=%ld, "
                        "new overheat temp=%d\n\n", currTemp, (int)num);
        }
 
        /* External temperature h/w shutdown limit */
-       temp = temperature_to_value(pdata->ext_range, (u8)num);
-       err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, temp);
+       temp = temperature_to_value(pdata->ext_range, (s16)num);
+       err = nct1008_write_reg(client, EXT_THERM_LIMIT_WR, temp);
        if (err < 0)
                goto error;
 
        /* Local temperature h/w shutdown limit */
-       temp = temperature_to_value(pdata->ext_range, (u8)num);
-       err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, temp);
+       temp = temperature_to_value(pdata->ext_range, (s16)num);
+       err = nct1008_write_reg(client, LOCAL_THERM_LIMIT_WR, temp);
        if (err < 0)
                goto error;
        return count;
@@ -251,26 +326,24 @@ static ssize_t nct1008_show_temp_alert(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
-       u8 value;
-       u8 temp, temp2;
-       /* External Temperature Throttling limit */
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_RD);
+       int value;
+       s16 temp_hi, temp_lo;
+       /* External Temperature Throttling hi-limit */
+       value = nct1008_read_reg(client, EXT_TEMP_HI_LIMIT_HI_BYTE_RD);
        if (value < 0)
                goto error;
-       temp2 = value_to_temperature(pdata->ext_range, value);
+       temp_hi = value_to_temperature(pdata->ext_range, value);
 
-       /* Local Temperature Throttling limit */
-       value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_HI_LIMIT_RD);
+       /* External Temperature Throttling lo-limit */
+       value = nct1008_read_reg(client, EXT_TEMP_LO_LIMIT_HI_BYTE_RD);
        if (value < 0)
                goto error;
-       temp = value_to_temperature(pdata->ext_range, value);
+       temp_lo = value_to_temperature(pdata->ext_range, value);
 
-       return snprintf(buf, MAX_STR_PRINT, "%d %d\n", temp, temp2);
+       return snprintf(buf, MAX_STR_PRINT, "lo:%d hi:%d\n", temp_lo, temp_hi);
 error:
-       snprintf(buf, MAX_STR_PRINT, " Rd overheat Error\n");
-       dev_err(dev, "%s: failed to read temperature-overheat "
-               "\n", __func__);
-       return value;
+       dev_err(dev, "%s: failed to read temperature-alert\n", __func__);
+       return snprintf(buf, MAX_STR_PRINT, " Rd alert Error\n");
 }
 
 static ssize_t nct1008_set_temp_alert(struct device *dev,
@@ -283,7 +356,7 @@ static ssize_t nct1008_set_temp_alert(struct device *dev,
        struct i2c_client *client = to_i2c_client(dev);
        struct nct1008_platform_data *pdata = client->dev.platform_data;
 
-       if (strict_strtoul(buf, 0, &num)) {
+       if (strict_strtol(buf, 0, &num)) {
                dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
                        __LINE__, __func__);
                return -EINVAL;
@@ -295,15 +368,13 @@ static ssize_t nct1008_set_temp_alert(struct device *dev,
        }
 
        /* External Temperature Throttling limit */
-       value = temperature_to_value(pdata->ext_range, num);
-       err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR,
-               value);
+       value = temperature_to_value(pdata->ext_range, (s16)num);
+       err = nct1008_write_reg(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR, value);
        if (err < 0)
                goto error;
 
        /* Local Temperature Throttling limit */
-       err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR,
-               value);
+       err = nct1008_write_reg(client, LOCAL_TEMP_HI_LIMIT_WR, value);
        if (err < 0)
                goto error;
 
@@ -318,24 +389,37 @@ static ssize_t nct1008_show_ext_temp(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       signed int temp_value = 0;
-       u8 data = 0;
+       struct nct1008_platform_data *pdata = client->dev.platform_data;
+       s16 temp_value;
+       int data = 0;
+       int data_lo;
 
        if (!dev || !buf || !attr)
                return -EINVAL;
 
-       data = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
-       if (data < 0) {
+       /* When reading the full external temperature value, read the
+        * LSB first. This causes the MSB to be locked (that is, the
+        * ADC does not write to it) until it is read */
+       data_lo = nct1008_read_reg(client, EXT_TEMP_RD_LO);
+       if (data_lo < 0) {
                dev_err(&client->dev, "%s: failed to read "
-                       "ext_temperature\n", __func__);
-               return -EINVAL;
+                       "ext_temperature, i2c error=%d\n", __func__, data_lo);
+               goto error;
        }
 
-       temp_value = (signed int)data;
+       data = nct1008_read_reg(client, EXT_TEMP_RD_HI);
+       if (data < 0) {
+               dev_err(&client->dev, "%s: failed to read "
+                       "ext_temperature, i2c error=%d\n", __func__, data);
+               goto error;
+       }
 
-       data = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO);
+       temp_value = value_to_temperature(pdata->ext_range, data);
 
-       return sprintf(buf, "%d.%d\n", temp_value, (25 * (data >> 6)));
+       return snprintf(buf, MAX_STR_PRINT, "%d.%d\n", temp_value,
+               (25 * (data_lo >> 6)));
+error:
+       return snprintf(buf, MAX_STR_PRINT, "Error read ext temperature\n");
 }
 
 static DEVICE_ATTR(temperature, S_IRUGO, nct1008_show_temp, NULL);
@@ -357,262 +441,553 @@ static const struct attribute_group nct1008_attr_group = {
        .attrs = nct1008_attributes,
 };
 
-static void nct1008_enable(struct i2c_client *client)
+static int nct1008_thermal_set_limits(struct nct1008_data *data,
+                                     long lo_limit_milli,
+                                     long hi_limit_milli)
 {
-       struct nct1008_data *data = i2c_get_clientdata(client);
+       int err;
+       u8 value;
+       bool extended_range = data->plat_data.ext_range;
+       long lo_limit = MILLICELSIUS_TO_CELSIUS(lo_limit_milli);
+       long hi_limit = MILLICELSIUS_TO_CELSIUS(hi_limit_milli);
+
+       if (lo_limit >= hi_limit)
+               return -EINVAL;
+
+       if (data->current_lo_limit != lo_limit) {
+               value = temperature_to_value(extended_range, lo_limit);
+               pr_debug("%s: set lo_limit %ld\n", __func__, lo_limit);
+               err = nct1008_write_reg(data->client,
+                               EXT_TEMP_LO_LIMIT_HI_BYTE_WR, value);
+               if (err)
+                       return err;
+
+               data->current_lo_limit = lo_limit;
+       }
+
+       if (data->current_hi_limit != hi_limit) {
+               value = temperature_to_value(extended_range, hi_limit);
+               pr_debug("%s: set hi_limit %ld\n", __func__, hi_limit);
+               err = nct1008_write_reg(data->client,
+                               EXT_TEMP_HI_LIMIT_HI_BYTE_WR, value);
+               if (err)
+                       return err;
 
-       i2c_smbus_write_byte_data(client, CONFIG_WR,
-                                 data->config & ~STANDBY_BIT);
+               data->current_hi_limit = hi_limit;
+       }
+
+       return 0;
 }
 
-static void nct1008_disable(struct i2c_client *client)
+#ifdef CONFIG_THERMAL
+static void nct1008_update(struct nct1008_data *data)
 {
-       struct nct1008_data *data = i2c_get_clientdata(client);
+       struct thermal_zone_device *thz = data->nct_ext;
+       long low_temp = 0, high_temp = NCT1008_MAX_TEMP * 1000;
+       struct thermal_trip_info *trip_state;
+       long temp, trip_temp, hysteresis_temp;
+       int count;
+       enum events type = 0;
+
+       if (!thz)
+               return;
 
-       i2c_smbus_write_byte_data(client, CONFIG_WR,
-                                 data->config | STANDBY_BIT);
+       thermal_zone_device_update(thz);
+
+       thz->ops->get_temp(thz, &temp);
+
+       for (count = 0; count < thz->trips; count++) {
+               trip_state = &data->plat_data.trips[count];
+               trip_temp = trip_state->trip_temp;
+               hysteresis_temp = trip_temp - trip_state->hysteresis;
+               if ((trip_state->trip_type == THERMAL_TRIP_PASSIVE) &&
+                   !trip_state->tripped)
+                       hysteresis_temp = trip_temp;
+
+               if ((trip_temp >= temp) && (trip_temp < high_temp)) {
+                       high_temp = trip_temp;
+                       type = THERMAL_AUX1;
+               }
+
+               if ((hysteresis_temp < temp) && (hysteresis_temp > low_temp)) {
+                       low_temp = hysteresis_temp;
+                       type = THERMAL_AUX0;
+               }
+       }
+
+       thermal_generate_netlink_event(thz->id, type);
+       nct1008_thermal_set_limits(data, low_temp, high_temp);
 }
 
-static int nct1008_disable_alert(struct nct1008_data *data)
+static int nct1008_ext_get_temp(struct thermal_zone_device *thz,
+                                       unsigned long *temp)
 {
+       struct nct1008_data *data = thz->devdata;
        struct i2c_client *client = data->client;
-       int ret = 0;
-       u8 val;
-
-       /*
-        * Disable ALERT# output, because these chips don't implement
-        * SMBus alert correctly; they should only hold the alert line
-        * low briefly.
-        */
-       val = i2c_smbus_read_byte_data(data->client, CONFIG_RD);
-       ret = i2c_smbus_write_byte_data(client, CONFIG_WR, val | ALERT_BIT);
-       if (ret)
-               pr_err("%s: fail to disable alert#\n", __func__);
+       struct nct1008_platform_data *pdata = client->dev.platform_data;
+       s16 temp_ext_hi;
+       s16 temp_ext_lo;
+       long temp_ext_milli;
+       u8 value;
 
-       return ret;
+       /* Read External Temp */
+       value = nct1008_read_reg(client, EXT_TEMP_RD_LO);
+       if (value < 0)
+               return -1;
+       temp_ext_lo = (value >> 6);
+
+       value = nct1008_read_reg(client, EXT_TEMP_RD_HI);
+       if (value < 0)
+               return -1;
+       temp_ext_hi = value_to_temperature(pdata->ext_range, value);
+
+       temp_ext_milli = CELSIUS_TO_MILLICELSIUS(temp_ext_hi) +
+                        temp_ext_lo * 250;
+       *temp = temp_ext_milli;
+       data->etemp = temp_ext_milli;
+
+       return 0;
 }
 
-static int nct1008_enable_alert(struct nct1008_data *data)
+static int nct1008_ext_bind(struct thermal_zone_device *thz,
+                           struct thermal_cooling_device *cdev)
 {
-       u8 val;
-       int ret;
-
-       val = i2c_smbus_read_byte_data(data->client, CONFIG_RD);
-       val &= ~(ALERT_BIT | THERM2_BIT);
-       ret = i2c_smbus_write_byte_data(data->client, CONFIG_WR, val);
-       if (ret) {
-               pr_err("%s: fail to enable alert#\n", __func__);
-               return ret;
+       struct nct1008_data *data = thz->devdata;
+       int i;
+       bool bind = false;
+
+       for (i = 0; i < data->plat_data.num_trips; i++) {
+               if (!strcmp(data->plat_data.trips[i].cdev_type, cdev->type)) {
+                       thermal_zone_bind_cooling_device(thz, i, cdev,
+                                       data->plat_data.trips[i].upper,
+                                       data->plat_data.trips[i].lower);
+                       bind = true;
+               }
        }
 
-       return ret;
+       if (bind)
+               nct1008_update(data);
+
+       return 0;
 }
 
-static bool throttle_enb;
-static void therm_throttle(struct nct1008_data *data, bool enable)
+static int nct1008_ext_unbind(struct thermal_zone_device *thz,
+                             struct thermal_cooling_device *cdev)
 {
-       if (!data->alarm_fn) {
-               pr_err("system too hot. no way to cool down!\n");
-               return;
+       struct nct1008_data *data = thz->devdata;
+       int i;
+
+       for (i = 0; i < data->plat_data.num_trips; i++) {
+               if (!strcmp(data->plat_data.trips[i].cdev_type, cdev->type))
+                       thermal_zone_unbind_cooling_device(thz, i, cdev);
        }
+       return 0;
+}
 
-       if (throttle_enb != enable) {
-               mutex_lock(&data->mutex);
-               data->alarm_fn(enable);
-               throttle_enb = enable;
-               mutex_unlock(&data->mutex);
+static int nct1008_ext_get_trip_temp(struct thermal_zone_device *thz,
+                                    int trip,
+                                    unsigned long *temp)
+{
+       struct nct1008_data *data = thz->devdata;
+       struct thermal_trip_info *trip_state = &data->plat_data.trips[trip];
+
+       *temp = trip_state->trip_temp;
+
+       if (trip_state->trip_type != THERMAL_TRIP_PASSIVE)
+               return 0;
+
+       if (thz->temperature >= *temp) {
+               trip_state->tripped = true;
+       } else if (trip_state->tripped) {
+               *temp -= trip_state->hysteresis;
+               if (thz->temperature < *temp)
+                       trip_state->tripped = false;
        }
+
+       return 0;
 }
 
-#define ALERT_HYSTERESIS       3
-static int edp_thermal_zone_val = -1;
-static int current_hi_limit = -1;
-static int current_lo_limit = -1;
+static int nct1008_ext_set_trip_temp(struct thermal_zone_device *thz,
+                                    int trip,
+                                    unsigned long temp)
+{
+       struct nct1008_data *data = thz->devdata;
 
-static void nct1008_work_func(struct work_struct *work)
+       data->plat_data.trips[trip].trip_temp = temp;
+       nct1008_update(data);
+       return 0;
+}
+
+static int nct1008_ext_get_trip_type(struct thermal_zone_device *thz,
+                                    int trip,
+                                    enum thermal_trip_type *type)
 {
-       struct nct1008_data *data = container_of(work, struct nct1008_data,
-                                               work);
-       bool extended_range = data->plat_data.ext_range;
-       u8 temperature, value;
-       int err = 0, i;
-       int nentries = data->limits_sz;
-       int lo_limit = 0, hi_limit = 0;
-       int intr_status = i2c_smbus_read_byte_data(data->client, STATUS_RD);
-       bool throttle = false;
-
-       err = nct1008_get_temp(&data->client->dev, &temperature);
-       if (err) {
-               pr_err("%s: get temp fail(%d)", __func__, err);
-               return;
+       struct nct1008_data *data = thz->devdata;
+
+       *type = data->plat_data.trips[trip].trip_type;
+       return 0;
+}
+
+static int nct1008_ext_get_trend(struct thermal_zone_device *thz,
+                                int trip,
+                                enum thermal_trend *trend)
+{
+       struct nct1008_data *data = thz->devdata;
+       struct thermal_trip_info *trip_state;
+
+       trip_state = &data->plat_data.trips[trip];
+
+       switch (trip_state->trip_type) {
+       case THERMAL_TRIP_ACTIVE:
+               /* aggressive active cooling */
+               *trend = THERMAL_TREND_RAISING;
+               break;
+       case THERMAL_TRIP_PASSIVE:
+               if (data->etemp > trip_state->trip_temp)
+                       *trend = THERMAL_TREND_RAISING;
+               else
+                       *trend = THERMAL_TREND_DROPPING;
+               break;
+       default:
+               return -EINVAL;
        }
 
-       intr_status &= (BIT(3) | BIT(4));
-       if (!intr_status)
-               return;
+       return 0;
+}
 
-       nct1008_disable_alert(data);
+static int nct1008_int_get_temp(struct thermal_zone_device *thz,
+                               unsigned long *temp)
+{
+       struct nct1008_data *data = thz->devdata;
+       struct i2c_client *client = data->client;
+       struct nct1008_platform_data *pdata = client->dev.platform_data;
+       s16 temp_local;
+       long temp_local_milli;
+       u8 value;
 
-       if (temperature < data->limits[0]) {
-               lo_limit = 0;
-               hi_limit = data->limits[0];
-       } else if (temperature >= data->limits[nentries-1]) {
-               lo_limit = data->limits[nentries-1] - ALERT_HYSTERESIS;
-               hi_limit = data->plat_data.shutdown_ext_limit;
-               throttle = true;
-       } else {
-               for (i = 0; (i + 1) < nentries; i++) {
-                       if (temperature >= data->limits[i] &&
-                           temperature < data->limits[i + 1]) {
-                               lo_limit = data->limits[i] - ALERT_HYSTERESIS;
-                               hi_limit = data->limits[i + 1];
-                               break;
-                       }
-               }
-       }
+       /* Read Local Temp */
+       value = nct1008_read_reg(client, LOCAL_TEMP_RD);
+       if (value < 0)
+               return -1;
+       temp_local = value_to_temperature(pdata->ext_range, value);
 
-       if (lo_limit == hi_limit) {
-               err = -ENODATA;
-               goto out;
-       }
+       temp_local_milli = CELSIUS_TO_MILLICELSIUS(temp_local);
+       *temp = temp_local_milli;
 
-       if (current_lo_limit == lo_limit && current_hi_limit == hi_limit)
-               goto out;
+       return 0;
+}
 
-       if (current_lo_limit != lo_limit) {
-               value = temperature_to_value(extended_range, lo_limit);
-               pr_debug("%s: %d\n", __func__, value);
-               err = i2c_smbus_write_byte_data(data->client,
-                       EXT_TEMP_LO_LIMIT_HI_BYTE_WR, value);
-               if (err)
-                       goto out;
+static int nct1008_int_bind(struct thermal_zone_device *thz,
+                           struct thermal_cooling_device *cdev)
+{
+       return 0;
+}
 
-               current_lo_limit = lo_limit;
-       }
+static int nct1008_int_get_trip_temp(struct thermal_zone_device *thz,
+                                    int trip,
+                                    unsigned long *temp)
+{
+       return -1;
+}
 
-       if (current_hi_limit != hi_limit) {
-               value = temperature_to_value(extended_range, hi_limit);
-               pr_debug("%s: %d\n", __func__, value);
-               err = i2c_smbus_write_byte_data(data->client,
-                       EXT_TEMP_HI_LIMIT_HI_BYTE_WR, value);
-               if (err)
-                       goto out;
+static int nct1008_int_get_trip_type(struct thermal_zone_device *thz,
+                                    int trip,
+                                    enum thermal_trip_type *type)
+{
+       return -1;
+}
 
-               current_hi_limit = hi_limit;
-       }
+static struct thermal_zone_device_ops nct_int_ops = {
+       .get_temp = nct1008_int_get_temp,
+       .bind = nct1008_int_bind,
+       .unbind = nct1008_int_bind,
+       .get_trip_type = nct1008_int_get_trip_type,
+       .get_trip_temp = nct1008_int_get_trip_temp,
+};
+
+static struct thermal_zone_device_ops nct_ext_ops = {
+       .get_temp = nct1008_ext_get_temp,
+       .bind = nct1008_ext_bind,
+       .unbind = nct1008_ext_unbind,
+       .get_trip_type = nct1008_ext_get_trip_type,
+       .get_trip_temp = nct1008_ext_get_trip_temp,
+       .set_trip_temp = nct1008_ext_set_trip_temp,
+       .get_trend = nct1008_ext_get_trend,
+};
+#else
+static void nct1008_update(struct nct1008_data *data)
+{
+}
+#endif /* CONFIG_THERMAL */
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static void print_reg(const char *reg_name, struct seq_file *s,
+               int offset)
+{
+       struct nct1008_data *nct_data = s->private;
+       int ret;
+
+       ret = nct1008_read_reg(nct_data->client, offset);
+       if (ret >= 0)
+               seq_printf(s, "Reg %s Addr = 0x%02x Reg 0x%02x "
+               "Value 0x%02x\n", reg_name,
+               nct_data->client->addr,
+                       offset, ret);
+       else
+               seq_printf(s, "%s: line=%d, i2c read error=%d\n",
+               __func__, __LINE__, ret);
+}
+
+static int dbg_nct1008_show(struct seq_file *s, void *unused)
+{
+       seq_printf(s, "nct1008 nct72 Registers\n");
+       seq_printf(s, "------------------\n");
+       print_reg("Local Temp Value    ",     s, 0x00);
+       print_reg("Ext Temp Value Hi   ",     s, 0x01);
+       print_reg("Status              ",     s, 0x02);
+       print_reg("Configuration       ",     s, 0x03);
+       print_reg("Conversion Rate     ",     s, 0x04);
+       print_reg("Local Temp Hi Limit ",     s, 0x05);
+       print_reg("Local Temp Lo Limit ",     s, 0x06);
+       print_reg("Ext Temp Hi Limit Hi",     s, 0x07);
+       print_reg("Ext Temp Hi Limit Lo",     s, 0x13);
+       print_reg("Ext Temp Lo Limit Hi",     s, 0x08);
+       print_reg("Ext Temp Lo Limit Lo",     s, 0x14);
+       print_reg("Ext Temp Value Lo   ",     s, 0x10);
+       print_reg("Ext Temp Offset Hi  ",     s, 0x11);
+       print_reg("Ext Temp Offset Lo  ",     s, 0x12);
+       print_reg("Ext THERM Limit     ",     s, 0x19);
+       print_reg("Local THERM Limit   ",     s, 0x20);
+       print_reg("THERM Hysteresis    ",     s, 0x21);
+       print_reg("Consecutive ALERT   ",     s, 0x22);
+       return 0;
+}
+
+static int dbg_nct1008_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_nct1008_show, inode->i_private);
+}
 
-       if (throttle) {
-               /* start throttling */
-               therm_throttle(data, true);
-               goto out;
+static const struct file_operations debug_fops = {
+       .open           = dbg_nct1008_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int nct1008_debuginit(struct nct1008_data *nct)
+{
+       int err = 0;
+       struct dentry *d;
+       if (nct->chip == NCT72)
+               d = debugfs_create_file("nct72", S_IRUGO, NULL,
+                               (void *)nct, &debug_fops);
+       else
+               d = debugfs_create_file("nct1008", S_IRUGO, NULL,
+                               (void *)nct, &debug_fops);
+       if ((!d) || IS_ERR(d)) {
+               dev_err(&nct->client->dev, "Error: %s debugfs_create_file"
+                       " returned an error\n", __func__);
+               err = -ENOENT;
+               goto end;
+       }
+       if (d == ERR_PTR(-ENODEV)) {
+               dev_err(&nct->client->dev, "Error: %s debugfs not supported "
+                       "error=-ENODEV\n", __func__);
+               err = -ENODEV;
        } else {
-               /* switch off throttling */
-               therm_throttle(data, false);
+               nct->dent = d;
        }
+end:
+       return err;
+}
+#else
+static int nct1008_debuginit(struct nct1008_data *nct)
+{
+       return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
 
-       /* inform edp governor */
-       if (edp_thermal_zone_val != temperature)
-               // FIXME: Move this direct tegra_ function call to be called via
-               //        a pointer in 'struct nct1008_data' (like 'alarm_fn')
-               tegra_edp_update_thermal_zone(temperature);
+static int nct1008_enable(struct i2c_client *client)
+{
+       struct nct1008_data *data = i2c_get_clientdata(client);
+       int err;
 
-       edp_thermal_zone_val = temperature;
+       err = nct1008_write_reg(client, CONFIG_WR, data->config);
+       if (err < 0)
+               dev_err(&client->dev, "%s, line=%d, i2c write error=%d\n",
+               __func__, __LINE__, err);
+       return err;
+}
 
-out:
-       nct1008_enable_alert(data);
+static int nct1008_disable(struct i2c_client *client)
+{
+       struct nct1008_data *data = i2c_get_clientdata(client);
+       int err;
 
-       if (err)
-               pr_err("%s: fail(%d)\n", __func__, err);
-       else
-               pr_debug("%s: done\n", __func__);
+       err = nct1008_write_reg(client, CONFIG_WR,
+                               data->config | STANDBY_BIT);
+       if (err < 0)
+               dev_err(&client->dev, "%s, line=%d, i2c write error=%d\n",
+               __func__, __LINE__, err);
+       return err;
+}
+
+static int nct1008_within_limits(struct nct1008_data *data)
+{
+       int intr_status;
+
+       intr_status = nct1008_read_reg(data->client, STATUS_RD);
+       if (intr_status < 0)
+               return intr_status;
+
+       return !(intr_status & (BIT(3) | BIT(4)));
+}
+
+static void nct1008_work_func(struct work_struct *work)
+{
+       struct nct1008_data *data = container_of(work, struct nct1008_data,
+                                               work);
+       int err;
+       struct timespec ts;
+
+       err = nct1008_disable(data->client);
+       if (err == -ENODEV)
+               return;
+
+       if (!nct1008_within_limits(data))
+               nct1008_update(data);
+
+       /* Initiate one-shot conversion */
+       nct1008_write_reg(data->client, ONE_SHOT, 0x1);
+
+       /* Give hardware necessary time to finish conversion */
+       ts = ns_to_timespec(MAX_CONV_TIME_ONESHOT_MS * 1000 * 1000);
+       hrtimer_nanosleep(&ts, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+
+       nct1008_read_reg(data->client, STATUS_RD);
+
+       nct1008_enable(data->client);
+
+       enable_irq(data->client->irq);
 }
 
 static irqreturn_t nct1008_irq(int irq, void *dev_id)
 {
        struct nct1008_data *data = dev_id;
 
-       schedule_work(&data->work);
+       disable_irq_nosync(irq);
+       queue_work(data->workqueue, &data->work);
+
        return IRQ_HANDLED;
 }
 
-static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
+static void nct1008_power_control(struct nct1008_data *data, bool is_enable)
+{
+       int ret;
+       if (!data->nct_reg) {
+               data->nct_reg = regulator_get(&data->client->dev, "vdd");
+               if (IS_ERR_OR_NULL(data->nct_reg)) {
+                       if (PTR_ERR(data->nct_reg) == -ENODEV)
+                               dev_info(&data->client->dev,
+                                       "no regulator found for vdd."
+                                       " Assuming vdd is always powered");
+                       else
+                               dev_warn(&data->client->dev, "Error [%ld] in "
+                                       "getting the regulator handle for"
+                                       " vdd\n", PTR_ERR(data->nct_reg));
+                       data->nct_reg = NULL;
+                       return;
+               }
+       }
+       if (is_enable) {
+               ret = regulator_enable(data->nct_reg);
+               msleep(POWER_ON_DELAY);
+       } else {
+               ret = regulator_disable(data->nct_reg);
+       }
+
+       if (ret < 0)
+               dev_err(&data->client->dev, "Error in %s rail vdd_nct%s, "
+                       "error %d\n", (is_enable) ? "enabling" : "disabling",
+                       (data->chip == NCT72) ? "72" : "1008",
+                       ret);
+       else
+               dev_info(&data->client->dev, "success in %s rail vdd_nct%s\n",
+                       (is_enable) ? "enabling" : "disabling",
+                       (data->chip == NCT72) ? "72" : "1008");
+}
+
+static int nct1008_configure_sensor(struct nct1008_data *data)
 {
        struct i2c_client *client = data->client;
        struct nct1008_platform_data *pdata = client->dev.platform_data;
-       u8 value, temp, temp2;
+       u8 value;
+       s16 temp;
+       u8 temp2;
        int err;
-       int hi_limit;
 
        if (!pdata || !pdata->supported_hwrev)
                return -ENODEV;
 
-       /*
-        * Initial Configuration - device is placed in standby and
-        * ALERT/THERM2 pin is configured as THERM2
-        */
-       data->config = pdata->ext_range ?
-               (STANDBY_BIT | EXTENDED_RANGE_BIT) : STANDBY_BIT;
-
-       if (pdata->thermal_zones_sz)
-               data->config &= ~(THERM2_BIT | ALERT_BIT);
-       else
-               data->config |= (ALERT_BIT | THERM2_BIT);
-
-       value = data->config;
-       err = i2c_smbus_write_byte_data(client, CONFIG_WR, value);
-       if (err)
-               goto error;
-
-       /* Temperature conversion rate */
-       err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate);
+       /* Initially place in Standby */
+       err = nct1008_write_reg(client, CONFIG_WR, STANDBY_BIT);
        if (err)
                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);
+                                       pdata->shutdown_ext_limit);
+       err = nct1008_write_reg(client, EXT_THERM_LIMIT_WR, value);
        if (err)
                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);
+                                       pdata->shutdown_local_limit);
+       err = nct1008_write_reg(client, LOCAL_THERM_LIMIT_WR, value);
        if (err)
                goto error;
 
-       if (pdata->thermal_zones_sz) {
-               data->limits = pdata->thermal_zones;
-               data->limits_sz = pdata->thermal_zones_sz;
+       /* set extended range mode if needed */
+       if (pdata->ext_range)
+               data->config |= EXTENDED_RANGE_BIT;
+       data->config &= ~(THERM2_BIT | ALERT_BIT);
+
+       err = nct1008_write_reg(client, CONFIG_WR, data->config);
+       if (err)
+               goto error;
 
-               /* setup alarm */
-               hi_limit = pdata->thermal_zones[0];
+       /* Temperature conversion rate */
+       err = nct1008_write_reg(client, CONV_RATE_WR, pdata->conv_rate);
+       if (err)
+               goto error;
 
-               err = i2c_smbus_write_byte_data(client, EXT_TEMP_LO_LIMIT_HI_BYTE_WR, 0);
-               if (err)
-                       goto error;
+       data->conv_period_ms = conv_period_ms_table[pdata->conv_rate];
 
-               err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR, NCT1008_MAX_TEMP);
-               if (err)
-                       goto error;
+       /* Setup local hi and lo limits */
+       err = nct1008_write_reg(client,
+               LOCAL_TEMP_HI_LIMIT_WR, NCT1008_MAX_TEMP);
+       if (err)
+               goto error;
 
-               err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_LO_LIMIT_WR, 0);
-               if (err)
-                       goto error;
-       } else {
-               /* External Temperature Throttling limit */
-               hi_limit = pdata->throttling_ext_limit;
-       }
+       err = nct1008_write_reg(client, LOCAL_TEMP_LO_LIMIT_WR, 0);
+       if (err)
+               goto error;
 
-       value = temperature_to_value(pdata->ext_range, hi_limit);
-       err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR,
-                       value);
+       /* Setup external hi and lo limits */
+       err = nct1008_write_reg(client, EXT_TEMP_LO_LIMIT_HI_BYTE_WR, 0);
+       if (err)
+               goto error;
+       err = nct1008_write_reg(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR,
+                       NCT1008_MAX_TEMP);
        if (err)
                goto error;
 
        /* read initial temperature */
-       value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
+       value = nct1008_read_reg(client, LOCAL_TEMP_RD);
        if (value < 0) {
                err = value;
                goto error;
@@ -620,13 +995,13 @@ static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
        temp = value_to_temperature(pdata->ext_range, value);
        dev_dbg(&client->dev, "\n initial local temp = %d ", temp);
 
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LOW);
+       value = nct1008_read_reg(client, EXT_TEMP_RD_LO);
        if (value < 0) {
                err = value;
                goto error;
        }
        temp2 = (value >> 6);
-       value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
+       value = nct1008_read_reg(client, EXT_TEMP_RD_HI);
        if (value < 0) {
                err = value;
                goto error;
@@ -640,24 +1015,31 @@ static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
                dev_dbg(&client->dev, "\n initial ext temp = %d.0 deg", temp);
 
        /* Remote channel offset */
-       err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset);
+       err = nct1008_write_reg(client, OFFSET_WR, pdata->offset / 4);
        if (err < 0)
                goto error;
 
-       /* THERM hysteresis */
-       err = i2c_smbus_write_byte_data(client, THERM_HYSTERESIS_WR,
-                       pdata->hysteresis);
+       /* Remote channel offset fraction (quarters) */
+       err = nct1008_write_reg(client, OFFSET_QUARTER_WR,
+                                       (pdata->offset % 4) << 6);
        if (err < 0)
                goto error;
 
-       /* register sysfs hooks */
-       err = sysfs_create_group(&client->dev.kobj, &nct1008_attr_group);
-       if (err < 0) {
-               dev_err(&client->dev, "\n sysfs create err=%d ", err);
+       /* Reset current hi/lo limit values with register values */
+       value = nct1008_read_reg(data->client, EXT_TEMP_LO_LIMIT_HI_BYTE_RD);
+       if (value < 0) {
+               err = value;
                goto error;
        }
+       data->current_lo_limit = value_to_temperature(pdata->ext_range, value);
+
+       value = nct1008_read_reg(data->client, EXT_TEMP_HI_LIMIT_HI_BYTE_RD);
+       if (value < 0) {
+               err = value;
+               goto error;
+       }
+       data->current_hi_limit = value_to_temperature(pdata->ext_range, value);
 
-       data->alarm_fn = pdata->alarm_fn;
        return 0;
 error:
        dev_err(&client->dev, "\n exit %s, err=%d ", __func__, err);
@@ -666,33 +1048,60 @@ error:
 
 static int __devinit nct1008_configure_irq(struct nct1008_data *data)
 {
+       data->workqueue = create_singlethread_workqueue((data->chip == NCT72) \
+                                                       ? "nct72" : "nct1008");
+
        INIT_WORK(&data->work, nct1008_work_func);
 
        if (data->client->irq < 0)
                return 0;
        else
                return request_irq(data->client->irq, nct1008_irq,
-                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                       DRIVER_NAME, data);
+                       IRQF_TRIGGER_LOW,
+                       (data->chip == NCT72) ? "nct72" : "nct1008",
+                       data);
 }
 
+/*
+ * Manufacturer(OnSemi) recommended sequence for
+ * Extended Range mode is as follows
+ * 1. Place in Standby
+ * 2. Scale the THERM and ALERT limits
+ *     appropriately(for Extended Range mode).
+ * 3. Enable Extended Range mode.
+ *     ALERT mask/THERM2 mode may be done here
+ *     as these are not critical
+ * 4. Set Conversion Rate as required
+ * 5. Take device out of Standby
+ */
+
+/*
+ * function nct1008_probe takes care of initial configuration
+ */
 static int __devinit nct1008_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
        struct nct1008_data *data;
        int err;
+       int i;
+       int mask = 0;
+       char nct_int_name[THERMAL_NAME_LENGTH];
+       char nct_ext_name[THERMAL_NAME_LENGTH];
 
        data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL);
-
        if (!data)
                return -ENOMEM;
 
        data->client = client;
+       data->chip = id->driver_data;
        memcpy(&data->plat_data, client->dev.platform_data,
                sizeof(struct nct1008_platform_data));
        i2c_set_clientdata(client, data);
        mutex_init(&data->mutex);
 
+       nct1008_power_control(data, true);
+       /* extended range recommended steps 1 through 4 taken care
+        * in nct1008_configure_sensor function */
        err = nct1008_configure_sensor(data);   /* sensor is in standby */
        if (err < 0) {
                dev_err(&client->dev, "\n error file: %s : %s(), line=%d ",
@@ -708,12 +1117,76 @@ static int __devinit nct1008_probe(struct i2c_client *client,
        }
        dev_info(&client->dev, "%s: initialized\n", __func__);
 
-       nct1008_enable(client);         /* sensor is running */
+       /* extended range recommended step 5 is in nct1008_enable function */
+       err = nct1008_enable(client);           /* sensor is running */
+       if (err < 0) {
+               dev_err(&client->dev, "Error: %s, line=%d, error=%d\n",
+                       __func__, __LINE__, err);
+               goto error;
+       }
 
+       /* register sysfs hooks */
+       err = sysfs_create_group(&client->dev.kobj, &nct1008_attr_group);
+       if (err < 0) {
+               dev_err(&client->dev, "\n sysfs create err=%d ", err);
+               goto error;
+       }
+
+       err = nct1008_debuginit(data);
+       if (err < 0)
+               err = 0; /* without debugfs we may continue */
+
+#ifdef CONFIG_THERMAL
+       for (i = 0; i < data->plat_data.num_trips; i++)
+               mask |= (1 << i);
+
+       if (data->plat_data.loc_name) {
+               strcpy(nct_int_name, "nct_int_");
+               strcpy(nct_ext_name, "nct_ext_");
+               strncat(nct_int_name, data->plat_data.loc_name,
+                       (THERMAL_NAME_LENGTH - strlen("nct_int_")) - 1);
+               strncat(nct_ext_name, data->plat_data.loc_name,
+                       (THERMAL_NAME_LENGTH - strlen("nct_ext_")) - 1);
+       } else {
+               strcpy(nct_int_name, "nct_int");
+               strcpy(nct_ext_name, "nct_ext");
+       }
+
+       data->nct_int = thermal_zone_device_register(nct_int_name,
+                                               0,
+                                               0x0,
+                                               data,
+                                               &nct_int_ops,
+                                               NULL,
+                                               2000,
+                                               0);
+       if (IS_ERR_OR_NULL(data->nct_int))
+               goto error;
+
+       data->nct_ext = thermal_zone_device_register(nct_ext_name,
+                                       data->plat_data.num_trips,
+                                       mask,
+                                       data,
+                                       &nct_ext_ops,
+                                       data->plat_data.tzp,
+                                       data->plat_data.passive_delay,
+                                       0);
+       if (IS_ERR_OR_NULL(data->nct_ext)) {
+               thermal_zone_device_unregister(data->nct_int);
+               data->nct_int = NULL;
+               goto error;
+       }
+
+       nct1008_update(data);
+#endif
        return 0;
 
 error:
        dev_err(&client->dev, "\n exit %s, err=%d ", __func__, err);
+       nct1008_power_control(data, false);
+       mutex_destroy(&data->mutex);
+       if (data->nct_reg)
+               regulator_put(data->nct_reg);
        kfree(data);
        return err;
 }
@@ -722,52 +1195,92 @@ static int __devexit nct1008_remove(struct i2c_client *client)
 {
        struct nct1008_data *data = i2c_get_clientdata(client);
 
+       if (data->dent)
+               debugfs_remove(data->dent);
+
        free_irq(data->client->irq, data);
        cancel_work_sync(&data->work);
        sysfs_remove_group(&client->dev.kobj, &nct1008_attr_group);
+       nct1008_power_control(data, false);
+       if (data->nct_reg)
+               regulator_put(data->nct_reg);
+       mutex_destroy(&data->mutex);
        kfree(data);
 
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int nct1008_suspend(struct i2c_client *client, pm_message_t state)
+static void nct1008_shutdown(struct i2c_client *client)
 {
-       disable_irq(client->irq);
-       nct1008_disable(client);
+       struct nct1008_data *data = i2c_get_clientdata(client);
+       if (client->irq)
+               disable_irq(client->irq);
 
-       return 0;
+       cancel_work_sync(&data->work);
+
+       mutex_lock(&data->mutex);
+       data->shutdown_complete = 1;
+       mutex_unlock(&data->mutex);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nct1008_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int err;
+       struct nct1008_data *data = i2c_get_clientdata(client);
+
+       disable_irq(client->irq);
+       err = nct1008_disable(client);
+       nct1008_power_control(data, false);
+       return err;
 }
 
-static int nct1008_resume(struct i2c_client *client)
+static int nct1008_resume(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
+       int err;
        struct nct1008_data *data = i2c_get_clientdata(client);
 
-       nct1008_enable(client);
+       nct1008_power_control(data, true);
+       nct1008_configure_sensor(data);
+       err = nct1008_enable(client);
+       if (err < 0) {
+               dev_err(&client->dev, "Error: %s, error=%d\n",
+                       __func__, err);
+               return err;
+       }
+       nct1008_update(data);
        enable_irq(client->irq);
-       schedule_work(&data->work);
 
        return 0;
 }
+
+static const struct dev_pm_ops nct1008_pm_ops = {
+       .suspend        = nct1008_suspend,
+       .resume         = nct1008_resume,
+};
+
 #endif
 
 static const struct i2c_device_id nct1008_id[] = {
-       { DRIVER_NAME, 0 },
-       { }
+       { "nct1008", NCT1008 },
+       { "nct72", NCT72},
+       {}
 };
 MODULE_DEVICE_TABLE(i2c, nct1008_id);
 
 static struct i2c_driver nct1008_driver = {
        .driver = {
-               .name   = DRIVER_NAME,
+               .name   = "nct1008_nct72",
+#ifdef CONFIG_PM_SLEEP
+               .pm = &nct1008_pm_ops,
+#endif
        },
        .probe          = nct1008_probe,
        .remove         = __devexit_p(nct1008_remove),
        .id_table       = nct1008_id,
-#ifdef CONFIG_PM
-       .suspend        = nct1008_suspend,
-       .resume         = nct1008_resume,
-#endif
+       .shutdown       = nct1008_shutdown,
 };
 
 static int __init nct1008_init(void)
@@ -780,8 +1293,8 @@ static void __exit nct1008_exit(void)
        i2c_del_driver(&nct1008_driver);
 }
 
-MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008");
+MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008/NCT72");
 MODULE_LICENSE("GPL");
 
-module_init (nct1008_init);
-module_exit (nct1008_exit);
+module_init(nct1008_init);
+module_exit(nct1008_exit);