hwmon: Add support for Texas Instruments/Burr-Brown ADS7828

Signed-off-by: Steve Hardy <steve@linuxrealtime.co.uk>
Acked-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>

authored by Steve Hardy and committed by Mark M. Hoffman 5812f928 360f9452

+344
+36
Documentation/hwmon/ads7828
··· 1 + Kernel driver ads7828 2 + ===================== 3 + 4 + Supported chips: 5 + * Texas Instruments/Burr-Brown ADS7828 6 + Prefix: 'ads7828' 7 + Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4b 8 + Datasheet: Publicly available at the Texas Instruments website : 9 + http://focus.ti.com/lit/ds/symlink/ads7828.pdf 10 + 11 + Authors: 12 + Steve Hardy <steve@linuxrealtime.co.uk> 13 + 14 + Module Parameters 15 + ----------------- 16 + 17 + * se_input: bool (default Y) 18 + Single ended operation - set to N for differential mode 19 + * int_vref: bool (default Y) 20 + Operate with the internal 2.5V reference - set to N for external reference 21 + * vref_mv: int (default 2500) 22 + If using an external reference, set this to the reference voltage in mV 23 + 24 + Description 25 + ----------- 26 + 27 + This driver implements support for the Texas Instruments ADS7828. 28 + 29 + This device is a 12-bit 8-channel A-D converter. 30 + 31 + It can operate in single ended mode (8 +ve inputs) or in differential mode, 32 + where 4 differential pairs can be measured. 33 + 34 + The chip also has the facility to use an external voltage reference. This 35 + may be required if your hardware supplies the ADS7828 from a 5V supply, see 36 + the datasheet for more details.
+10
drivers/hwmon/Kconfig
··· 588 588 This driver can also be built as a module. If so, the module 589 589 will be called smsc47b397. 590 590 591 + config SENSORS_ADS7828 592 + tristate "Texas Instruments ADS7828" 593 + depends on I2C 594 + help 595 + If you say yes here you get support for Texas Instruments ADS7828 596 + 12-bit 8-channel ADC device. 597 + 598 + This driver can also be built as a module. If so, the module 599 + will be called ads7828. 600 + 591 601 config SENSORS_THMC50 592 602 tristate "Texas Instruments THMC50 / Analog Devices ADM1022" 593 603 depends on I2C && EXPERIMENTAL
+1
drivers/hwmon/Makefile
··· 22 22 obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o 23 23 obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o 24 24 obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o 25 + obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o 25 26 obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o 26 27 obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o 27 28 obj-$(CONFIG_SENSORS_AMS) += ams/
+297
drivers/hwmon/ads7828.c
··· 1 + /* 2 + ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC 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 <steve@linuxrealtime.co.uk> 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> 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> 32 + #include <linux/hwmon-sysfs.h> 33 + #include <linux/err.h> 34 + #include <linux/mutex.h> 35 + 36 + /* The ADS7828 registers */ 37 + #define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */ 38 + #define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ 39 + #define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */ 40 + #define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */ 41 + #define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */ 42 + #define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */ 43 + #define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */ 44 + #define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ 45 + 46 + /* Addresses to scan */ 47 + static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 48 + I2C_CLIENT_END }; 49 + 50 + /* Insmod parameters */ 51 + I2C_CLIENT_INSMOD_1(ads7828); 52 + 53 + /* Other module parameters */ 54 + static int se_input = 1; /* Default is SE, 0 == diff */ 55 + static int int_vref = 1; /* Default is internal ref ON */ 56 + static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */ 57 + module_param(se_input, bool, S_IRUGO); 58 + module_param(int_vref, bool, S_IRUGO); 59 + module_param(vref_mv, int, S_IRUGO); 60 + 61 + /* Global Variables */ 62 + static u8 ads7828_cmd_byte; /* cmd byte without channel bits */ 63 + static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */ 64 + 65 + /* Each client has this additional data */ 66 + struct ads7828_data { 67 + struct i2c_client client; 68 + struct device *hwmon_dev; 69 + struct mutex update_lock; /* mutex protect updates */ 70 + char valid; /* !=0 if following fields are valid */ 71 + unsigned long last_updated; /* In jiffies */ 72 + u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */ 73 + }; 74 + 75 + /* Function declaration - necessary due to function dependencies */ 76 + static int ads7828_detect(struct i2c_adapter *adapter, int address, int kind); 77 + 78 + /* The ADS7828 returns the 12-bit sample in two bytes, 79 + these are read as a word then byte-swapped */ 80 + static u16 ads7828_read_value(struct i2c_client *client, u8 reg) 81 + { 82 + return swab16(i2c_smbus_read_word_data(client, reg)); 83 + } 84 + 85 + static inline u8 channel_cmd_byte(int ch) 86 + { 87 + /* cmd byte C2,C1,C0 - see datasheet */ 88 + u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4); 89 + cmd |= ads7828_cmd_byte; 90 + return cmd; 91 + } 92 + 93 + /* Update data for the device (all 8 channels) */ 94 + static struct ads7828_data *ads7828_update_device(struct device *dev) 95 + { 96 + struct i2c_client *client = to_i2c_client(dev); 97 + struct ads7828_data *data = i2c_get_clientdata(client); 98 + 99 + mutex_lock(&data->update_lock); 100 + 101 + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 102 + || !data->valid) { 103 + unsigned int ch; 104 + dev_dbg(&client->dev, "Starting ads7828 update\n"); 105 + 106 + for (ch = 0; ch < ADS7828_NCH; ch++) { 107 + u8 cmd = channel_cmd_byte(ch); 108 + data->adc_input[ch] = ads7828_read_value(client, cmd); 109 + } 110 + data->last_updated = jiffies; 111 + data->valid = 1; 112 + } 113 + 114 + mutex_unlock(&data->update_lock); 115 + 116 + return data; 117 + } 118 + 119 + /* sysfs callback function */ 120 + static ssize_t show_in(struct device *dev, struct device_attribute *da, 121 + char *buf) 122 + { 123 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 124 + struct ads7828_data *data = ads7828_update_device(dev); 125 + /* Print value (in mV as specified in sysfs-interface documentation) */ 126 + return sprintf(buf, "%d\n", (data->adc_input[attr->index] * 127 + ads7828_lsb_resol)/1000); 128 + } 129 + 130 + #define in_reg(offset)\ 131 + static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ 132 + NULL, offset) 133 + 134 + in_reg(0); 135 + in_reg(1); 136 + in_reg(2); 137 + in_reg(3); 138 + in_reg(4); 139 + in_reg(5); 140 + in_reg(6); 141 + in_reg(7); 142 + 143 + static struct attribute *ads7828_attributes[] = { 144 + &sensor_dev_attr_in0_input.dev_attr.attr, 145 + &sensor_dev_attr_in1_input.dev_attr.attr, 146 + &sensor_dev_attr_in2_input.dev_attr.attr, 147 + &sensor_dev_attr_in3_input.dev_attr.attr, 148 + &sensor_dev_attr_in4_input.dev_attr.attr, 149 + &sensor_dev_attr_in5_input.dev_attr.attr, 150 + &sensor_dev_attr_in6_input.dev_attr.attr, 151 + &sensor_dev_attr_in7_input.dev_attr.attr, 152 + NULL 153 + }; 154 + 155 + static const struct attribute_group ads7828_group = { 156 + .attrs = ads7828_attributes, 157 + }; 158 + 159 + static int ads7828_attach_adapter(struct i2c_adapter *adapter) 160 + { 161 + if (!(adapter->class & I2C_CLASS_HWMON)) 162 + return 0; 163 + return i2c_probe(adapter, &addr_data, ads7828_detect); 164 + } 165 + 166 + static int ads7828_detach_client(struct i2c_client *client) 167 + { 168 + struct ads7828_data *data = i2c_get_clientdata(client); 169 + hwmon_device_unregister(data->hwmon_dev); 170 + sysfs_remove_group(&client->dev.kobj, &ads7828_group); 171 + i2c_detach_client(client); 172 + kfree(i2c_get_clientdata(client)); 173 + return 0; 174 + } 175 + 176 + /* This is the driver that will be inserted */ 177 + static struct i2c_driver ads7828_driver = { 178 + .driver = { 179 + .name = "ads7828", 180 + }, 181 + .attach_adapter = ads7828_attach_adapter, 182 + .detach_client = ads7828_detach_client, 183 + }; 184 + 185 + /* This function is called by i2c_probe */ 186 + static int ads7828_detect(struct i2c_adapter *adapter, int address, int kind) 187 + { 188 + struct i2c_client *client; 189 + struct ads7828_data *data; 190 + int err = 0; 191 + const char *name = ""; 192 + 193 + /* Check we have a valid client */ 194 + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) 195 + goto exit; 196 + 197 + /* OK. For now, we presume we have a valid client. We now create the 198 + client structure, even though we cannot fill it completely yet. 199 + But it allows us to access ads7828_read_value. */ 200 + data = kzalloc(sizeof(struct ads7828_data), GFP_KERNEL); 201 + if (!data) { 202 + err = -ENOMEM; 203 + goto exit; 204 + } 205 + 206 + client = &data->client; 207 + i2c_set_clientdata(client, data); 208 + client->addr = address; 209 + client->adapter = adapter; 210 + client->driver = &ads7828_driver; 211 + 212 + /* Now, we do the remaining detection. There is no identification 213 + dedicated register so attempt to sanity check using knowledge of 214 + the chip 215 + - Read from the 8 channel addresses 216 + - Check the top 4 bits of each result are not set (12 data bits) 217 + */ 218 + if (kind < 0) { 219 + int ch; 220 + for (ch = 0; ch < ADS7828_NCH; ch++) { 221 + u16 in_data; 222 + u8 cmd = channel_cmd_byte(ch); 223 + in_data = ads7828_read_value(client, cmd); 224 + if (in_data & 0xF000) { 225 + printk(KERN_DEBUG 226 + "%s : Doesn't look like an ads7828 device\n", 227 + __FUNCTION__); 228 + goto exit_free; 229 + } 230 + } 231 + } 232 + 233 + /* Determine the chip type - only one kind supported! */ 234 + if (kind <= 0) 235 + kind = ads7828; 236 + 237 + if (kind == ads7828) 238 + name = "ads7828"; 239 + 240 + /* Fill in the remaining client fields, put it into the global list */ 241 + strlcpy(client->name, name, I2C_NAME_SIZE); 242 + 243 + mutex_init(&data->update_lock); 244 + 245 + /* Tell the I2C layer a new client has arrived */ 246 + err = i2c_attach_client(client); 247 + if (err) 248 + goto exit_free; 249 + 250 + /* Register sysfs hooks */ 251 + err = sysfs_create_group(&client->dev.kobj, &ads7828_group); 252 + if (err) 253 + goto exit_detach; 254 + 255 + data->hwmon_dev = hwmon_device_register(&client->dev); 256 + if (IS_ERR(data->hwmon_dev)) { 257 + err = PTR_ERR(data->hwmon_dev); 258 + goto exit_remove; 259 + } 260 + 261 + return 0; 262 + 263 + exit_remove: 264 + sysfs_remove_group(&client->dev.kobj, &ads7828_group); 265 + exit_detach: 266 + i2c_detach_client(client); 267 + exit_free: 268 + kfree(data); 269 + exit: 270 + return err; 271 + } 272 + 273 + static int __init sensors_ads7828_init(void) 274 + { 275 + /* Initialize the command byte according to module parameters */ 276 + ads7828_cmd_byte = se_input ? 277 + ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF; 278 + ads7828_cmd_byte |= int_vref ? 279 + ADS7828_CMD_PD3 : ADS7828_CMD_PD1; 280 + 281 + /* Calculate the LSB resolution */ 282 + ads7828_lsb_resol = (vref_mv*1000)/4096; 283 + 284 + return i2c_add_driver(&ads7828_driver); 285 + } 286 + 287 + static void __exit sensors_ads7828_exit(void) 288 + { 289 + i2c_del_driver(&ads7828_driver); 290 + } 291 + 292 + MODULE_AUTHOR("Steve Hardy <steve@linuxrealtime.co.uk>"); 293 + MODULE_DESCRIPTION("ADS7828 driver"); 294 + MODULE_LICENSE("GPL"); 295 + 296 + module_init(sensors_ads7828_init); 297 + module_exit(sensors_ads7828_exit);