diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/hwmon/ads7828.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'drivers/hwmon/ads7828.c')
-rw-r--r-- | drivers/hwmon/ads7828.c | 307 |
1 files changed, 180 insertions, 127 deletions
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 409b5c16def..c42c5a69a66 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c | |||
@@ -1,72 +1,92 @@ | |||
1 | /* | 1 | /* |
2 | * ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles | 2 | ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC |
3 | * (C) 2007 EADS Astrium | 3 | (C) 2007 EADS Astrium |
4 | * | ||
5 | * This driver is based on the lm75 and other lm_sensors/hwmon drivers | ||
6 | * | ||
7 | * Written by Steve Hardy <shardy@redhat.com> | ||
8 | * | ||
9 | * ADS7830 support, by Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> | ||
10 | * | ||
11 | * For further information, see the Documentation/hwmon/ads7828 file. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
26 | */ | ||
27 | 4 | ||
28 | #include <linux/err.h> | 5 | This driver is based on the lm75 and other lm_sensors/hwmon drivers |
29 | #include <linux/hwmon.h> | 6 | |
30 | #include <linux/hwmon-sysfs.h> | 7 | Written by Steve Hardy <shardy@redhat.com> |
31 | #include <linux/i2c.h> | 8 | |
9 | Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf | ||
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 as published by | ||
13 | the Free Software Foundation; either version 2 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
32 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/slab.h> | ||
33 | #include <linux/jiffies.h> | 29 | #include <linux/jiffies.h> |
34 | #include <linux/module.h> | 30 | #include <linux/i2c.h> |
31 | #include <linux/hwmon.h> | ||
32 | #include <linux/hwmon-sysfs.h> | ||
33 | #include <linux/err.h> | ||
35 | #include <linux/mutex.h> | 34 | #include <linux/mutex.h> |
36 | #include <linux/platform_data/ads7828.h> | ||
37 | #include <linux/slab.h> | ||
38 | 35 | ||
39 | /* The ADS7828 registers */ | 36 | /* The ADS7828 registers */ |
40 | #define ADS7828_NCH 8 /* 8 channels supported */ | 37 | #define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */ |
41 | #define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ | 38 | #define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ |
42 | #define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */ | 39 | #define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */ |
43 | #define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */ | 40 | #define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */ |
44 | #define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ | 41 | #define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */ |
45 | #define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */ | 42 | #define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */ |
46 | #define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */ | 43 | #define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */ |
47 | 44 | #define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ | |
48 | /* List of supported devices */ | 45 | |
49 | enum ads7828_chips { ads7828, ads7830 }; | 46 | /* Addresses to scan */ |
50 | 47 | static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, | |
51 | /* Client specific data */ | 48 | I2C_CLIENT_END }; |
49 | |||
50 | /* Module parameters */ | ||
51 | static int se_input = 1; /* Default is SE, 0 == diff */ | ||
52 | static int int_vref = 1; /* Default is internal ref ON */ | ||
53 | static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */ | ||
54 | module_param(se_input, bool, S_IRUGO); | ||
55 | module_param(int_vref, bool, S_IRUGO); | ||
56 | module_param(vref_mv, int, S_IRUGO); | ||
57 | |||
58 | /* Global Variables */ | ||
59 | static u8 ads7828_cmd_byte; /* cmd byte without channel bits */ | ||
60 | static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */ | ||
61 | |||
62 | /* Each client has this additional data */ | ||
52 | struct ads7828_data { | 63 | struct ads7828_data { |
53 | struct device *hwmon_dev; | 64 | struct device *hwmon_dev; |
54 | struct mutex update_lock; /* Mutex protecting updates */ | 65 | struct mutex update_lock; /* mutex protect updates */ |
55 | unsigned long last_updated; /* Last updated time (in jiffies) */ | 66 | char valid; /* !=0 if following fields are valid */ |
56 | u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */ | 67 | unsigned long last_updated; /* In jiffies */ |
57 | bool valid; /* Validity flag */ | 68 | u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */ |
58 | bool diff_input; /* Differential input */ | ||
59 | bool ext_vref; /* External voltage reference */ | ||
60 | unsigned int vref_mv; /* voltage reference value */ | ||
61 | u8 cmd_byte; /* Command byte without channel bits */ | ||
62 | unsigned int lsb_resol; /* Resolution of the ADC sample LSB */ | ||
63 | s32 (*read_channel)(const struct i2c_client *client, u8 command); | ||
64 | }; | 69 | }; |
65 | 70 | ||
66 | /* Command byte C2,C1,C0 - see datasheet */ | 71 | /* Function declaration - necessary due to function dependencies */ |
67 | static inline u8 ads7828_cmd_byte(u8 cmd, int ch) | 72 | static int ads7828_detect(struct i2c_client *client, |
73 | struct i2c_board_info *info); | ||
74 | static int ads7828_probe(struct i2c_client *client, | ||
75 | const struct i2c_device_id *id); | ||
76 | |||
77 | /* The ADS7828 returns the 12-bit sample in two bytes, | ||
78 | these are read as a word then byte-swapped */ | ||
79 | static u16 ads7828_read_value(struct i2c_client *client, u8 reg) | ||
68 | { | 80 | { |
69 | return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4); | 81 | return swab16(i2c_smbus_read_word_data(client, reg)); |
82 | } | ||
83 | |||
84 | static inline u8 channel_cmd_byte(int ch) | ||
85 | { | ||
86 | /* cmd byte C2,C1,C0 - see datasheet */ | ||
87 | u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4); | ||
88 | cmd |= ads7828_cmd_byte; | ||
89 | return cmd; | ||
70 | } | 90 | } |
71 | 91 | ||
72 | /* Update data for the device (all 8 channels) */ | 92 | /* Update data for the device (all 8 channels) */ |
@@ -83,11 +103,11 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) | |||
83 | dev_dbg(&client->dev, "Starting ads7828 update\n"); | 103 | dev_dbg(&client->dev, "Starting ads7828 update\n"); |
84 | 104 | ||
85 | for (ch = 0; ch < ADS7828_NCH; ch++) { | 105 | for (ch = 0; ch < ADS7828_NCH; ch++) { |
86 | u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch); | 106 | u8 cmd = channel_cmd_byte(ch); |
87 | data->adc_input[ch] = data->read_channel(client, cmd); | 107 | data->adc_input[ch] = ads7828_read_value(client, cmd); |
88 | } | 108 | } |
89 | data->last_updated = jiffies; | 109 | data->last_updated = jiffies; |
90 | data->valid = true; | 110 | data->valid = 1; |
91 | } | 111 | } |
92 | 112 | ||
93 | mutex_unlock(&data->update_lock); | 113 | mutex_unlock(&data->update_lock); |
@@ -96,25 +116,28 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) | |||
96 | } | 116 | } |
97 | 117 | ||
98 | /* sysfs callback function */ | 118 | /* sysfs callback function */ |
99 | static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da, | 119 | static ssize_t show_in(struct device *dev, struct device_attribute *da, |
100 | char *buf) | 120 | char *buf) |
101 | { | 121 | { |
102 | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 122 | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); |
103 | struct ads7828_data *data = ads7828_update_device(dev); | 123 | struct ads7828_data *data = ads7828_update_device(dev); |
104 | unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] * | 124 | /* Print value (in mV as specified in sysfs-interface documentation) */ |
105 | data->lsb_resol, 1000); | 125 | return sprintf(buf, "%d\n", (data->adc_input[attr->index] * |
106 | 126 | ads7828_lsb_resol)/1000); | |
107 | return sprintf(buf, "%d\n", value); | ||
108 | } | 127 | } |
109 | 128 | ||
110 | static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0); | 129 | #define in_reg(offset)\ |
111 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1); | 130 | static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ |
112 | static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2); | 131 | NULL, offset) |
113 | static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3); | 132 | |
114 | static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4); | 133 | in_reg(0); |
115 | static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5); | 134 | in_reg(1); |
116 | static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6); | 135 | in_reg(2); |
117 | static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7); | 136 | in_reg(3); |
137 | in_reg(4); | ||
138 | in_reg(5); | ||
139 | in_reg(6); | ||
140 | in_reg(7); | ||
118 | 141 | ||
119 | static struct attribute *ads7828_attributes[] = { | 142 | static struct attribute *ads7828_attributes[] = { |
120 | &sensor_dev_attr_in0_input.dev_attr.attr, | 143 | &sensor_dev_attr_in0_input.dev_attr.attr, |
@@ -135,9 +158,60 @@ static const struct attribute_group ads7828_group = { | |||
135 | static int ads7828_remove(struct i2c_client *client) | 158 | static int ads7828_remove(struct i2c_client *client) |
136 | { | 159 | { |
137 | struct ads7828_data *data = i2c_get_clientdata(client); | 160 | struct ads7828_data *data = i2c_get_clientdata(client); |
138 | |||
139 | hwmon_device_unregister(data->hwmon_dev); | 161 | hwmon_device_unregister(data->hwmon_dev); |
140 | sysfs_remove_group(&client->dev.kobj, &ads7828_group); | 162 | sysfs_remove_group(&client->dev.kobj, &ads7828_group); |
163 | kfree(i2c_get_clientdata(client)); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static const struct i2c_device_id ads7828_id[] = { | ||
168 | { "ads7828", 0 }, | ||
169 | { } | ||
170 | }; | ||
171 | MODULE_DEVICE_TABLE(i2c, ads7828_id); | ||
172 | |||
173 | /* This is the driver that will be inserted */ | ||
174 | static struct i2c_driver ads7828_driver = { | ||
175 | .class = I2C_CLASS_HWMON, | ||
176 | .driver = { | ||
177 | .name = "ads7828", | ||
178 | }, | ||
179 | .probe = ads7828_probe, | ||
180 | .remove = ads7828_remove, | ||
181 | .id_table = ads7828_id, | ||
182 | .detect = ads7828_detect, | ||
183 | .address_list = normal_i2c, | ||
184 | }; | ||
185 | |||
186 | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||
187 | static int ads7828_detect(struct i2c_client *client, | ||
188 | struct i2c_board_info *info) | ||
189 | { | ||
190 | struct i2c_adapter *adapter = client->adapter; | ||
191 | int ch; | ||
192 | |||
193 | /* Check we have a valid client */ | ||
194 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) | ||
195 | return -ENODEV; | ||
196 | |||
197 | /* Now, we do the remaining detection. There is no identification | ||
198 | dedicated register so attempt to sanity check using knowledge of | ||
199 | the chip | ||
200 | - Read from the 8 channel addresses | ||
201 | - Check the top 4 bits of each result are not set (12 data bits) | ||
202 | */ | ||
203 | for (ch = 0; ch < ADS7828_NCH; ch++) { | ||
204 | u16 in_data; | ||
205 | u8 cmd = channel_cmd_byte(ch); | ||
206 | in_data = ads7828_read_value(client, cmd); | ||
207 | if (in_data & 0xF000) { | ||
208 | pr_debug("%s : Doesn't look like an ads7828 device\n", | ||
209 | __func__); | ||
210 | return -ENODEV; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | strlcpy(info->type, "ads7828", I2C_NAME_SIZE); | ||
141 | 215 | ||
142 | return 0; | 216 | return 0; |
143 | } | 217 | } |
@@ -145,82 +219,61 @@ static int ads7828_remove(struct i2c_client *client) | |||
145 | static int ads7828_probe(struct i2c_client *client, | 219 | static int ads7828_probe(struct i2c_client *client, |
146 | const struct i2c_device_id *id) | 220 | const struct i2c_device_id *id) |
147 | { | 221 | { |
148 | struct ads7828_platform_data *pdata = client->dev.platform_data; | ||
149 | struct ads7828_data *data; | 222 | struct ads7828_data *data; |
150 | int err; | 223 | int err; |
151 | 224 | ||
152 | data = devm_kzalloc(&client->dev, sizeof(struct ads7828_data), | 225 | data = kzalloc(sizeof(struct ads7828_data), GFP_KERNEL); |
153 | GFP_KERNEL); | 226 | if (!data) { |
154 | if (!data) | 227 | err = -ENOMEM; |
155 | return -ENOMEM; | 228 | goto exit; |
156 | |||
157 | if (pdata) { | ||
158 | data->diff_input = pdata->diff_input; | ||
159 | data->ext_vref = pdata->ext_vref; | ||
160 | if (data->ext_vref) | ||
161 | data->vref_mv = pdata->vref_mv; | ||
162 | } | ||
163 | |||
164 | /* Bound Vref with min/max values if it was provided */ | ||
165 | if (data->vref_mv) | ||
166 | data->vref_mv = SENSORS_LIMIT(data->vref_mv, | ||
167 | ADS7828_EXT_VREF_MV_MIN, | ||
168 | ADS7828_EXT_VREF_MV_MAX); | ||
169 | else | ||
170 | data->vref_mv = ADS7828_INT_VREF_MV; | ||
171 | |||
172 | /* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */ | ||
173 | if (id->driver_data == ads7828) { | ||
174 | data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); | ||
175 | data->read_channel = i2c_smbus_read_word_swapped; | ||
176 | } else { | ||
177 | data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256); | ||
178 | data->read_channel = i2c_smbus_read_byte_data; | ||
179 | } | 229 | } |
180 | 230 | ||
181 | data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; | ||
182 | if (!data->diff_input) | ||
183 | data->cmd_byte |= ADS7828_CMD_SD_SE; | ||
184 | |||
185 | i2c_set_clientdata(client, data); | 231 | i2c_set_clientdata(client, data); |
186 | mutex_init(&data->update_lock); | 232 | mutex_init(&data->update_lock); |
187 | 233 | ||
234 | /* Register sysfs hooks */ | ||
188 | err = sysfs_create_group(&client->dev.kobj, &ads7828_group); | 235 | err = sysfs_create_group(&client->dev.kobj, &ads7828_group); |
189 | if (err) | 236 | if (err) |
190 | return err; | 237 | goto exit_free; |
191 | 238 | ||
192 | data->hwmon_dev = hwmon_device_register(&client->dev); | 239 | data->hwmon_dev = hwmon_device_register(&client->dev); |
193 | if (IS_ERR(data->hwmon_dev)) { | 240 | if (IS_ERR(data->hwmon_dev)) { |
194 | err = PTR_ERR(data->hwmon_dev); | 241 | err = PTR_ERR(data->hwmon_dev); |
195 | goto error; | 242 | goto exit_remove; |
196 | } | 243 | } |
197 | 244 | ||
198 | return 0; | 245 | return 0; |
199 | 246 | ||
200 | error: | 247 | exit_remove: |
201 | sysfs_remove_group(&client->dev.kobj, &ads7828_group); | 248 | sysfs_remove_group(&client->dev.kobj, &ads7828_group); |
249 | exit_free: | ||
250 | kfree(data); | ||
251 | exit: | ||
202 | return err; | 252 | return err; |
203 | } | 253 | } |
204 | 254 | ||
205 | static const struct i2c_device_id ads7828_device_ids[] = { | 255 | static int __init sensors_ads7828_init(void) |
206 | { "ads7828", ads7828 }, | 256 | { |
207 | { "ads7830", ads7830 }, | 257 | /* Initialize the command byte according to module parameters */ |
208 | { } | 258 | ads7828_cmd_byte = se_input ? |
209 | }; | 259 | ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF; |
210 | MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); | 260 | ads7828_cmd_byte |= int_vref ? |
261 | ADS7828_CMD_PD3 : ADS7828_CMD_PD1; | ||
211 | 262 | ||
212 | static struct i2c_driver ads7828_driver = { | 263 | /* Calculate the LSB resolution */ |
213 | .driver = { | 264 | ads7828_lsb_resol = (vref_mv*1000)/4096; |
214 | .name = "ads7828", | ||
215 | }, | ||
216 | 265 | ||
217 | .id_table = ads7828_device_ids, | 266 | return i2c_add_driver(&ads7828_driver); |
218 | .probe = ads7828_probe, | 267 | } |
219 | .remove = ads7828_remove, | ||
220 | }; | ||
221 | 268 | ||
222 | module_i2c_driver(ads7828_driver); | 269 | static void __exit sensors_ads7828_exit(void) |
270 | { | ||
271 | i2c_del_driver(&ads7828_driver); | ||
272 | } | ||
223 | 273 | ||
224 | MODULE_LICENSE("GPL"); | ||
225 | MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>"); | 274 | MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>"); |
226 | MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles"); | 275 | MODULE_DESCRIPTION("ADS7828 driver"); |
276 | MODULE_LICENSE("GPL"); | ||
277 | |||
278 | module_init(sensors_ads7828_init); | ||
279 | module_exit(sensors_ads7828_exit); | ||