summaryrefslogtreecommitdiff
path: root/drivers/hwmon/ab8500.c
blob: 65a0f381d01997aa428606db3172ec2fe3b9aecb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * 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:
 *
 * 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
 * triggered by user space within a certain time frame,
 * pm_power off is called.
 *
 * If/when AB8500 thermal shutdown temperature is reached a hardware
 * shutdown of the AB8500 will occur.
 */

#include <linux/slab.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
#include <linux/platform_device.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include "abx500.h"
#include <asm/mach-types.h>

#define DEFAULT_POWER_OFF_DELAY 10000

/*
 * The driver monitors GPADC - ADC_AUX1, ADC_AUX2, BTEMP_BALL
 * and BAT_CTRL.
 */
#define NUM_MONITORED_SENSORS 4

static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor)
{
	int val;
	/*
	 * Special treatment for the BAT_CTRL node, since this
	 * temperature measurement is more complex than just
	 * an ADC readout
	 */
	if (sensor == BAT_CTRL)
		val = ab8500_btemp_get_batctrl_temp(data->ab8500_btemp);
	else
		val = ab8500_gpadc_convert(data->ab8500_gpadc, sensor);

	return val;
}

static void ab8500_thermal_power_off(struct work_struct *work)
{
	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 ssize_t ab8500_show_name(struct device *dev,
				struct device_attribute *devattr,
				char *buf)
{
	return sprintf(buf, "ab8500\n");
}

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);
	int index = attr->index;

	/*
	 * Make sure these labels correspond to the attribute indexes
	 * used when calling SENSOR_DEVICE_ATRR.
	 * Temperature sensors outside ab8500 (read via GPADC) are marked
	 * with prefix ext_
	 */
	switch (index) {
	case 1:
		name = "ext_rtc_xtal";
		break;
	case 2:
		name = "ext_db8500";
		break;
	case 3:
		name = "bat_temp";
		break;
	case 4:
		name = "bat_ctrl";
		break;
	case 5:
		name = "ab8500";
		break;
	default:
		return -EINVAL;
	}
	return sprintf(buf, "%s\n", name);
}

static int ab8500_is_visible(struct attribute *attr, int n)
{
	if (!strcmp(attr->name, "temp5_input") ||
	    !strcmp(attr->name, "temp5_min") ||
	    !strcmp(attr->name, "temp5_max") ||
	    !strcmp(attr->name, "temp5_max_hyst") ||
	    !strcmp(attr->name, "temp5_min_alarm") ||
	    !strcmp(attr->name, "temp5_max_alarm") ||
	    !strcmp(attr->name, "temp5_max_hyst_alarm"))
		return 0;

	return attr->mode;
}

static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
{
	unsigned long delay_in_jiffies;
	/*
	 * Make sure the magic numbers below corresponds to the node
	 * used for AB8500 thermal warning from HW.
	 */
	mutex_lock(&data->lock);
	data->crit_alarm[4] = 1;
	mutex_unlock(&data->lock);

	hwmon_notify(data->crit_alarm[4], NULL);
	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 0;
}

int __init ab8500_hwmon_init(struct abx500_temp *data)
{
	data->ab8500_gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
	if (IS_ERR(data->ab8500_gpadc))
		return PTR_ERR(data->ab8500_gpadc);

	data->ab8500_btemp = ab8500_btemp_get();
	if (IS_ERR(data->ab8500_btemp))
		return PTR_ERR(data->ab8500_btemp);

	INIT_DELAYED_WORK(&data->power_off_work, ab8500_thermal_power_off);

	/*
	 * Setup HW defined data.
	 *
	 * Reference hardware (HREF):
	 *
	 * GPADC - ADC_AUX1, connected to NTC R2148 next to RTC_XTAL on HREF
	 * GPADC - ADC_AUX2, connected to NTC R2150 near DB8500 on HREF
	 * Hence, temp#_min/max/max_hyst refer to millivolts and not
	 * millidegrees
	 * This is not the case for BAT_CTRL where millidegrees is used
	 *
	 * HREF HW does not support reading AB8500 temperature. BUT an
	 * AB8500 IRQ will be launched if die crit temp limit is reached.
	 *
	 * Make sure indexes correspond to the attribute indexes
	 * used when calling SENSOR_DEVICE_ATRR
	 */
	data->gpadc_addr[0] = ADC_AUX1;
	data->gpadc_addr[1] = ADC_AUX2;
	data->gpadc_addr[2] = BTEMP_BALL;
	data->gpadc_addr[3] = BAT_CTRL;
	data->gpadc_addr[4] = DIE_TEMP;
	data->power_off_delay = DEFAULT_POWER_OFF_DELAY;
	data->monitored_sensors = NUM_MONITORED_SENSORS;

	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;
	data->ops.is_visible = ab8500_is_visible;

	return 0;
}