diff options
author | Paul Thomas <pthomas8589@gmail.com> | 2010-05-24 17:33:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-25 11:07:07 -0400 |
commit | e0c70b80786296d4f3c35ebe0d52591cebf8f916 (patch) | |
tree | a7d5193ceb5fed6a2a6647f592912ebeda497602 /drivers/hwmon/ads7871.c | |
parent | 4a70a413ccfd1c14ef29a290a4d2dada04ccbefb (diff) |
hwmon: add TI ads7871 a/d converter driver
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Paul Thomas <pthomas8589@gmail.com>
Cc: Jonathan Cameron <jic23@cam.ac.uk>
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/hwmon/ads7871.c')
-rw-r--r-- | drivers/hwmon/ads7871.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c new file mode 100644 index 000000000000..b300a2048af1 --- /dev/null +++ b/drivers/hwmon/ads7871.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* | ||
2 | * ads7871 - driver for TI ADS7871 A/D converter | ||
3 | * | ||
4 | * Copyright (c) 2010 Paul Thomas <pthomas8589@gmail.com> | ||
5 | * | ||
6 | * This program is distributed in the hope that it will be useful, | ||
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | * GNU General Public License for more details. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 or | ||
13 | * later as publishhed by the Free Software Foundation. | ||
14 | * | ||
15 | * You need to have something like this in struct spi_board_info | ||
16 | * { | ||
17 | * .modalias = "ads7871", | ||
18 | * .max_speed_hz = 2*1000*1000, | ||
19 | * .chip_select = 0, | ||
20 | * .bus_num = 1, | ||
21 | * }, | ||
22 | */ | ||
23 | |||
24 | /*From figure 18 in the datasheet*/ | ||
25 | /*Register addresses*/ | ||
26 | #define REG_LS_BYTE 0 /*A/D Output Data, LS Byte*/ | ||
27 | #define REG_MS_BYTE 1 /*A/D Output Data, MS Byte*/ | ||
28 | #define REG_PGA_VALID 2 /*PGA Valid Register*/ | ||
29 | #define REG_AD_CONTROL 3 /*A/D Control Register*/ | ||
30 | #define REG_GAIN_MUX 4 /*Gain/Mux Register*/ | ||
31 | #define REG_IO_STATE 5 /*Digital I/O State Register*/ | ||
32 | #define REG_IO_CONTROL 6 /*Digital I/O Control Register*/ | ||
33 | #define REG_OSC_CONTROL 7 /*Rev/Oscillator Control Register*/ | ||
34 | #define REG_SER_CONTROL 24 /*Serial Interface Control Register*/ | ||
35 | #define REG_ID 31 /*ID Register*/ | ||
36 | |||
37 | /*From figure 17 in the datasheet | ||
38 | * These bits get ORed with the address to form | ||
39 | * the instruction byte */ | ||
40 | /*Instruction Bit masks*/ | ||
41 | #define INST_MODE_bm (1<<7) | ||
42 | #define INST_READ_bm (1<<6) | ||
43 | #define INST_16BIT_bm (1<<5) | ||
44 | |||
45 | /*From figure 18 in the datasheet*/ | ||
46 | /*bit masks for Rev/Oscillator Control Register*/ | ||
47 | #define MUX_CNV_bv 7 | ||
48 | #define MUX_CNV_bm (1<<MUX_CNV_bv) | ||
49 | #define MUX_M3_bm (1<<3) /*M3 selects single ended*/ | ||
50 | #define MUX_G_bv 4 /*allows for reg = (gain << MUX_G_bv) | ...*/ | ||
51 | |||
52 | /*From figure 18 in the datasheet*/ | ||
53 | /*bit masks for Rev/Oscillator Control Register*/ | ||
54 | #define OSC_OSCR_bm (1<<5) | ||
55 | #define OSC_OSCE_bm (1<<4) | ||
56 | #define OSC_REFE_bm (1<<3) | ||
57 | #define OSC_BUFE_bm (1<<2) | ||
58 | #define OSC_R2V_bm (1<<1) | ||
59 | #define OSC_RBG_bm (1<<0) | ||
60 | |||
61 | #include <linux/module.h> | ||
62 | #include <linux/init.h> | ||
63 | #include <linux/spi/spi.h> | ||
64 | #include <linux/hwmon.h> | ||
65 | #include <linux/hwmon-sysfs.h> | ||
66 | #include <linux/err.h> | ||
67 | #include <linux/mutex.h> | ||
68 | #include <linux/delay.h> | ||
69 | |||
70 | #define DEVICE_NAME "ads7871" | ||
71 | |||
72 | struct ads7871_data { | ||
73 | struct device *hwmon_dev; | ||
74 | struct mutex update_lock; | ||
75 | }; | ||
76 | |||
77 | static int ads7871_read_reg8(struct spi_device *spi, int reg) | ||
78 | { | ||
79 | int ret; | ||
80 | reg = reg | INST_READ_bm; | ||
81 | ret = spi_w8r8(spi, reg); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static int ads7871_read_reg16(struct spi_device *spi, int reg) | ||
86 | { | ||
87 | int ret; | ||
88 | reg = reg | INST_READ_bm | INST_16BIT_bm; | ||
89 | ret = spi_w8r16(spi, reg); | ||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) | ||
94 | { | ||
95 | u8 tmp[2] = {reg, val}; | ||
96 | return spi_write(spi, tmp, sizeof(tmp)); | ||
97 | } | ||
98 | |||
99 | static ssize_t show_voltage(struct device *dev, | ||
100 | struct device_attribute *da, char *buf) | ||
101 | { | ||
102 | struct spi_device *spi = to_spi_device(dev); | ||
103 | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | ||
104 | int ret, val, i = 0; | ||
105 | uint8_t channel, mux_cnv; | ||
106 | |||
107 | channel = attr->index; | ||
108 | /*TODO: add support for conversions | ||
109 | *other than single ended with a gain of 1*/ | ||
110 | /*MUX_M3_bm forces single ended*/ | ||
111 | /*This is also where the gain of the PGA would be set*/ | ||
112 | ads7871_write_reg8(spi, REG_GAIN_MUX, | ||
113 | (MUX_CNV_bm | MUX_M3_bm | channel)); | ||
114 | |||
115 | ret = ads7871_read_reg8(spi, REG_GAIN_MUX); | ||
116 | mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv); | ||
117 | /*on 400MHz arm9 platform the conversion | ||
118 | *is already done when we do this test*/ | ||
119 | while ((i < 2) && mux_cnv) { | ||
120 | i++; | ||
121 | ret = ads7871_read_reg8(spi, REG_GAIN_MUX); | ||
122 | mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv); | ||
123 | msleep_interruptible(1); | ||
124 | } | ||
125 | |||
126 | if (mux_cnv == 0) { | ||
127 | val = ads7871_read_reg16(spi, REG_LS_BYTE); | ||
128 | /*result in volts*10000 = (val/8192)*2.5*10000*/ | ||
129 | val = ((val>>2) * 25000) / 8192; | ||
130 | return sprintf(buf, "%d\n", val); | ||
131 | } else { | ||
132 | return -1; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0); | ||
137 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1); | ||
138 | static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2); | ||
139 | static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 3); | ||
140 | static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 4); | ||
141 | static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5); | ||
142 | static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6); | ||
143 | static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7); | ||
144 | |||
145 | static struct attribute *ads7871_attributes[] = { | ||
146 | &sensor_dev_attr_in0_input.dev_attr.attr, | ||
147 | &sensor_dev_attr_in1_input.dev_attr.attr, | ||
148 | &sensor_dev_attr_in2_input.dev_attr.attr, | ||
149 | &sensor_dev_attr_in3_input.dev_attr.attr, | ||
150 | &sensor_dev_attr_in4_input.dev_attr.attr, | ||
151 | &sensor_dev_attr_in5_input.dev_attr.attr, | ||
152 | &sensor_dev_attr_in6_input.dev_attr.attr, | ||
153 | &sensor_dev_attr_in7_input.dev_attr.attr, | ||
154 | NULL | ||
155 | }; | ||
156 | |||
157 | static const struct attribute_group ads7871_group = { | ||
158 | .attrs = ads7871_attributes, | ||
159 | }; | ||
160 | |||
161 | static int __devinit ads7871_probe(struct spi_device *spi) | ||
162 | { | ||
163 | int status, ret, err = 0; | ||
164 | uint8_t val; | ||
165 | struct ads7871_data *pdata; | ||
166 | |||
167 | dev_dbg(&spi->dev, "probe\n"); | ||
168 | |||
169 | pdata = kzalloc(sizeof(struct ads7871_data), GFP_KERNEL); | ||
170 | if (!pdata) { | ||
171 | err = -ENOMEM; | ||
172 | goto exit; | ||
173 | } | ||
174 | |||
175 | status = sysfs_create_group(&spi->dev.kobj, &ads7871_group); | ||
176 | if (status < 0) | ||
177 | goto error_free; | ||
178 | |||
179 | pdata->hwmon_dev = hwmon_device_register(&spi->dev); | ||
180 | if (IS_ERR(pdata->hwmon_dev)) { | ||
181 | err = PTR_ERR(pdata->hwmon_dev); | ||
182 | goto error_remove; | ||
183 | } | ||
184 | |||
185 | spi_set_drvdata(spi, pdata); | ||
186 | |||
187 | /* Configure the SPI bus */ | ||
188 | spi->mode = (SPI_MODE_0); | ||
189 | spi->bits_per_word = 8; | ||
190 | spi_setup(spi); | ||
191 | |||
192 | ads7871_write_reg8(spi, REG_SER_CONTROL, 0); | ||
193 | ads7871_write_reg8(spi, REG_AD_CONTROL, 0); | ||
194 | |||
195 | val = (OSC_OSCR_bm | OSC_OSCE_bm | OSC_REFE_bm | OSC_BUFE_bm); | ||
196 | ads7871_write_reg8(spi, REG_OSC_CONTROL, val); | ||
197 | ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); | ||
198 | |||
199 | dev_dbg(&spi->dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret); | ||
200 | /*because there is no other error checking on an SPI bus | ||
201 | we need to make sure we really have a chip*/ | ||
202 | if (val != ret) { | ||
203 | err = -ENODEV; | ||
204 | goto error_remove; | ||
205 | } | ||
206 | |||
207 | return 0; | ||
208 | |||
209 | error_remove: | ||
210 | sysfs_remove_group(&spi->dev.kobj, &ads7871_group); | ||
211 | error_free: | ||
212 | kfree(pdata); | ||
213 | exit: | ||
214 | return err; | ||
215 | } | ||
216 | |||
217 | static int __devexit ads7871_remove(struct spi_device *spi) | ||
218 | { | ||
219 | struct ads7871_data *pdata = spi_get_drvdata(spi); | ||
220 | |||
221 | hwmon_device_unregister(pdata->hwmon_dev); | ||
222 | sysfs_remove_group(&spi->dev.kobj, &ads7871_group); | ||
223 | kfree(pdata); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static struct spi_driver ads7871_driver = { | ||
228 | .driver = { | ||
229 | .name = DEVICE_NAME, | ||
230 | .bus = &spi_bus_type, | ||
231 | .owner = THIS_MODULE, | ||
232 | }, | ||
233 | |||
234 | .probe = ads7871_probe, | ||
235 | .remove = __devexit_p(ads7871_remove), | ||
236 | }; | ||
237 | |||
238 | static int __init ads7871_init(void) | ||
239 | { | ||
240 | return spi_register_driver(&ads7871_driver); | ||
241 | } | ||
242 | |||
243 | static void __exit ads7871_exit(void) | ||
244 | { | ||
245 | spi_unregister_driver(&ads7871_driver); | ||
246 | } | ||
247 | |||
248 | module_init(ads7871_init); | ||
249 | module_exit(ads7871_exit); | ||
250 | |||
251 | MODULE_AUTHOR("Paul Thomas <pthomas8589@gmail.com>"); | ||
252 | MODULE_DESCRIPTION("TI ADS7871 A/D driver"); | ||
253 | MODULE_LICENSE("GPL"); | ||