aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2013-03-12 06:38:46 -0400
committerGuenter Roeck <linux@roeck-us.net>2013-04-08 00:16:38 -0400
commit51c2a4871c1b47255ff8d74f0a86b2a0defff319 (patch)
tree151e26fe0ff76d26dc69eb55d3f15562d598ea72 /drivers
parentc55dc91e92bdf21427dd8f5ad779ed9d63caacbd (diff)
hwmon: (adt7410) Add support for the adt7310/adt7320
The adt7310/adt7320 is the SPI version of the adt7410/adt7420. The register map layout is a bit different, i.e. the register addresses differ between the two variants, but the bit layouts of the individual registers are identical. So both chip variants can easily be supported by the same driver. The issue of non matching register address layouts is solved by a simple look-up table which translates the I2C addresses to the SPI addresses. The patch moves the bulk of the adt7410 driver to a common module that will be shared by the adt7410 and adt7310 drivers. This common module implements the driver logic and uses a set of virtual functions to perform IO access. The adt7410 and adt7310 driver modules provide proper implementations of these IO accessor functions for I2C respective SPI. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Reviewed-by: Hartmut Knaack <knaack.h@gmx.de> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig20
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/adt7310.c123
-rw-r--r--drivers/hwmon/adt7410.c467
-rw-r--r--drivers/hwmon/adt7x10.c476
-rw-r--r--drivers/hwmon/adt7x10.h37
6 files changed, 689 insertions, 436 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 89ac1cb26f24..aaa14f4a0f7d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -179,9 +179,29 @@ config SENSORS_ADM9240
179 This driver can also be built as a module. If so, the module 179 This driver can also be built as a module. If so, the module
180 will be called adm9240. 180 will be called adm9240.
181 181
182config SENSORS_ADT7X10
183 tristate
184 help
185 This module contains common code shared by the ADT7310/ADT7320 and
186 ADT7410/ADT7420 temperature monitoring chip drivers.
187
188 If build as a module, the module will be called adt7x10.
189
190config SENSORS_ADT7310
191 tristate "Analog Devices ADT7310/ADT7320"
192 depends on SPI_MASTER
193 select SENSORS_ADT7X10
194 help
195 If you say yes here you get support for the Analog Devices
196 ADT7310 and ADT7320 temperature monitoring chips.
197
198 This driver can also be built as a module. If so, the module
199 will be called adt7310.
200
182config SENSORS_ADT7410 201config SENSORS_ADT7410
183 tristate "Analog Devices ADT7410/ADT7420" 202 tristate "Analog Devices ADT7410/ADT7420"
184 depends on I2C 203 depends on I2C
204 select SENSORS_ADT7X10
185 help 205 help
186 If you say yes here you get support for the Analog Devices 206 If you say yes here you get support for the Analog Devices
187 ADT7410 and ADT7420 temperature monitoring chips. 207 ADT7410 and ADT7420 temperature monitoring chips.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8d6d97ea7c1e..5d36a57c055b 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
34obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o 34obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o
35obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o 35obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o
36obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o 36obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o
37obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o
38obj-$(CONFIG_SENSORS_ADT7310) += adt7310.o
37obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o 39obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o
38obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o 40obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
39obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o 41obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
new file mode 100644
index 000000000000..f8ea6292bc74
--- /dev/null
+++ b/drivers/hwmon/adt7310.c
@@ -0,0 +1,123 @@
1/*
2 * ADT7310/ADT7310 digital temperature sensor driver
3 *
4 * Copyright 2012-2013 Analog Devices Inc.
5 * Author: Lars-Peter Clausen <lars@metafoo.de>
6 *
7 * Licensed under the GPL-2 or later.
8 */
9
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/spi/spi.h>
13#include <asm/unaligned.h>
14
15#include "adt7x10.h"
16
17#define ADT7310_STATUS 0
18#define ADT7310_CONFIG 1
19#define ADT7310_TEMPERATURE 2
20#define ADT7310_ID 3
21#define ADT7310_T_CRIT 4
22#define ADT7310_T_HYST 5
23#define ADT7310_T_ALARM_HIGH 6
24#define ADT7310_T_ALARM_LOW 7
25
26static const u8 adt7310_reg_table[] = {
27 [ADT7X10_TEMPERATURE] = ADT7310_TEMPERATURE,
28 [ADT7X10_STATUS] = ADT7310_STATUS,
29 [ADT7X10_CONFIG] = ADT7310_CONFIG,
30 [ADT7X10_T_ALARM_HIGH] = ADT7310_T_ALARM_HIGH,
31 [ADT7X10_T_ALARM_LOW] = ADT7310_T_ALARM_LOW,
32 [ADT7X10_T_CRIT] = ADT7310_T_CRIT,
33 [ADT7X10_T_HYST] = ADT7310_T_HYST,
34 [ADT7X10_ID] = ADT7310_ID,
35};
36
37#define ADT7310_CMD_REG_OFFSET 3
38#define ADT7310_CMD_READ 0x40
39
40#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
41
42static int adt7310_spi_read_word(struct device *dev, u8 reg)
43{
44 struct spi_device *spi = to_spi_device(dev);
45 int ret;
46
47 ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
48 if (ret < 0)
49 return ret;
50
51 return be16_to_cpu(ret);
52}
53
54static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data)
55{
56 struct spi_device *spi = to_spi_device(dev);
57 u8 buf[3];
58
59 buf[0] = AD7310_COMMAND(reg);
60 put_unaligned_be16(data, &buf[1]);
61
62 return spi_write(spi, buf, sizeof(buf));
63}
64
65static int adt7310_spi_read_byte(struct device *dev, u8 reg)
66{
67 struct spi_device *spi = to_spi_device(dev);
68
69 return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
70}
71
72static int adt7310_spi_write_byte(struct device *dev, u8 reg,
73 u8 data)
74{
75 struct spi_device *spi = to_spi_device(dev);
76 u8 buf[2];
77
78 buf[0] = AD7310_COMMAND(reg);
79 buf[1] = data;
80
81 return spi_write(spi, buf, sizeof(buf));
82}
83
84static const struct adt7x10_ops adt7310_spi_ops = {
85 .read_word = adt7310_spi_read_word,
86 .write_word = adt7310_spi_write_word,
87 .read_byte = adt7310_spi_read_byte,
88 .write_byte = adt7310_spi_write_byte,
89};
90
91static int adt7310_spi_probe(struct spi_device *spi)
92{
93 return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name,
94 &adt7310_spi_ops);
95}
96
97static int adt7310_spi_remove(struct spi_device *spi)
98{
99 return adt7x10_remove(&spi->dev);
100}
101
102static const struct spi_device_id adt7310_id[] = {
103 { "adt7310", 0 },
104 { "adt7320", 0 },
105 {}
106};
107MODULE_DEVICE_TABLE(spi, adt7310_id);
108
109static struct spi_driver adt7310_driver = {
110 .driver = {
111 .name = "adt7310",
112 .owner = THIS_MODULE,
113 .pm = ADT7X10_DEV_PM_OPS,
114 },
115 .probe = adt7310_spi_probe,
116 .remove = adt7310_spi_remove,
117 .id_table = adt7310_id,
118};
119module_spi_driver(adt7310_driver);
120
121MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
122MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
123MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 894ff71499b3..d294445c86dd 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -1,485 +1,80 @@
1/* 1/*
2 * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware 2 * ADT7410/ADT7420 digital temperature sensor driver
3 * monitoring
4 * This driver handles the ADT7410 and compatible digital temperature sensors.
5 * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
6 * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
7 * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
8 * 3 *
9 * This program is free software; you can redistribute it and/or modify 4 * Copyright 2012-2013 Analog Devices Inc.
10 * it under the terms of the GNU General Public License as published by 5 * Author: Lars-Peter Clausen <lars@metafoo.de>
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * 6 *
14 * This program is distributed in the hope that it will be useful, 7 * Licensed under the GPL-2 or later.
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */ 8 */
23 9
24#include <linux/module.h> 10#include <linux/module.h>
25#include <linux/init.h> 11#include <linux/init.h>
26#include <linux/slab.h>
27#include <linux/jiffies.h>
28#include <linux/i2c.h> 12#include <linux/i2c.h>
29#include <linux/hwmon.h>
30#include <linux/hwmon-sysfs.h>
31#include <linux/err.h>
32#include <linux/mutex.h>
33#include <linux/delay.h>
34
35/*
36 * ADT7410 registers definition
37 */
38
39#define ADT7410_TEMPERATURE 0
40#define ADT7410_STATUS 2
41#define ADT7410_CONFIG 3
42#define ADT7410_T_ALARM_HIGH 4
43#define ADT7410_T_ALARM_LOW 6
44#define ADT7410_T_CRIT 8
45#define ADT7410_T_HYST 0xA
46
47/*
48 * ADT7410 status
49 */
50#define ADT7410_STAT_T_LOW (1 << 4)
51#define ADT7410_STAT_T_HIGH (1 << 5)
52#define ADT7410_STAT_T_CRIT (1 << 6)
53#define ADT7410_STAT_NOT_RDY (1 << 7)
54
55/*
56 * ADT7410 config
57 */
58#define ADT7410_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
59#define ADT7410_CT_POLARITY (1 << 2)
60#define ADT7410_INT_POLARITY (1 << 3)
61#define ADT7410_EVENT_MODE (1 << 4)
62#define ADT7410_MODE_MASK (1 << 5 | 1 << 6)
63#define ADT7410_FULL (0 << 5 | 0 << 6)
64#define ADT7410_PD (1 << 5 | 1 << 6)
65#define ADT7410_RESOLUTION (1 << 7)
66
67/*
68 * ADT7410 masks
69 */
70#define ADT7410_T13_VALUE_MASK 0xFFF8
71#define ADT7410_T_HYST_MASK 0xF
72
73/* straight from the datasheet */
74#define ADT7410_TEMP_MIN (-55000)
75#define ADT7410_TEMP_MAX 150000
76
77enum adt7410_type { /* keep sorted in alphabetical order */
78 adt7410,
79};
80
81static const u8 ADT7410_REG_TEMP[4] = {
82 ADT7410_TEMPERATURE, /* input */
83 ADT7410_T_ALARM_HIGH, /* high */
84 ADT7410_T_ALARM_LOW, /* low */
85 ADT7410_T_CRIT, /* critical */
86};
87
88/* Each client has this additional data */
89struct adt7410_data {
90 struct device *hwmon_dev;
91 struct mutex update_lock;
92 u8 config;
93 u8 oldconfig;
94 bool valid; /* true if registers valid */
95 unsigned long last_updated; /* In jiffies */
96 s16 temp[4]; /* Register values,
97 0 = input
98 1 = high
99 2 = low
100 3 = critical */
101 u8 hyst; /* hysteresis offset */
102};
103
104/*
105 * adt7410 register access by I2C
106 */
107static int adt7410_temp_ready(struct i2c_client *client)
108{
109 int i, status;
110
111 for (i = 0; i < 6; i++) {
112 status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
113 if (status < 0)
114 return status;
115 if (!(status & ADT7410_STAT_NOT_RDY))
116 return 0;
117 msleep(60);
118 }
119 return -ETIMEDOUT;
120}
121
122static int adt7410_update_temp(struct device *dev)
123{
124 struct i2c_client *client = to_i2c_client(dev);
125 struct adt7410_data *data = i2c_get_clientdata(client);
126 int ret = 0;
127
128 mutex_lock(&data->update_lock);
129
130 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
131 || !data->valid) {
132 int temp;
133
134 dev_dbg(&client->dev, "Starting update\n");
135
136 ret = adt7410_temp_ready(client); /* check for new value */
137 if (ret)
138 goto abort;
139
140 temp = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[0]);
141 if (temp < 0) {
142 ret = temp;
143 dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
144 ADT7410_REG_TEMP[0], ret);
145 goto abort;
146 }
147 data->temp[0] = temp;
148
149 data->last_updated = jiffies;
150 data->valid = true;
151 }
152
153abort:
154 mutex_unlock(&data->update_lock);
155 return ret;
156}
157
158static int adt7410_fill_cache(struct i2c_client *client)
159{
160 struct adt7410_data *data = i2c_get_clientdata(client);
161 int ret;
162 int i;
163
164 for (i = 1; i < ARRAY_SIZE(ADT7410_REG_TEMP); i++) {
165 ret = i2c_smbus_read_word_swapped(client, ADT7410_REG_TEMP[i]);
166 if (ret < 0) {
167 dev_dbg(&client->dev,
168 "Failed to read value: reg %d, error %d\n",
169 ADT7410_REG_TEMP[i], ret);
170 return ret;
171 }
172 data->temp[i] = ret;
173 }
174
175 ret = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
176 if (ret < 0) {
177 dev_dbg(&client->dev,
178 "Failed to read value: hyst reg, error %d\n",
179 ret);
180 return ret;
181 }
182 data->hyst = ret;
183
184 return 0;
185}
186
187static s16 ADT7410_TEMP_TO_REG(long temp)
188{
189 return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
190 ADT7410_TEMP_MAX) * 128, 1000);
191}
192
193static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
194{
195 /* in 13 bit mode, bits 0-2 are status flags - mask them out */
196 if (!(data->config & ADT7410_RESOLUTION))
197 reg &= ADT7410_T13_VALUE_MASK;
198 /*
199 * temperature is stored in twos complement format, in steps of
200 * 1/128°C
201 */
202 return DIV_ROUND_CLOSEST(reg * 1000, 128);
203}
204
205/*-----------------------------------------------------------------------*/
206 13
207/* sysfs attributes for hwmon */ 14#include "adt7x10.h"
208 15
209static ssize_t adt7410_show_temp(struct device *dev, 16static int adt7410_i2c_read_word(struct device *dev, u8 reg)
210 struct device_attribute *da, char *buf)
211{ 17{
212 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 18 return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
213 struct i2c_client *client = to_i2c_client(dev);
214 struct adt7410_data *data = i2c_get_clientdata(client);
215
216 if (attr->index == 0) {
217 int ret;
218
219 ret = adt7410_update_temp(dev);
220 if (ret)
221 return ret;
222 }
223
224 return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
225 data->temp[attr->index]));
226} 19}
227 20
228static ssize_t adt7410_set_temp(struct device *dev, 21static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
229 struct device_attribute *da,
230 const char *buf, size_t count)
231{ 22{
232 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 23 return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
233 struct i2c_client *client = to_i2c_client(dev);
234 struct adt7410_data *data = i2c_get_clientdata(client);
235 int nr = attr->index;
236 long temp;
237 int ret;
238
239 ret = kstrtol(buf, 10, &temp);
240 if (ret)
241 return ret;
242
243 mutex_lock(&data->update_lock);
244 data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
245 ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
246 data->temp[nr]);
247 if (ret)
248 count = ret;
249 mutex_unlock(&data->update_lock);
250 return count;
251} 24}
252 25
253static ssize_t adt7410_show_t_hyst(struct device *dev, 26static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
254 struct device_attribute *da,
255 char *buf)
256{ 27{
257 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 28 return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
258 struct i2c_client *client = to_i2c_client(dev);
259 struct adt7410_data *data = i2c_get_clientdata(client);
260 int nr = attr->index;
261 int hyst;
262
263 hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
264
265 /*
266 * hysteresis is stored as a 4 bit offset in the device, convert it
267 * to an absolute value
268 */
269 if (nr == 2) /* min has positive offset, others have negative */
270 hyst = -hyst;
271 return sprintf(buf, "%d\n",
272 ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
273} 29}
274 30
275static ssize_t adt7410_set_t_hyst(struct device *dev, 31static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
276 struct device_attribute *da,
277 const char *buf, size_t count)
278{ 32{
279 struct i2c_client *client = to_i2c_client(dev); 33 return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
280 struct adt7410_data *data = i2c_get_clientdata(client);
281 int limit, ret;
282 long hyst;
283
284 ret = kstrtol(buf, 10, &hyst);
285 if (ret)
286 return ret;
287 /* convert absolute hysteresis value to a 4 bit delta value */
288 limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
289 hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
290 data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
291 ADT7410_T_HYST_MASK);
292 ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
293 if (ret)
294 return ret;
295
296 return count;
297} 34}
298 35
299static ssize_t adt7410_show_alarm(struct device *dev, 36static const struct adt7x10_ops adt7410_i2c_ops = {
300 struct device_attribute *da, 37 .read_word = adt7410_i2c_read_word,
301 char *buf) 38 .write_word = adt7410_i2c_write_word,
302{ 39 .read_byte = adt7410_i2c_read_byte,
303 struct i2c_client *client = to_i2c_client(dev); 40 .write_byte = adt7410_i2c_write_byte,
304 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
305 int ret;
306
307 ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
308 if (ret < 0)
309 return ret;
310
311 return sprintf(buf, "%d\n", !!(ret & attr->index));
312}
313
314static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
315static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
316 adt7410_show_temp, adt7410_set_temp, 1);
317static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
318 adt7410_show_temp, adt7410_set_temp, 2);
319static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
320 adt7410_show_temp, adt7410_set_temp, 3);
321static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
322 adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
323static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
324 adt7410_show_t_hyst, NULL, 2);
325static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
326 adt7410_show_t_hyst, NULL, 3);
327static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
328 NULL, ADT7410_STAT_T_LOW);
329static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
330 NULL, ADT7410_STAT_T_HIGH);
331static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
332 NULL, ADT7410_STAT_T_CRIT);
333
334static struct attribute *adt7410_attributes[] = {
335 &sensor_dev_attr_temp1_input.dev_attr.attr,
336 &sensor_dev_attr_temp1_max.dev_attr.attr,
337 &sensor_dev_attr_temp1_min.dev_attr.attr,
338 &sensor_dev_attr_temp1_crit.dev_attr.attr,
339 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
340 &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
341 &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
342 &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
343 &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
344 &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
345 NULL
346}; 41};
347 42
348static const struct attribute_group adt7410_group = { 43static int adt7410_i2c_probe(struct i2c_client *client,
349 .attrs = adt7410_attributes, 44 const struct i2c_device_id *id)
350};
351
352/*-----------------------------------------------------------------------*/
353
354/* device probe and removal */
355
356static int adt7410_probe(struct i2c_client *client,
357 const struct i2c_device_id *id)
358{ 45{
359 struct adt7410_data *data;
360 int ret;
361
362 if (!i2c_check_functionality(client->adapter, 46 if (!i2c_check_functionality(client->adapter,
363 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) 47 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
364 return -ENODEV; 48 return -ENODEV;
365 49
366 data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data), 50 return adt7x10_probe(&client->dev, NULL, &adt7410_i2c_ops);
367 GFP_KERNEL);
368 if (!data)
369 return -ENOMEM;
370
371 i2c_set_clientdata(client, data);
372 mutex_init(&data->update_lock);
373
374 /* configure as specified */
375 ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
376 if (ret < 0) {
377 dev_dbg(&client->dev, "Can't read config? %d\n", ret);
378 return ret;
379 }
380 data->oldconfig = ret;
381 /*
382 * Set to 16 bit resolution, continous conversion and comparator mode.
383 */
384 ret &= ~ADT7410_MODE_MASK;
385 data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
386 ADT7410_EVENT_MODE;
387 if (data->config != data->oldconfig) {
388 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
389 data->config);
390 if (ret)
391 return ret;
392 }
393 dev_dbg(&client->dev, "Config %02x\n", data->config);
394
395 ret = adt7410_fill_cache(client);
396 if (ret)
397 goto exit_restore;
398
399 /* Register sysfs hooks */
400 ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
401 if (ret)
402 goto exit_restore;
403
404 data->hwmon_dev = hwmon_device_register(&client->dev);
405 if (IS_ERR(data->hwmon_dev)) {
406 ret = PTR_ERR(data->hwmon_dev);
407 goto exit_remove;
408 }
409
410 dev_info(&client->dev, "sensor '%s'\n", client->name);
411
412 return 0;
413
414exit_remove:
415 sysfs_remove_group(&client->dev.kobj, &adt7410_group);
416exit_restore:
417 i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
418 return ret;
419} 51}
420 52
421static int adt7410_remove(struct i2c_client *client) 53static int adt7410_i2c_remove(struct i2c_client *client)
422{ 54{
423 struct adt7410_data *data = i2c_get_clientdata(client); 55 return adt7x10_remove(&client->dev);
424
425 hwmon_device_unregister(data->hwmon_dev);
426 sysfs_remove_group(&client->dev.kobj, &adt7410_group);
427 if (data->oldconfig != data->config)
428 i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
429 data->oldconfig);
430 return 0;
431} 56}
432 57
433static const struct i2c_device_id adt7410_ids[] = { 58static const struct i2c_device_id adt7410_ids[] = {
434 { "adt7410", adt7410, }, 59 { "adt7410", 0 },
435 { "adt7420", adt7410, }, 60 { "adt7420", 0 },
436 { /* LIST END */ } 61 {}
437}; 62};
438MODULE_DEVICE_TABLE(i2c, adt7410_ids); 63MODULE_DEVICE_TABLE(i2c, adt7410_ids);
439 64
440#ifdef CONFIG_PM_SLEEP
441static int adt7410_suspend(struct device *dev)
442{
443 int ret;
444 struct i2c_client *client = to_i2c_client(dev);
445 struct adt7410_data *data = i2c_get_clientdata(client);
446
447 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
448 data->config | ADT7410_PD);
449 return ret;
450}
451
452static int adt7410_resume(struct device *dev)
453{
454 int ret;
455 struct i2c_client *client = to_i2c_client(dev);
456 struct adt7410_data *data = i2c_get_clientdata(client);
457
458 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
459 return ret;
460}
461
462static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
463
464#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
465#else
466#define ADT7410_DEV_PM_OPS NULL
467#endif /* CONFIG_PM */
468
469static struct i2c_driver adt7410_driver = { 65static struct i2c_driver adt7410_driver = {
470 .class = I2C_CLASS_HWMON, 66 .class = I2C_CLASS_HWMON,
471 .driver = { 67 .driver = {
472 .name = "adt7410", 68 .name = "adt7410",
473 .pm = ADT7410_DEV_PM_OPS, 69 .pm = ADT7X10_DEV_PM_OPS,
474 }, 70 },
475 .probe = adt7410_probe, 71 .probe = adt7410_i2c_probe,
476 .remove = adt7410_remove, 72 .remove = adt7410_i2c_remove,
477 .id_table = adt7410_ids, 73 .id_table = adt7410_ids,
478 .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b), 74 .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
479}; 75};
480
481module_i2c_driver(adt7410_driver); 76module_i2c_driver(adt7410_driver);
482 77
483MODULE_AUTHOR("Hartmut Knaack"); 78MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
484MODULE_DESCRIPTION("ADT7410/ADT7420 driver"); 79MODULE_DESCRIPTION("ADT7410/AD7420 driver");
485MODULE_LICENSE("GPL"); 80MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
new file mode 100644
index 000000000000..84b3dfc1c1cd
--- /dev/null
+++ b/drivers/hwmon/adt7x10.c
@@ -0,0 +1,476 @@
1/*
2 * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware
3 * monitoring
4 * This driver handles the ADT7410 and compatible digital temperature sensors.
5 * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
6 * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
7 * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/slab.h>
27#include <linux/jiffies.h>
28#include <linux/hwmon.h>
29#include <linux/hwmon-sysfs.h>
30#include <linux/err.h>
31#include <linux/mutex.h>
32#include <linux/delay.h>
33
34#include "adt7x10.h"
35
36/*
37 * ADT7X10 status
38 */
39#define ADT7X10_STAT_T_LOW (1 << 4)
40#define ADT7X10_STAT_T_HIGH (1 << 5)
41#define ADT7X10_STAT_T_CRIT (1 << 6)
42#define ADT7X10_STAT_NOT_RDY (1 << 7)
43
44/*
45 * ADT7X10 config
46 */
47#define ADT7X10_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
48#define ADT7X10_CT_POLARITY (1 << 2)
49#define ADT7X10_INT_POLARITY (1 << 3)
50#define ADT7X10_EVENT_MODE (1 << 4)
51#define ADT7X10_MODE_MASK (1 << 5 | 1 << 6)
52#define ADT7X10_FULL (0 << 5 | 0 << 6)
53#define ADT7X10_PD (1 << 5 | 1 << 6)
54#define ADT7X10_RESOLUTION (1 << 7)
55
56/*
57 * ADT7X10 masks
58 */
59#define ADT7X10_T13_VALUE_MASK 0xFFF8
60#define ADT7X10_T_HYST_MASK 0xF
61
62/* straight from the datasheet */
63#define ADT7X10_TEMP_MIN (-55000)
64#define ADT7X10_TEMP_MAX 150000
65
66/* Each client has this additional data */
67struct adt7x10_data {
68 const struct adt7x10_ops *ops;
69 const char *name;
70 struct device *hwmon_dev;
71 struct mutex update_lock;
72 u8 config;
73 u8 oldconfig;
74 bool valid; /* true if registers valid */
75 unsigned long last_updated; /* In jiffies */
76 s16 temp[4]; /* Register values,
77 0 = input
78 1 = high
79 2 = low
80 3 = critical */
81 u8 hyst; /* hysteresis offset */
82};
83
84static int adt7x10_read_byte(struct device *dev, u8 reg)
85{
86 struct adt7x10_data *d = dev_get_drvdata(dev);
87 return d->ops->read_byte(dev, reg);
88}
89
90static int adt7x10_write_byte(struct device *dev, u8 reg, u8 data)
91{
92 struct adt7x10_data *d = dev_get_drvdata(dev);
93 return d->ops->write_byte(dev, reg, data);
94}
95
96static int adt7x10_read_word(struct device *dev, u8 reg)
97{
98 struct adt7x10_data *d = dev_get_drvdata(dev);
99 return d->ops->read_word(dev, reg);
100}
101
102static int adt7x10_write_word(struct device *dev, u8 reg, u16 data)
103{
104 struct adt7x10_data *d = dev_get_drvdata(dev);
105 return d->ops->write_word(dev, reg, data);
106}
107
108static const u8 ADT7X10_REG_TEMP[4] = {
109 ADT7X10_TEMPERATURE, /* input */
110 ADT7X10_T_ALARM_HIGH, /* high */
111 ADT7X10_T_ALARM_LOW, /* low */
112 ADT7X10_T_CRIT, /* critical */
113};
114
115static int adt7x10_temp_ready(struct device *dev)
116{
117 int i, status;
118
119 for (i = 0; i < 6; i++) {
120 status = adt7x10_read_byte(dev, ADT7X10_STATUS);
121 if (status < 0)
122 return status;
123 if (!(status & ADT7X10_STAT_NOT_RDY))
124 return 0;
125 msleep(60);
126 }
127 return -ETIMEDOUT;
128}
129
130static int adt7x10_update_temp(struct device *dev)
131{
132 struct adt7x10_data *data = dev_get_drvdata(dev);
133 int ret = 0;
134
135 mutex_lock(&data->update_lock);
136
137 if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
138 || !data->valid) {
139 int temp;
140
141 dev_dbg(dev, "Starting update\n");
142
143 ret = adt7x10_temp_ready(dev); /* check for new value */
144 if (ret)
145 goto abort;
146
147 temp = adt7x10_read_word(dev, ADT7X10_REG_TEMP[0]);
148 if (temp < 0) {
149 ret = temp;
150 dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
151 ADT7X10_REG_TEMP[0], ret);
152 goto abort;
153 }
154 data->temp[0] = temp;
155 data->last_updated = jiffies;
156 data->valid = true;
157 }
158
159abort:
160 mutex_unlock(&data->update_lock);
161 return ret;
162}
163
164static int adt7x10_fill_cache(struct device *dev)
165{
166 struct adt7x10_data *data = dev_get_drvdata(dev);
167 int ret;
168 int i;
169
170 for (i = 1; i < ARRAY_SIZE(data->temp); i++) {
171 ret = adt7x10_read_word(dev, ADT7X10_REG_TEMP[i]);
172 if (ret < 0) {
173 dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
174 ADT7X10_REG_TEMP[i], ret);
175 return ret;
176 }
177 data->temp[i] = ret;
178 }
179
180 ret = adt7x10_read_byte(dev, ADT7X10_T_HYST);
181 if (ret < 0) {
182 dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
183 ADT7X10_T_HYST, ret);
184 return ret;
185 }
186 data->hyst = ret;
187
188 return 0;
189}
190
191static s16 ADT7X10_TEMP_TO_REG(long temp)
192{
193 return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN,
194 ADT7X10_TEMP_MAX) * 128, 1000);
195}
196
197static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg)
198{
199 /* in 13 bit mode, bits 0-2 are status flags - mask them out */
200 if (!(data->config & ADT7X10_RESOLUTION))
201 reg &= ADT7X10_T13_VALUE_MASK;
202 /*
203 * temperature is stored in twos complement format, in steps of
204 * 1/128°C
205 */
206 return DIV_ROUND_CLOSEST(reg * 1000, 128);
207}
208
209/*-----------------------------------------------------------------------*/
210
211/* sysfs attributes for hwmon */
212
213static ssize_t adt7x10_show_temp(struct device *dev,
214 struct device_attribute *da,
215 char *buf)
216{
217 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
218 struct adt7x10_data *data = dev_get_drvdata(dev);
219
220
221 if (attr->index == 0) {
222 int ret;
223
224 ret = adt7x10_update_temp(dev);
225 if (ret)
226 return ret;
227 }
228
229 return sprintf(buf, "%d\n", ADT7X10_REG_TO_TEMP(data,
230 data->temp[attr->index]));
231}
232
233static ssize_t adt7x10_set_temp(struct device *dev,
234 struct device_attribute *da,
235 const char *buf, size_t count)
236{
237 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
238 struct adt7x10_data *data = dev_get_drvdata(dev);
239 int nr = attr->index;
240 long temp;
241 int ret;
242
243 ret = kstrtol(buf, 10, &temp);
244 if (ret)
245 return ret;
246
247 mutex_lock(&data->update_lock);
248 data->temp[nr] = ADT7X10_TEMP_TO_REG(temp);
249 ret = adt7x10_write_word(dev, ADT7X10_REG_TEMP[nr], data->temp[nr]);
250 if (ret)
251 count = ret;
252 mutex_unlock(&data->update_lock);
253 return count;
254}
255
256static ssize_t adt7x10_show_t_hyst(struct device *dev,
257 struct device_attribute *da,
258 char *buf)
259{
260 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
261 struct adt7x10_data *data = dev_get_drvdata(dev);
262 int nr = attr->index;
263 int hyst;
264
265 hyst = (data->hyst & ADT7X10_T_HYST_MASK) * 1000;
266
267 /*
268 * hysteresis is stored as a 4 bit offset in the device, convert it
269 * to an absolute value
270 */
271 if (nr == 2) /* min has positive offset, others have negative */
272 hyst = -hyst;
273 return sprintf(buf, "%d\n",
274 ADT7X10_REG_TO_TEMP(data, data->temp[nr]) - hyst);
275}
276
277static ssize_t adt7x10_set_t_hyst(struct device *dev,
278 struct device_attribute *da,
279 const char *buf, size_t count)
280{
281 struct adt7x10_data *data = dev_get_drvdata(dev);
282 int limit, ret;
283 long hyst;
284
285 ret = kstrtol(buf, 10, &hyst);
286 if (ret)
287 return ret;
288 /* convert absolute hysteresis value to a 4 bit delta value */
289 limit = ADT7X10_REG_TO_TEMP(data, data->temp[1]);
290 hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX);
291 data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000),
292 0, ADT7X10_T_HYST_MASK);
293 ret = adt7x10_write_byte(dev, ADT7X10_T_HYST, data->hyst);
294 if (ret)
295 return ret;
296
297 return count;
298}
299
300static ssize_t adt7x10_show_alarm(struct device *dev,
301 struct device_attribute *da,
302 char *buf)
303{
304 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
305 int ret;
306
307 ret = adt7x10_read_byte(dev, ADT7X10_STATUS);
308 if (ret < 0)
309 return ret;
310
311 return sprintf(buf, "%d\n", !!(ret & attr->index));
312}
313
314static ssize_t adt7x10_show_name(struct device *dev,
315 struct device_attribute *da,
316 char *buf)
317{
318 struct adt7x10_data *data = dev_get_drvdata(dev);
319
320 return sprintf(buf, "%s\n", data->name);
321}
322
323static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7x10_show_temp, NULL, 0);
324static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
325 adt7x10_show_temp, adt7x10_set_temp, 1);
326static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
327 adt7x10_show_temp, adt7x10_set_temp, 2);
328static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
329 adt7x10_show_temp, adt7x10_set_temp, 3);
330static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
331 adt7x10_show_t_hyst, adt7x10_set_t_hyst, 1);
332static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
333 adt7x10_show_t_hyst, NULL, 2);
334static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
335 adt7x10_show_t_hyst, NULL, 3);
336static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7x10_show_alarm,
337 NULL, ADT7X10_STAT_T_LOW);
338static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7x10_show_alarm,
339 NULL, ADT7X10_STAT_T_HIGH);
340static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7x10_show_alarm,
341 NULL, ADT7X10_STAT_T_CRIT);
342static DEVICE_ATTR(name, S_IRUGO, adt7x10_show_name, NULL);
343
344static struct attribute *adt7x10_attributes[] = {
345 &sensor_dev_attr_temp1_input.dev_attr.attr,
346 &sensor_dev_attr_temp1_max.dev_attr.attr,
347 &sensor_dev_attr_temp1_min.dev_attr.attr,
348 &sensor_dev_attr_temp1_crit.dev_attr.attr,
349 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
350 &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
351 &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
352 &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
353 &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
354 &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
355 NULL
356};
357
358static const struct attribute_group adt7x10_group = {
359 .attrs = adt7x10_attributes,
360};
361
362int adt7x10_probe(struct device *dev, const char *name,
363 const struct adt7x10_ops *ops)
364{
365 struct adt7x10_data *data;
366 int ret;
367
368 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
369 if (!data)
370 return -ENOMEM;
371
372 data->ops = ops;
373 data->name = name;
374
375 dev_set_drvdata(dev, data);
376 mutex_init(&data->update_lock);
377
378 /* configure as specified */
379 ret = adt7x10_read_byte(dev, ADT7X10_CONFIG);
380 if (ret < 0) {
381 dev_dbg(dev, "Can't read config? %d\n", ret);
382 return ret;
383 }
384 data->oldconfig = ret;
385
386 /*
387 * Set to 16 bit resolution, continous conversion and comparator mode.
388 */
389 data->config = data->oldconfig;
390 data->config &= ~ADT7X10_MODE_MASK;
391 data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE;
392 if (data->config != data->oldconfig) {
393 ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config);
394 if (ret)
395 return ret;
396 }
397 dev_dbg(dev, "Config %02x\n", data->config);
398
399 ret = adt7x10_fill_cache(dev);
400 if (ret)
401 goto exit_restore;
402
403 /* Register sysfs hooks */
404 ret = sysfs_create_group(&dev->kobj, &adt7x10_group);
405 if (ret)
406 goto exit_restore;
407
408 /*
409 * The I2C device will already have it's own 'name' attribute, but for
410 * the SPI device we need to register it. name will only be non NULL if
411 * the device doesn't register the 'name' attribute on its own.
412 */
413 if (name) {
414 ret = device_create_file(dev, &dev_attr_name);
415 if (ret)
416 goto exit_remove;
417 }
418
419 data->hwmon_dev = hwmon_device_register(dev);
420 if (IS_ERR(data->hwmon_dev)) {
421 ret = PTR_ERR(data->hwmon_dev);
422 goto exit_remove_name;
423 }
424
425 return 0;
426
427exit_remove_name:
428 if (name)
429 device_remove_file(dev, &dev_attr_name);
430exit_remove:
431 sysfs_remove_group(&dev->kobj, &adt7x10_group);
432exit_restore:
433 adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig);
434 return ret;
435}
436EXPORT_SYMBOL_GPL(adt7x10_probe);
437
438int adt7x10_remove(struct device *dev)
439{
440 struct adt7x10_data *data = dev_get_drvdata(dev);
441
442 hwmon_device_unregister(data->hwmon_dev);
443 if (data->name)
444 device_remove_file(dev, &dev_attr_name);
445 sysfs_remove_group(&dev->kobj, &adt7x10_group);
446 if (data->oldconfig != data->config)
447 adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig);
448 return 0;
449}
450EXPORT_SYMBOL_GPL(adt7x10_remove);
451
452#ifdef CONFIG_PM_SLEEP
453
454static int adt7x10_suspend(struct device *dev)
455{
456 struct adt7x10_data *data = dev_get_drvdata(dev);
457
458 return adt7x10_write_byte(dev, ADT7X10_CONFIG,
459 data->config | ADT7X10_PD);
460}
461
462static int adt7x10_resume(struct device *dev)
463{
464 struct adt7x10_data *data = dev_get_drvdata(dev);
465
466 return adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config);
467}
468
469SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
470EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops);
471
472#endif /* CONFIG_PM_SLEEP */
473
474MODULE_AUTHOR("Hartmut Knaack");
475MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
476MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
new file mode 100644
index 000000000000..803d9b91c5db
--- /dev/null
+++ b/drivers/hwmon/adt7x10.h
@@ -0,0 +1,37 @@
1#ifndef __HWMON_ADT7X10_H__
2#define __HWMON_ADT7X10_H__
3
4#include <linux/types.h>
5#include <linux/pm.h>
6
7/* ADT7410 registers definition */
8#define ADT7X10_TEMPERATURE 0
9#define ADT7X10_STATUS 2
10#define ADT7X10_CONFIG 3
11#define ADT7X10_T_ALARM_HIGH 4
12#define ADT7X10_T_ALARM_LOW 6
13#define ADT7X10_T_CRIT 8
14#define ADT7X10_T_HYST 0xA
15#define ADT7X10_ID 0xB
16
17struct device;
18
19struct adt7x10_ops {
20 int (*read_byte)(struct device *, u8 reg);
21 int (*write_byte)(struct device *, u8 reg, u8 data);
22 int (*read_word)(struct device *, u8 reg);
23 int (*write_word)(struct device *, u8 reg, u16 data);
24};
25
26int adt7x10_probe(struct device *dev, const char *name,
27 const struct adt7x10_ops *ops);
28int adt7x10_remove(struct device *dev);
29
30#ifdef CONFIG_PM_SLEEP
31extern const struct dev_pm_ops adt7x10_dev_pm_ops;
32#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops)
33#else
34#define ADT7X10_DEV_PM_OPS NULL
35#endif
36
37#endif