On 20 June 2012 15:04, hongbo.zhang hongbo.zhang@linaro.org wrote:
From: "hongbo.zhang" hongbo.zhang@stericsson.com
arch/arm/configs/u8500_defconfig | 4 + arch/arm/mach-ux500/board-mop500.c | 71 ++++ drivers/thermal/Kconfig | 16 + drivers/thermal/Makefile | 4 +- drivers/thermal/cpu_cooling.c | 3 +- drivers/thermal/db8500_cpufreq_cooling.c | 159 +++++++++ drivers/thermal/db8500_thermal.c | 445 ++++++++++++++++++++++++++ include/linux/platform_data/db8500_thermal.h | 39 +++ 8 files changed, 738 insertions(+), 3 deletions(-) create mode 100644 drivers/thermal/db8500_cpufreq_cooling.c create mode 100755 drivers/thermal/db8500_thermal.c create mode 100644 include/linux/platform_data/db8500_thermal.h
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index 4dc11da..ad6e7ab 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -118,3 +118,7 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO=y # CONFIG_FTRACE is not set CONFIG_DEBUG_USER=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_DB8500_THERMAL=y +CONFIG_DB8500_CPUFREQ_COOLING=y diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 77d03c1..0c95bce 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -29,6 +29,7 @@ #include <linux/smsc911x.h> #include <linux/gpio_keys.h> #include <linux/delay.h> +#include <linux/platform_data/db8500_thermal.h>
#include <linux/of.h> #include <linux/of_platform.h> @@ -215,6 +216,74 @@ struct platform_device ab8500_device = { };
/*
- Thermal Sensor
- */
+#ifdef CONFIG_DB8500_THERMAL +static struct resource db8500_thsens_resources[] = {
{
.name = "IRQ_HOTMON_LOW",
.start = IRQ_PRCMU_HOTMON_LOW,
.end = IRQ_PRCMU_HOTMON_LOW,
.flags = IORESOURCE_IRQ,
},
{
.name = "IRQ_HOTMON_HIGH",
.start = IRQ_PRCMU_HOTMON_HIGH,
.end = IRQ_PRCMU_HOTMON_HIGH,
.flags = IORESOURCE_IRQ,
},
+};
+static struct db8500_trip_point db8500_trips_table[] = {
[0] = {
.temp = 70000,
.type = THERMAL_TRIP_ACTIVE,
.cooling_dev_name = {
[0] = "thermal-cpufreq-0",
},
},
[1] = {
.temp = 75000,
.type = THERMAL_TRIP_ACTIVE,
.cooling_dev_name = {
[0] = "thermal-cpufreq-1",
},
},
[2] = {
.temp = 80000,
.type = THERMAL_TRIP_ACTIVE,
.cooling_dev_name = {
[0] = "thermal-cpufreq-2",
},
},
[3] = {
.temp = 85000,
.type = THERMAL_TRIP_CRITICAL,
},
+};
+static struct db8500_thsens_platform_data db8500_thsens_data = {
.trip_points = db8500_trips_table,
.num_trips = ARRAY_SIZE(db8500_trips_table),
+};
+static struct platform_device u8500_thsens_device = {
.name = "db8500_thermal",
.resource = db8500_thsens_resources,
.num_resources = ARRAY_SIZE(db8500_thsens_resources),
.dev = {
.platform_data = &db8500_thsens_data,
},
+}; +#endif
+#ifdef CONFIG_DB8500_CPUFREQ_COOLING +static struct platform_device u8500_cpufreq_cooling_device = {
.name = "db8500_cpufreq_cooling",
+}; +#endif +/*
- TPS61052
*/
@@ -607,6 +676,8 @@ static struct platform_device *snowball_platform_devs[] __initdata = { &snowball_key_dev, &snowball_sbnet_dev, &ab8500_device,
&u8500_thsens_device,
&u8500_cpufreq_cooling_device,
};
static void __init mop500_init_machine(void) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index d9c529f..eeabe01 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -30,6 +30,22 @@ config CPU_THERMAL and not the ACPI interface. If you want this support, you should say Y or M here.
+config DB8500_THERMAL
tristate "db8500 thermal management"
depends on THERMAL
default y
help
Adds DB8500 thermal management implementation according to the thermal
management framework.
+config DB8500_CPUFREQ_COOLING
tristate "db8500 cpufreq cooling"
depends on CPU_THERMAL
default y
help
Adds DB8500 cpufreq cooling devices, and these cooling devicesd can be
binded to thermal zone device trip points.
config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 30c456c..d146456 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -3,5 +3,7 @@ #
obj-$(CONFIG_THERMAL) += thermal_sys.o -obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o +obj-$(CONFIG_DB8500_CPUFREQ_COOLING) +=db8500_cpufreq_cooling.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index c40d9a0..a8aa10f 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -399,8 +399,7 @@ struct thermal_cooling_device *cpufreq_cooling_register( /*Verify that all the entries of freq_clip_table are present*/ for (i = 0; i < tab_size; i++) { clip_tab = ((struct freq_clip_table *)&tab_ptr[i]);
if (!clip_tab->freq_clip_max || !clip_tab->mask_val
|| !clip_tab->temp_level) {
if (!clip_tab->freq_clip_max || !clip_tab->mask_val) {
please make a separate patch for this modification
kfree(cpufreq_dev); return ERR_PTR(-EINVAL); }
diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c new file mode 100644 index 0000000..0dc2fcb --- /dev/null +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -0,0 +1,159 @@ +/*
- db8500_cpufreq_cooling.c - db8500 cpufreq works as cooling device.
- Copyright (C) 2012 ST-Ericsson
- Copyright (C) 2012 Linaro Ltd.
- Author: Hongbo Zhang hognbo.zhang@stericsson.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; either version 2 of the License, or
- (at your option) any later version.
- 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/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/cpufreq.h> +#include <linux/cpu_cooling.h>
+#define CPU_FREQS_MAX 8 +#define CPU_CDEVS_MAX 8 /* should equal or larger than CPU_FREQS_MAX */
+static struct freq_clip_table db8500_clip_table[CPU_FREQS_MAX]; +static struct thermal_cooling_device *db8500_cool_devs[CPU_CDEVS_MAX];
+static int num_freqs; +static int num_cdevs;
+static int cpufreq_table_create(void) +{
struct cpufreq_frequency_table *table;
unsigned int freq_scratch[CPU_FREQS_MAX];
unsigned int temp;
int i, j, count = 0;
table = cpufreq_frequency_get_table(0);
if (!table)
return -EINVAL;
/* Check number of frequencies */
for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
count++;
}
if(unlikely(count > CPU_FREQS_MAX)) {
pr_err("CPU_FREQS_MAX is not large enough.\n");
return -EINVAL;
}
num_freqs = count;
/* Save frequencies */
count= 0;
memset(freq_scratch, 0, sizeof(freq_scratch));
for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
freq_scratch[count] = table[i].frequency;
count++;
}
/* Descending order frequencies */
for (i = 0; i <= num_freqs - 2; i++)
for (j = i + 1; j <= num_freqs - 1; j++)
if (freq_scratch[i] < freq_scratch[j]) {
temp = freq_scratch[i];
freq_scratch[i] = freq_scratch[j];
freq_scratch[j] = temp;
}
/* Create freq clip table */
memset(db8500_clip_table, 0, sizeof(db8500_clip_table));
for (i = 0; i < num_freqs; i++) {
db8500_clip_table[i].freq_clip_max = freq_scratch[i];
db8500_clip_table[i].mask_val = cpumask_of(0);
cpumask_of(0) is false because both cpu are linked for their frequency use cpu_present_mask instead
pr_info("db8500_clip_table %d: %d\n",i, db8500_clip_table[i].freq_clip_max);
}
return 0;
+}
+static int __devinit db8500_cpufreq_cooling_probe(struct platform_device *pdev) +{
struct freq_clip_table *clip_data;
int i, count = 0;
if (cpufreq_table_create())
return -EINVAL;
memset(db8500_cool_devs, 0, sizeof(db8500_cool_devs));
could you dynamically allocate db8500_cool_dev and db8500_clip_table based on the count result that has been done in cpufreq_table_create function
more generally speaking, could you prevent using "global" variable ?
db8500_cool_devs, db8500_clip_table, num_freqs and num_cdevs should be linked with your driver and not a static variabale
/* Create one cooling device for each clip frequency */
for (i = 0; i < num_freqs; i++) {
clip_data = &(db8500_clip_table[i]);
db8500_cool_devs[i] = cpufreq_cooling_register(clip_data, 1);
if (!db8500_cool_devs[i]) {
pr_err("Failed to register cpufreq cooling device\n");
goto exit;
}
count++;
pr_info("Cooling device regestered: %s\n", db8500_cool_devs[i]->type);
}
num_cdevs = count;
return 0;
+exit:
for (i = 0; i < count; i++)
cpufreq_cooling_unregister(db8500_cool_devs[i]);
return -EINVAL;
+}
+static int __devexit db8500_cpufreq_cooling_remove(struct platform_device *pdev) +{
int i;
for (i = 0; i < num_cdevs; i++)
cpufreq_cooling_unregister(db8500_cool_devs[i]);
return 0;
+}
+/* No actions required in suspend/resume, so lack of them */ +static struct platform_driver db8500_cpufreq_cooling_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "db8500_cpufreq_cooling",
},
.probe = db8500_cpufreq_cooling_probe,
.remove = __devexit_p(db8500_cpufreq_cooling_remove),
+};
+static int __init db8500_cpufreq_cooling_init(void) +{
return platform_driver_register(&db8500_cpufreq_cooling_driver);
+}
+static void __exit db8500_cpufreq_cooling_exit(void) +{
platform_driver_unregister(&db8500_cpufreq_cooling_driver);
+}
+/* Should be later than db8500_cpufreq_register() */ +late_initcall(db8500_cpufreq_cooling_init); +module_exit(db8500_cpufreq_cooling_exit);
+MODULE_AUTHOR("Hongbo Zhang hongbo.zhang@stericsson.com"); +MODULE_DESCRIPTION("db8500 cpufreq cooling driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c new file mode 100755 index 0000000..7e85969 --- /dev/null +++ b/drivers/thermal/db8500_thermal.c @@ -0,0 +1,445 @@ +/*
- db8500_thermal.c - db8500 Thermal Management Implementation
- Copyright (C) 2012 ST-Ericsson
- Copyright (C) 2012 Linaro Ltd.
- Author: Hongbo Zhang hognbo.zhang@stericsson.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; either version 2 of the License, or
- (at your option) any later version.
- 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/compiler.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/thermal.h> +#include <linux/cpu_cooling.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/db8500_thermal.h>
+#define PRCMU_DEFAULT_MEASURE_TIME 0xFFF +#define PRCMU_DEFAULT_LOW_TEMP 0
+struct db8500_thermal_zone {
struct thermal_zone_device *therm_dev;
enum thermal_device_mode mode;
struct mutex th_lock;
struct platform_device *thsens_pdev;
struct work_struct therm_work;
unsigned long cur_low;
unsigned long cur_high;
int low_irq;
int high_irq;
+};
+static struct db8500_thermal_zone *th_zone = NULL;
+/* Bind callback functions for thermal zone */ +static int db8500_cdev_bind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
+{
struct db8500_thermal_zone *pzone;
struct db8500_thsens_platform_data *ptrips;
int i, j, ret;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
ptrips = pzone->thsens_pdev->dev.platform_data;
for (i = 0; i < ptrips->num_trips; i++)
for (j = 0; j < COOLING_DEV_MAX; j++)
if (ptrips->trip_points[i].cooling_dev_name[j] && cdev->type)
if (strcmp(ptrips->trip_points[i].cooling_dev_name[j], cdev->type) == 0) {
ret = thermal_zone_bind_cooling_device(thermal, i, cdev);
if (ret)
pr_err("Error binding cooling device.\n");
else
pr_info("Cooling device %s binded to trip point %d.\n", cdev->type, i);
}
schedule_work(&pzone->therm_work);
return 0;
+}
+/* Unbind callback functions for thermal zone */ +static int db8500_cdev_unbind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
+{
struct db8500_thermal_zone *pzone;
struct db8500_thsens_platform_data *ptrips;
int i, j, ret;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
ptrips = pzone->thsens_pdev->dev.platform_data;
for (i = 0; i < ptrips->num_trips; i++)
for (j = 0; j < COOLING_DEV_MAX; j++)
if (ptrips->trip_points[i].cooling_dev_name[j] && cdev->type)
if (strcmp(ptrips->trip_points[i].cooling_dev_name[j], cdev->type) == 0) {
ret = thermal_zone_unbind_cooling_device(thermal, i, cdev);
if (ret)
pr_err("Error unbinding cooling device.\n");
else
pr_info("Cooling device %s unbinded from trip point %d.\n", cdev->type, i);
}
return 0;
+}
+/* Get temperature callback functions for thermal zone */ +static int db8500_sys_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
+{
struct db8500_thermal_zone *pzone;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
/* There is no PRCMU interface to get temperature data currently,
so just returns temperature between two trip points, it works for the thermal framework
and this will be fixed when the PRCMU interface to get temperature is available */
*temp = (pzone->cur_high + pzone->cur_low)/2;
return 0;
+}
+/* Get mode callback functions for thermal zone */ +static int db8500_sys_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
+{
struct db8500_thermal_zone *pzone;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
mutex_lock(&pzone->th_lock);
*mode = pzone->mode;
mutex_unlock(&pzone->th_lock);
return 0;
+}
+/* Set mode callback functions for thermal zone */ +static int db8500_sys_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
+{
struct db8500_thermal_zone *pzone;
struct thermal_zone_device *pthdev;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
pthdev = pzone->therm_dev;
if (!pthdev) {
pr_err("Thermal zone not registered.\n");
return 0;
}
mutex_lock(&pzone->th_lock);
if (mode == THERMAL_DEVICE_ENABLED) {
if (pzone->mode == THERMAL_DEVICE_DISABLED)
pr_info("Thermal function started.\n");
else
pr_warning("Thermal function already started.\n");
} else {
if (pzone->mode == THERMAL_DEVICE_ENABLED)
pr_info("Thermal function stoped.\n");
else
pr_warning("Thermal function already stoped.\n");
}
pzone->mode = mode;
mutex_unlock(&pzone->th_lock);
return 0;
+}
+/* Get trip type callback function for thermal zone */ +static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
enum thermal_trip_type *type)
+{
struct db8500_thermal_zone *pzone;
struct db8500_thsens_platform_data *ptrips;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
ptrips = pzone->thsens_pdev->dev.platform_data;
if (trip >= ptrips->num_trips)
return -EINVAL;
*type = ptrips->trip_points[trip].type;
return 0;
+}
+/* Get trip temperature callback function for thermal zone */ +static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal, int trip,
unsigned long *temp)
+{
struct db8500_thermal_zone *pzone;
struct db8500_thsens_platform_data *ptrips;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
ptrips = pzone->thsens_pdev->dev.platform_data;
if (trip >= ptrips->num_trips)
return -EINVAL;
*temp = ptrips->trip_points[trip].temp;
return 0;
+}
+/* Get critical temperature callback function for thermal zone */ +static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
+{
struct db8500_thermal_zone *pzone;
struct db8500_thsens_platform_data *ptrips;
int i;
pzone = (struct db8500_thermal_zone *)thermal->devdata;
ptrips = pzone->thsens_pdev->dev.platform_data;
for (i = (ptrips->num_trips - 1) ; i > 0 ; i--) {
if (ptrips->trip_points[i].type == THERMAL_TRIP_CRITICAL) {
*temp = ptrips->trip_points[i].temp;
return 0;
}
}
return -EINVAL;
+}
+static struct thermal_zone_device_ops thdev_ops = {
.bind = db8500_cdev_bind,
.unbind = db8500_cdev_unbind,
.get_temp = db8500_sys_get_temp,
.get_mode = db8500_sys_get_mode,
.set_mode = db8500_sys_set_mode,
.get_trip_type = db8500_sys_get_trip_type,
.get_trip_temp = db8500_sys_get_trip_temp,
.get_crit_temp = db8500_sys_get_crit_temp,
+};
+static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data) +{
struct db8500_thermal_zone *pzone = irq_data;
struct db8500_thsens_platform_data *ptrips;
unsigned long next_low, next_high;
int i;
ptrips = pzone->thsens_pdev->dev.platform_data;
for(i = 0; i < ptrips->num_trips; i++) {
if (pzone->cur_high == ptrips->trip_points[i].temp)
break;
}
if (i <= 1) {
next_high = ptrips->trip_points[0].temp;
next_low = PRCMU_DEFAULT_LOW_TEMP;
} else {
next_high = ptrips->trip_points[i-1].temp;
next_low = ptrips->trip_points[i-2].temp;
}
(void) prcmu_stop_temp_sense();
(void) prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
(void) prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
pzone->cur_high = next_high;
pzone->cur_low = next_low;
pr_debug("PRCMU set max %ld, set min %ld\n", next_high, next_low);
schedule_work(&pzone->therm_work);
return IRQ_HANDLED;
+}
+static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data) +{
struct db8500_thermal_zone *pzone = irq_data;
struct db8500_thsens_platform_data *ptrips;
unsigned long next_low, next_high;
int i;
ptrips = pzone->thsens_pdev->dev.platform_data;
for(i = 0; i < ptrips->num_trips; i++) {
if (pzone->cur_high == ptrips->trip_points[i].temp)
break;
}
/* not likely over critial trip temp, e.g. i < ptrips->num_trips-1 here */
next_high = ptrips->trip_points[i+1].temp;
next_low = ptrips->trip_points[i].temp;
(void) prcmu_stop_temp_sense();
(void) prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
(void) prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
pzone->cur_high = next_high;
pzone->cur_low = next_low;
pr_debug("PRCMU set max %ld, set min %ld\n", next_high, next_low);
schedule_work(&pzone->therm_work);
return IRQ_HANDLED;
+}
+static void db8500_thermal_work(struct work_struct *work) +{
enum thermal_device_mode cur_mode;
struct db8500_thermal_zone *pzone;
pzone = container_of(work, struct db8500_thermal_zone, therm_work);
mutex_lock(&pzone->th_lock);
cur_mode = pzone->mode;
mutex_unlock(&pzone->th_lock);
if (cur_mode == THERMAL_DEVICE_DISABLED) {
pr_warn("Warning: thermal function disabled.\n");
return;
}
thermal_zone_device_update(pzone->therm_dev);
pr_debug("db8500_thermal_work finished. \n");
+}
+static int __devinit db8500_thermal_probe(struct platform_device *pdev) +{
struct db8500_thermal_zone *pzone = NULL;
struct db8500_thsens_platform_data *ptrips;
int low_irq, high_irq, ret = 0;
unsigned long dft_low, dft_high;
pr_info("Function db8500_thermal_probe.\n");
pzone = kzalloc(sizeof(struct db8500_thermal_zone), GFP_KERNEL);
if (!pzone)
return ENOMEM;
pzone->thsens_pdev = pdev;
low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
if (low_irq < 0) {
pr_err("Get IRQ_HOTMON_LOW failed.\n");
goto exit_irq;
}
ret = request_threaded_irq(low_irq, NULL, prcmu_low_irq_handler,
IRQF_NO_SUSPEND, "dbx500_temp_low", pzone);
if (ret < 0) {
pr_err("Failed to allocate temp low irq.\n");
goto exit_irq;
}
high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
if (high_irq < 0) {
pr_err("Get IRQ_HOTMON_HIGH failed.\n");
goto exit_irq;
}
ret = request_threaded_irq(high_irq, NULL, prcmu_high_irq_handler,
IRQF_NO_SUSPEND, "dbx500_temp_high", pzone);
if (ret < 0) {
pr_err("Failed to allocate temp high irq.\n");
goto exit_irq;
}
pzone->low_irq = low_irq;
pzone->high_irq = high_irq;
ptrips = (struct db8500_thsens_platform_data *)pdev->dev.platform_data;
pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone",
ptrips->num_trips, pzone, &thdev_ops, 0, 0, 0, 0);
if (IS_ERR(pzone->therm_dev)) {
pr_err("Failed to register thermal zone device\n");
ret = -EINVAL;
goto exit_th;
}
mutex_init(&pzone->th_lock);
INIT_WORK(&pzone->therm_work, db8500_thermal_work);
/* PRCMU initialize */
dft_low = PRCMU_DEFAULT_LOW_TEMP;
dft_high = (ptrips->trip_points[0].temp);
(void) prcmu_stop_temp_sense();
(void) prcmu_config_hotmon((u8)(dft_low/1000), (u8)(dft_high/1000));
(void) prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
pzone->cur_low = dft_low;
pzone->cur_high = dft_high;
pzone->mode = THERMAL_DEVICE_ENABLED;
th_zone = pzone;
return 0;
+exit_th:
if (pzone->therm_dev)
thermal_zone_device_unregister(pzone->therm_dev);
+exit_irq:
if (pzone->low_irq > 0)
free_irq(pzone->low_irq, pzone);
if (pzone->low_irq > 0)
free_irq(pzone->high_irq, pzone);
kfree(pzone);
return ret;
+}
+static int __devexit db8500_thermal_remove(struct platform_device *pdev) +{
struct db8500_thermal_zone *pzone = th_zone;
if (pzone && pzone->therm_dev)
thermal_zone_device_unregister(pzone->therm_dev);
if (pzone && pzone->low_irq)
free_irq(pzone->low_irq, pzone);
if (pzone && pzone->high_irq)
free_irq(pzone->high_irq, pzone);
if (pzone)
kfree(pzone);
return 0;
+}
+/* No actions required in suspend/resume, so lack of them */ +static struct platform_driver db8500_thermal_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "db8500_thermal"
},
.probe = db8500_thermal_probe,
.remove = __devexit_p(db8500_thermal_remove),
+};
+static int __init db8500_thermal_init(void) +{
return platform_driver_register(&db8500_thermal_driver);
+}
+static void __exit db8500_thermal_exit(void) +{
platform_driver_unregister(&db8500_thermal_driver);
+}
+module_init(db8500_thermal_init); +module_exit(db8500_thermal_exit);
+MODULE_AUTHOR("Hongbo Zhang hongbo.zhang@stericsson.com"); +MODULE_DESCRIPTION("db8500 thermal driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/db8500_thermal.h b/include/linux/platform_data/db8500_thermal.h new file mode 100644 index 0000000..0b6d164 --- /dev/null +++ b/include/linux/platform_data/db8500_thermal.h @@ -0,0 +1,39 @@ +/*
- db8500_thermal.h - db8500 Thermal Management Implementation
- Copyright (C) 2012 ST-Ericsson
- Copyright (C) 2012 Linaro Ltd.
- Author: Hongbo Zhang hognbo.zhang@stericsson.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; either version 2 of the License, or
- (at your option) any later version.
- 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.
- */
+#ifndef _DB8500_THERMAL_H_ +#define _DB8500_THERMAL_H_
+#include <linux/thermal.h>
+#define COOLING_DEV_MAX 8
+struct db8500_trip_point {
unsigned long temp;
enum thermal_trip_type type;
char *cooling_dev_name[COOLING_DEV_MAX];
+};
+struct db8500_thsens_platform_data {
struct db8500_trip_point *trip_points;
int num_trips;
+};
+#endif /* _DB8500_THERMAL_H_ */
1.7.10
linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev