summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/ab8500.c
diff options
context:
space:
mode:
authorHongbo Zhang <hongbo.zhang@linaro.org>2013-04-03 08:18:12 -0400
committerAnton Vorontsov <anton@enomsg.org>2013-04-16 21:27:52 -0400
commit0bbb06ed564d211d10eae12bdb423fce6178468f (patch)
tree5150a86a4ffe8cc225fb76f9a3b3cd04d730407c /drivers/hwmon/ab8500.c
parentea2be6f21071b4af3b765a0f228be2bef08515e9 (diff)
hwmon: Add ST-Ericsson ABX500 hwmon driver
Each of ST-Ericsson X500 chip set series consists of both ABX500 and DBX500 chips. This is ABX500 hwmon driver, where the abx500.c is a common layer for all ABX500s, and the ab8500.c is specific for AB8500 chip. Under this designed structure, other chip specific files can be added simply using the same common layer abx500.c. Signed-off-by: Hongbo Zhang <hongbo.zhang@linaro.org> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Acked-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Anton Vorontsov <anton@enomsg.org>
Diffstat (limited to 'drivers/hwmon/ab8500.c')
-rw-r--r--drivers/hwmon/ab8500.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
new file mode 100644
index 000000000000..d844dc806853
--- /dev/null
+++ b/drivers/hwmon/ab8500.c
@@ -0,0 +1,206 @@
1/*
2 * Copyright (C) ST-Ericsson 2010 - 2013
3 * Author: Martin Persson <martin.persson@stericsson.com>
4 * Hongbo Zhang <hongbo.zhang@linaro.org>
5 * License Terms: GNU General Public License v2
6 *
7 * When the AB8500 thermal warning temperature is reached (threshold cannot
8 * be changed by SW), an interrupt is set, and if no further action is taken
9 * within a certain time frame, pm_power off will be called.
10 *
11 * When AB8500 thermal shutdown temperature is reached a hardware shutdown of
12 * the AB8500 will occur.
13 */
14
15#include <linux/err.h>
16#include <linux/hwmon.h>
17#include <linux/hwmon-sysfs.h>
18#include <linux/mfd/abx500.h>
19#include <linux/mfd/abx500/ab8500-bm.h>
20#include <linux/mfd/abx500/ab8500-gpadc.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/power/ab8500.h>
24#include <linux/slab.h>
25#include <linux/sysfs.h>
26#include "abx500.h"
27
28#define DEFAULT_POWER_OFF_DELAY (HZ * 10)
29#define THERMAL_VCC 1800
30#define PULL_UP_RESISTOR 47000
31/* Number of monitored sensors should not greater than NUM_SENSORS */
32#define NUM_MONITORED_SENSORS 4
33
34struct ab8500_gpadc_cfg {
35 const struct abx500_res_to_temp *temp_tbl;
36 int tbl_sz;
37 int vcc;
38 int r_up;
39};
40
41struct ab8500_temp {
42 struct ab8500_gpadc *gpadc;
43 struct ab8500_btemp *btemp;
44 struct delayed_work power_off_work;
45 struct ab8500_gpadc_cfg cfg;
46 struct abx500_temp *abx500_data;
47};
48
49/*
50 * The hardware connection is like this:
51 * VCC----[ R_up ]-----[ NTC ]----GND
52 * where R_up is pull-up resistance, and GPADC measures voltage on NTC.
53 * and res_to_temp table is strictly sorted by falling resistance values.
54 */
55static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg,
56 int v_ntc, int *temp)
57{
58 int r_ntc, i = 0, tbl_sz = cfg->tbl_sz;
59 const struct abx500_res_to_temp *tbl = cfg->temp_tbl;
60
61 if (cfg->vcc < 0 || v_ntc >= cfg->vcc)
62 return -EINVAL;
63
64 r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc);
65 if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist)
66 return -EINVAL;
67
68 while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) &&
69 i < tbl_sz - 2)
70 i++;
71
72 /* return milli-Celsius */
73 *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 *
74 (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
75
76 return 0;
77}
78
79static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp)
80{
81 int voltage, ret;
82 struct ab8500_temp *ab8500_data = data->plat_data;
83
84 if (sensor == BAT_CTRL) {
85 *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp);
86 } else if (sensor == BTEMP_BALL) {
87 *temp = ab8500_btemp_get_temp(ab8500_data->btemp);
88 } else {
89 voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor);
90 if (voltage < 0)
91 return voltage;
92
93 ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
94 if (ret < 0)
95 return ret;
96 }
97
98 return 0;
99}
100
101static void ab8500_thermal_power_off(struct work_struct *work)
102{
103 struct ab8500_temp *ab8500_data = container_of(work,
104 struct ab8500_temp, power_off_work.work);
105 struct abx500_temp *abx500_data = ab8500_data->abx500_data;
106
107 dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
108
109 pm_power_off();
110}
111
112static ssize_t ab8500_show_name(struct device *dev,
113 struct device_attribute *devattr, char *buf)
114{
115 return sprintf(buf, "ab8500\n");
116}
117
118static ssize_t ab8500_show_label(struct device *dev,
119 struct device_attribute *devattr, char *buf)
120{
121 char *label;
122 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
123 int index = attr->index;
124
125 switch (index) {
126 case 1:
127 label = "ext_adc1";
128 break;
129 case 2:
130 label = "ext_adc2";
131 break;
132 case 3:
133 label = "bat_temp";
134 break;
135 case 4:
136 label = "bat_ctrl";
137 break;
138 default:
139 return -EINVAL;
140 }
141
142 return sprintf(buf, "%s\n", label);
143}
144
145static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
146{
147 struct ab8500_temp *ab8500_data = data->plat_data;
148
149 dev_warn(&data->pdev->dev, "Power off in %d s\n",
150 DEFAULT_POWER_OFF_DELAY / HZ);
151
152 schedule_delayed_work(&ab8500_data->power_off_work,
153 DEFAULT_POWER_OFF_DELAY);
154 return 0;
155}
156
157int abx500_hwmon_init(struct abx500_temp *data)
158{
159 struct ab8500_temp *ab8500_data;
160
161 ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data),
162 GFP_KERNEL);
163 if (!ab8500_data)
164 return -ENOMEM;
165
166 ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
167 if (IS_ERR(ab8500_data->gpadc))
168 return PTR_ERR(ab8500_data->gpadc);
169
170 ab8500_data->btemp = ab8500_btemp_get();
171 if (IS_ERR(ab8500_data->btemp))
172 return PTR_ERR(ab8500_data->btemp);
173
174 INIT_DELAYED_WORK(&ab8500_data->power_off_work,
175 ab8500_thermal_power_off);
176
177 ab8500_data->cfg.vcc = THERMAL_VCC;
178 ab8500_data->cfg.r_up = PULL_UP_RESISTOR;
179 ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor;
180 ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size;
181
182 data->plat_data = ab8500_data;
183
184 /*
185 * ADC_AUX1 and ADC_AUX2, connected to external NTC
186 * BTEMP_BALL and BAT_CTRL, fixed usage
187 */
188 data->gpadc_addr[0] = ADC_AUX1;
189 data->gpadc_addr[1] = ADC_AUX2;
190 data->gpadc_addr[2] = BTEMP_BALL;
191 data->gpadc_addr[3] = BAT_CTRL;
192 data->monitored_sensors = NUM_MONITORED_SENSORS;
193
194 data->ops.read_sensor = ab8500_read_sensor;
195 data->ops.irq_handler = ab8500_temp_irq_handler;
196 data->ops.show_name = ab8500_show_name;
197 data->ops.show_label = ab8500_show_label;
198 data->ops.is_visible = NULL;
199
200 return 0;
201}
202EXPORT_SYMBOL(abx500_hwmon_init);
203
204MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>");
205MODULE_DESCRIPTION("AB8500 temperature driver");
206MODULE_LICENSE("GPL");