diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-13 11:27:59 +0900 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-13 11:27:59 +0900 |
commit | a3920a6efa158b445b8a39080b463b9b29337425 (patch) | |
tree | 1726a21d317bea8039c1f0f041b7443af76f6ca1 | |
parent | 18a022de47bc11ee20d7d0f4dd72d42d2cfdc51c (diff) | |
parent | d1d4a81b842db21b144ffd2334ca5eee3eb740f3 (diff) |
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
Pull ACPI & Thermal updates from Len Brown:
"The generic Linux thermal layer is gaining some new capabilities
(generic cooling via cpufreq) and some new customers (ARM).
Also, an ACPI EC bug fix plus a regression fix."
* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux: (30 commits)
tools/power/acpi/acpidump: remove duplicated include from acpidump.c
ACPI idle, CPU hotplug: Fix NULL pointer dereference during hotplug
cpuidle / ACPI: fix potential NULL pointer dereference
ACPI: EC: Add a quirk for CLEVO M720T/M730T laptop
ACPI: EC: Make the GPE storm threshold a module parameter
thermal: Exynos: Fix NULL pointer dereference in exynos_unregister_thermal()
Thermal: Fix bug on cpu_cooling, cooling device's id conflict problem.
thermal: exynos: Use devm_* functions
ARM: exynos: add thermal sensor driver platform data support
thermal: exynos: register the tmu sensor with the kernel thermal layer
thermal: exynos5: add exynos5250 thermal sensor driver support
hwmon: exynos4: move thermal sensor driver to driver/thermal directory
thermal: add generic cpufreq cooling implementation
Fix a build error.
thermal: Fix potential NULL pointer accesses
thermal: add Renesas R-Car thermal sensor support
thermal: fix potential out-of-bounds memory access
Thermal: Introduce locking for cdev.thermal_instances list.
Thermal: Unify the code for both active and passive cooling
Thermal: Introduce simple arbitrator for setting device cooling state
...
25 files changed, 2205 insertions, 736 deletions
diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt new file mode 100644 index 00000000000..fca24c931ec --- /dev/null +++ b/Documentation/thermal/cpu-cooling-api.txt @@ -0,0 +1,32 @@ +CPU cooling APIs How To +=================================== + +Written by Amit Daniel Kachhap <amit.kachhap@linaro.org> + +Updated: 12 May 2012 + +Copyright (c) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) + +0. Introduction + +The generic cpu cooling(freq clipping) provides registration/unregistration APIs +to the caller. The binding of the cooling devices to the trip point is left for +the user. The registration APIs returns the cooling device pointer. + +1. cpu cooling APIs + +1.1 cpufreq registration/unregistration APIs +1.1.1 struct thermal_cooling_device *cpufreq_cooling_register( + struct cpumask *clip_cpus) + + This interface function registers the cpufreq cooling device with the name + "thermal-cpufreq-%x". This api can support multiple instances of cpufreq + cooling devices. + + clip_cpus: cpumask of cpus where the frequency constraints will happen. + +1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) + + This interface function unregisters the "thermal-cpufreq-%x" cooling device. + + cdev: Cooling device pointer which has to be unregistered. diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/thermal/exynos_thermal index c3c6b41db60..2b46f67b1cc 100644 --- a/Documentation/hwmon/exynos4_tmu +++ b/Documentation/thermal/exynos_thermal @@ -46,36 +46,7 @@ The threshold levels are defined as follows: The threshold and each trigger_level are set through the corresponding registers. -When an interrupt occurs, this driver notify user space of -one of four threshold levels for the interrupt -through kobject_uevent_env and sysfs_notify functions. +When an interrupt occurs, this driver notify kernel thermal framework +with the function exynos4_report_trigger. Although an interrupt condition for level_0 can be set, -it is not notified to user space through sysfs_notify function. - -Sysfs Interface ---------------- -name name of the temperature sensor - RO - -temp1_input temperature - RO - -temp1_max temperature for level_1 interrupt - RO - -temp1_crit temperature for level_2 interrupt - RO - -temp1_emergency temperature for level_3 interrupt - RO - -temp1_max_alarm alarm for level_1 interrupt - RO - -temp1_crit_alarm - alarm for level_2 interrupt - RO - -temp1_emergency_alarm - alarm for level_3 interrupt - RO +it can be used to synchronize the cooling action. diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index c087dbcf353..ca1a1a34970 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -84,7 +84,8 @@ temperature) and throttle appropriate devices. 1.3 interface for binding a thermal zone device with a thermal cooling device 1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, - int trip, struct thermal_cooling_device *cdev); + int trip, struct thermal_cooling_device *cdev, + unsigned long upper, unsigned long lower); This interface function bind a thermal cooling device to the certain trip point of a thermal zone device. @@ -93,6 +94,12 @@ temperature) and throttle appropriate devices. cdev: thermal cooling device trip: indicates which trip point the cooling devices is associated with in this thermal zone. + upper:the Maximum cooling state for this trip point. + THERMAL_NO_LIMIT means no upper limit, + and the cooling device can be in max_state. + lower:the Minimum cooling state can be used for this trip point. + THERMAL_NO_LIMIT means no lower limit, + and the cooling device can be in cooling state 0. 1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, int trip, struct thermal_cooling_device *cdev); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 7edaccce664..a51df968131 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -71,9 +71,6 @@ enum ec_command { #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ -#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts - per one transaction */ - enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_GPE_STORM, /* GPE storm detected */ @@ -87,6 +84,15 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); +/* + * If the number of false interrupts per one transaction exceeds + * this threshold, will think there is a GPE storm happened and + * will disable the GPE for normal transaction. + */ +static unsigned int ec_storm_threshold __read_mostly = 8; +module_param(ec_storm_threshold, uint, 0644); +MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); + /* If we find an EC via the ECDT, we need to keep a ptr to its context */ /* External interfaces use first EC only, so remember */ typedef int (*acpi_ec_query_func) (void *data); @@ -319,7 +325,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) msleep(1); /* It is safe to enable the GPE outside of the transaction. */ acpi_enable_gpe(NULL, ec->gpe); - } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { + } else if (t->irq_count > ec_storm_threshold) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); @@ -924,6 +930,17 @@ static int ec_flag_msi(const struct dmi_system_id *id) return 0; } +/* + * Clevo M720 notebook actually works ok with IRQ mode, if we lifted + * the GPE storm threshold back to 20 + */ +static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) +{ + pr_debug("Setting the EC GPE storm threshold to 20\n"); + ec_storm_threshold = 20; + return 0; +} + static struct dmi_system_id __initdata ec_dmi_table[] = { { ec_skip_dsdt_scan, "Compal JFL92", { @@ -955,10 +972,13 @@ static struct dmi_system_id __initdata ec_dmi_table[] = { { ec_validate_ecdt, "ASUS hardware", { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL}, + { + ec_enlarge_storm_threshold, "CLEVO hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), + DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL}, {}, }; - int __init acpi_ec_ecdt_probe(void) { acpi_status status; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3655ab92381..e8086c72530 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) int acpi_processor_hotplug(struct acpi_processor *pr) { int ret = 0; - struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id); + struct cpuidle_device *dev; if (disabled_by_idle_boot_param()) return 0; @@ -1147,6 +1147,7 @@ int acpi_processor_hotplug(struct acpi_processor *pr) if (!pr->flags.power_setup_done) return -ENODEV; + dev = per_cpu(acpi_cpuidle_device, pr->id); cpuidle_pause_and_lock(); cpuidle_disable_device(dev); acpi_processor_get_power_info(pr); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index edda74a4340..804204d4199 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -708,6 +708,40 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, return -EINVAL; } +static int thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct acpi_thermal *tz = thermal->devdata; + enum thermal_trip_type type; + int i; + + if (thermal_get_trip_type(thermal, trip, &type)) + return -EINVAL; + + if (type == THERMAL_TRIP_ACTIVE) { + /* aggressive active cooling */ + *trend = THERMAL_TREND_RAISING; + return 0; + } + + /* + * tz->temperature has already been updated by generic thermal layer, + * before this callback being invoked + */ + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 + * (tz->temperature - tz->trips.passive.temperature)); + + if (i > 0) + *trend = THERMAL_TREND_RAISING; + else if (i < 0) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + return 0; +} + + static int thermal_notify(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type trip_type) { @@ -731,11 +765,9 @@ static int thermal_notify(struct thermal_zone_device *thermal, int trip, return 0; } -typedef int (*cb)(struct thermal_zone_device *, int, - struct thermal_cooling_device *); static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev, - cb action) + bool bind) { struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; @@ -759,11 +791,19 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, i++) { handle = tz->trips.passive.devices.handles[i]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = + thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = + thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -776,11 +816,17 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j++) { handle = tz->trips.active[i].devices.handles[j]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -788,7 +834,14 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, handle = tz->devices.handles[i]; status = acpi_bus_get_device(handle, &dev); if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, -1, cdev); + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, -1, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, -1, cdev); if (result) goto failed; } @@ -802,16 +855,14 @@ static int acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - return acpi_thermal_cooling_device_cb(thermal, cdev, - thermal_zone_bind_cooling_device); + return acpi_thermal_cooling_device_cb(thermal, cdev, true); } static int acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - return acpi_thermal_cooling_device_cb(thermal, cdev, - thermal_zone_unbind_cooling_device); + return acpi_thermal_cooling_device_cb(thermal, cdev, false); } static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { @@ -823,6 +874,7 @@ static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { .get_trip_type = thermal_get_trip_type, .get_trip_temp = thermal_get_trip_temp, .get_crit_temp = thermal_get_crit_temp, + .get_trend = thermal_get_trend, .notify = thermal_notify, }; @@ -849,15 +901,12 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, &acpi_thermal_zone_ops, - tz->trips.passive.tc1, - tz->trips.passive.tc2, tz->trips.passive.tsp*100, tz->polling_frequency*100); else tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, - 0, 0, 0, + &acpi_thermal_zone_ops, 0, tz->polling_frequency*100); if (IS_ERR(tz->thermal_zone)) return -ENODEV; diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index e28f6ea46f1..7f15b8514a1 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -368,7 +368,7 @@ EXPORT_SYMBOL_GPL(cpuidle_enable_device); */ void cpuidle_disable_device(struct cpuidle_device *dev) { - if (!dev->enabled) + if (!dev || !dev->enabled) return; if (!cpuidle_get_driver() || !cpuidle_curr_governor) return; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c74e73b2069..c4633de6446 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -334,16 +334,6 @@ config SENSORS_DA9052_ADC This driver can also be built as module. If so, the module will be called da9052-hwmon. -config SENSORS_EXYNOS4_TMU - tristate "Temperature sensor on Samsung EXYNOS4" - depends on ARCH_EXYNOS4 - help - If you say yes here you get support for TMU (Thermal Management - Unit) on SAMSUNG EXYNOS4 series of SoC. - - This driver can also be built as a module. If so, the module - will be called exynos4-tmu. - config SENSORS_I5K_AMB tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a62ce17ddbf..8d5fcb5e8e9 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o -obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c deleted file mode 100644 index e912059140c..00000000000 --- a/drivers/hwmon/exynos4_tmu.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit) - * - * Copyright (C) 2011 Samsung Electronics - * Donggeun Kim <dg77.kim@samsung.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. - * - * 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/kernel.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/workqueue.h> -#include <linux/sysfs.h> -#include <linux/kobject.h> -#include <linux/io.h> -#include <linux/mutex.h> - -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> - -#include <linux/platform_data/exynos4_tmu.h> - -#define EXYNOS4_TMU_REG_TRIMINFO 0x0 -#define EXYNOS4_TMU_REG_CONTROL 0x20 -#define EXYNOS4_TMU_REG_STATUS 0x28 -#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40 -#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44 -#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50 -#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54 -#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58 -#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C -#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60 -#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64 -#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68 -#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C -#define EXYNOS4_TMU_REG_INTEN 0x70 -#define EXYNOS4_TMU_REG_INTSTAT 0x74 -#define EXYNOS4_TMU_REG_INTCLEAR 0x78 - -#define EXYNOS4_TMU_GAIN_SHIFT 8 -#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24 - -#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff -#define EXYNOS4_TMU_CORE_ON 3 -#define EXYNOS4_TMU_CORE_OFF 2 -#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50 -#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1 -#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10 -#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100 -#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000 -#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111 - -struct exynos4_tmu_data { - struct exynos4_tmu_platform_data *pdata; - struct device *hwmon_dev; - struct resource *mem; - void __iomem *base; - int irq; - struct work_struct irq_work; - struct mutex lock; - struct clk *clk; - u8 temp_error1, temp_error2; -}; - -/* - * TMU treats temperature as a mapped temperature code. - * The temperature is converted differently depending on the calibration type. - */ -static int temp_to_code(struct exynos4_tmu_data *data, u8 temp) -{ - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp_code; - - /* temp should range between 25 and 125 */ - if (temp < 25 || temp > 125) { - temp_code = -EINVAL; - goto out; - } - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp_code = (temp - 25) * - (data->temp_error2 - data->temp_error1) / - (85 - 25) + data->temp_error1; - break; - case TYPE_ONE_POINT_TRIMMING: - temp_code = temp + data->temp_error1 - 25; - break; - default: - temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; - break; - } -out: - return temp_code; -} - -/* - * Calculate a temperature value from a temperature code. - * The unit of the temperature is degree Celsius. - */ -static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code) -{ - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp; - - /* temp_code should range between 75 and 175 */ - if (temp_code < 75 || temp_code > 175) { - temp = -ENODATA; - goto out; - } - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp = (temp_code - data->temp_error1) * (85 - 25) / - (data->temp_error2 - data->temp_error1) + 25; - break; - case TYPE_ONE_POINT_TRIMMING: - temp = temp_code - data->temp_error1 + 25; - break; - default: - temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; - break; - } -out: - return temp; -} - -static int exynos4_tmu_initialize(struct platform_device *pdev) -{ - struct exynos4_tmu_data *data = platform_get_drvdata(pdev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int status, trim_info; - int ret = 0, threshold_code; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - status = readb(data->base + EXYNOS4_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } - - /* Save trimming info in order to perform calibration */ - trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO); - data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK; - data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK); - - /* Write temperature code for threshold */ - threshold_code = temp_to_code(data, pdata->threshold); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - writeb(threshold_code, - data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP); - - writeb(pdata->trigger_levels[0], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0); - writeb(pdata->trigger_levels[1], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1); - writeb(pdata->trigger_levels[2], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2); - writeb(pdata->trigger_levels[3], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3); - - writel(EXYNOS4_TMU_INTCLEAR_VAL, - data->base + EXYNOS4_TMU_REG_INTCLEAR); -out: - clk_disable(data->clk); - mutex_unlock(&data->lock); - - return ret; -} - -static void exynos4_tmu_control(struct platform_device *pdev, bool on) -{ - struct exynos4_tmu_data *data = platform_get_drvdata(pdev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int con, interrupt_en; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT | - pdata->gain << EXYNOS4_TMU_GAIN_SHIFT; - if (on) { - con |= EXYNOS4_TMU_CORE_ON; - interrupt_en = pdata->trigger_level3_en << 12 | - pdata->trigger_level2_en << 8 | - pdata->trigger_level1_en << 4 | - pdata->trigger_level0_en; - } else { - con |= EXYNOS4_TMU_CORE_OFF; - interrupt_en = 0; /* Disable all interrupts */ - } - writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN); - writel(con, data->base + EXYNOS4_TMU_REG_CONTROL); - - clk_disable(data->clk); - mutex_unlock(&data->lock); -} - -static int exynos4_tmu_read(struct exynos4_tmu_data *data) -{ - u8 temp_code; - int temp; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP); - temp = code_to_temp(data, temp_code); - - clk_disable(data->clk); - mutex_unlock(&data->lock); - - return temp; -} - -static void exynos4_tmu_work(struct work_struct *work) -{ - struct exynos4_tmu_data *data = container_of(work, - struct exynos4_tmu_data, irq_work); - - mutex_lock(&data->lock); - clk_enable(data->clk); - - writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR); - - kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); - - enable_irq(data->irq); - - clk_disable(data->clk); - mutex_unlock(&data->lock); -} - -static irqreturn_t exynos4_tmu_irq(int irq, void *id) -{ - struct exynos4_tmu_data *data = id; - - disable_irq_nosync(irq); - schedule_work(&data->irq_work); - - return IRQ_HANDLED; -} - -static ssize_t exynos4_tmu_show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "exynos4-tmu\n"); -} - -static ssize_t exynos4_tmu_show_temp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - int ret; - - ret = exynos4_tmu_read(data); - if (ret < 0) - return ret; - - /* convert from degree Celsius to millidegree Celsius */ - return sprintf(buf, "%d\n", ret * 1000); -} - -static ssize_t exynos4_tmu_show_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp; - unsigned int trigger_level; - - temp = exynos4_tmu_read(data); - if (temp < 0) - return temp; - - trigger_level = pdata->threshold + pdata->trigger_levels[attr->index]; - - return sprintf(buf, "%d\n", !!(temp > trigger_level)); -} - -static ssize_t exynos4_tmu_show_level(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int temp = pdata->threshold + - pdata->trigger_levels[attr->index]; - - return sprintf(buf, "%u\n", temp * 1000); -} - -static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0); - -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 3); - -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2); -static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO, - exynos4_tmu_show_level, NULL, 3); - -static struct attribute *exynos4_tmu_attributes[] = { - &dev_attr_name.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_emergency.dev_attr.attr, - NULL, -}; - -static const struct attribute_group exynos4_tmu_attr_group = { - .attrs = exynos4_tmu_attributes, -}; - -static int __devinit exynos4_tmu_probe(struct platform_device *pdev) -{ - struct exynos4_tmu_data *data; - struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&pdev->dev, "No platform init data supplied.\n"); - return -ENODEV; - } - - data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL); - if (!data) { - dev_err(&pdev->dev, "Failed to allocate driver structure\n"); - return -ENOMEM; - } - - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) { - ret = data->irq; - dev_err(&pdev->dev, "Failed to get platform irq\n"); - goto err_free; - } - - INIT_WORK(&data->irq_work, exynos4_tmu_work); - - data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!data->mem) { - ret = -ENOENT; - dev_err(&pdev->dev, "Failed to get platform resource\n"); - goto err_free; - } - - data->mem = request_mem_region(data->mem->start, - resource_size(data->mem), pdev->name); - if (!data->mem) { - ret = -ENODEV; - dev_err(&pdev->dev, "Failed to request memory region\n"); - goto err_free; - } - - data->base = ioremap(data->mem->start, resource_size(data->mem)); - if (!data->base) { - ret = -ENODEV; - dev_err(&pdev->dev, "Failed to ioremap memory\n"); - goto err_mem_region; - } - - ret = request_irq(data->irq, exynos4_tmu_irq, - IRQF_TRIGGER_RISING, - "exynos4-tmu", data); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); - goto err_io_remap; - } - - data->clk = clk_get(NULL, "tmu_apbif"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - dev_err(&pdev->dev, "Failed to get clock\n"); - goto err_irq; - } - - data->pdata = pdata; - platform_set_drvdata(pdev, data); - mutex_init(&data->lock); - - ret = exynos4_tmu_initialize(pdev); - if (ret) { - dev_err(&pdev->dev, "Failed to initialize TMU\n"); - goto err_clk; - } - - ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); - if (ret) { |