/drivers/hwmon/pcf8591.c
C | 311 lines | 206 code | 55 blank | 50 comment | 24 complexity | cadf276d5692c918d157ea5d1d59b4e8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* 2 Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> 3 Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with 4 the help of Jean Delvare <khali@linux-fr.org> 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23#include <linux/module.h> 24#include <linux/init.h> 25#include <linux/slab.h> 26#include <linux/i2c.h> 27#include <linux/mutex.h> 28#include <linux/err.h> 29#include <linux/hwmon.h> 30 31/* Insmod parameters */ 32 33static int input_mode; 34module_param(input_mode, int, 0); 35MODULE_PARM_DESC(input_mode, 36 "Analog input mode:\n" 37 " 0 = four single ended inputs\n" 38 " 1 = three differential inputs\n" 39 " 2 = single ended and differential mixed\n" 40 " 3 = two differential inputs\n"); 41 42/* The PCF8591 control byte 43 7 6 5 4 3 2 1 0 44 | 0 |AOEF| AIP | 0 |AINC| AICH | */ 45 46/* Analog Output Enable Flag (analog output active if 1) */ 47#define PCF8591_CONTROL_AOEF 0x40 48 49/* Analog Input Programming 50 0x00 = four single ended inputs 51 0x10 = three differential inputs 52 0x20 = single ended and differential mixed 53 0x30 = two differential inputs */ 54#define PCF8591_CONTROL_AIP_MASK 0x30 55 56/* Autoincrement Flag (switch on if 1) */ 57#define PCF8591_CONTROL_AINC 0x04 58 59/* Channel selection 60 0x00 = channel 0 61 0x01 = channel 1 62 0x02 = channel 2 63 0x03 = channel 3 */ 64#define PCF8591_CONTROL_AICH_MASK 0x03 65 66/* Initial values */ 67#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) 68#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ 69 70/* Conversions */ 71#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) 72 73struct pcf8591_data { 74 struct device *hwmon_dev; 75 struct mutex update_lock; 76 77 u8 control; 78 u8 aout; 79}; 80 81static void pcf8591_init_client(struct i2c_client *client); 82static int pcf8591_read_channel(struct device *dev, int channel); 83 84/* following are the sysfs callback functions */ 85#define show_in_channel(channel) \ 86static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ 87{ \ 88 return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ 89} \ 90static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ 91 show_in##channel##_input, NULL); 92 93show_in_channel(0); 94show_in_channel(1); 95show_in_channel(2); 96show_in_channel(3); 97 98static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) 99{ 100 struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); 101 return sprintf(buf, "%d\n", data->aout * 10); 102} 103 104static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 105{ 106 unsigned int value; 107 struct i2c_client *client = to_i2c_client(dev); 108 struct pcf8591_data *data = i2c_get_clientdata(client); 109 if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { 110 data->aout = value; 111 i2c_smbus_write_byte_data(client, data->control, data->aout); 112 return count; 113 } 114 return -EINVAL; 115} 116 117static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, 118 show_out0_ouput, set_out0_output); 119 120static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) 121{ 122 struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); 123 return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); 124} 125 126static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 127{ 128 struct i2c_client *client = to_i2c_client(dev); 129 struct pcf8591_data *data = i2c_get_clientdata(client); 130 unsigned long val = simple_strtoul(buf, NULL, 10); 131 132 mutex_lock(&data->update_lock); 133 if (val) 134 data->control |= PCF8591_CONTROL_AOEF; 135 else 136 data->control &= ~PCF8591_CONTROL_AOEF; 137 i2c_smbus_write_byte(client, data->control); 138 mutex_unlock(&data->update_lock); 139 return count; 140} 141 142static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, 143 show_out0_enable, set_out0_enable); 144 145static struct attribute *pcf8591_attributes[] = { 146 &dev_attr_out0_enable.attr, 147 &dev_attr_out0_output.attr, 148 &dev_attr_in0_input.attr, 149 &dev_attr_in1_input.attr, 150 NULL 151}; 152 153static const struct attribute_group pcf8591_attr_group = { 154 .attrs = pcf8591_attributes, 155}; 156 157static struct attribute *pcf8591_attributes_opt[] = { 158 &dev_attr_in2_input.attr, 159 &dev_attr_in3_input.attr, 160 NULL 161}; 162 163static const struct attribute_group pcf8591_attr_group_opt = { 164 .attrs = pcf8591_attributes_opt, 165}; 166 167/* 168 * Real code 169 */ 170 171static int pcf8591_probe(struct i2c_client *client, 172 const struct i2c_device_id *id) 173{ 174 struct pcf8591_data *data; 175 int err; 176 177 if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { 178 err = -ENOMEM; 179 goto exit; 180 } 181 182 i2c_set_clientdata(client, data); 183 mutex_init(&data->update_lock); 184 185 /* Initialize the PCF8591 chip */ 186 pcf8591_init_client(client); 187 188 /* Register sysfs hooks */ 189 err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); 190 if (err) 191 goto exit_kfree; 192 193 /* Register input2 if not in "two differential inputs" mode */ 194 if (input_mode != 3) { 195 if ((err = device_create_file(&client->dev, 196 &dev_attr_in2_input))) 197 goto exit_sysfs_remove; 198 } 199 200 /* Register input3 only in "four single ended inputs" mode */ 201 if (input_mode == 0) { 202 if ((err = device_create_file(&client->dev, 203 &dev_attr_in3_input))) 204 goto exit_sysfs_remove; 205 } 206 207 data->hwmon_dev = hwmon_device_register(&client->dev); 208 if (IS_ERR(data->hwmon_dev)) { 209 err = PTR_ERR(data->hwmon_dev); 210 goto exit_sysfs_remove; 211 } 212 213 return 0; 214 215exit_sysfs_remove: 216 sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); 217 sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); 218exit_kfree: 219 kfree(data); 220exit: 221 return err; 222} 223 224static int pcf8591_remove(struct i2c_client *client) 225{ 226 struct pcf8591_data *data = i2c_get_clientdata(client); 227 228 hwmon_device_unregister(data->hwmon_dev); 229 sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); 230 sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); 231 kfree(i2c_get_clientdata(client)); 232 return 0; 233} 234 235/* Called when we have found a new PCF8591. */ 236static void pcf8591_init_client(struct i2c_client *client) 237{ 238 struct pcf8591_data *data = i2c_get_clientdata(client); 239 data->control = PCF8591_INIT_CONTROL; 240 data->aout = PCF8591_INIT_AOUT; 241 242 i2c_smbus_write_byte_data(client, data->control, data->aout); 243 244 /* The first byte transmitted contains the conversion code of the 245 previous read cycle. FLUSH IT! */ 246 i2c_smbus_read_byte(client); 247} 248 249static int pcf8591_read_channel(struct device *dev, int channel) 250{ 251 u8 value; 252 struct i2c_client *client = to_i2c_client(dev); 253 struct pcf8591_data *data = i2c_get_clientdata(client); 254 255 mutex_lock(&data->update_lock); 256 257 if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { 258 data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) 259 | channel; 260 i2c_smbus_write_byte(client, data->control); 261 262 /* The first byte transmitted contains the conversion code of 263 the previous read cycle. FLUSH IT! */ 264 i2c_smbus_read_byte(client); 265 } 266 value = i2c_smbus_read_byte(client); 267 268 mutex_unlock(&data->update_lock); 269 270 if ((channel == 2 && input_mode == 2) || 271 (channel != 3 && (input_mode == 1 || input_mode == 3))) 272 return (10 * REG_TO_SIGNED(value)); 273 else 274 return (10 * value); 275} 276 277static const struct i2c_device_id pcf8591_id[] = { 278 { "pcf8591", 0 }, 279 { } 280}; 281MODULE_DEVICE_TABLE(i2c, pcf8591_id); 282 283static struct i2c_driver pcf8591_driver = { 284 .driver = { 285 .name = "pcf8591", 286 }, 287 .probe = pcf8591_probe, 288 .remove = pcf8591_remove, 289 .id_table = pcf8591_id, 290}; 291 292static int __init pcf8591_init(void) 293{ 294 if (input_mode < 0 || input_mode > 3) { 295 pr_warn("invalid input_mode (%d)\n", input_mode); 296 input_mode = 0; 297 } 298 return i2c_add_driver(&pcf8591_driver); 299} 300 301static void __exit pcf8591_exit(void) 302{ 303 i2c_del_driver(&pcf8591_driver); 304} 305 306MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); 307MODULE_DESCRIPTION("PCF8591 driver"); 308MODULE_LICENSE("GPL"); 309 310module_init(pcf8591_init); 311module_exit(pcf8591_exit);