Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: add Maxim MAX197 support

The MAX197 is an A/D converter, made by Maxim. This driver currently
supports the MAX197, and MAX199. They are both 8-Channel, Multi-Range,
5V, 12-Bit DAS with 8+4 Bus Interface and Fault Protection.

The available ranges for the MAX197 are {0,-5V} to 5V, and {0,-10V} to
10V, while they are {0,-2V} to 2V, and {0,-4V} to 4V on the MAX199.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Vivien Didelot and committed by
Guenter Roeck
6c1fe725 37f9648b

+440
+60
Documentation/hwmon/max197
··· 1 + Maxim MAX197 driver 2 + =================== 3 + 4 + Author: 5 + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6 + 7 + Supported chips: 8 + * Maxim MAX197 9 + Prefix: 'max197' 10 + Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX197.pdf 11 + 12 + * Maxim MAX199 13 + Prefix: 'max199' 14 + Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX199.pdf 15 + 16 + Description 17 + ----------- 18 + 19 + The A/D converters MAX197, and MAX199 are both 8-Channel, Multi-Range, 5V, 20 + 12-Bit DAS with 8+4 Bus Interface and Fault Protection. 21 + 22 + The available ranges for the MAX197 are {0,-5V} to 5V, and {0,-10V} to 10V, 23 + while they are {0,-2V} to 2V, and {0,-4V} to 4V on the MAX199. 24 + 25 + Platform data 26 + ------------- 27 + 28 + The MAX197 platform data (defined in linux/platform_data/max197.h) should be 29 + filled with a pointer to a conversion function, defined like: 30 + 31 + int convert(u8 ctrl); 32 + 33 + ctrl is the control byte to write to start a new conversion. 34 + On success, the function must return the 12-bit raw value read from the chip, 35 + or a negative error code otherwise. 36 + 37 + Control byte format: 38 + 39 + Bit Name Description 40 + 7,6 PD1,PD0 Clock and Power-Down modes 41 + 5 ACQMOD Internal or External Controlled Acquisition 42 + 4 RNG Full-scale voltage magnitude at the input 43 + 3 BIP Unipolar or Bipolar conversion mode 44 + 2,1,0 A2,A1,A0 Channel 45 + 46 + Sysfs interface 47 + --------------- 48 + 49 + * in[0-7]_input: The conversion value for the corresponding channel. 50 + RO 51 + 52 + * in[0-7]_min: The lower limit (in mV) for the corresponding channel. 53 + For the MAX197, it will be adjusted to -10000, -5000, or 0. 54 + For the MAX199, it will be adjusted to -4000, -2000, or 0. 55 + RW 56 + 57 + * in[0-7]_max: The higher limit (in mV) for the corresponding channel. 58 + For the MAX197, it will be adjusted to 0, 5000, or 10000. 59 + For the MAX199, it will be adjusted to 0, 2000, or 4000. 60 + RW
+9
drivers/hwmon/Kconfig
··· 813 813 This driver can also be built as a module. If so, the module 814 814 will be called max1668. 815 815 816 + config SENSORS_MAX197 817 + tristate "Maxim MAX197 and compatibles" 818 + help 819 + Support for the Maxim MAX197 A/D converter. 820 + Support will include, but not be limited to, MAX197, and MAX199. 821 + 822 + This driver can also be built as a module. If so, the module 823 + will be called max197. 824 + 816 825 config SENSORS_MAX6639 817 826 tristate "Maxim MAX6639 sensor chip" 818 827 depends on I2C && EXPERIMENTAL
+1
drivers/hwmon/Makefile
··· 95 95 obj-$(CONFIG_SENSORS_MAX16065) += max16065.o 96 96 obj-$(CONFIG_SENSORS_MAX1619) += max1619.o 97 97 obj-$(CONFIG_SENSORS_MAX1668) += max1668.o 98 + obj-$(CONFIG_SENSORS_MAX197) += max197.o 98 99 obj-$(CONFIG_SENSORS_MAX6639) += max6639.o 99 100 obj-$(CONFIG_SENSORS_MAX6642) += max6642.o 100 101 obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
+349
drivers/hwmon/max197.c
··· 1 + /* 2 + * Maxim MAX197 A/D Converter driver 3 + * 4 + * Copyright (c) 2012 Savoir-faire Linux Inc. 5 + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * For further information, see the Documentation/hwmon/max197 file. 12 + */ 13 + 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/init.h> 17 + #include <linux/err.h> 18 + #include <linux/slab.h> 19 + #include <linux/mutex.h> 20 + #include <linux/device.h> 21 + #include <linux/sysfs.h> 22 + #include <linux/hwmon.h> 23 + #include <linux/hwmon-sysfs.h> 24 + #include <linux/platform_device.h> 25 + #include <linux/platform_data/max197.h> 26 + 27 + #define MAX199_LIMIT 4000 /* 4V */ 28 + #define MAX197_LIMIT 10000 /* 10V */ 29 + 30 + #define MAX197_NUM_CH 8 /* 8 Analog Input Channels */ 31 + 32 + /* Control byte format */ 33 + #define MAX197_BIP (1 << 3) /* Bipolarity */ 34 + #define MAX197_RNG (1 << 4) /* Full range */ 35 + 36 + #define MAX197_SCALE 12207 /* Scale coefficient for raw data */ 37 + 38 + /* List of supported chips */ 39 + enum max197_chips { max197, max199 }; 40 + 41 + /** 42 + * struct max197_data - device instance specific data 43 + * @pdata: Platform data. 44 + * @hwmon_dev: The hwmon device. 45 + * @lock: Read/Write mutex. 46 + * @limit: Max range value (10V for MAX197, 4V for MAX199). 47 + * @scale: Need to scale. 48 + * @ctrl_bytes: Channels control byte. 49 + */ 50 + struct max197_data { 51 + struct max197_platform_data *pdata; 52 + struct device *hwmon_dev; 53 + struct mutex lock; 54 + int limit; 55 + bool scale; 56 + u8 ctrl_bytes[MAX197_NUM_CH]; 57 + }; 58 + 59 + static inline void max197_set_unipolarity(struct max197_data *data, int channel) 60 + { 61 + data->ctrl_bytes[channel] &= ~MAX197_BIP; 62 + } 63 + 64 + static inline void max197_set_bipolarity(struct max197_data *data, int channel) 65 + { 66 + data->ctrl_bytes[channel] |= MAX197_BIP; 67 + } 68 + 69 + static inline void max197_set_half_range(struct max197_data *data, int channel) 70 + { 71 + data->ctrl_bytes[channel] &= ~MAX197_RNG; 72 + } 73 + 74 + static inline void max197_set_full_range(struct max197_data *data, int channel) 75 + { 76 + data->ctrl_bytes[channel] |= MAX197_RNG; 77 + } 78 + 79 + static inline bool max197_is_bipolar(struct max197_data *data, int channel) 80 + { 81 + return data->ctrl_bytes[channel] & MAX197_BIP; 82 + } 83 + 84 + static inline bool max197_is_full_range(struct max197_data *data, int channel) 85 + { 86 + return data->ctrl_bytes[channel] & MAX197_RNG; 87 + } 88 + 89 + /* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */ 90 + static ssize_t max197_show_range(struct device *dev, 91 + struct device_attribute *devattr, char *buf) 92 + { 93 + struct max197_data *data = dev_get_drvdata(dev); 94 + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 95 + int channel = attr->index; 96 + bool is_min = attr->nr; 97 + int range; 98 + 99 + if (mutex_lock_interruptible(&data->lock)) 100 + return -ERESTARTSYS; 101 + 102 + range = max197_is_full_range(data, channel) ? 103 + data->limit : data->limit / 2; 104 + if (is_min) { 105 + if (max197_is_bipolar(data, channel)) 106 + range = -range; 107 + else 108 + range = 0; 109 + } 110 + 111 + mutex_unlock(&data->lock); 112 + 113 + return sprintf(buf, "%d\n", range); 114 + } 115 + 116 + /* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */ 117 + static ssize_t max197_store_range(struct device *dev, 118 + struct device_attribute *devattr, 119 + const char *buf, size_t count) 120 + { 121 + struct max197_data *data = dev_get_drvdata(dev); 122 + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 123 + int channel = attr->index; 124 + bool is_min = attr->nr; 125 + long value; 126 + int half = data->limit / 2; 127 + int full = data->limit; 128 + 129 + if (kstrtol(buf, 10, &value)) 130 + return -EINVAL; 131 + 132 + if (is_min) { 133 + if (value <= -full) 134 + value = -full; 135 + else if (value < 0) 136 + value = -half; 137 + else 138 + value = 0; 139 + } else { 140 + if (value >= full) 141 + value = full; 142 + else 143 + value = half; 144 + } 145 + 146 + if (mutex_lock_interruptible(&data->lock)) 147 + return -ERESTARTSYS; 148 + 149 + if (value == 0) { 150 + /* We can deduce only the polarity */ 151 + max197_set_unipolarity(data, channel); 152 + } else if (value == -half) { 153 + max197_set_bipolarity(data, channel); 154 + max197_set_half_range(data, channel); 155 + } else if (value == -full) { 156 + max197_set_bipolarity(data, channel); 157 + max197_set_full_range(data, channel); 158 + } else if (value == half) { 159 + /* We can deduce only the range */ 160 + max197_set_half_range(data, channel); 161 + } else if (value == full) { 162 + /* We can deduce only the range */ 163 + max197_set_full_range(data, channel); 164 + } 165 + 166 + mutex_unlock(&data->lock); 167 + 168 + return count; 169 + } 170 + 171 + /* Function called on read access on in{0,1,2,3,4,5,6,7}_input */ 172 + static ssize_t max197_show_input(struct device *dev, 173 + struct device_attribute *devattr, 174 + char *buf) 175 + { 176 + struct max197_data *data = dev_get_drvdata(dev); 177 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 178 + int channel = attr->index; 179 + s32 value; 180 + int ret; 181 + 182 + if (mutex_lock_interruptible(&data->lock)) 183 + return -ERESTARTSYS; 184 + 185 + ret = data->pdata->convert(data->ctrl_bytes[channel]); 186 + if (ret < 0) { 187 + dev_err(dev, "conversion failed\n"); 188 + goto unlock; 189 + } 190 + value = ret; 191 + 192 + /* 193 + * Coefficient to apply on raw value. 194 + * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet. 195 + */ 196 + if (data->scale) { 197 + value *= MAX197_SCALE; 198 + if (max197_is_full_range(data, channel)) 199 + value *= 2; 200 + value /= 10000; 201 + } 202 + 203 + ret = sprintf(buf, "%d\n", value); 204 + 205 + unlock: 206 + mutex_unlock(&data->lock); 207 + return ret; 208 + } 209 + 210 + static ssize_t max197_show_name(struct device *dev, 211 + struct device_attribute *attr, char *buf) 212 + { 213 + struct platform_device *pdev = to_platform_device(dev); 214 + return sprintf(buf, "%s\n", pdev->name); 215 + } 216 + 217 + #define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \ 218 + static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \ 219 + max197_show_input, NULL, chan); \ 220 + static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \ 221 + max197_show_range, \ 222 + max197_store_range, \ 223 + true, chan); \ 224 + static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \ 225 + max197_show_range, \ 226 + max197_store_range, \ 227 + false, chan) 228 + 229 + #define MAX197_SENSOR_DEV_ATTR_IN(chan) \ 230 + &sensor_dev_attr_in##chan##_input.dev_attr.attr, \ 231 + &sensor_dev_attr_in##chan##_max.dev_attr.attr, \ 232 + &sensor_dev_attr_in##chan##_min.dev_attr.attr 233 + 234 + static DEVICE_ATTR(name, S_IRUGO, max197_show_name, NULL); 235 + 236 + MAX197_SENSOR_DEVICE_ATTR_CH(0); 237 + MAX197_SENSOR_DEVICE_ATTR_CH(1); 238 + MAX197_SENSOR_DEVICE_ATTR_CH(2); 239 + MAX197_SENSOR_DEVICE_ATTR_CH(3); 240 + MAX197_SENSOR_DEVICE_ATTR_CH(4); 241 + MAX197_SENSOR_DEVICE_ATTR_CH(5); 242 + MAX197_SENSOR_DEVICE_ATTR_CH(6); 243 + MAX197_SENSOR_DEVICE_ATTR_CH(7); 244 + 245 + static const struct attribute_group max197_sysfs_group = { 246 + .attrs = (struct attribute *[]) { 247 + &dev_attr_name.attr, 248 + MAX197_SENSOR_DEV_ATTR_IN(0), 249 + MAX197_SENSOR_DEV_ATTR_IN(1), 250 + MAX197_SENSOR_DEV_ATTR_IN(2), 251 + MAX197_SENSOR_DEV_ATTR_IN(3), 252 + MAX197_SENSOR_DEV_ATTR_IN(4), 253 + MAX197_SENSOR_DEV_ATTR_IN(5), 254 + MAX197_SENSOR_DEV_ATTR_IN(6), 255 + MAX197_SENSOR_DEV_ATTR_IN(7), 256 + NULL 257 + }, 258 + }; 259 + 260 + static int __devinit max197_probe(struct platform_device *pdev) 261 + { 262 + int ch, ret; 263 + struct max197_data *data; 264 + struct max197_platform_data *pdata = pdev->dev.platform_data; 265 + enum max197_chips chip = platform_get_device_id(pdev)->driver_data; 266 + 267 + if (pdata == NULL) { 268 + dev_err(&pdev->dev, "no platform data supplied\n"); 269 + return -EINVAL; 270 + } 271 + 272 + if (pdata->convert == NULL) { 273 + dev_err(&pdev->dev, "no convert function supplied\n"); 274 + return -EINVAL; 275 + } 276 + 277 + data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL); 278 + if (!data) { 279 + dev_err(&pdev->dev, "devm_kzalloc failed\n"); 280 + return -ENOMEM; 281 + } 282 + 283 + data->pdata = pdata; 284 + mutex_init(&data->lock); 285 + 286 + if (chip == max197) { 287 + data->limit = MAX197_LIMIT; 288 + data->scale = true; 289 + } else { 290 + data->limit = MAX199_LIMIT; 291 + data->scale = false; 292 + } 293 + 294 + for (ch = 0; ch < MAX197_NUM_CH; ch++) 295 + data->ctrl_bytes[ch] = (u8) ch; 296 + 297 + platform_set_drvdata(pdev, data); 298 + 299 + ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group); 300 + if (ret) { 301 + dev_err(&pdev->dev, "sysfs create group failed\n"); 302 + return ret; 303 + } 304 + 305 + data->hwmon_dev = hwmon_device_register(&pdev->dev); 306 + if (IS_ERR(data->hwmon_dev)) { 307 + ret = PTR_ERR(data->hwmon_dev); 308 + dev_err(&pdev->dev, "hwmon device register failed\n"); 309 + goto error; 310 + } 311 + 312 + return 0; 313 + 314 + error: 315 + sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 316 + return ret; 317 + } 318 + 319 + static int __devexit max197_remove(struct platform_device *pdev) 320 + { 321 + struct max197_data *data = platform_get_drvdata(pdev); 322 + 323 + hwmon_device_unregister(data->hwmon_dev); 324 + sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); 325 + 326 + return 0; 327 + } 328 + 329 + static struct platform_device_id max197_device_ids[] = { 330 + { "max197", max197 }, 331 + { "max199", max199 }, 332 + { } 333 + }; 334 + MODULE_DEVICE_TABLE(platform, max197_device_ids); 335 + 336 + static struct platform_driver max197_driver = { 337 + .driver = { 338 + .name = "max197", 339 + .owner = THIS_MODULE, 340 + }, 341 + .probe = max197_probe, 342 + .remove = __devexit_p(max197_remove), 343 + .id_table = max197_device_ids, 344 + }; 345 + module_platform_driver(max197_driver); 346 + 347 + MODULE_LICENSE("GPL"); 348 + MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); 349 + MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver");
+21
include/linux/platform_data/max197.h
··· 1 + /* 2 + * Maxim MAX197 A/D Converter Driver 3 + * 4 + * Copyright (c) 2012 Savoir-faire Linux Inc. 5 + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + * For further information, see the Documentation/hwmon/max197 file. 12 + */ 13 + 14 + /** 15 + * struct max197_platform_data - MAX197 connectivity info 16 + * @convert: Function used to start a conversion with control byte ctrl. 17 + * It must return the raw data, or a negative error code. 18 + */ 19 + struct max197_platform_data { 20 + int (*convert)(u8 ctrl); 21 + };