summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorRajagopala V <rajagopala.v@stericsson.com>2011-08-25 13:20:04 +0530
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 16:00:10 +0200
commit2fcf80b44f6943ef03cda4f9e62c90b2effdf270 (patch)
tree215ca72fda0871794807e447cc8b7981968f2547 /drivers/hwmon
parenta38b3ab78080e28487109ceab5d3cb4435bff8ed (diff)
abx500: hwmon: generalize AB8500 temperature monitor driver
Generalize ab8500 hwmon driver to support other abx500 variants. Source code is split into generic and variant specific files. ST-Ericsson Linux next: NA ST-Ericsson ID: WP257616 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ia39feaac455160149d49a5982374da3729ea5301 Signed-off-by: Rajagopala V <rajagopala.v@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28138 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig2
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/ab8500.c714
-rw-r--r--drivers/hwmon/abx500.c667
-rw-r--r--drivers/hwmon/abx500.h84
5 files changed, 799 insertions, 670 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 602ba39286d..cba0d3ec649 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -41,7 +41,7 @@ comment "Native drivers"
config SENSORS_AB8500
tristate "AB8500 thermal monitoring"
- depends on AB8500_CORE
+ depends on AB8500_GPADC
default n
help
If you say yes here you get support for the thermal sensor part
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4f614d48b37..049b99a9192 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -19,7 +19,7 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
-obj-$(CONFIG_SENSORS_AB8500) += ab8500.o
+obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
obj-$(CONFIG_SENSORS_DB8500) += db8500.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
index 7469ffaa6cb..cb6cf4cf4d4 100644
--- a/drivers/hwmon/ab8500.c
+++ b/drivers/hwmon/ab8500.c
@@ -6,16 +6,6 @@
*
* Note:
*
- * AB8500 does not provide auto ADC, so to monitor the required
- * temperatures, a periodic work is used. It is more important
- * to not wake up the CPU than to perform this job, hence the use
- * of a deferred delay.
- *
- * A deferred delay for thermal monitor is considered safe because:
- * If the chip gets too hot during a sleep state it's most likely
- * due to external factors, such as the surrounding temperature.
- * I.e. no SW decisions will make any difference.
- *
* If/when the AB8500 thermal warning temperature is reached (threshold
* cannot be changed by SW), an interrupt is set and the driver
* notifies user space via a sysfs event. If a shut down is not
@@ -26,30 +16,17 @@
* shutdown of the AB8500 will occur.
*/
-#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/jiffies.h>
-#include <linux/mutex.h>
#include <linux/mfd/ab8500/gpadc.h>
#include <linux/mfd/ab8500/bm.h>
-#include <linux/pm.h>
+#include "abx500.h"
-/*
- * If AB8500 warm interrupt is set, user space will be notified.
- * If user space doesn't shut down the platform within this time
- * frame, this driver will. Time unit is ms.
- */
#define DEFAULT_POWER_OFF_DELAY 10000
-#define DEFAULT_MONITOR_DELAY 1000
-
-#define NUM_SENSORS 5
/*
* The driver monitors GPADC - ADC_AUX1, ADC_AUX2, BTEMP_BALL
@@ -57,265 +34,46 @@
*/
#define NUM_MONITORED_SENSORS 4
-struct ab8500_temp {
- struct platform_device *pdev;
- struct device *hwmon_dev;
- struct ab8500_gpadc *gpadc;
- struct ab8500_btemp *btemp;
- u8 gpadc_addr[NUM_SENSORS];
- unsigned long min[NUM_SENSORS];
- unsigned long max[NUM_SENSORS];
- unsigned long max_hyst[NUM_SENSORS];
- unsigned long crit[NUM_SENSORS];
- unsigned long min_alarm[NUM_SENSORS];
- unsigned long max_alarm[NUM_SENSORS];
- unsigned long max_hyst_alarm[NUM_SENSORS];
- unsigned long crit_alarm[NUM_SENSORS];
- struct delayed_work work;
- struct delayed_work power_off_work;
- struct mutex lock;
- /* Delay (ms) between temperature readings */
- unsigned long gpadc_monitor_delay;
- /* Delay (ms) before power off */
- unsigned long power_off_delay;
-};
-
-/*
- * Thresholds are considered inactive if set to 0.
- * To avoid confusion for user space applications,
- * the temp monitor delay is set to 0 if all thresholds
- * are 0.
- */
-static bool find_active_thresholds(struct ab8500_temp *data)
+static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor)
{
- int i;
- for (i = 0; i < NUM_MONITORED_SENSORS; i++)
- if (data->max[i] != 0 || data->max_hyst[i] != 0
- || data->min[i] != 0)
- return true;
-
- dev_dbg(&data->pdev->dev, "No active thresholds,"
- "cancel deferred job (if it exists)"
- "and reset temp monitor delay\n");
- cancel_delayed_work_sync(&data->work);
- return false;
-}
-
+ int val;
+ /*
+ * Special treatment for the BAT_CTRL node, since this
+ * temperature measurement is more complex than just
+ * an ADC readout
+ *
+ * DIE_TEMP input temperature reading is not supported
+ * in AB8500
+ */
+ if (sensor == DIE_TEMP)
+ val = 0;
+ else if (sensor == BAT_CTRL)
+ val = ab8500_btemp_get_batctrl_temp(data->ab8500_btemp);
+ else
+ val = ab8500_gpadc_convert(data->ab8500_gpadc, sensor);
-static inline void schedule_monitor(struct ab8500_temp *data) {
- unsigned long delay_in_jiffies;
- delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
- schedule_delayed_work(&data->work, delay_in_jiffies);
+ return val;
}
-static void thermal_power_off(struct work_struct *work)
+static void ab8500_thermal_power_off(struct work_struct *work)
{
- struct ab8500_temp *data = container_of(work, struct ab8500_temp,
+ struct abx500_temp *data = container_of(work, struct abx500_temp,
power_off_work.work);
dev_warn(&data->pdev->dev, "Power off due to AB8500 thermal warning\n");
pm_power_off();
}
-static void gpadc_monitor(struct work_struct *work)
-{
- unsigned long delay_in_jiffies;
- int val, i, ret;
- /* Container for alarm node name */
- char alarm_node[30];
-
- bool updated_min_alarm = false;
- bool updated_max_alarm = false;
- bool updated_max_hyst_alarm = false;
- struct ab8500_temp *data = container_of(work, struct ab8500_temp,
- work.work);
-
- for (i = 0; i < NUM_MONITORED_SENSORS; i++) {
- /* Thresholds are considered inactive if set to 0 */
- if (data->max[i] == 0 && data->max_hyst[i] == 0
- && data->min[i] == 0)
- continue;
-
- /*
- * Special treatment for the BAT_CTRL node, since this
- * temperature measurement is more complex than just
- * an ADC readout
- */
- if (data->gpadc_addr[i] == BAT_CTRL) {
- val = ab8500_btemp_get_batctrl_temp(data->btemp);
- } else {
- val = ab8500_gpadc_convert(data->gpadc,
- data->gpadc_addr[i]);
- if (val < 0) {
- dev_err(&data->pdev->dev,
- "GPADC read failed\n");
- continue;
- }
- }
-
- mutex_lock(&data->lock);
- if (data->min[i] != 0) {
- if (val < data->min[i]) {
- if (data->min_alarm[i] == 0) {
- data->min_alarm[i] = 1;
- updated_min_alarm = true;
- }
- } else {
- if (data->min_alarm[i] == 1) {
- data->min_alarm[i] = 0;
- updated_min_alarm = true;
- }
- }
-
- }
- if (data->max[i] != 0) {
- if (val > data->max[i]) {
- if (data->max_alarm[i] == 0) {
- data->max_alarm[i] = 1;
- updated_max_alarm = true;
- }
- } else {
- if (data->max_alarm[i] == 1) {
- data->max_alarm[i] = 0;
- updated_max_alarm = true;
- }
- }
-
- }
- if (data->max_hyst[i] != 0) {
- if (val > data->max_hyst[i]) {
- if (data->max_hyst_alarm[i] == 0) {
- data->max_hyst_alarm[i] = 1;
- updated_max_hyst_alarm = true;
- }
- } else {
- if (data->max_hyst_alarm[i] == 1) {
- data->max_hyst_alarm[i] = 0;
- updated_max_hyst_alarm = true;
- }
- }
- }
- mutex_unlock(&data->lock);
-
- /* hwmon attr index starts at 1, thus "i+1" below */
- if (updated_min_alarm) {
- ret = snprintf(alarm_node, 16, "temp%d_min_alarm",
- (i + 1));
- if (ret < 0) {
- dev_err(&data->pdev->dev,
- "Unable to update alarm node (%d)",
- ret);
- break;
- }
- sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
- }
- if (updated_max_alarm) {
- ret = snprintf(alarm_node, 16, "temp%d_max_alarm",
- (i + 1));
- if (ret < 0) {
- dev_err(&data->pdev->dev,
- "Unable to update alarm node (%d)",
- ret);
- break;
- }
- sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
- }
- if (updated_max_hyst_alarm) {
- ret = snprintf(alarm_node, 21, "temp%d_max_hyst_alarm",
- (i + 1));
- if (ret < 0) {
- dev_err(&data->pdev->dev,
- "Unable to update alarm node (%d)",
- ret);
- break;
- }
- sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
- }
- }
- delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
- schedule_delayed_work(&data->work, delay_in_jiffies);
-}
-
-static inline void gpadc_monitor_exit(struct platform_device *pdev)
-{
- struct ab8500_temp *data = platform_get_drvdata(pdev);
- cancel_delayed_work_sync(&data->work);
-}
-
-static ssize_t set_temp_monitor_delay(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- int res;
- unsigned long delay_in_s;
- struct ab8500_temp *data = dev_get_drvdata(dev);
-
- res = strict_strtoul(buf, 10, &delay_in_s);
- if (res < 0)
- return res;
-
- mutex_lock(&data->lock);
- data->gpadc_monitor_delay = delay_in_s * 1000;
-
- if (find_active_thresholds(data)) {
- schedule_monitor(data);
- }
-
- mutex_unlock(&data->lock);
-
- return count;
-}
-
-static ssize_t set_temp_power_off_delay(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- int res;
- unsigned long delay_in_s;
- struct ab8500_temp *data = dev_get_drvdata(dev);
-
- res = strict_strtoul(buf, 10, &delay_in_s);
- if (res < 0)
- return res;
-
- mutex_lock(&data->lock);
- data->power_off_delay = delay_in_s * 1000;
- mutex_unlock(&data->lock);
-
- return count;
-}
-
-static ssize_t show_temp_monitor_delay(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- /* return time in s, not ms */
- return sprintf(buf, "%lu\n", (data->gpadc_monitor_delay) / 1000);
-}
-
-static ssize_t show_temp_power_off_delay(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
+static ssize_t ab8500_show_name(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- /* return time in s, not ms */
- return sprintf(buf, "%lu\n", (data->power_off_delay) / 1000);
-}
-
-/* HWMON sysfs interface */
-static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
- char *buf)
-{
- /*
- * To avoid confusion between sensor label and chip name, the function
- * "show_label" is not used to return the chip name.
- */
return sprintf(buf, "ab8500\n");
}
-static ssize_t show_label(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t ab8500_show_label(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
{
char *name;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -349,310 +107,9 @@ static ssize_t show_label(struct device *dev,
return sprintf(buf, "%s\n", name);
}
-static ssize_t show_input(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int val;
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- u8 gpadc_addr = data->gpadc_addr[attr->index - 1];
-
- /*
- * Special treatment for the BAT_CTRL node, since this
- * temperature measurement is more complex than just
- * an ADC readout
- */
- if (gpadc_addr == BAT_CTRL) {
- val = ab8500_btemp_get_batctrl_temp(data->btemp);
- } else {
- val = ab8500_gpadc_convert(data->gpadc, gpadc_addr);
- if (val < 0)
- dev_err(&data->pdev->dev, "GPADC read failed\n");
- }
-
- return sprintf(buf, "%d\n", val);
-}
-
-/* set functions (RW nodes) */
-static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- unsigned long val;
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- int res = strict_strtoul(buf, 10, &val);
- if (res < 0)
- return res;
-
- mutex_lock(&data->lock);
- /*
- * Threshold is considered inactive if set to 0
- * hwmon attr index starts at 1, thus "attr->index-1" below
- */
- if (val == 0)
- data->min_alarm[attr->index - 1] = 0;
-
- data->min[attr->index - 1] = val;
-
- if (val == 0)
- (void) find_active_thresholds(data);
- else
- schedule_monitor(data);
-
- mutex_unlock(&data->lock);
-
- return count;
-}
-
-static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- unsigned long val;
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- int res = strict_strtoul(buf, 10, &val);
- if (res < 0)
- return res;
-
- mutex_lock(&data->lock);
- /*
- * Threshold is considered inactive if set to 0
- * hwmon attr index starts at 1, thus "attr->index-1" below
- */
- if (val == 0)
- data->max_alarm[attr->index - 1] = 0;
-
- data->max[attr->index - 1] = val;
-
- if (val == 0)
- (void) find_active_thresholds(data);
- else
- schedule_monitor(data);
-
- mutex_unlock(&data->lock);
-
- return count;
-}
-
-static ssize_t set_max_hyst(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- unsigned long val;
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- int res = strict_strtoul(buf, 10, &val);
- if (res < 0)
- return res;
-
- mutex_lock(&data->lock);
- /*
- * Threshold is considered inactive if set to 0
- * hwmon attr index starts at 1, thus "attr->index-1" below
- */
- if (val == 0)
- data->max_hyst_alarm[attr->index - 1] = 0;
-
- data->max_hyst[attr->index - 1] = val;
-
- if (val == 0)
- (void) find_active_thresholds(data);
- else
- schedule_monitor(data);
-
- mutex_unlock(&data->lock);
-
- return count;
-}
-
-/*
- * show functions (RO nodes)
- * Notice that min/max/max_hyst refer to millivolts and not millidegrees
- */
-static ssize_t show_min(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->min[attr->index - 1]);
-}
-
-static ssize_t show_max(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->max[attr->index - 1]);
-}
-
-static ssize_t show_max_hyst(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->max_hyst[attr->index - 1]);
-}
-
-/* Alarms */
-static ssize_t show_min_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->min_alarm[attr->index - 1]);
-}
-
-static ssize_t show_max_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->max_alarm[attr->index - 1]);
-}
-
-static ssize_t show_max_hyst_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->max_hyst_alarm[attr->index - 1]);
-}
-
-static ssize_t show_crit_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct ab8500_temp *data = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- /* hwmon attr index starts at 1, thus "attr->index-1" below */
- return sprintf(buf, "%ld\n", data->crit_alarm[attr->index - 1]);
-}
-
-/*These node are not included in the kernel hwmon sysfs interface */
-static SENSOR_DEVICE_ATTR(temp_monitor_delay, S_IRUGO | S_IWUSR,
- show_temp_monitor_delay, set_temp_monitor_delay, 0);
-static SENSOR_DEVICE_ATTR(temp_power_off_delay, S_IRUGO | S_IWUSR,
- show_temp_power_off_delay,
- set_temp_power_off_delay, 0);
-
-/* Chip name, required by hwmon*/
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
-
-/* GPADC - ADC_AUX1 */
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
- show_max_hyst, set_max_hyst, 1);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst_alarm, S_IRUGO,
- show_max_hyst_alarm, NULL, 1);
-
-/* GPADC - ADC_AUX2 */
-static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
-static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
- show_max_hyst, set_max_hyst, 2);
-static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_max_hyst_alarm, S_IRUGO,
- show_max_hyst_alarm, NULL, 2);
-
-/* GPADC - BTEMP_BALL */
-static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
-static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
-static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
- show_max_hyst, set_max_hyst, 3);
-static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp3_max_hyst_alarm, S_IRUGO,
- show_max_hyst_alarm, NULL, 3);
-
-/* GPADC - BAT_CTRL */
-static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 4);
-static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 4);
-static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
- show_max_hyst, set_max_hyst, 4);
-static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp4_max_hyst_alarm, S_IRUGO,
- show_max_hyst_alarm, NULL, 4);
-
-/* AB8500 */
-static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, show_label, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
- show_crit_alarm, NULL, 5);
-
-static struct attribute *ab8500_temp_attributes[] = {
- &sensor_dev_attr_temp_power_off_delay.dev_attr.attr,
- &sensor_dev_attr_temp_monitor_delay.dev_attr.attr,
- &sensor_dev_attr_name.dev_attr.attr,
- /* GPADC - ADC_AUX1 */
- &sensor_dev_attr_temp1_label.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst_alarm.dev_attr.attr,
- /* GPADC - ADC_AUX2 */
- &sensor_dev_attr_temp2_label.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_hyst_alarm.dev_attr.attr,
- /* GPADC - BTEMP_BALL */
- &sensor_dev_attr_temp3_label.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_max_hyst_alarm.dev_attr.attr,
- /* GPADC - BAT_CTRL */
- &sensor_dev_attr_temp4_label.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp4_min.dev_attr.attr,
- &sensor_dev_attr_temp4_max.dev_attr.attr,
- &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_max_hyst_alarm.dev_attr.attr,
- /* AB8500 */
- &sensor_dev_attr_temp5_label.dev_attr.attr,
- &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group ab8500_temp_group = {
- .attrs = ab8500_temp_attributes,
-};
-
-static irqreturn_t ab8500_temp_irq_handler(int irq, void *irq_data)
+static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
{
unsigned long delay_in_jiffies;
- struct platform_device *pdev = irq_data;
- struct ab8500_temp *data = platform_get_drvdata(pdev);
-
/*
* Make sure the magic numbers below corresponds to the node
* used for AB8500 thermal warning from HW.
@@ -660,55 +117,25 @@ static irqreturn_t ab8500_temp_irq_handler(int irq, void *irq_data)
mutex_lock(&data->lock);
data->crit_alarm[4] = 1;
mutex_unlock(&data->lock);
- sysfs_notify(&pdev->dev.kobj, NULL, "temp5_crit_alarm");
- dev_info(&pdev->dev, "AB8500 thermal warning, power off in %lu s\n",
- data->power_off_delay);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, "temp5_crit_alarm");
+ dev_info(&data->pdev->dev, "AB8500 thermal warning,"
+ " power off in %lu s\n", data->power_off_delay);
delay_in_jiffies = msecs_to_jiffies(data->power_off_delay);
schedule_delayed_work(&data->power_off_work, delay_in_jiffies);
- return IRQ_HANDLED;
-}
-
-static int setup_irqs(struct platform_device *pdev)
-{
- int ret;
- int irq = platform_get_irq_byname(pdev, "AB8500_TEMP_WARM");
-
- if (irq < 0)
- dev_err(&pdev->dev, "Get irq by name failed\n");
-
- ret = request_threaded_irq(irq, NULL, ab8500_temp_irq_handler,
- IRQF_NO_SUSPEND, "ab8500-temp", pdev);
- if (ret < 0)
- dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
-
- return ret;
+ return 0;
}
-static int __devinit ab8500_temp_probe(struct platform_device *pdev)
+int __init abx500_hwmon_init(struct abx500_temp *data)
{
- struct ab8500_temp *data;
- int err;
-
- data = kzalloc(sizeof(struct ab8500_temp), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ data->ab8500_gpadc = ab8500_gpadc_get();
+ if (IS_ERR(data->ab8500_gpadc))
+ return PTR_ERR(data->ab8500_gpadc);
- err = setup_irqs(pdev);
- if (err < 0)
- goto exit;
-
- data->gpadc = ab8500_gpadc_get();
- data->btemp = ab8500_btemp_get();
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
- goto exit;
- }
+ data->ab8500_btemp = ab8500_btemp_get();
+ if (IS_ERR(data->ab8500_btemp))
+ return PTR_ERR(data->ab8500_btemp);
- INIT_DELAYED_WORK_DEFERRABLE(&data->work, gpadc_monitor);
- INIT_DELAYED_WORK(&data->power_off_work, thermal_power_off);
+ INIT_DELAYED_WORK(&data->power_off_work, ab8500_thermal_power_off);
/*
* Setup HW defined data.
@@ -731,63 +158,14 @@ static int __devinit ab8500_temp_probe(struct platform_device *pdev)
data->gpadc_addr[1] = ADC_AUX2;
data->gpadc_addr[2] = BTEMP_BALL;
data->gpadc_addr[3] = BAT_CTRL;
- mutex_init(&data->lock);
- data->pdev = pdev;
+ data->gpadc_addr[4] = DIE_TEMP;
data->power_off_delay = DEFAULT_POWER_OFF_DELAY;
- data->gpadc_monitor_delay = DEFAULT_MONITOR_DELAY;
+ data->monitored_sensors = NUM_MONITORED_SENSORS;
- platform_set_drvdata(pdev, data);
-
- err = sysfs_create_group(&pdev->dev.kobj, &ab8500_temp_group);
- if (err < 0) {
- dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
- goto exit_platform_data;
- }
+ data->ops.read_sensor = ab8500_read_sensor;
+ data->ops.irq_handler = ab8500_temp_irq_handler;
+ data->ops.show_name = ab8500_show_name;
+ data->ops.show_label = ab8500_show_label;
return 0;
-
-exit_platform_data:
- platform_set_drvdata(pdev, NULL);
-exit:
- kfree(data);
- return err;
}
-
-static int __devexit ab8500_temp_remove(struct platform_device *pdev)
-{
- struct ab8500_temp *data = platform_get_drvdata(pdev);
-
- gpadc_monitor_exit(pdev);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &ab8500_temp_group);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
- return 0;
-}
-
-/* No action required in suspend/resume, thus the lack of functions */
-static struct platform_driver ab8500_temp_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ab8500-temp",
- },
- .probe = ab8500_temp_probe,
- .remove = __devexit_p(ab8500_temp_remove),
-};
-
-static int __init ab8500_temp_init(void)
-{
- return platform_driver_register(&ab8500_temp_driver);
-}
-
-static void __exit ab8500_temp_exit(void)
-{
- platform_driver_unregister(&ab8500_temp_driver);
-}
-
-MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
-MODULE_DESCRIPTION("AB8500 temperature driver");
-MODULE_LICENSE("GPL");
-
-module_init(ab8500_temp_init)
-module_exit(ab8500_temp_exit)
diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c
new file mode 100644
index 00000000000..9d522ef6f62
--- /dev/null
+++ b/drivers/hwmon/abx500.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Persson <martin.persson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ * Note:
+ *
+ * ABX500 does not provide auto ADC, so to monitor the required
+ * temperatures, a periodic work is used. It is more important
+ * to not wake up the CPU than to perform this job, hence the use
+ * of a deferred delay.
+ *
+ * A deferred delay for thermal monitor is considered safe because:
+ * If the chip gets too hot during a sleep state it's most likely
+ * due to external factors, such as the surrounding temperature.
+ * I.e. no SW decisions will make any difference.
+ *
+ * If/when the ABX500 thermal warning temperature is reached (threshold
+ * cannot be changed by SW), an interrupt is set and the driver
+ * notifies user space via a sysfs event.
+ *
+ * If/when ABX500 thermal shutdown temperature is reached a hardware
+ * shutdown of the ABX500 will occur.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include "abx500.h"
+
+#define DEFAULT_MONITOR_DELAY 1000
+
+/*
+ * Thresholds are considered inactive if set to 0.
+ * To avoid confusion for user space applications,
+ * the temp monitor delay is set to 0 if all thresholds
+ * are 0.
+ */
+static bool find_active_thresholds(struct abx500_temp *data)
+{
+ int i;
+ for (i = 0; i < data->monitored_sensors; i++)
+ if (data->max[i] != 0 || data->max_hyst[i] != 0
+ || data->min[i] != 0)
+ return true;
+
+ dev_dbg(&data->pdev->dev, "No active thresholds,"
+ "cancel deferred job (if it exists)"
+ "and reset temp monitor delay\n");
+ cancel_delayed_work_sync(&data->work);
+ return false;
+}
+
+static inline void schedule_monitor(struct abx500_temp *data)
+{
+ unsigned long delay_in_jiffies;
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+}
+
+static inline void gpadc_monitor_exit(struct abx500_temp *data)
+{
+ cancel_delayed_work_sync(&data->work);
+}
+
+static void gpadc_monitor(struct work_struct *work)
+{
+ unsigned long delay_in_jiffies;
+ int val, i, ret;
+ /* Container for alarm node name */
+ char alarm_node[30];
+
+ bool updated_min_alarm = false;
+ bool updated_max_alarm = false;
+ bool updated_max_hyst_alarm = false;
+ struct abx500_temp *data = container_of(work, struct abx500_temp,
+ work.work);
+
+ for (i = 0; i < data->monitored_sensors; i++) {
+ /* Thresholds are considered inactive if set to 0 */
+ if (data->max[i] == 0 && data->max_hyst[i] == 0
+ && data->min[i] == 0)
+ continue;
+
+ val = data->ops.read_sensor(data, data->gpadc_addr[i]);
+ if (val < 0) {
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+ continue;
+ }
+
+ mutex_lock(&data->lock);
+ if (data->min[i] != 0) {
+ if (val < data->min[i]) {
+ if (data->min_alarm[i] == 0) {
+ data->min_alarm[i] = 1;
+ updated_min_alarm = true;
+ }
+ } else {
+ if (data->min_alarm[i] == 1) {
+ data->min_alarm[i] = 0;
+ updated_min_alarm = true;
+ }
+ }
+
+ }
+ if (data->max[i] != 0) {
+ if (val > data->max[i]) {
+ if (data->max_alarm[i] == 0) {
+ data->max_alarm[i] = 1;
+ updated_max_alarm = true;
+ }
+ } else {
+ if (data->max_alarm[i] == 1) {
+ data->max_alarm[i] = 0;
+ updated_max_alarm = true;
+ }
+ }
+
+ }
+ if (data->max_hyst[i] != 0) {
+ if (val > data->max_hyst[i]) {
+ if (data->max_hyst_alarm[i] == 0) {
+ data->max_hyst_alarm[i] = 1;
+ updated_max_hyst_alarm = true;
+ }
+ } else {
+ if (data->max_hyst_alarm[i] == 1) {
+ data->max_hyst_alarm[i] = 0;
+ updated_max_hyst_alarm = true;
+ }
+ }
+ }
+ mutex_unlock(&data->lock);
+
+ /* hwmon attr index starts at 1, thus "i+1" below */
+ if (updated_min_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_min_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_max_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_hyst_alarm) {
+ ret = snprintf(alarm_node, 21, "temp%d_max_hyst_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ }
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+}
+
+static ssize_t set_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_s;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->gpadc_monitor_delay = delay_in_s * 1000;
+
+ if (find_active_thresholds(data))
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_s;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->power_off_delay = delay_in_s * 1000;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t show_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->gpadc_monitor_delay) / 1000);
+}
+
+static ssize_t show_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->power_off_delay) / 1000);
+}
+
+/* HWMON sysfs interface */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ /*
+ * To avoid confusion between sensor label and chip name, the function
+ * "show_label" is not used to return the chip name.
+ */
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ return data->ops.show_name(dev, devattr, buf);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ return data->ops.show_label(dev, devattr, buf);
+}
+
+static ssize_t show_input(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ u8 gpadc_addr = data->gpadc_addr[attr->index - 1];
+
+ val = data->ops.read_sensor(data, gpadc_addr);
+ if (val < 0)
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+
+ return sprintf(buf, "%d\n", val);
+}
+
+/* set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->min_alarm[attr->index - 1] = 0;
+
+ data->min[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_alarm[attr->index - 1] = 0;
+
+ data->max[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_hyst_alarm[attr->index - 1] = 0;
+
+ data->max_hyst[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/*
+ * show functions (RO nodes)
+ */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min[attr->index - 1]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst[attr->index - 1]);
+}
+
+/* Alarms */
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst_alarm[attr->index - 1]);
+}
+
+static ssize_t show_crit_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->crit_alarm[attr->index - 1]);
+}
+
+static SENSOR_DEVICE_ATTR(temp_monitor_delay, S_IRUGO | S_IWUSR,
+ show_temp_monitor_delay, set_temp_monitor_delay, 0);
+static SENSOR_DEVICE_ATTR(temp_power_off_delay, S_IRUGO | S_IWUSR,
+ show_temp_power_off_delay,
+ set_temp_power_off_delay, 0);
+
+/* Chip name, required by hwmon*/
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - SENSOR1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 1);
+
+/* GPADC - SENSOR2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 2);
+
+/* GPADC - SENSOR3 */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 3);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 3);
+
+/* GPADC - SENSOR4 */
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 4);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 4);
+
+/* GPADC - SENSOR4 */
+static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, show_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_input, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
+ show_crit_alarm, NULL, 5);
+
+struct attribute *abx500_temp_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_temp_monitor_delay.dev_attr.attr,
+ &sensor_dev_attr_temp_power_off_delay.dev_attr.attr,
+ /* GPADC SENSOR1 */
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR2 */
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR3 */
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR4 */
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR5*/
+ &sensor_dev_attr_temp5_label.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group abx500_temp_group = {
+ .attrs = abx500_temp_attributes,
+};
+
+static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+ data->ops.irq_handler(irq, data);
+ return IRQ_HANDLED;
+}
+
+static int setup_irqs(struct platform_device *pdev)
+{
+ int ret;
+ int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
+
+ if (irq < 0)
+ dev_err(&pdev->dev, "Get irq by name failed\n");
+
+ ret = request_threaded_irq(irq, NULL, abx500_temp_irq_handler,
+ IRQF_NO_SUSPEND, "abx500-temp", pdev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int __devinit abx500_temp_probe(struct platform_device *pdev)
+{
+ struct abx500_temp *data;
+ int err;
+
+ data = kzalloc(sizeof(struct abx500_temp), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdev = pdev;
+ mutex_init(&data->lock);
+
+ /* Chip specific initialization */
+ err = abx500_hwmon_init(data);
+ if (err < 0) {
+ dev_err(&pdev->dev, "abx500 init failed");
+ goto exit;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->work, gpadc_monitor);
+ data->gpadc_monitor_delay = DEFAULT_MONITOR_DELAY;
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ goto exit_platform_data;
+ }
+
+ err = setup_irqs(pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "irq setup failed (%d)\n", err);
+ goto exit_sysfs_group;
+ }
+ return 0;
+
+exit_sysfs_group:
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+exit_platform_data:
+ hwmon_device_unregister(data->hwmon_dev);
+ platform_set_drvdata(pdev, NULL);
+exit:
+ kfree(data);
+ return err;
+}
+
+static int __devexit abx500_temp_remove(struct platform_device *pdev)
+{
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ gpadc_monitor_exit(data);
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+ return 0;
+}
+
+/* No action required in suspend/resume, thus the lack of functions */
+static struct platform_driver abx500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "abx500-temp",
+ },
+ .probe = abx500_temp_probe,
+ .remove = __devexit_p(abx500_temp_remove),
+};
+
+static int __init abx500_temp_init(void)
+{
+ return platform_driver_register(&abx500_temp_driver);
+}
+
+static void __exit abx500_temp_exit(void)
+{
+ platform_driver_unregister(&abx500_temp_driver);
+}
+
+MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 temperature driver");
+MODULE_LICENSE("GPL");
+
+module_init(abx500_temp_init)
+module_exit(abx500_temp_exit)
diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h
new file mode 100644
index 00000000000..a089a63939d
--- /dev/null
+++ b/drivers/hwmon/abx500.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * License terms: GNU General Public License v2
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ */
+
+#ifndef _ABX500_H
+#define _ABX500_H
+
+#define NUM_SENSORS 5
+
+struct ab8500_gpadc;
+struct ab8500_btemp;
+struct abx500_temp;
+
+/**
+ * struct abx500_temp_ops - abx500 chip specific ops
+ * @read_sensor: reads gpadc output
+ * @irq_handler: irq handler
+ * @show_name: hwmon device name
+ * @show_label: hwmon attribute label
+ */
+struct abx500_temp_ops {
+ int (*read_sensor)(struct abx500_temp *, u8);
+ int (*irq_handler)(int, struct abx500_temp *);
+ ssize_t (*show_name)(struct device *,
+ struct device_attribute *, char *);
+ ssize_t (*show_label) (struct device *,
+ struct device_attribute *, char *);
+};
+
+/**
+ * struct abx500_temp - representation of temp mon device
+ * @pdev: platform device
+ * @hwmon_dev: hwmon device
+ * @ab8500_gpadc: gpadc interface for ab8500
+ * @btemp: battery temperature interface for ab8500
+ * @gpadc_addr: gpadc channel address
+ * @temp: sensor temperature input value
+ * @min: sensor temperature min value
+ * @max: sensor temperature max value
+ * @max_hyst: sensor temperature hysteresis value for max limit
+ * @crit: sensor temperature critical value
+ * @min_alarm: sensor temperature min alarm
+ * @max_alarm: sensor temperature max alarm
+ * @max_hyst_alarm: sensor temperature hysteresis alarm
+ * @crit_alarm: sensor temperature critical value alarm
+ * @work: delayed work scheduled to monitor temperature periodically
+ * @power_off_work: delayed work scheduled to power off the system
+ when critical temperature is reached
+ * @lock: mutex
+ * @gpadc_monitor_delay: delay between temperature readings in ms
+ * @power_off_delay: delay before power off in ms
+ * @monitored_sensors: number of monitored sensors
+ */
+struct abx500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ struct ab8500_gpadc *ab8500_gpadc;
+ struct ab8500_btemp *ab8500_btemp;
+ struct abx500_temp_ops ops;
+ u8 gpadc_addr[NUM_SENSORS];
+ unsigned long temp[NUM_SENSORS];
+ unsigned long min[NUM_SENSORS];
+ unsigned long max[NUM_SENSORS];
+ unsigned long max_hyst[NUM_SENSORS];
+ unsigned long crit[NUM_SENSORS];
+ unsigned long min_alarm[NUM_SENSORS];
+ unsigned long max_alarm[NUM_SENSORS];
+ unsigned long max_hyst_alarm[NUM_SENSORS];
+ unsigned long crit_alarm[NUM_SENSORS];
+ struct delayed_work work;
+ struct delayed_work power_off_work;
+ struct mutex lock;
+ /* Delay (ms) between temperature readings */
+ unsigned long gpadc_monitor_delay;
+ /* Delay (ms) before power off */
+ unsigned long power_off_delay;
+ int monitored_sensors;
+};
+
+int abx500_hwmon_init(struct abx500_temp *data) __init;
+
+#endif /* _ABX500_H */