I sent the mail before finishing the review. additional one will come soon On 5 July 2012 16:13, Vincent Guittot <vincent.guit...@linaro.org> wrote: > On 20 June 2012 15:04, hongbo.zhang <hongbo.zh...@linaro.org> wrote: >> From: "hongbo.zhang" <hongbo.zh...@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.zh...@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.zh...@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.zh...@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.zh...@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.zh...@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
_______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev