thermal: Add palmas thermal support
Pradeep Goudagunta [Thu, 29 Aug 2013 10:55:42 +0000 (15:55 +0530)]
Palmas series pmic chip supports hot die interrupt configurable for
four different critical temperature thresholds.

Bug 1355680

Change-Id: I0167d5faf5be596018ec34d67911dd631d2cc71f
Signed-off-by: Pradeep Goudagunta <pgoudagunta@nvidia.com>
Reviewed-on: http://git-master/r/267913
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Laxman Dewangan <ldewangan@nvidia.com>

drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/palmas_thermal.c [new file with mode: 0644]
include/linux/mfd/palmas.h

index b0103ae..4378652 100644 (file)
@@ -198,4 +198,12 @@ config PWM_FAN
        help
          Enables the fan driver that is controlled by pwm
 
+config PALMAS_THERMAL
+       tristate "TI Palmas Thermal driver"
+       depends on MFD_PALMAS
+       help
+        Palmas series pmic chip supports hot die interrupt configurable for 4
+        different critical temperature thresholds from which we can notify
+        thermal core to orderly power off the system.
+
 endif
index f0bd8ce..5798a35 100644 (file)
@@ -26,4 +26,5 @@ obj-$(CONFIG_DB8500_CPUFREQ_COOLING)  += db8500_cpufreq_cooling.o
 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
 obj-$(CONFIG_PWM_FAN)          += pwm_fan.o
 obj-$(CONFIG_GENERIC_ADC_THERMAL)      += generic_adc_thermal.o
+obj-$(CONFIG_PALMAS_THERMAL)   += palmas_thermal.o
 
diff --git a/drivers/thermal/palmas_thermal.c b/drivers/thermal/palmas_thermal.c
new file mode 100644 (file)
index 0000000..f8016e3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * palmas_thermal.c -- TI PALMAS THERMAL.
+ *
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Pradeep Goudagunta <pgoudagunta@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/thermal.h>
+#include <linux/mfd/palmas.h>
+
+#define PALMAS_NORMAL_OPERATING_TEMP 100000
+#define PALMAS_CRITICAL_DEFUALT_TEMP 108000
+
+struct palmas_therm_zone {
+       struct device                   *dev;
+       struct palmas                   *palmas;
+       struct thermal_zone_device      *tz_device;
+       int                             irq;
+       int                             is_crit_temp;
+};
+
+struct palmas_trip_point {
+       unsigned long temp;
+        enum thermal_trip_type type;
+};
+
+static struct palmas_trip_point palmas_tpoint = {
+               .temp = PALMAS_CRITICAL_DEFUALT_TEMP,
+               .type = THERMAL_TRIP_CRITICAL,
+};
+
+static int palmas_thermal_get_crit_temp(struct thermal_zone_device *tz_device,
+                       unsigned long *temp)
+{
+       *temp = palmas_tpoint.temp;
+
+       return 0;
+}
+
+static int palmas_thermal_get_temp(struct thermal_zone_device *tz_device,
+                       unsigned long *temp)
+{
+       struct palmas_therm_zone *ptherm_zone = tz_device->devdata;
+
+       if (ptherm_zone->is_crit_temp) {
+               /*
+                * Set temperature greater than Critical trip
+                * temp to trigger orderly power down sequence
+                */
+               palmas_thermal_get_crit_temp(tz_device, temp);
+               *temp += 1;
+               return 0;
+       }
+
+       *temp = PALMAS_NORMAL_OPERATING_TEMP;
+       return 0;
+}
+
+static int palmas_thermal_get_trip_type(struct thermal_zone_device *tz_device,
+                       int trip, enum thermal_trip_type *type)
+{
+       if (trip >= 1)
+               return -EINVAL;
+
+       *type = palmas_tpoint.type;
+       return 0;
+}
+
+static int palmas_thermal_get_trip_temp(struct thermal_zone_device *tz_device,
+                       int trip, unsigned long *temp)
+{
+       if (trip >= 1)
+               return -EINVAL;
+
+       *temp = palmas_tpoint.temp;
+       return 0;
+}
+
+static struct thermal_zone_device_ops palmas_tz_ops = {
+       .get_temp = palmas_thermal_get_temp,
+       .get_crit_temp = palmas_thermal_get_crit_temp,
+       .get_trip_type = palmas_thermal_get_trip_type,
+       .get_trip_temp = palmas_thermal_get_trip_temp,
+};
+
+static irqreturn_t palmas_thermal_irq(int irq, void *data)
+{
+       struct palmas_therm_zone *ptherm_zone = data;
+
+       ptherm_zone->is_crit_temp = 1;
+       /*
+        * Necessary action can be taken here
+        * e.g: thermal_zone_device_update(pz->tz_device);
+        * will trigger orderly power down sequence.
+        */
+
+       return IRQ_HANDLED;
+}
+
+static int palmas_thermal_probe(struct platform_device *pdev)
+{
+       struct palmas_therm_zone *ptherm_zone;
+       struct palmas_platform_data *pdata;
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       char *default_tz_name = "palmas-junc-tz";
+       int ret;
+       u8 val;
+
+       pdata = dev_get_platdata(pdev->dev.parent);
+
+       if (!pdata || !(pdata->hd_threshold_temp)) {
+               dev_err(&pdev->dev, "No platform data\n");
+               return -ENODEV;
+       }
+
+       ptherm_zone = devm_kzalloc(&pdev->dev, sizeof(*ptherm_zone),
+                       GFP_KERNEL);
+       if (!ptherm_zone) {
+               dev_err(&pdev->dev, "No available free memory\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, ptherm_zone);
+       ptherm_zone->dev = &pdev->dev;
+       if (!(pdata->tz_name))
+               pdata->tz_name = default_tz_name;
+
+       ptherm_zone->tz_device = thermal_zone_device_register(pdata->tz_name,
+                                       1, 0, ptherm_zone, &palmas_tz_ops,
+                                       NULL, 0, 0);
+       if (IS_ERR_OR_NULL(ptherm_zone->tz_device)) {
+               dev_err(ptherm_zone->dev,
+                       "Register thermal zone device failed.\n");
+               return PTR_ERR(ptherm_zone->tz_device);
+       }
+
+       palmas_tpoint.temp = pdata->hd_threshold_temp;
+
+       ptherm_zone->irq = platform_get_irq(pdev, 0);
+       ret = request_threaded_irq(ptherm_zone->irq, NULL,
+                       palmas_thermal_irq,
+                       IRQF_ONESHOT, dev_name(&pdev->dev),
+                       ptherm_zone);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "request irq %d failed: %dn", ptherm_zone->irq, ret);
+               goto int_req_failed;
+       }
+
+       switch (palmas_tpoint.temp) {
+       case 108000:
+               val = 0;
+               break;
+       case 112000:
+               val = 1;
+               break;
+       case 116000:
+               val = 2;
+               break;
+       case 120000:
+               val = 3;
+               break;
+       default:
+               dev_err(&pdev->dev, "%ld threshold is not supported",
+                               palmas_tpoint.temp);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       val <<= PALMAS_OSC_THERM_CTRL_THERM_HD_SEL_SHIFT;
+       ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+                       PALMAS_OSC_THERM_CTRL,
+                       PALMAS_OSC_THERM_CTRL_THERM_HD_SEL_MASK, val);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "osc_therm_ctrl reg update failed.\n");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       free_irq(ptherm_zone->irq, ptherm_zone);
+int_req_failed:
+       thermal_zone_device_unregister(ptherm_zone->tz_device);
+       return ret;
+}
+
+static int palmas_thermal_remove(struct platform_device *pdev)
+{
+       struct palmas_therm_zone *ptherm_zone = platform_get_drvdata(pdev);
+
+       thermal_zone_device_unregister(ptherm_zone->tz_device);
+       free_irq(ptherm_zone->irq, ptherm_zone);
+       kfree(ptherm_zone);
+       return 0;
+}
+
+static struct platform_driver palmas_thermal_driver = {
+       .probe = palmas_thermal_probe,
+       .remove = palmas_thermal_remove,
+       .driver = {
+               .name = "palmas-thermal",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init palmas_thermal_init(void)
+{
+       return platform_driver_register(&palmas_thermal_driver);
+}
+module_init(palmas_thermal_init);
+
+static void __exit palmas_thermal_exit(void)
+{
+       platform_driver_unregister(&palmas_thermal_driver);
+}
+module_exit(palmas_thermal_exit);
+
+MODULE_DESCRIPTION("Palmas Thermal driver");
+MODULE_AUTHOR("Pradeep Goudagunta<pgoudagunta@nvidia.com>");
+MODULE_ALIAS("platform:palmas-thermal");
+MODULE_LICENSE("GPL v2");
index 8f93195..5da9b42 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kthread.h>
 #include <linux/iio/machine.h>
 #include <linux/extcon.h>
+#include <linux/thermal.h>
 
 #define PALMAS_NUM_CLIENTS     4
 
@@ -615,6 +616,10 @@ struct palmas_platform_data {
 
        int watchdog_timer_initial_period;
 
+       /* Hotdie Threshold temperature */
+       unsigned long hd_threshold_temp;
+       char *tz_name;
+
        /* Long press delay for hard shutdown */
        int long_press_delay;
 };