Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index bf7c687..de5ded3 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -22,3 +22,62 @@
 	  requires a 2.10.7/3.0.2 or later lm-sensors userspace.
 
 	  Say Y if your user-space is new enough.
+
+config THERMAL_PM8901
+	tristate "Qualcomm PM8901 Temperature Alarm"
+	depends on PMIC8901
+	depends on THERMAL
+	default n
+	help
+	  This enables a thermal Sysfs driver for the PMIC 8901 device. It
+	  shows up in Sysfs as a thermal zone with multiple trip points.
+	  Enabling the thermal zone device via the mode file results in
+	  shifting over temperature shutdown control of the PMIC from hardware
+	  to software.
+
+config THERMAL_PM8058
+	tristate "Qualcomm PM8058 Temperature Alarm"
+	depends on PMIC8058
+	depends on THERMAL
+	depends on SENSORS_MSM_ADC
+	default n
+	help
+	  This enables a thermal Sysfs driver for the PMIC 8058 device. It
+	  shows up in Sysfs as a thermal zone with multiple trip points.
+	  Enabling the thermal zone device via the mode file results in
+	  shifting over temperature shutdown control of the PMIC from hardware
+	  to software.
+
+config THERMAL_MSM_POPMEM
+	tristate "Qualcomm MSM POP memory temperature sensor"
+	depends on THERMAL
+	default n
+	help
+	  This enables a thermal sysfs driver for MSM POP memory. It shows up in
+	  sysfs as a thermal zone with one trip point. Due to hardware
+	  limitations, the temperatures are reported as "Low Temperature" (20 C)
+	  "Normal Temperature" (50 C) and "Out of Spec High Temperature" (85 C).
+	  This driver is designed to be used in conjunction with a user space
+	  application to make all policy decisions.
+
+config THERMAL_TSENS
+	tristate "Qualcomm Tsens Temperature Alarm"
+	depends on THERMAL
+	default n
+	help
+	  This enables the thermal sysfs driver for the Tsens device. It shows
+	  up in Sysfs as a thermal zone with mutiple trip points. Disabling the
+	  thermal zone device via the mode file results in disabling the sensor.
+	  Also able to set threshold temperature for both hot and cold and update
+	  when a threshold is reached.
+
+config THERMAL_PM8XXX
+	tristate "Qualcomm PMIC PM8xxx Temperature Alarm"
+	depends on THERMAL
+	depends on MFD_PM8XXX
+	help
+	  This enables a thermal Sysfs driver for the PMIC PM8xxx devices. It
+	  shows up in Sysfs as a thermal zone with multiple trip points.
+	  Enabling the thermal zone device via the mode file results in
+	  shifting over temperature shutdown control of the PMIC from hardware
+	  to software.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 31108a0..d1bb466 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,3 +3,8 @@
 #
 
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
+obj-$(CONFIG_THERMAL_PM8901)	+= pmic8901-tm.o
+obj-$(CONFIG_THERMAL_PM8058)	+= pmic8058-tm.o
+obj-$(CONFIG_THERMAL_MSM_POPMEM)	+= msm_popmem-tm.o
+obj-$(CONFIG_THERMAL_TSENS)	+= msm_tsens.o
+obj-$(CONFIG_THERMAL_PM8XXX)	+= pm8xxx-tm.o
diff --git a/drivers/thermal/msm_popmem-tm.c b/drivers/thermal/msm_popmem-tm.c
new file mode 100644
index 0000000..583b2db
--- /dev/null
+++ b/drivers/thermal/msm_popmem-tm.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/thermal.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/msm_memtypes.h>
+
+#define POP_MEM_LPDDR1_REFRESH_MASK	0x00000700
+#define POP_MEM_LPDDR1_REFRESH_SHIFT	0x8
+
+#define POP_MEM_LPDDR2_REFRESH_MASK	0x00000007
+#define POP_MEM_LPDDR2_REFRESH_SHIFT	0x0
+
+#define POP_MEM_REFRESH_REG		0x3C
+
+#define POP_MEM_LOW_TEMPERATURE		25000
+#define POP_MEM_NORMAL_TEMPERATURE	50000
+#define POP_MEM_HIGH_TEMPERATURE	85000
+
+#define POP_MEM_TRIP_OUT_OF_SPEC	0
+#define POP_MEM_TRIP_NUM		1
+
+struct pop_mem_tm_device {
+	unsigned long			baseaddr;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			refresh_mask;
+	unsigned int			refresh_shift;
+};
+
+
+static int pop_mem_tm_read_refresh(struct pop_mem_tm_device *tm,
+				   unsigned int *ref_rate){
+	unsigned int ref;
+
+	ref = __raw_readl(tm->baseaddr + POP_MEM_REFRESH_REG);
+	*ref_rate = (ref & tm->refresh_mask) >> tm->refresh_shift;
+
+	return 0;
+}
+
+
+static int pop_mem_tm_get_temperature(struct thermal_zone_device *thermal,
+			       unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+	unsigned int ref_rate;
+	int rc;
+
+	if (!tm || !temperature)
+		return -EINVAL;
+
+	rc = pop_mem_tm_read_refresh(tm, &ref_rate);
+	if (rc < 0)
+		return rc;
+
+	switch (ref_rate) {
+	case 0:
+	case 1:
+	case 2:
+		*temperature = POP_MEM_LOW_TEMPERATURE;
+		break;
+	case 3:
+	case 4:
+		*temperature = POP_MEM_NORMAL_TEMPERATURE;
+		break;
+	case 5:
+	case 6:
+	case 7:
+		*temperature = POP_MEM_HIGH_TEMPERATURE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pop_mem_tm_get_trip_type(struct thermal_zone_device *thermal,
+				    int trip, enum thermal_trip_type *type)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !type)
+		return -EINVAL;
+
+	if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int pop_mem_tm_get_trip_temperature(struct thermal_zone_device *thermal,
+				    int trip, unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !temperature)
+		return -EINVAL;
+
+	if (trip == POP_MEM_TRIP_OUT_OF_SPEC)
+		*temperature = POP_MEM_HIGH_TEMPERATURE;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static int pop_mem_tm_get_crit_temperature(struct thermal_zone_device *thermal,
+				    unsigned long *temperature)
+{
+	struct pop_mem_tm_device *tm = thermal->devdata;
+
+	if (!tm || !temperature)
+		return -EINVAL;
+
+	*temperature = POP_MEM_HIGH_TEMPERATURE;
+
+	return 0;
+}
+
+
+static struct thermal_zone_device_ops pop_mem_thermal_zone_ops = {
+	.get_temp = pop_mem_tm_get_temperature,
+	.get_trip_type = pop_mem_tm_get_trip_type,
+	.get_trip_temp = pop_mem_tm_get_trip_temperature,
+	.get_crit_temp = pop_mem_tm_get_crit_temperature,
+};
+
+
+static int __devinit pop_mem_tm_probe(struct platform_device *pdev)
+{
+	int rc, len, numcontrollers;
+	struct resource *controller_mem = NULL;
+	struct resource *res_mem = NULL;
+	struct pop_mem_tm_device *tmdev = NULL;
+	void __iomem *base = NULL;
+
+	rc = len = 0;
+	numcontrollers = get_num_populated_chipselects();
+
+	if (pdev->id >= numcontrollers) {
+		pr_err("%s: memory controller %d does not exist", __func__,
+			pdev->id);
+		rc = -ENODEV;
+		goto fail;
+	}
+
+	controller_mem = platform_get_resource_byname(pdev,
+						  IORESOURCE_MEM, "physbase");
+	if (!controller_mem) {
+		pr_err("%s: could not get resources for controller %d",
+			__func__, pdev->id);
+		rc = -EFAULT;
+		goto fail;
+	}
+
+	len = controller_mem->end - controller_mem->start + 1;
+
+	res_mem = request_mem_region(controller_mem->start, len,
+				     controller_mem->name);
+	if (!res_mem) {
+		pr_err("%s: Could not request memory region: "
+			"start=%p, len=%d\n", __func__,
+			(void *) controller_mem->start, len);
+		rc = -EBUSY;
+		goto fail;
+
+	}
+
+	base = ioremap(res_mem->start, len);
+	if (!base) {
+		pr_err("%s: Could not ioremap: start=%p, len=%d\n",
+			 __func__, (void *) controller_mem->start, len);
+		rc = -EBUSY;
+		goto fail;
+
+	}
+
+	tmdev = kzalloc(sizeof(*tmdev), GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	if (numcontrollers == 1) {
+		tmdev->refresh_mask = POP_MEM_LPDDR1_REFRESH_MASK;
+		tmdev->refresh_shift = POP_MEM_LPDDR1_REFRESH_SHIFT;
+	} else {
+		tmdev->refresh_mask = POP_MEM_LPDDR2_REFRESH_MASK;
+		tmdev->refresh_shift = POP_MEM_LPDDR2_REFRESH_SHIFT;
+	}
+	tmdev->baseaddr = (unsigned long) base;
+	tmdev->tz_dev = thermal_zone_device_register("msm_popmem_tz",
+						     POP_MEM_TRIP_NUM, tmdev,
+						     &pop_mem_thermal_zone_ops,
+						     0, 0, 0, 0);
+
+	if (tmdev->tz_dev == NULL) {
+		pr_err("%s: thermal_zone_device_register() failed.\n",
+			__func__);
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, tmdev);
+
+	pr_notice("%s: device %d probed successfully\n", __func__, pdev->id);
+
+	return rc;
+
+fail:
+	if (base)
+		iounmap(base);
+	if (res_mem)
+		release_mem_region(controller_mem->start, len);
+	kfree(tmdev);
+
+	return rc;
+}
+
+static int __devexit pop_mem_tm_remove(struct platform_device *pdev)
+{
+
+	int len;
+	struct pop_mem_tm_device *tmdev = platform_get_drvdata(pdev);
+	struct resource *controller_mem;
+
+	iounmap((void __iomem *)tmdev->baseaddr);
+
+	controller_mem = platform_get_resource_byname(pdev,
+						  IORESOURCE_MEM, "physbase");
+	len = controller_mem->end - controller_mem->start + 1;
+	release_mem_region(controller_mem->start, len);
+
+	thermal_zone_device_unregister(tmdev->tz_dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(tmdev);
+
+	return 0;
+}
+
+static struct platform_driver pop_mem_tm_driver = {
+	.probe          = pop_mem_tm_probe,
+	.remove         = pop_mem_tm_remove,
+	.driver         = {
+		.name = "msm_popmem-tm",
+		.owner = THIS_MODULE
+	},
+};
+
+static int __init pop_mem_tm_init(void)
+{
+	return platform_driver_register(&pop_mem_tm_driver);
+}
+
+static void __exit pop_mem_tm_exit(void)
+{
+	platform_driver_unregister(&pop_mem_tm_driver);
+}
+
+module_init(pop_mem_tm_init);
+module_exit(pop_mem_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Pop memory thermal manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:popmem-tm");
diff --git a/drivers/thermal/msm_tsens.c b/drivers/thermal/msm_tsens.c
new file mode 100644
index 0000000..f4e094e
--- /dev/null
+++ b/drivers/thermal/msm_tsens.c
@@ -0,0 +1,618 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm TSENS Thermal Manager driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+
+/* Trips: from very hot to very cold */
+enum tsens_trip_type {
+	TSENS_TRIP_STAGE3 = 0,
+	TSENS_TRIP_STAGE2,
+	TSENS_TRIP_STAGE1,
+	TSENS_TRIP_STAGE0,
+	TSENS_TRIP_NUM,
+};
+
+#define TSENS_NUM_SENSORS	1 /* There are 5 but only 1 is useful now */
+#define TSENS_CAL_DEGC		30 /* degree C used for calibration */
+#define TSENS_QFPROM_ADDR (MSM_QFPROM_BASE + 0x000000bc)
+#define TSENS_QFPROM_RED_TEMP_SENSOR0_SHIFT 24
+#define TSENS_QFPROM_TEMP_SENSOR0_SHIFT 16
+#define TSENS_QFPROM_TEMP_SENSOR0_MASK (255 << TSENS_QFPROM_TEMP_SENSOR0_SHIFT)
+#define TSENS_SLOPE (0.702)  /* slope in (degrees_C / ADC_code) */
+#define TSENS_FACTOR (1000)  /* convert floating-point into integer */
+#define TSENS_CONFIG 01      /* this setting found to be optimal */
+#define TSENS_CONFIG_SHIFT 28
+#define TSENS_CONFIG_MASK (3 << TSENS_CONFIG_SHIFT)
+#define TSENS_CNTL_ADDR (MSM_CLK_CTL_BASE + 0x00003620)
+#define TSENS_EN (1 << 0)
+#define TSENS_SW_RST (1 << 1)
+#define SENSOR0_EN (1 << 3)
+#define SENSOR1_EN (1 << 4)
+#define SENSOR2_EN (1 << 5)
+#define SENSOR3_EN (1 << 6)
+#define SENSOR4_EN (1 << 7)
+#define TSENS_MIN_STATUS_MASK (1 << 8)
+#define TSENS_LOWER_STATUS_CLR (1 << 9)
+#define TSENS_UPPER_STATUS_CLR (1 << 10)
+#define TSENS_MAX_STATUS_MASK (1 << 11)
+#define TSENS_MEASURE_PERIOD 4 /* 1 sec. default as required by Willie */
+#define TSENS_SLP_CLK_ENA (1 << 24)
+#define TSENS_THRESHOLD_ADDR (MSM_CLK_CTL_BASE + 0x00003624)
+#define TSENS_THRESHOLD_MAX_CODE (0xff)
+#define TSENS_THRESHOLD_MAX_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 24)
+#define TSENS_THRESHOLD_MIN_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 16)
+#define TSENS_THRESHOLD_UPPER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 8)
+#define TSENS_THRESHOLD_LOWER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << 0)
+/* Initial temperature threshold values */
+#define TSENS_LOWER_LIMIT_TH   0x50
+#define TSENS_UPPER_LIMIT_TH   0xdf
+#define TSENS_MIN_LIMIT_TH     0x38
+#define TSENS_MAX_LIMIT_TH     0xff
+
+#define TSENS_S0_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003628)
+#define TSENS_INT_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x0000363c)
+#define TSENS_LOWER_INT_MASK (1 << 1)
+#define TSENS_UPPER_INT_MASK (1 << 2)
+#define TSENS_TRDY_MASK (1 << 7)
+
+struct tsens_tm_device_sensor {
+	struct thermal_zone_device	*tz_dev;
+	enum thermal_device_mode	mode;
+	unsigned int			sensor_num;
+};
+
+struct tsens_tm_device {
+	struct tsens_tm_device_sensor sensor[TSENS_NUM_SENSORS];
+	bool prev_reading_avail;
+	int offset;
+	struct work_struct work;
+};
+
+struct tsens_tm_device *tmdev;
+
+/* Temperature on y axis and ADC-code on x-axis */
+static int tsens_tz_code_to_degC(int adc_code)
+{
+	int degC, degcbeforefactor;
+	degcbeforefactor = adc_code * (int)(TSENS_SLOPE * TSENS_FACTOR)
+				+ tmdev->offset;
+	if (degcbeforefactor == 0)
+		degC = degcbeforefactor;
+	else if (degcbeforefactor > 0)
+		degC = (degcbeforefactor + TSENS_FACTOR/2) / TSENS_FACTOR;
+	else  /* rounding for negative degrees */
+		degC = (degcbeforefactor - TSENS_FACTOR/2) / TSENS_FACTOR;
+	return degC;
+}
+
+static int tsens_tz_degC_to_code(int degC)
+{
+	int code = (degC * TSENS_FACTOR - tmdev->offset
+			+ (int)(TSENS_FACTOR * TSENS_SLOPE)/2)
+			/ (int)(TSENS_FACTOR * TSENS_SLOPE);
+	if (code > 255) /* upper bound */
+		code = 255;
+	else if (code < 0) /* lower bound */
+		code = 0;
+	return code;
+}
+
+static int tsens_tz_get_temp(struct thermal_zone_device *thermal,
+			     unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int code;
+
+	if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED || !temp)
+		return -EINVAL;
+
+	if (!tmdev->prev_reading_avail) {
+		while (!(readl(TSENS_INT_STATUS_ADDR) & TSENS_TRDY_MASK))
+			msleep(1);
+		tmdev->prev_reading_avail = 1;
+	}
+
+	code = readl(TSENS_S0_STATUS_ADDR + (tm_sensor->sensor_num << 2));
+	*temp = tsens_tz_code_to_degC(code);
+
+	return 0;
+}
+
+static int tsens_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || !mode)
+		return -EINVAL;
+
+	*mode = tm_sensor->mode;
+
+	return 0;
+}
+
+static int tsens_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg, mask;
+
+	if (!tm_sensor)
+		return -EINVAL;
+
+	if (mode != tm_sensor->mode) {
+		pr_info("%s: mode: %d --> %d\n", __func__, tm_sensor->mode,
+									 mode);
+
+		reg = readl(TSENS_CNTL_ADDR);
+		mask = 1 << (tm_sensor->sensor_num + 3);
+		if (mode == THERMAL_DEVICE_ENABLED) {
+			writel(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+			reg |= mask | TSENS_SLP_CLK_ENA | TSENS_EN;
+			tmdev->prev_reading_avail = 0;
+		} else {
+			reg &= ~mask;
+			if (!(reg & (((1 << TSENS_NUM_SENSORS) - 1) << 3)))
+				reg &= ~(TSENS_SLP_CLK_ENA | TSENS_EN);
+		}
+
+		writel(reg, TSENS_CNTL_ADDR);
+	}
+	tm_sensor->mode = mode;
+
+	return 0;
+}
+
+static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+	if (!tm_sensor || trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TSENS_TRIP_STAGE2:
+		*type = THERMAL_TRIP_CONFIGURABLE_HI;
+		break;
+	case TSENS_TRIP_STAGE1:
+		*type = THERMAL_TRIP_CONFIGURABLE_LOW;
+		break;
+	case TSENS_TRIP_STAGE0:
+		*type = THERMAL_TRIP_CRITICAL_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal,
+			int trip, enum thermal_trip_activation_mode mode)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_cntl, reg_th, code, hi_code, lo_code, mask;
+
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = 0;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	reg_cntl = readl(TSENS_CNTL_ADDR);
+	reg_th = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> 24;
+		mask = TSENS_MAX_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+		mask = TSENS_UPPER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE1:
+		code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> 0;
+		mask = TSENS_LOWER_STATUS_CLR;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> 16;
+		mask = TSENS_MIN_STATUS_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
+		writel(reg_cntl | mask, TSENS_CNTL_ADDR);
+	else {
+		if (code < lo_code || code > hi_code)
+			return -EINVAL;
+		writel(reg_cntl & ~mask, TSENS_CNTL_ADDR);
+	}
+
+	return 0;
+}
+
+static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg;
+
+	if (!tm_sensor || trip < 0 || !temp)
+		return -EINVAL;
+
+	reg = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		reg = (reg & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> 24;
+		break;
+	case TSENS_TRIP_STAGE2:
+		reg = (reg & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+		break;
+	case TSENS_TRIP_STAGE1:
+		reg = (reg & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> 0;
+		break;
+	case TSENS_TRIP_STAGE0:
+		reg = (reg & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> 16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = tsens_tz_code_to_degC(reg);
+
+	return 0;
+}
+
+static int tsens_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				  unsigned long *temp)
+{
+	return tsens_tz_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp);
+}
+
+static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, long temp)
+{
+	struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+	unsigned int reg_th, reg_cntl;
+	int code, hi_code, lo_code, code_err_chk;
+
+	code_err_chk = code = tsens_tz_degC_to_code(temp);
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	lo_code = 0;
+	hi_code = TSENS_THRESHOLD_MAX_CODE;
+
+	reg_cntl = readl(TSENS_CNTL_ADDR);
+	reg_th = readl(TSENS_THRESHOLD_ADDR);
+	switch (trip) {
+	case TSENS_TRIP_STAGE3:
+		code <<= 24;
+		reg_th &= ~TSENS_THRESHOLD_MAX_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE2:
+		code <<= 8;
+		reg_th &= ~TSENS_THRESHOLD_UPPER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		break;
+	case TSENS_TRIP_STAGE1:
+		reg_th &= ~TSENS_THRESHOLD_LOWER_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_MIN_STATUS_MASK))
+			lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK)
+									>> 16;
+		if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	case TSENS_TRIP_STAGE0:
+		code <<= 16;
+		reg_th &= ~TSENS_THRESHOLD_MIN_LIMIT_MASK;
+
+		if (!(reg_cntl & TSENS_LOWER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK);
+		else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR))
+			hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK)
+									>> 8;
+		else if (!(reg_cntl & TSENS_MAX_STATUS_MASK))
+			hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK)
+									>> 24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (code_err_chk < lo_code || code_err_chk > hi_code)
+		return -EINVAL;
+
+	writel(reg_th | code, TSENS_THRESHOLD_ADDR);
+	return 0;
+}
+
+static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
+	.get_temp = tsens_tz_get_temp,
+	.get_mode = tsens_tz_get_mode,
+	.set_mode = tsens_tz_set_mode,
+	.get_trip_type = tsens_tz_get_trip_type,
+	.activate_trip_type = tsens_tz_activate_trip_type,
+	.get_trip_temp = tsens_tz_get_trip_temp,
+	.set_trip_temp = tsens_tz_set_trip_temp,
+	.get_crit_temp = tsens_tz_get_crit_temp,
+};
+
+static void notify_uspace_tsens_fn(struct work_struct *work)
+{
+	struct tsens_tm_device *tm = container_of(work, struct tsens_tm_device,
+					work);
+	/* Currently only Sensor0 is supported. We added support
+	   to notify only the supported Sensor and this portion
+	   needs to be revisited once other sensors are supported */
+	sysfs_notify(&tm->sensor[0].tz_dev->device.kobj,
+					NULL, "type");
+}
+
+static irqreturn_t tsens_isr(int irq, void *data)
+{
+	unsigned int reg = readl(TSENS_CNTL_ADDR);
+
+	writel(reg | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR,
+			TSENS_CNTL_ADDR);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t tsens_isr_thread(int irq, void *data)
+{
+	struct tsens_tm_device *tm = data;
+	unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
+	bool upper_th_x, lower_th_x;
+	int adc_code;
+
+	mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR);
+	threshold = readl(TSENS_THRESHOLD_ADDR);
+	threshold_low = threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK;
+	threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> 8;
+	reg = sensor = readl(TSENS_CNTL_ADDR);
+	sensor &= (SENSOR0_EN | SENSOR1_EN | SENSOR2_EN |
+						SENSOR3_EN | SENSOR4_EN);
+	sensor >>= 3;
+	for (i = 0; i < TSENS_NUM_SENSORS; i++) {
+		if (sensor & 1) {
+			code = readl(TSENS_S0_STATUS_ADDR + (i << 2));
+			upper_th_x = code >= threshold;
+			lower_th_x = code <= threshold_low;
+			if (upper_th_x)
+				mask |= TSENS_UPPER_STATUS_CLR;
+			if (lower_th_x)
+				mask |= TSENS_LOWER_STATUS_CLR;
+			if (upper_th_x || lower_th_x) {
+				thermal_zone_device_update(
+							tm->sensor[i].tz_dev);
+
+				/* Notify user space */
+				schedule_work(&tm->work);
+				adc_code = readl(TSENS_S0_STATUS_ADDR
+							+ (i << 2));
+				printk(KERN_INFO"\nTrip point triggered by "
+					"current temperature (%d degrees) "
+					"measured by Temperature-Sensor %d\n",
+					tsens_tz_code_to_degC(adc_code), i);
+			}
+		}
+		sensor >>= 1;
+	}
+	writel(reg & mask, TSENS_CNTL_ADDR);
+	return IRQ_HANDLED;
+}
+
+static int __devinit tsens_tm_probe(struct platform_device *pdev)
+{
+	unsigned int reg, i, calib_data, calib_data_backup;
+	int rc;
+
+	calib_data = (readl(TSENS_QFPROM_ADDR) & TSENS_QFPROM_TEMP_SENSOR0_MASK)
+					>> TSENS_QFPROM_TEMP_SENSOR0_SHIFT;
+	calib_data_backup = readl(TSENS_QFPROM_ADDR)
+					>> TSENS_QFPROM_RED_TEMP_SENSOR0_SHIFT;
+
+	if (calib_data_backup)
+		calib_data = calib_data_backup;
+
+	if (!calib_data) {
+		pr_err("%s: No temperature sensor data for calibration"
+						" in QFPROM!\n", __func__);
+		return -ENODEV;
+	}
+
+	tmdev = kzalloc(sizeof(struct tsens_tm_device), GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, tmdev);
+
+	tmdev->offset = TSENS_FACTOR * TSENS_CAL_DEGC
+			- (int)(TSENS_FACTOR * TSENS_SLOPE) * calib_data;
+	tmdev->prev_reading_avail = 0;
+
+	INIT_WORK(&tmdev->work, notify_uspace_tsens_fn);
+
+	reg = readl(TSENS_CNTL_ADDR);
+	writel(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
+	reg |= TSENS_SLP_CLK_ENA | TSENS_EN | (TSENS_MEASURE_PERIOD << 16) |
+		TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
+		TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
+		(((1 << TSENS_NUM_SENSORS) - 1) << 3);
+
+	/* set TSENS_CONFIG bits (bits 29:28 of TSENS_CNTL) to '01';
+		this setting found to be optimal. */
+	reg = (reg & ~TSENS_CONFIG_MASK) | (TSENS_CONFIG << TSENS_CONFIG_SHIFT);
+
+	writel(reg, TSENS_CNTL_ADDR);
+
+	writel((TSENS_LOWER_LIMIT_TH << 0) | (TSENS_UPPER_LIMIT_TH << 8) |
+		(TSENS_MIN_LIMIT_TH << 16) | (TSENS_MAX_LIMIT_TH << 24),
+			TSENS_THRESHOLD_ADDR);
+
+	for (i = 0; i < TSENS_NUM_SENSORS; i++) {
+		char name[17];
+		sprintf(name, "tsens_tz_sensor%d", i);
+
+		tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+		tmdev->sensor[i].tz_dev = thermal_zone_device_register(name,
+				TSENS_TRIP_NUM, &tmdev->sensor[i],
+				&tsens_thermal_zone_ops, 0, 0, 0, 0);
+		if (tmdev->sensor[i].tz_dev == NULL) {
+			pr_err("%s: thermal_zone_device_register() failed.\n",
+			__func__);
+			kfree(tmdev);
+			return -ENODEV;
+		}
+		tmdev->sensor[i].sensor_num = i;
+		thermal_zone_device_update(tmdev->sensor[i].tz_dev);
+		tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED;
+	}
+
+	rc = request_threaded_irq(TSENS_UPPER_LOWER_INT, tsens_isr,
+		tsens_isr_thread, 0, "tsens", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+		kfree(tmdev);
+		return rc;
+	}
+
+	writel(reg & ~((((1 << TSENS_NUM_SENSORS) - 1) << 3)
+			| TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit tsens_tm_remove(struct platform_device *pdev)
+{
+	struct tsens_tm_device *tmdev = platform_get_drvdata(pdev);
+	unsigned int reg, i;
+
+	reg = readl(TSENS_CNTL_ADDR);
+	writel(reg & ~(TSENS_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR);
+
+	for (i = 0; i < TSENS_NUM_SENSORS; i++)
+		thermal_zone_device_unregister(tmdev->sensor[i].tz_dev);
+	platform_set_drvdata(pdev, NULL);
+	free_irq(TSENS_UPPER_LOWER_INT, tmdev);
+	kfree(tmdev);
+
+	return 0;
+}
+
+static struct platform_driver tsens_tm_driver = {
+	.probe	= tsens_tm_probe,
+	.remove	= __devexit_p(tsens_tm_remove),
+	.driver	= {
+		.name = "tsens-tm",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init tsens_init(void)
+{
+	return platform_driver_register(&tsens_tm_driver);
+}
+
+static void __exit tsens_exit(void)
+{
+	platform_driver_unregister(&tsens_tm_driver);
+}
+
+module_init(tsens_init);
+module_exit(tsens_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Temperature Sensor driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:tsens-tm");
diff --git a/drivers/thermal/pm8xxx-tm.c b/drivers/thermal/pm8xxx-tm.c
new file mode 100644
index 0000000..a094aed
--- /dev/null
+++ b/drivers/thermal/pm8xxx-tm.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Qualcomm PMIC PM8xxx Thermal Manager driver
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/tm.h>
+#include <linux/completion.h>
+#include <linux/mfd/pm8921-adc.h>
+
+/* Register TEMP_ALARM_CTRL bits */
+#define	TEMP_ALARM_CTRL_ST3_SD		0x80
+#define	TEMP_ALARM_CTRL_ST2_SD		0x40
+#define	TEMP_ALARM_CTRL_STATUS_MASK	0x30
+#define	TEMP_ALARM_CTRL_STATUS_SHIFT	4
+#define	TEMP_ALARM_CTRL_THRESH_MASK	0x0C
+#define	TEMP_ALARM_CTRL_THRESH_SHIFT	2
+#define	TEMP_ALARM_CTRL_OVRD_ST3	0x02
+#define	TEMP_ALARM_CTRL_OVRD_ST2	0x01
+#define	TEMP_ALARM_CTRL_OVRD_MASK	0x03
+
+#define	TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
+#define	TEMP_STAGE_HYSTERESIS		2000
+
+#define	TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
+#define	TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+/* Register TEMP_ALARM_PWM bits */
+#define	TEMP_ALARM_PWM_EN_MASK		0xC0
+#define	TEMP_ALARM_PWM_EN_SHIFT		6
+#define	TEMP_ALARM_PWM_PER_PRE_MASK	0x38
+#define	TEMP_ALARM_PWM_PER_PRE_SHIFT	3
+#define	TEMP_ALARM_PWM_PER_DIV_MASK	0x07
+#define	TEMP_ALARM_PWM_PER_DIV_SHIFT	0
+
+/* Trips: from critical to less critical */
+#define TRIP_STAGE3			0
+#define TRIP_STAGE2			1
+#define TRIP_STAGE1			2
+#define TRIP_NUM			3
+
+struct pm8xxx_tm_chip {
+	struct pm8xxx_tm_core_data	cdata;
+	struct work_struct		irq_work;
+	struct device			*dev;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			temp;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			stage;
+	unsigned int			tempstat_irq;
+	unsigned int			overtemp_irq;
+	void				*adc_handle;
+};
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int pm8xxx_tm_read_ctrl(struct pm8xxx_tm_chip *chip, u8 *reg)
+{
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent,
+			  chip->cdata.reg_addr_temp_alarm_ctrl, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_readb(0x%03X) failed, rc=%d\n",
+			chip->cdata.tm_name,
+			chip->cdata.reg_addr_temp_alarm_ctrl, rc);
+
+	return rc;
+}
+
+static inline int pm8xxx_tm_write_ctrl(struct pm8xxx_tm_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent,
+			   chip->cdata.reg_addr_temp_alarm_ctrl, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
+		       chip->cdata.tm_name,
+		       chip->cdata.reg_addr_temp_alarm_ctrl, reg, rc);
+
+	return rc;
+}
+
+static inline int pm8xxx_tm_write_pwm(struct pm8xxx_tm_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8xxx_writeb(chip->dev->parent,
+			   chip->cdata.reg_addr_temp_alarm_pwm, reg);
+	if (rc)
+		pr_err("%s: pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n",
+			chip->cdata.tm_name,
+			chip->cdata.reg_addr_temp_alarm_pwm, reg, rc);
+
+	return rc;
+}
+
+static inline int
+pm8xxx_tm_shutdown_override(struct pm8xxx_tm_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	reg &= ~(TEMP_ALARM_CTRL_OVRD_MASK | TEMP_ALARM_CTRL_STATUS_MASK);
+	if (mode == SOFTWARE_OVERRIDE_ENABLED)
+		reg |= (TEMP_ALARM_CTRL_OVRD_ST3 | TEMP_ALARM_CTRL_OVRD_ST2) &
+			TEMP_ALARM_CTRL_OVRD_MASK;
+
+	rc = pm8xxx_tm_write_ctrl(chip, reg);
+
+	return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int pm8xxx_tm_init_temp_no_adc(struct pm8xxx_tm_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+			>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+			>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+
+	if (chip->stage)
+		chip->temp = chip->thresh * TEMP_THRESH_MIN +
+			   (chip->stage - 1) * TEMP_STAGE_STEP +
+			   TEMP_THRESH_MIN;
+	else
+		chip->temp = chip->cdata.default_no_adc_temp;
+
+	return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int pm8xxx_tm_update_temp_no_adc(struct pm8xxx_tm_chip *chip)
+{
+	unsigned int stage;
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+		>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+			>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+
+	if (stage > chip->stage) {
+		/* increasing stage, use lower bound */
+		chip->temp = (stage - 1) * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	} else if (stage < chip->stage) {
+		/* decreasing stage, use upper bound */
+		chip->temp = stage * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	}
+
+	chip->stage = stage;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+				     unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	rc = pm8xxx_tm_update_temp_no_adc(chip);
+	if (rc < 0)
+		return rc;
+
+	*temp = chip->temp;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_temp_pm8921_adc(struct thermal_zone_device *thermal,
+				      unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	struct pm8921_adc_chan_result result = {
+		.physical = 0lu,
+	};
+	int rc;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	*temp = chip->temp;
+
+	rc = pm8921_adc_read(chip->cdata.adc_channel, &result);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
+			chip->cdata.tm_name, rc);
+		return rc;
+	}
+
+	*temp = result.physical;
+	chip->temp = result.physical;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip || !mode)
+		return -EINVAL;
+
+	*mode = chip->mode;
+
+	return 0;
+}
+
+static int pm8xxx_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip)
+		return -EINVAL;
+
+	if (mode != chip->mode) {
+		if (mode == THERMAL_DEVICE_ENABLED)
+			pm8xxx_tm_shutdown_override(chip,
+						    SOFTWARE_OVERRIDE_ENABLED);
+		else
+			pm8xxx_tm_shutdown_override(chip,
+						    SOFTWARE_OVERRIDE_DISABLED);
+	}
+	chip->mode = mode;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	if (trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+	int thresh_temp;
+
+	if (!chip || trip < 0 || !temp)
+		return -EINVAL;
+
+	thresh_temp = chip->thresh * TEMP_THRESH_STEP +
+			TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		thresh_temp += 2 * TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE2:
+		thresh_temp += TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = thresh_temp;
+
+	return 0;
+}
+
+static int pm8xxx_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   unsigned long *temp)
+{
+	struct pm8xxx_tm_chip *chip = thermal->devdata;
+
+	if (!chip || !temp)
+		return -EINVAL;
+
+	*temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+		2 * TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_no_adc = {
+	.get_temp = pm8xxx_tz_get_temp_no_adc,
+	.get_mode = pm8xxx_tz_get_mode,
+	.set_mode = pm8xxx_tz_set_mode,
+	.get_trip_type = pm8xxx_tz_get_trip_type,
+	.get_trip_temp = pm8xxx_tz_get_trip_temp,
+	.get_crit_temp = pm8xxx_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8921_adc = {
+	.get_temp = pm8xxx_tz_get_temp_pm8921_adc,
+	.get_mode = pm8xxx_tz_get_mode,
+	.set_mode = pm8xxx_tz_set_mode,
+	.get_trip_type = pm8xxx_tz_get_trip_type,
+	.get_trip_temp = pm8xxx_tz_get_trip_temp,
+	.get_crit_temp = pm8xxx_tz_get_crit_temp,
+};
+
+static void pm8xxx_tm_work(struct work_struct *work)
+{
+	struct pm8xxx_tm_chip *chip
+		= container_of(work, struct pm8xxx_tm_chip, irq_work);
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		goto bail;
+
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
+		rc = pm8xxx_tm_update_temp_no_adc(chip);
+		if (rc < 0)
+			goto bail;
+		pr_info("%s: Temp Alarm - stage=%u, threshold=%u, "
+			"temp=%lu mC\n", chip->cdata.tm_name, chip->stage,
+			chip->thresh, chip->temp);
+	} else {
+		chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+				>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+		chip->thresh = (reg & TEMP_ALARM_CTRL_THRESH_MASK)
+				>> TEMP_ALARM_CTRL_THRESH_SHIFT;
+		pr_info("%s: Temp Alarm - stage=%u, threshold=%u\n",
+			chip->cdata.tm_name, chip->stage, chip->thresh);
+	}
+
+	/* Clear status bits. */
+	if (reg & (TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD)) {
+		reg &= ~(TEMP_ALARM_CTRL_ST2_SD | TEMP_ALARM_CTRL_ST3_SD
+			 | TEMP_ALARM_CTRL_STATUS_MASK);
+
+		pm8xxx_tm_write_ctrl(chip, reg);
+	}
+
+	thermal_zone_device_update(chip->tz_dev);
+
+	/* Notify user space */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		kobject_uevent(&chip->tz_dev->device.kobj, KOBJ_CHANGE);
+
+bail:
+	enable_irq(chip->tempstat_irq);
+	enable_irq(chip->overtemp_irq);
+}
+
+static irqreturn_t pm8xxx_tm_isr(int irq, void *data)
+{
+	struct pm8xxx_tm_chip *chip = data;
+
+	disable_irq_nosync(chip->tempstat_irq);
+	disable_irq_nosync(chip->overtemp_irq);
+	schedule_work(&chip->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static int pm8xxx_tm_init_reg(struct pm8xxx_tm_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	chip->stage = (reg & TEMP_ALARM_CTRL_STATUS_MASK)
+			>> TEMP_ALARM_CTRL_STATUS_SHIFT;
+	chip->temp = 0;
+
+	/* Use temperature threshold set 0: (105, 125, 145) */
+	chip->thresh = 0;
+	reg = (chip->thresh << TEMP_ALARM_CTRL_THRESH_SHIFT)
+		& TEMP_ALARM_CTRL_THRESH_MASK;
+	rc = pm8xxx_tm_write_ctrl(chip, reg);
+	if (rc < 0)
+		return rc;
+
+	/*
+	 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
+	 * helps cut down on the number of unnecessary interrupts fired when
+	 * changing between thermal stages.  Also, Enable the over temperature
+	 * PWM whenever the PMIC is enabled.
+	 */
+	reg =  (1 << TEMP_ALARM_PWM_EN_SHIFT)
+		| (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
+		| (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
+
+	rc = pm8xxx_tm_write_pwm(chip, reg);
+
+	return rc;
+}
+
+static int __devinit pm8xxx_tm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_tm_core_data *cdata = pdev->dev.platform_data;
+	struct thermal_zone_device_ops *tz_ops;
+	struct pm8xxx_tm_chip *chip;
+	struct resource *res;
+	int rc = 0;
+
+	if (!cdata) {
+		pr_err("missing core data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_tm_chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	memcpy(&(chip->cdata), cdata, sizeof(struct pm8xxx_tm_core_data));
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		chip->cdata.irq_name_temp_stat);
+	if (res) {
+		chip->tempstat_irq = res->start;
+	} else {
+		pr_err("temp stat IRQ not specified\n");
+		goto err_free_chip;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		chip->cdata.irq_name_over_temp);
+	if (res) {
+		chip->overtemp_irq = res->start;
+	} else {
+		pr_err("over temp IRQ not specified\n");
+		goto err_free_chip;
+	}
+
+	/* Select proper thermal zone ops functions based on ADC type. */
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8921_ADC)
+		tz_ops = &pm8xxx_thermal_zone_ops_pm8921_adc;
+	else
+		tz_ops = &pm8xxx_thermal_zone_ops_no_adc;
+
+	chip->tz_dev = thermal_zone_device_register(chip->cdata.tm_name,
+			TRIP_NUM, chip, tz_ops, 0, 0, 0, 0);
+	if (chip->tz_dev == NULL) {
+		pr_err("thermal_zone_device_register() failed.\n");
+		rc = -ENODEV;
+		goto err_free_chip;
+	}
+
+	rc = pm8xxx_tm_init_reg(chip);
+	if (rc < 0)
+		goto err_free_tz;
+	rc = pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc < 0)
+		goto err_free_tz;
+
+	if (chip->cdata.adc_type == PM8XXX_TM_ADC_NONE) {
+		rc = pm8xxx_tm_init_temp_no_adc(chip);
+		if (rc < 0)
+			goto err_free_tz;
+	}
+
+	/* Start in HW control; switch to SW control when user changes mode. */
+	chip->mode = THERMAL_DEVICE_DISABLED;
+	thermal_zone_device_update(chip->tz_dev);
+
+	INIT_WORK(&chip->irq_work, pm8xxx_tm_work);
+
+	rc = request_irq(chip->tempstat_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
+		chip->cdata.irq_name_temp_stat, chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed: %d\n", chip->tempstat_irq, rc);
+		goto err_cancel_work;
+	}
+
+	rc = request_irq(chip->overtemp_irq, pm8xxx_tm_isr, IRQF_TRIGGER_RISING,
+		chip->cdata.irq_name_over_temp, chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed: %d\n", chip->overtemp_irq, rc);
+		goto err_free_irq_tempstat;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	pr_info("OK\n");
+
+	return 0;
+
+err_free_irq_tempstat:
+	free_irq(chip->tempstat_irq, chip);
+err_cancel_work:
+	cancel_work_sync(&chip->irq_work);
+err_free_tz:
+	thermal_zone_device_unregister(chip->tz_dev);
+err_free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_tm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	if (chip) {
+		platform_set_drvdata(pdev, NULL);
+		cancel_work_sync(&chip->irq_work);
+		free_irq(chip->overtemp_irq, chip);
+		free_irq(chip->tempstat_irq, chip);
+		pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+		thermal_zone_device_unregister(chip->tz_dev);
+		kfree(chip);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pm8xxx_tm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	/* Clear override bits in suspend to allow hardware control */
+	pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int pm8xxx_tm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8xxx_tm_chip *chip = platform_get_drvdata(pdev);
+
+	/* Override hardware actions so software can control */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		pm8xxx_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8xxx_tm_pm_ops = {
+	.suspend = pm8xxx_tm_suspend,
+	.resume = pm8xxx_tm_resume,
+};
+
+#define PM8XXX_TM_PM_OPS	(&pm8xxx_tm_pm_ops)
+#else
+#define PM8XXX_TM_PM_OPS	NULL
+#endif
+
+static struct platform_driver pm8xxx_tm_driver = {
+	.probe	= pm8xxx_tm_probe,
+	.remove	= __devexit_p(pm8xxx_tm_remove),
+	.driver	= {
+		.name = PM8XXX_TM_DEV_NAME,
+		.owner = THIS_MODULE,
+		.pm = PM8XXX_TM_PM_OPS,
+	},
+};
+
+static int __init pm8xxx_tm_init(void)
+{
+	return platform_driver_register(&pm8xxx_tm_driver);
+}
+
+static void __exit pm8xxx_tm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_tm_driver);
+}
+
+module_init(pm8xxx_tm_init);
+module_exit(pm8xxx_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8xxx Thermal Manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_TM_DEV_NAME);
diff --git a/drivers/thermal/pmic8058-tm.c b/drivers/thermal/pmic8058-tm.c
new file mode 100644
index 0000000..cc98f37
--- /dev/null
+++ b/drivers/thermal/pmic8058-tm.c
@@ -0,0 +1,509 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PMIC8058 Thermal Manager driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/completion.h>
+
+#include <linux/msm_adc.h>
+
+/* PMIC8058 TEMP_ALRM registers */
+#define	SSBI_REG_TEMP_ALRM_CTRL		0x1B
+#define	SSBI_REG_TEMP_ALRM_PWM		0x9B
+#define	SSBI_REG_TEMP_ALRM_TEST1	0x7A
+#define	SSBI_REG_TEMP_ALRM_TEST2	0xAB
+
+/* TEMP_ALRM_CTRL */
+#define	PM8058_TEMP_ST3_SD		0x80
+#define	PM8058_TEMP_ST2_SD		0x40
+#define	PM8058_TEMP_STATUS_MASK		0x30
+#define	PM8058_TEMP_STATUS_SHIFT	4
+#define	PM8058_TEMP_THRESH_MASK		0x0C
+#define	PM8058_TEMP_THRESH_SHIFT	2
+#define	PM8058_TEMP_OVRD_ST3		0x02
+#define	PM8058_TEMP_OVRD_ST2		0x01
+#define	PM8058_TEMP_OVRD_MASK		0x03
+
+#define	PM8058_TEMP_STAGE_STEP		20000	/* Stage step: 20 C */
+#define	PM8058_TEMP_STAGE_HYSTERESIS	2000
+
+#define	PM8058_TEMP_THRESH_MIN		105000	/* Threshold Min: 105 C */
+#define	PM8058_TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+/* TEMP_ALRM_PWM */
+#define	PM8058_TEMP_PWM_EN_MASK		0xC0
+#define	PM8058_TEMP_PWM_EN_SHIFT	6
+#define	PM8058_TEMP_PWM_PER_PRE_MASK	0x38
+#define	PM8058_TEMP_PWM_PER_PRE_SHIFT	3
+#define	PM8058_TEMP_PWM_PER_DIV_MASK	0x07
+#define	PM8058_TEMP_PWM_PER_DIV_SHIFT	0
+
+/* Trips: from critical to less critical */
+#define PM8058_TRIP_STAGE3	0
+#define PM8058_TRIP_STAGE2	1
+#define PM8058_TRIP_STAGE1	2
+#define PM8058_TRIP_NUM		3
+
+#define PM8058_TEMP_ADC_CH	CHANNEL_ADC_DIE_TEMP
+
+struct pm8058_tm_device {
+	struct pm8058_chip		*pm_chip;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			temp;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			stage;
+	unsigned int			irq;
+	void				*adc_handle;
+};
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int pm8058_tm_read_ctrl(struct pm8058_chip *chip, u8 *reg)
+{
+	int rc;
+
+	rc = pm8058_read(chip, SSBI_REG_TEMP_ALRM_CTRL, reg, 1);
+	if (rc)
+		pr_err("%s: pm8058_read FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int pm8058_tm_write_ctrl(struct pm8058_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8058_write(chip, SSBI_REG_TEMP_ALRM_CTRL, &reg, 1);
+	if (rc)
+		pr_err("%s: pm8058_write FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int pm8058_tm_write_pwm(struct pm8058_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8058_write(chip, SSBI_REG_TEMP_ALRM_PWM, &reg, 1);
+	if (rc)
+		pr_err("%s: pm8058_write FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int
+pm8058_tm_shutdown_override(struct pm8058_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8058_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	reg &= ~(PM8058_TEMP_OVRD_MASK | PM8058_TEMP_STATUS_MASK);
+	if (mode == SOFTWARE_OVERRIDE_ENABLED)
+		reg |= (PM8058_TEMP_OVRD_ST3 | PM8058_TEMP_OVRD_ST2) &
+			PM8058_TEMP_OVRD_MASK;
+
+	rc = pm8058_tm_write_ctrl(chip, reg);
+
+	return rc;
+}
+
+static int pm8058_tz_get_temp(struct thermal_zone_device *thermal,
+			      unsigned long *temp)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+	DECLARE_COMPLETION_ONSTACK(wait);
+	struct adc_chan_result adc_result = {
+		.physical = 0lu,
+	};
+	int rc;
+
+	if (!tm || !temp)
+		return -EINVAL;
+
+	*temp = tm->temp;
+
+	rc = adc_channel_request_conv(tm->adc_handle, &wait);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_request_conv() failed, rc = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	wait_for_completion(&wait);
+
+	rc = adc_channel_read_result(tm->adc_handle, &adc_result);
+	if (rc < 0) {
+		pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	*temp = adc_result.physical;
+	tm->temp = adc_result.physical;
+
+	return 0;
+}
+
+static int pm8058_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+
+	if (!tm || !mode)
+		return -EINVAL;
+
+	*mode = tm->mode;
+
+	return 0;
+}
+
+static int pm8058_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+
+	if (!tm)
+		return -EINVAL;
+
+	if (mode != tm->mode) {
+		if (mode == THERMAL_DEVICE_ENABLED)
+			pm8058_tm_shutdown_override(tm->pm_chip,
+						    SOFTWARE_OVERRIDE_ENABLED);
+		else
+			pm8058_tm_shutdown_override(tm->pm_chip,
+						    SOFTWARE_OVERRIDE_DISABLED);
+	}
+	tm->mode = mode;
+
+	return 0;
+}
+
+static int pm8058_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case PM8058_TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case PM8058_TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case PM8058_TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pm8058_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+	int thresh_temp;
+
+	if (!tm || trip < 0 || !temp)
+		return -EINVAL;
+
+	thresh_temp = tm->thresh * PM8058_TEMP_THRESH_STEP +
+		      PM8058_TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case PM8058_TRIP_STAGE3:
+		thresh_temp += 2 * PM8058_TEMP_STAGE_STEP;
+		break;
+	case PM8058_TRIP_STAGE2:
+		thresh_temp += PM8058_TEMP_STAGE_STEP;
+		break;
+	case PM8058_TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temp = thresh_temp;
+
+	return 0;
+}
+
+static int pm8058_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   unsigned long *temp)
+{
+	struct pm8058_tm_device *tm = thermal->devdata;
+
+	if (!tm || !temp)
+		return -EINVAL;
+
+	*temp = tm->thresh * PM8058_TEMP_THRESH_STEP + PM8058_TEMP_THRESH_MIN +
+		2 * PM8058_TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops pm8058_thermal_zone_ops = {
+	.get_temp = pm8058_tz_get_temp,
+	.get_mode = pm8058_tz_get_mode,
+	.set_mode = pm8058_tz_set_mode,
+	.get_trip_type = pm8058_tz_get_trip_type,
+	.get_trip_temp = pm8058_tz_get_trip_temp,
+	.get_crit_temp = pm8058_tz_get_crit_temp,
+};
+
+static irqreturn_t pm8058_tm_isr(int irq, void *data)
+{
+	struct pm8058_tm_device *tm = data;
+	int rc;
+	u8 reg;
+
+	rc = pm8058_tm_read_ctrl(tm->pm_chip, &reg);
+	if (rc < 0)
+		goto isr_handled;
+
+	tm->stage = (reg & PM8058_TEMP_STATUS_MASK) >> PM8058_TEMP_STATUS_SHIFT;
+	tm->thresh = (reg & PM8058_TEMP_THRESH_MASK) >>
+			PM8058_TEMP_THRESH_SHIFT;
+
+	if (reg & (PM8058_TEMP_ST2_SD | PM8058_TEMP_ST3_SD)) {
+		reg &= ~(PM8058_TEMP_ST2_SD | PM8058_TEMP_ST3_SD |
+			 PM8058_TEMP_STATUS_MASK);
+		pm8058_tm_write_ctrl(tm->pm_chip, reg);
+	}
+
+	thermal_zone_device_update(tm->tz_dev);
+
+	/* Notify user space */
+	if (tm->mode == THERMAL_DEVICE_ENABLED)
+		kobject_uevent(&tm->tz_dev->device.kobj, KOBJ_CHANGE);
+
+isr_handled:
+	return IRQ_HANDLED;
+}
+
+static int pm8058_tm_init_reg(struct pm8058_tm_device *tm)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8058_tm_read_ctrl(tm->pm_chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	tm->stage = (reg & PM8058_TEMP_STATUS_MASK) >> PM8058_TEMP_STATUS_SHIFT;
+	tm->temp = 0;
+
+	/* Use temperature threshold set 0: (105, 125, 145) */
+	tm->thresh = 0;
+	reg = (tm->thresh << PM8058_TEMP_THRESH_SHIFT) &
+	      PM8058_TEMP_THRESH_MASK;
+	rc = pm8058_tm_write_ctrl(tm->pm_chip, reg);
+	if (rc < 0)
+		return rc;
+
+	/*
+	 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
+	 * helps cut down on the number of unnecessary interrupts fired when
+	 * changing between thermal stages.  Also, Enable the over temperature
+	 * PWM whenever the PMIC is enabled.
+	 */
+	reg =  1 << PM8058_TEMP_PWM_EN_SHIFT |
+	       3 << PM8058_TEMP_PWM_PER_PRE_SHIFT |
+	       3 << PM8058_TEMP_PWM_PER_DIV_SHIFT;
+
+	rc = pm8058_tm_write_pwm(tm->pm_chip, reg);
+
+	return rc;
+}
+
+static int __devinit pmic8058_tm_probe(struct platform_device *pdev)
+{
+	DECLARE_COMPLETION_ONSTACK(wait);
+	struct pm8058_tm_device *tmdev;
+	struct pm8058_chip *pm_chip;
+	unsigned int irq;
+	int rc;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s: no driver data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		pr_err("%s: no IRQ passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	tmdev = kzalloc(sizeof *tmdev, GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	rc = adc_channel_open(PM8058_TEMP_ADC_CH, &(tmdev->adc_handle));
+	if (rc < 0) {
+		pr_err("%s: adc_channel_open() failed.\n", __func__);
+		kfree(tmdev);
+		return rc;
+	}
+
+	/* calibrate the die temperature sensor */
+	if (adc_calib_request(tmdev->adc_handle, &wait) == CALIB_STARTED)
+		wait_for_completion(&wait);
+
+	tmdev->pm_chip = pm_chip;
+	tmdev->tz_dev = thermal_zone_device_register("pm8058_tz",
+						     PM8058_TRIP_NUM, tmdev,
+						     &pm8058_thermal_zone_ops,
+						     0, 0, 0, 0);
+	if (tmdev->tz_dev == NULL) {
+		pr_err("%s: thermal_zone_device_register() failed.\n",
+		       __func__);
+		adc_channel_close(tmdev->adc_handle);
+		kfree(tmdev);
+		return -ENODEV;
+	}
+
+	rc = pm8058_tm_init_reg(tmdev);
+	pm8058_tm_shutdown_override(tmdev->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc < 0) {
+		thermal_zone_device_unregister(tmdev->tz_dev);
+		adc_channel_close(tmdev->adc_handle);
+		kfree(tmdev);
+		return rc;
+	}
+
+	/* start in HW control, switch to SW control when user changes mode */
+	tmdev->mode = THERMAL_DEVICE_DISABLED;
+	thermal_zone_device_update(tmdev->tz_dev);
+
+	platform_set_drvdata(pdev, tmdev);
+
+	rc = request_threaded_irq(irq, NULL, pm8058_tm_isr,
+			 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+			 "pm8058-tm-irq", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_irq(%d) FAIL: %d\n", __func__, irq, rc);
+		thermal_zone_device_unregister(tmdev->tz_dev);
+		platform_set_drvdata(pdev, tmdev->pm_chip);
+		adc_channel_close(tmdev->adc_handle);
+		kfree(tmdev);
+		return rc;
+	}
+	tmdev->irq = irq;
+
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit pmic8058_tm_remove(struct platform_device *pdev)
+{
+	struct pm8058_tm_device *tmdev = platform_get_drvdata(pdev);
+
+	thermal_zone_device_unregister(tmdev->tz_dev);
+	platform_set_drvdata(pdev, tmdev->pm_chip);
+	pm8058_tm_shutdown_override(tmdev->pm_chip, THERMAL_DEVICE_DISABLED);
+	adc_channel_close(tmdev->adc_handle);
+	free_irq(tmdev->irq, tmdev);
+	kfree(tmdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pmic8058_tm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8058_tm_device *tm = platform_get_drvdata(pdev);
+
+	/* Clear override bits in suspend to allow hardware control */
+	pm8058_tm_shutdown_override(tm->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int pmic8058_tm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8058_tm_device *tm = platform_get_drvdata(pdev);
+
+	/* Override hardware actions so software can control */
+	if (tm->mode == THERMAL_DEVICE_ENABLED)
+		pm8058_tm_shutdown_override(tm->pm_chip,
+					    SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pmic8058_tm_pm_ops = {
+	.suspend = pmic8058_tm_suspend,
+	.resume = pmic8058_tm_resume,
+};
+
+#define PM8058_TM_PM_OPS	(&pmic8058_tm_pm_ops)
+#else
+#define PM8058_TM_PM_OPS	NULL
+#endif
+
+static struct platform_driver pmic8058_tm_driver = {
+	.probe	= pmic8058_tm_probe,
+	.remove	= __devexit_p(pmic8058_tm_remove),
+	.driver	= {
+		.name = "pm8058-tm",
+		.owner = THIS_MODULE,
+		.pm = PM8058_TM_PM_OPS,
+	},
+};
+
+static int __init pm8058_tm_init(void)
+{
+	return platform_driver_register(&pmic8058_tm_driver);
+}
+
+static void __exit pm8058_tm_exit(void)
+{
+	platform_driver_unregister(&pmic8058_tm_driver);
+}
+
+module_init(pm8058_tm_init);
+module_exit(pm8058_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 Thermal Manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-tm");
diff --git a/drivers/thermal/pmic8901-tm.c b/drivers/thermal/pmic8901-tm.c
new file mode 100644
index 0000000..0ff5788
--- /dev/null
+++ b/drivers/thermal/pmic8901-tm.c
@@ -0,0 +1,594 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * Qualcomm PMIC8901 Thermal Manager driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/mfd/pmic8901.h>
+
+/* PMIC8901 TEMP_ALRM registers */
+#define	SSBI_REG_TEMP_ALRM_CTRL		0x23
+#define	SSBI_REG_TEMP_ALRM_PWM		0x24
+
+/* TEMP_ALRM_CTRL */
+#define	PM8901_TEMP_ST3_SD		0x80
+#define	PM8901_TEMP_ST2_SD		0x40
+#define	PM8901_TEMP_STATUS_MASK		0x30
+#define	PM8901_TEMP_STATUS_SHIFT	4
+#define	PM8901_TEMP_THRESH_MASK		0x0C
+#define	PM8901_TEMP_THRESH_SHIFT	2
+#define	PM8901_TEMP_OVRD_ST3		0x02
+#define	PM8901_TEMP_OVRD_ST2		0x01
+#define	PM8901_TEMP_OVRD_MASK		0x03
+
+#define	PM8901_TEMP_STAGE_STEP		20000	/* Stage step: 20 C */
+#define	PM8901_TEMP_STAGE_HYSTERESIS	2000
+
+#define	PM8901_TEMP_THRESH_MIN		105000	/* Threshold Min: 105 C */
+#define	PM8901_TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+/* TEMP_ALRM_PWM */
+#define	PM8901_TEMP_PWM_EN_MASK		0xC0
+#define	PM8901_TEMP_PWM_EN_SHIFT	6
+#define	PM8901_TEMP_PWM_PER_PRE_MASK	0x38
+#define	PM8901_TEMP_PWM_PER_PRE_SHIFT	3
+#define	PM8901_TEMP_PWM_PER_DIV_MASK	0x07
+#define	PM8901_TEMP_PWM_PER_DIV_SHIFT	0
+
+/* Trips: from critical to less critical */
+#define PM8901_TRIP_STAGE3	0
+#define PM8901_TRIP_STAGE2	1
+#define PM8901_TRIP_STAGE1	2
+#define PM8901_TRIP_NUM		3
+
+/* Used because there is no means to read the die temperature */
+#define DEFAULT_NO_ADC_TEMP	37000
+
+struct pm8901_tm_device {
+	struct pm8901_chip		*pm_chip;
+	struct thermal_zone_device	*tz_dev;
+	unsigned long			temp;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			stage;
+	unsigned int			irq;
+	unsigned int			hi_irq;
+};
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int pm8901_tm_read_ctrl(struct pm8901_chip *chip, u8 *reg)
+{
+	int rc;
+
+	rc = pm8901_read(chip, SSBI_REG_TEMP_ALRM_CTRL, reg, 1);
+	if (rc)
+		pr_err("%s: pm8901_read FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int pm8901_tm_write_ctrl(struct pm8901_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8901_write(chip, SSBI_REG_TEMP_ALRM_CTRL, &reg, 1);
+	if (rc)
+		pr_err("%s: pm8901_write FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int pm8901_tm_read_pwm(struct pm8901_chip *chip, u8 *reg)
+{
+	int rc;
+
+	rc = pm8901_read(chip, SSBI_REG_TEMP_ALRM_PWM, reg, 1);
+	if (rc)
+		pr_err("%s: pm8901_read FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int pm8901_tm_write_pwm(struct pm8901_chip *chip, u8 reg)
+{
+	int rc;
+
+	rc = pm8901_write(chip, SSBI_REG_TEMP_ALRM_PWM, &reg, 1);
+	if (rc)
+		pr_err("%s: pm8901_write FAIL: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static inline int
+pm8901_tm_shutdown_override(struct pm8901_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8901_tm_read_ctrl(chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	reg &= ~(PM8901_TEMP_OVRD_MASK | PM8901_TEMP_STATUS_MASK);
+	if (mode == SOFTWARE_OVERRIDE_ENABLED)
+		reg |= (PM8901_TEMP_OVRD_ST3 | PM8901_TEMP_OVRD_ST2) &
+			PM8901_TEMP_OVRD_MASK;
+
+	rc = pm8901_tm_write_ctrl(chip, reg);
+
+	return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int pm8901_tm_init_temp(struct pm8901_tm_device *tm)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8901_tm_read_ctrl(tm->pm_chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	tm->stage = (reg & PM8901_TEMP_STATUS_MASK) >> PM8901_TEMP_STATUS_SHIFT;
+	tm->thresh = (reg & PM8901_TEMP_THRESH_MASK) >>
+			PM8901_TEMP_THRESH_SHIFT;
+
+	if (tm->stage) {
+		tm->temp = tm->thresh * PM8901_TEMP_THRESH_STEP +
+			   (tm->stage - 1) * PM8901_TEMP_STAGE_STEP +
+			   PM8901_TEMP_THRESH_MIN;
+	} else
+		tm->temp = DEFAULT_NO_ADC_TEMP;
+
+	return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int pm8901_tm_update_temp(struct pm8901_tm_device *tm)
+{
+	unsigned int stage;
+	int rc;
+	u8 reg;
+
+	rc = pm8901_tm_read_ctrl(tm->pm_chip, &reg);
+	if (rc < 0)
+		return rc;
+
+	stage = (reg & PM8901_TEMP_STATUS_MASK) >> PM8901_TEMP_STATUS_SHIFT;
+	tm->thresh = (reg & PM8901_TEMP_THRESH_MASK) >>
+			PM8901_TEMP_THRESH_SHIFT;
+
+	if (stage > tm->stage) {
+		/* increasing stage, use lower bound */
+		tm->temp = (stage-1) * PM8901_TEMP_STAGE_STEP +
+			   tm->thresh * PM8901_TEMP_THRESH_STEP +
+			   PM8901_TEMP_STAGE_HYSTERESIS +
+			   PM8901_TEMP_THRESH_MIN;
+	} else if (stage < tm->stage) {
+		/* decreasing stage, use upper bound */
+		tm->temp = stage * PM8901_TEMP_STAGE_STEP +
+			   tm->thresh * PM8901_TEMP_THRESH_STEP -
+			   PM8901_TEMP_STAGE_HYSTERESIS +
+			   PM8901_TEMP_THRESH_MIN;
+	}
+
+	tm->stage = stage;
+
+	return 0;
+}
+
+static int pm8901_tz_get_temp(struct thermal_zone_device *thermal,
+			      unsigned long *temp)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+	int rc;
+
+	if (!tm || !temp)
+		return -EINVAL;
+
+	rc = pm8901_tm_update_temp(tm);
+	if (rc < 0)
+		return rc;
+
+	*temp = tm->temp;
+
+	return 0;
+}
+
+static int pm8901_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+
+	if (!tm || !mode)
+		return -EINVAL;
+
+	*mode = tm->mode;
+
+	return 0;
+}
+
+static int pm8901_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+
+	if (!tm)
+		return -EINVAL;
+
+	if (mode != tm->mode) {
+		pr_info("%s: mode: %d --> %d\n", __func__, tm->mode, mode);
+
+		if (mode == THERMAL_DEVICE_ENABLED)
+			pm8901_tm_shutdown_override(tm->pm_chip,
+						    SOFTWARE_OVERRIDE_ENABLED);
+		else
+			pm8901_tm_shutdown_override(tm->pm_chip,
+						    SOFTWARE_OVERRIDE_DISABLED);
+	}
+	tm->mode = mode;
+
+	return 0;
+}
+
+static int pm8901_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+
+	if (!tm || trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case PM8901_TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case PM8901_TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case PM8901_TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pm8901_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temp)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+	int thresh_temp;
+
+	if (!tm || trip < 0 || !temp)
+		return -EINVAL;
+
+	thresh_temp = tm->thresh * PM8901_TEMP_THRESH_STEP +
+		      PM8901_TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case PM8901_TRIP_STAGE3:
+		thresh_temp += 2 * PM8901_TEMP_STAGE_STEP;
+		break;
+	case PM8901_TRIP_STAGE2:
+		thresh_temp += PM8901_TEMP_STAGE_STEP;
+		break;
+	case PM8901_TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+	*temp = thresh_temp;
+
+	return 0;
+}
+
+static int pm8901_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   unsigned long *temp)
+{
+	struct pm8901_tm_device *tm = thermal->devdata;
+
+	if (!tm || !temp)
+		return -EINVAL;
+
+	*temp = tm->thresh * PM8901_TEMP_THRESH_STEP +
+			PM8901_TEMP_THRESH_MIN + 2 * PM8901_TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops pm8901_thermal_zone_ops = {
+	.get_temp = pm8901_tz_get_temp,
+	.get_mode = pm8901_tz_get_mode,
+	.set_mode = pm8901_tz_set_mode,
+	.get_trip_type = pm8901_tz_get_trip_type,
+	.get_trip_temp = pm8901_tz_get_trip_temp,
+	.get_crit_temp = pm8901_tz_get_crit_temp,
+};
+
+static irqreturn_t pm8901_tm_isr(int irq, void *data)
+{
+	struct pm8901_tm_device *tm = data;
+	int rc;
+	u8 reg;
+
+	rc = pm8901_tm_update_temp(tm);
+	if (rc < 0)
+		goto isr_handled;
+
+	rc = pm8901_tm_read_ctrl(tm->pm_chip, &reg);
+	if (rc < 0)
+		goto isr_handled;
+
+	pr_info("%s: Temp Alarm - stage=%u, threshold=%u, temp=%lu\n",
+		__func__, tm->stage, tm->thresh, tm->temp);
+
+	if (reg & (PM8901_TEMP_ST2_SD | PM8901_TEMP_ST3_SD)) {
+		reg &= ~(PM8901_TEMP_ST2_SD | PM8901_TEMP_ST3_SD |
+			 PM8901_TEMP_STATUS_MASK);
+
+		pm8901_tm_write_ctrl(tm->pm_chip, reg);
+	}
+
+	thermal_zone_device_update(tm->tz_dev);
+
+	/* Notify user space */
+	if (tm->mode == THERMAL_DEVICE_ENABLED)
+		kobject_uevent(&tm->tz_dev->device.kobj, KOBJ_CHANGE);
+
+isr_handled:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8901_tm_isr1(int irq, void *data)
+{
+	struct pm8901_tm_device *tm = data;
+	irqreturn_t rc;
+
+	disable_irq(tm->hi_irq);
+	rc = pm8901_tm_isr(irq, data);
+	enable_irq(tm->hi_irq);
+
+	return rc;
+}
+
+static irqreturn_t pm8901_tm_isr2(int irq, void *data)
+{
+	struct pm8901_tm_device *tm = data;
+	irqreturn_t rc;
+
+	disable_irq(tm->irq);
+	rc = pm8901_tm_isr(irq, data);
+	enable_irq(tm->irq);
+
+	return rc;
+}
+
+static int pm8901_tm_init_reg(struct pm8901_tm_device *tm)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8901_tm_init_temp(tm);
+	if (rc < 0)
+		return rc;
+
+	/* Use temperature threshold set 0: (105, 125, 145) */
+	tm->thresh = 0;
+	reg = (tm->thresh << PM8901_TEMP_THRESH_SHIFT) &
+	      PM8901_TEMP_THRESH_MASK;
+	rc = pm8901_tm_write_ctrl(tm->pm_chip, reg);
+	if (rc < 0)
+		return rc;
+
+	/*
+	 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
+	 * helps cut down on the number of unnecessary interrupts fired when
+	 * changing between thermal stages.  Also, Enable the over temperature
+	 * PWM whenever the PMIC is enabled.
+	 */
+	reg =  1 << PM8901_TEMP_PWM_EN_SHIFT |
+	       3 << PM8901_TEMP_PWM_PER_PRE_SHIFT |
+	       3 << PM8901_TEMP_PWM_PER_DIV_SHIFT;
+
+	rc = pm8901_tm_write_pwm(tm->pm_chip, reg);
+
+	return rc;
+}
+
+static int __devinit pmic8901_tm_probe(struct platform_device *pdev)
+{
+	struct pm8901_tm_device	*tmdev;
+	struct pm8901_chip *pm_chip;
+	unsigned int irq, hi_irq;
+	int rc;
+
+	pm_chip = dev_get_drvdata(pdev->dev.parent);
+	if (pm_chip == NULL) {
+		pr_err("%s: no driver data passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		pr_err("%s: no IRQ passed in.\n", __func__);
+		return -EFAULT;
+	}
+	hi_irq = platform_get_irq(pdev, 1);
+	if (!hi_irq) {
+		pr_err("%s: no HI IRQ passed in.\n", __func__);
+		return -EFAULT;
+	}
+
+	tmdev = kzalloc(sizeof *tmdev, GFP_KERNEL);
+	if (tmdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	tmdev->pm_chip = pm_chip;
+	tmdev->tz_dev = thermal_zone_device_register("pm8901_tz",
+						     PM8901_TRIP_NUM, tmdev,
+						     &pm8901_thermal_zone_ops,
+						     0, 0, 0, 0);
+	if (tmdev->tz_dev == NULL) {
+		pr_err("%s: thermal_zone_device_register() failed.\n",
+		       __func__);
+		kfree(tmdev);
+		return -ENODEV;
+	}
+
+	rc = pm8901_tm_init_reg(tmdev);
+	pm8901_tm_shutdown_override(tmdev->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc < 0) {
+		thermal_zone_device_unregister(tmdev->tz_dev);
+		kfree(tmdev);
+		return rc;
+	}
+
+	/* start in HW control, switch to SW control when user changes mode */
+	tmdev->mode = THERMAL_DEVICE_DISABLED;
+	thermal_zone_device_update(tmdev->tz_dev);
+
+	platform_set_drvdata(pdev, tmdev);
+
+	rc = request_threaded_irq(irq, pm8901_tm_isr1, NULL,
+			 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+			 "pm8901-tm-irq", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_threaded_irq(%d) FAIL: %d\n",
+		       __func__, irq, rc);
+
+		thermal_zone_device_unregister(tmdev->tz_dev);
+		platform_set_drvdata(pdev, tmdev->pm_chip);
+		kfree(tmdev);
+		return -ENODEV;
+	}
+	tmdev->irq = irq;
+
+	rc = request_threaded_irq(hi_irq, pm8901_tm_isr2, NULL,
+			 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+			 "pm8901-tm-irq2", tmdev);
+	if (rc < 0) {
+		pr_err("%s: request_threaded_irq(%d) FAIL: %d\n",
+		       __func__, hi_irq, rc);
+
+		free_irq(irq, tmdev);
+		thermal_zone_device_unregister(tmdev->tz_dev);
+		platform_set_drvdata(pdev, tmdev->pm_chip);
+		kfree(tmdev);
+		return -ENODEV;
+	}
+	tmdev->hi_irq = hi_irq;
+
+	pr_notice("%s: OK\n", __func__);
+	return 0;
+}
+
+static int __devexit pmic8901_tm_remove(struct platform_device *pdev)
+{
+	struct pm8901_tm_device *tmdev = platform_get_drvdata(pdev);
+
+	free_irq(tmdev->hi_irq, tmdev);
+	free_irq(tmdev->irq, tmdev);
+	thermal_zone_device_unregister(tmdev->tz_dev);
+	platform_set_drvdata(pdev, tmdev->pm_chip);
+	pm8901_tm_shutdown_override(tmdev->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
+	kfree(tmdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pmic8901_tm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8901_tm_device *tm = platform_get_drvdata(pdev);
+
+	pm8901_tm_shutdown_override(tm->pm_chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int pmic8901_tm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pm8901_tm_device *tm = platform_get_drvdata(pdev);
+
+	pm8901_tm_init_temp(tm);
+
+	if (tm->mode == THERMAL_DEVICE_ENABLED)
+		pm8901_tm_shutdown_override(tm->pm_chip,
+					    SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pmic8901_tm_pm_ops = {
+	.suspend = pmic8901_tm_suspend,
+	.resume = pmic8901_tm_resume,
+};
+
+#define PM8901_TM_PM_OPS	(&pmic8901_tm_pm_ops)
+#else
+#define PM8901_TM_PM_OPS	NULL
+#endif
+
+static struct platform_driver pmic8901_tm_driver = {
+	.probe		= pmic8901_tm_probe,
+	.remove		= __devexit_p(pmic8901_tm_remove),
+	.driver		= {
+		.name = "pm8901-tm",
+		.owner = THIS_MODULE,
+		.pm = PM8901_TM_PM_OPS,
+	},
+};
+
+static int __init pm8901_tm_init(void)
+{
+	return platform_driver_register(&pmic8901_tm_driver);
+}
+
+static void __exit pm8901_tm_exit(void)
+{
+	platform_driver_unregister(&pmic8901_tm_driver);
+}
+
+module_init(pm8901_tm_init);
+module_exit(pm8901_tm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8901 Thermal Manager driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8901-tm");
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 0b1c82a..e0d8ef7 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -188,6 +188,12 @@
 		return sprintf(buf, "critical\n");
 	case THERMAL_TRIP_HOT:
 		return sprintf(buf, "hot\n");
+	case THERMAL_TRIP_CONFIGURABLE_HI:
+		return sprintf(buf, "configurable_hi\n");
+	case THERMAL_TRIP_CONFIGURABLE_LOW:
+		return sprintf(buf, "configurable_low\n");
+	case THERMAL_TRIP_CRITICAL_LOW:
+		return sprintf(buf, "critical_low\n");
 	case THERMAL_TRIP_PASSIVE:
 		return sprintf(buf, "passive\n");
 	case THERMAL_TRIP_ACTIVE:
@@ -198,6 +204,34 @@
 }
 
 static ssize_t
+trip_point_type_activate(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct thermal_zone_device *tz = to_thermal_zone(dev);
+	int trip, result;
+
+	if (!tz->ops->activate_trip_type)
+		return -EPERM;
+
+	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
+		return -EINVAL;
+
+	if (!strncmp(buf, "enabled", sizeof("enabled")))
+		result = tz->ops->activate_trip_type(tz, trip,
+					THERMAL_TRIP_ACTIVATION_ENABLED);
+	else if (!strncmp(buf, "disabled", sizeof("disabled")))
+		result = tz->ops->activate_trip_type(tz, trip,
+					THERMAL_TRIP_ACTIVATION_DISABLED);
+	else
+		result = -EINVAL;
+
+	if (result)
+		return result;
+
+	return count;
+}
+
+static ssize_t
 trip_point_temp_show(struct device *dev, struct device_attribute *attr,
 		     char *buf)
 {
@@ -220,6 +254,30 @@
 }
 
 static ssize_t
+trip_point_temp_set(struct device *dev, struct device_attribute *attr,
+		     const char *buf, size_t count)
+{
+	struct thermal_zone_device *tz = to_thermal_zone(dev);
+	int trip, ret;
+	long temperature;
+
+	if (!tz->ops->set_trip_temp)
+		return -EPERM;
+
+	if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
+		return -EINVAL;
+
+	if (!sscanf(buf, "%ld", &temperature))
+		return -EINVAL;
+
+	ret = tz->ops->set_trip_temp(tz, trip, temperature);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
 passive_store(struct device *dev, struct device_attribute *attr,
 		    const char *buf, size_t count)
 {
@@ -287,30 +345,54 @@
 		   passive_store);
 
 static struct device_attribute trip_point_attrs[] = {
-	__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_10_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_10_temp, 0444, trip_point_temp_show, NULL),
-	__ATTR(trip_point_11_type, 0444, trip_point_type_show, NULL),
-	__ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
+	__ATTR(trip_point_0_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_1_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_2_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_3_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_4_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_5_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_6_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_7_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_8_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_9_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_10_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
+	__ATTR(trip_point_11_type, 0644, trip_point_type_show,
+					trip_point_type_activate),
+	__ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
+					trip_point_temp_set),
 };
 
 #define TRIP_POINT_ATTR_ADD(_dev, _index, result)     \
@@ -992,6 +1074,29 @@
 				if (tz->ops->notify)
 					tz->ops->notify(tz, count, trip_type);
 			break;
+		case THERMAL_TRIP_CONFIGURABLE_HI:
+			if (temp >= trip_temp)
+				if (tz->ops->notify)
+					tz->ops->notify(tz, count, trip_type);
+			break;
+		case THERMAL_TRIP_CONFIGURABLE_LOW:
+			if (temp <= trip_temp)
+				if (tz->ops->notify)
+					tz->ops->notify(tz, count, trip_type);
+			break;
+		case THERMAL_TRIP_CRITICAL_LOW:
+			if (temp <= trip_temp) {
+				if (tz->ops->notify)
+					ret = tz->ops->notify(tz, count,
+								trip_type);
+			if (!ret) {
+				printk(KERN_EMERG
+				"Critical temperature reached (%ld C), \
+					shutting down.\n", temp/1000);
+				orderly_poweroff(true);
+				}
+			}
+			break;
 		case THERMAL_TRIP_ACTIVE:
 			list_for_each_entry(instance, &tz->cooling_devices,
 					    node) {