aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/hwmon/ads782835
-rw-r--r--drivers/hwmon/ads7828.c228
-rw-r--r--include/linux/platform_data/ads7828.h29
3 files changed, 140 insertions, 152 deletions
diff --git a/Documentation/hwmon/ads7828 b/Documentation/hwmon/ads7828
index 2bbebe6f771f..a987c94a88e5 100644
--- a/Documentation/hwmon/ads7828
+++ b/Documentation/hwmon/ads7828
@@ -4,22 +4,33 @@ Kernel driver ads7828
4Supported chips: 4Supported chips:
5 * Texas Instruments/Burr-Brown ADS7828 5 * Texas Instruments/Burr-Brown ADS7828
6 Prefix: 'ads7828' 6 Prefix: 'ads7828'
7 Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4b 7 Datasheet: Publicly available at the Texas Instruments website:
8 Datasheet: Publicly available at the Texas Instruments website :
9 http://focus.ti.com/lit/ds/symlink/ads7828.pdf 8 http://focus.ti.com/lit/ds/symlink/ads7828.pdf
10 9
11Authors: 10Authors:
12 Steve Hardy <shardy@redhat.com> 11 Steve Hardy <shardy@redhat.com>
12 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
13 13
14Module Parameters 14Platform data
15----------------- 15-------------
16 16
17* se_input: bool (default Y) 17The ads7828 driver accepts an optional ads7828_platform_data structure (defined
18 Single ended operation - set to N for differential mode 18in include/linux/platform_data/ads7828.h). The structure fields are:
19* int_vref: bool (default Y) 19
20 Operate with the internal 2.5V reference - set to N for external reference 20* diff_input: (bool) Differential operation
21* vref_mv: int (default 2500) 21 set to true for differential mode, false for default single ended mode.
22 If using an external reference, set this to the reference voltage in mV 22
23* ext_vref: (bool) External reference
24 set to true if it operates with an external reference, false for default
25 internal reference.
26
27* vref_mv: (unsigned int) Voltage reference
28 if using an external reference, set this to the reference voltage in mV,
29 otherwise it will default to the internal value (2500mV). This value will be
30 bounded with limits accepted by the chip, described in the datasheet.
31
32 If no structure is provided, the configuration defaults to single ended
33 operation and internal voltage reference (2.5V).
23 34
24Description 35Description
25----------- 36-----------
@@ -34,3 +45,7 @@ where 4 differential pairs can be measured.
34The chip also has the facility to use an external voltage reference. This 45The chip also has the facility to use an external voltage reference. This
35may be required if your hardware supplies the ADS7828 from a 5V supply, see 46may be required if your hardware supplies the ADS7828 from a 5V supply, see
36the datasheet for more details. 47the datasheet for more details.
48
49There is no reliable way to identify this chip, so the driver will not scan
50some addresses to try to auto-detect it. That means that you will have to
51statically declare the device in the platform support code.
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 1f9e8af0f322..42914fc1436d 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -6,7 +6,7 @@
6 * 6 *
7 * Written by Steve Hardy <shardy@redhat.com> 7 * Written by Steve Hardy <shardy@redhat.com>
8 * 8 *
9 * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf 9 * For further information, see the Documentation/hwmon/ads7828 file.
10 * 10 *
11 * This program is free software; you can redistribute it and/or modify 11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by 12 * it under the terms of the GNU General Public License as published by
@@ -23,63 +23,44 @@
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */ 24 */
25 25
26#include <linux/module.h> 26#include <linux/err.h>
27#include <linux/init.h>
28#include <linux/slab.h>
29#include <linux/jiffies.h>
30#include <linux/i2c.h>
31#include <linux/hwmon.h> 27#include <linux/hwmon.h>
32#include <linux/hwmon-sysfs.h> 28#include <linux/hwmon-sysfs.h>
33#include <linux/err.h> 29#include <linux/i2c.h>
30#include <linux/init.h>
31#include <linux/jiffies.h>
32#include <linux/module.h>
34#include <linux/mutex.h> 33#include <linux/mutex.h>
34#include <linux/platform_data/ads7828.h>
35#include <linux/slab.h>
35 36
36/* The ADS7828 registers */ 37/* The ADS7828 registers */
37#define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */ 38#define ADS7828_NCH 8 /* 8 channels supported */
38#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ 39#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
39#define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */ 40#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
40#define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */ 41#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
41#define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */ 42#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
42#define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */ 43#define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */
43#define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */ 44#define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */
44#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ 45
45 46/* Client specific data */
46/* Addresses to scan */
47static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
48 I2C_CLIENT_END };
49
50/* Module parameters */
51static bool se_input = 1; /* Default is SE, 0 == diff */
52static bool int_vref = 1; /* Default is internal ref ON */
53static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */
54module_param(se_input, bool, S_IRUGO);
55module_param(int_vref, bool, S_IRUGO);
56module_param(vref_mv, int, S_IRUGO);
57
58/* Global Variables */
59static u8 ads7828_cmd_byte; /* cmd byte without channel bits */
60static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */
61
62/* Each client has this additional data */
63struct ads7828_data { 47struct ads7828_data {
64 struct device *hwmon_dev; 48 struct device *hwmon_dev;
65 struct mutex update_lock; /* mutex protect updates */ 49 struct mutex update_lock; /* Mutex protecting updates */
66 char valid; /* !=0 if following fields are valid */ 50 unsigned long last_updated; /* Last updated time (in jiffies) */
67 unsigned long last_updated; /* In jiffies */ 51 u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */
68 u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */ 52 bool valid; /* Validity flag */
53 bool diff_input; /* Differential input */
54 bool ext_vref; /* External voltage reference */
55 unsigned int vref_mv; /* voltage reference value */
56 u8 cmd_byte; /* Command byte without channel bits */
57 unsigned int lsb_resol; /* Resolution of the ADC sample LSB */
69}; 58};
70 59
71/* Function declaration - necessary due to function dependencies */ 60/* Command byte C2,C1,C0 - see datasheet */
72static int ads7828_detect(struct i2c_client *client, 61static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
73 struct i2c_board_info *info);
74static int ads7828_probe(struct i2c_client *client,
75 const struct i2c_device_id *id);
76
77static inline u8 channel_cmd_byte(int ch)
78{ 62{
79 /* cmd byte C2,C1,C0 - see datasheet */ 63 return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
80 u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4);
81 cmd |= ads7828_cmd_byte;
82 return cmd;
83} 64}
84 65
85/* Update data for the device (all 8 channels) */ 66/* Update data for the device (all 8 channels) */
@@ -96,12 +77,12 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
96 dev_dbg(&client->dev, "Starting ads7828 update\n"); 77 dev_dbg(&client->dev, "Starting ads7828 update\n");
97 78
98 for (ch = 0; ch < ADS7828_NCH; ch++) { 79 for (ch = 0; ch < ADS7828_NCH; ch++) {
99 u8 cmd = channel_cmd_byte(ch); 80 u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch);
100 data->adc_input[ch] = 81 data->adc_input[ch] =
101 i2c_smbus_read_word_swapped(client, cmd); 82 i2c_smbus_read_word_swapped(client, cmd);
102 } 83 }
103 data->last_updated = jiffies; 84 data->last_updated = jiffies;
104 data->valid = 1; 85 data->valid = true;
105 } 86 }
106 87
107 mutex_unlock(&data->update_lock); 88 mutex_unlock(&data->update_lock);
@@ -110,28 +91,25 @@ static struct ads7828_data *ads7828_update_device(struct device *dev)
110} 91}
111 92
112/* sysfs callback function */ 93/* sysfs callback function */
113static ssize_t show_in(struct device *dev, struct device_attribute *da, 94static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da,
114 char *buf) 95 char *buf)
115{ 96{
116 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 97 struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
117 struct ads7828_data *data = ads7828_update_device(dev); 98 struct ads7828_data *data = ads7828_update_device(dev);
118 /* Print value (in mV as specified in sysfs-interface documentation) */ 99 unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] *
119 return sprintf(buf, "%d\n", (data->adc_input[attr->index] * 100 data->lsb_resol, 1000);
120 ads7828_lsb_resol)/1000);
121}
122 101
123#define in_reg(offset)\ 102 return sprintf(buf, "%d\n", value);
124static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ 103}
125 NULL, offset)
126 104
127in_reg(0); 105static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0);
128in_reg(1); 106static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1);
129in_reg(2); 107static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2);
130in_reg(3); 108static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3);
131in_reg(4); 109static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4);
132in_reg(5); 110static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5);
133in_reg(6); 111static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6);
134in_reg(7); 112static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7);
135 113
136static struct attribute *ads7828_attributes[] = { 114static struct attribute *ads7828_attributes[] = {
137 &sensor_dev_attr_in0_input.dev_attr.attr, 115 &sensor_dev_attr_in0_input.dev_attr.attr,
@@ -152,60 +130,9 @@ static const struct attribute_group ads7828_group = {
152static int ads7828_remove(struct i2c_client *client) 130static int ads7828_remove(struct i2c_client *client)
153{ 131{
154 struct ads7828_data *data = i2c_get_clientdata(client); 132 struct ads7828_data *data = i2c_get_clientdata(client);
133
155 hwmon_device_unregister(data->hwmon_dev); 134 hwmon_device_unregister(data->hwmon_dev);
156 sysfs_remove_group(&client->dev.kobj, &ads7828_group); 135 sysfs_remove_group(&client->dev.kobj, &ads7828_group);
157 return 0;
158}
159
160static const struct i2c_device_id ads7828_id[] = {
161 { "ads7828", 0 },
162 { }
163};
164MODULE_DEVICE_TABLE(i2c, ads7828_id);
165
166/* This is the driver that will be inserted */
167static struct i2c_driver ads7828_driver = {
168 .class = I2C_CLASS_HWMON,
169 .driver = {
170 .name = "ads7828",
171 },
172 .probe = ads7828_probe,
173 .remove = ads7828_remove,
174 .id_table = ads7828_id,
175 .detect = ads7828_detect,
176 .address_list = normal_i2c,
177};
178
179/* Return 0 if detection is successful, -ENODEV otherwise */
180static int ads7828_detect(struct i2c_client *client,
181 struct i2c_board_info *info)
182{
183 struct i2c_adapter *adapter = client->adapter;
184 int ch;
185
186 /* Check we have a valid client */
187 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
188 return -ENODEV;
189
190 /*
191 * Now, we do the remaining detection. There is no identification
192 * dedicated register so attempt to sanity check using knowledge of
193 * the chip
194 * - Read from the 8 channel addresses
195 * - Check the top 4 bits of each result are not set (12 data bits)
196 */
197 for (ch = 0; ch < ADS7828_NCH; ch++) {
198 u16 in_data;
199 u8 cmd = channel_cmd_byte(ch);
200 in_data = i2c_smbus_read_word_swapped(client, cmd);
201 if (in_data & 0xF000) {
202 pr_debug("%s : Doesn't look like an ads7828 device\n",
203 __func__);
204 return -ENODEV;
205 }
206 }
207
208 strlcpy(info->type, "ads7828", I2C_NAME_SIZE);
209 136
210 return 0; 137 return 0;
211} 138}
@@ -213,6 +140,7 @@ static int ads7828_detect(struct i2c_client *client,
213static int ads7828_probe(struct i2c_client *client, 140static int ads7828_probe(struct i2c_client *client,
214 const struct i2c_device_id *id) 141 const struct i2c_device_id *id)
215{ 142{
143 struct ads7828_platform_data *pdata = client->dev.platform_data;
216 struct ads7828_data *data; 144 struct ads7828_data *data;
217 int err; 145 int err;
218 146
@@ -221,10 +149,30 @@ static int ads7828_probe(struct i2c_client *client,
221 if (!data) 149 if (!data)
222 return -ENOMEM; 150 return -ENOMEM;
223 151
152 if (pdata) {
153 data->diff_input = pdata->diff_input;
154 data->ext_vref = pdata->ext_vref;
155 if (data->ext_vref)
156 data->vref_mv = pdata->vref_mv;
157 }
158
159 /* Bound Vref with min/max values if it was provided */
160 if (data->vref_mv)
161 data->vref_mv = SENSORS_LIMIT(data->vref_mv,
162 ADS7828_EXT_VREF_MV_MIN,
163 ADS7828_EXT_VREF_MV_MAX);
164 else
165 data->vref_mv = ADS7828_INT_VREF_MV;
166
167 data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096);
168
169 data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
170 if (!data->diff_input)
171 data->cmd_byte |= ADS7828_CMD_SD_SE;
172
224 i2c_set_clientdata(client, data); 173 i2c_set_clientdata(client, data);
225 mutex_init(&data->update_lock); 174 mutex_init(&data->update_lock);
226 175
227 /* Register sysfs hooks */
228 err = sysfs_create_group(&client->dev.kobj, &ads7828_group); 176 err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
229 if (err) 177 if (err)
230 return err; 178 return err;
@@ -232,38 +180,34 @@ static int ads7828_probe(struct i2c_client *client,
232 data->hwmon_dev = hwmon_device_register(&client->dev); 180 data->hwmon_dev = hwmon_device_register(&client->dev);
233 if (IS_ERR(data->hwmon_dev)) { 181 if (IS_ERR(data->hwmon_dev)) {
234 err = PTR_ERR(data->hwmon_dev); 182 err = PTR_ERR(data->hwmon_dev);
235 goto exit_remove; 183 goto error;
236 } 184 }
237 185
238 return 0; 186 return 0;
239 187
240exit_remove: 188error:
241 sysfs_remove_group(&client->dev.kobj, &ads7828_group); 189 sysfs_remove_group(&client->dev.kobj, &ads7828_group);
242 return err; 190 return err;
243} 191}
244 192
245static int __init sensors_ads7828_init(void) 193static const struct i2c_device_id ads7828_device_ids[] = {
246{ 194 { "ads7828", 0 },
247 /* Initialize the command byte according to module parameters */ 195 { }
248 ads7828_cmd_byte = se_input ? 196};
249 ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF; 197MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
250 ads7828_cmd_byte |= int_vref ?
251 ADS7828_CMD_PD3 : ADS7828_CMD_PD1;
252 198
253 /* Calculate the LSB resolution */ 199static struct i2c_driver ads7828_driver = {
254 ads7828_lsb_resol = (vref_mv*1000)/4096; 200 .driver = {
201 .name = "ads7828",
202 },
255 203
256 return i2c_add_driver(&ads7828_driver); 204 .id_table = ads7828_device_ids,
257} 205 .probe = ads7828_probe,
206 .remove = ads7828_remove,
207};
258 208
259static void __exit sensors_ads7828_exit(void) 209module_i2c_driver(ads7828_driver);
260{
261 i2c_del_driver(&ads7828_driver);
262}
263 210
264MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
265MODULE_DESCRIPTION("ADS7828 driver");
266MODULE_LICENSE("GPL"); 211MODULE_LICENSE("GPL");
267 212MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
268module_init(sensors_ads7828_init); 213MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter");
269module_exit(sensors_ads7828_exit);
diff --git a/include/linux/platform_data/ads7828.h b/include/linux/platform_data/ads7828.h
new file mode 100644
index 000000000000..3245f45f9d77
--- /dev/null
+++ b/include/linux/platform_data/ads7828.h
@@ -0,0 +1,29 @@
1/*
2 * TI ADS7828 A/D Converter platform data definition
3 *
4 * Copyright (c) 2012 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6 *
7 * For further information, see the Documentation/hwmon/ads7828 file.
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 version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#ifndef _PDATA_ADS7828_H
15#define _PDATA_ADS7828_H
16
17/**
18 * struct ads7828_platform_data - optional ADS7828 connectivity info
19 * @diff_input: Differential input mode.
20 * @ext_vref: Use an external voltage reference.
21 * @vref_mv: Voltage reference value, if external.
22 */
23struct ads7828_platform_data {
24 bool diff_input;
25 bool ext_vref;
26 unsigned int vref_mv;
27};
28
29#endif /* _PDATA_ADS7828_H */