linux/drivers/hwmon/pcf8591.c
<<
>>
Prefs
   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 <jdelvare@suse.de>
   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/*
  43 * The PCF8591 control byte
  44 *      7    6    5    4    3    2    1    0
  45 *   |  0 |AOEF|   AIP   |  0 |AINC|  AICH   |
  46 */
  47
  48/* Analog Output Enable Flag (analog output active if 1) */
  49#define PCF8591_CONTROL_AOEF            0x40
  50
  51/*
  52 * Analog Input Programming
  53 * 0x00 = four single ended inputs
  54 * 0x10 = three differential inputs
  55 * 0x20 = single ended and differential mixed
  56 * 0x30 = two differential inputs
  57 */
  58#define PCF8591_CONTROL_AIP_MASK        0x30
  59
  60/* Autoincrement Flag (switch on if 1) */
  61#define PCF8591_CONTROL_AINC            0x04
  62
  63/*
  64 * Channel selection
  65 * 0x00 = channel 0
  66 * 0x01 = channel 1
  67 * 0x02 = channel 2
  68 * 0x03 = channel 3
  69 */
  70#define PCF8591_CONTROL_AICH_MASK       0x03
  71
  72/* Initial values */
  73#define PCF8591_INIT_CONTROL    ((input_mode << 4) | PCF8591_CONTROL_AOEF)
  74#define PCF8591_INIT_AOUT       0       /* DAC out = 0 */
  75
  76/* Conversions */
  77#define REG_TO_SIGNED(reg)      (((reg) & 0x80) ? ((reg) - 256) : (reg))
  78
  79struct pcf8591_data {
  80        struct device *hwmon_dev;
  81        struct mutex update_lock;
  82
  83        u8 control;
  84        u8 aout;
  85};
  86
  87static void pcf8591_init_client(struct i2c_client *client);
  88static int pcf8591_read_channel(struct device *dev, int channel);
  89
  90/* following are the sysfs callback functions */
  91#define show_in_channel(channel)                                        \
  92static ssize_t show_in##channel##_input(struct device *dev,             \
  93                                        struct device_attribute *attr,  \
  94                                        char *buf)                      \
  95{                                                                       \
  96        return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\
  97}                                                                       \
  98static DEVICE_ATTR(in##channel##_input, S_IRUGO,                        \
  99                   show_in##channel##_input, NULL);
 100
 101show_in_channel(0);
 102show_in_channel(1);
 103show_in_channel(2);
 104show_in_channel(3);
 105
 106static ssize_t out0_output_show(struct device *dev,
 107                                struct device_attribute *attr, char *buf)
 108{
 109        struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
 110        return sprintf(buf, "%d\n", data->aout * 10);
 111}
 112
 113static ssize_t out0_output_store(struct device *dev,
 114                                 struct device_attribute *attr,
 115                                 const char *buf, size_t count)
 116{
 117        unsigned long val;
 118        struct i2c_client *client = to_i2c_client(dev);
 119        struct pcf8591_data *data = i2c_get_clientdata(client);
 120        int err;
 121
 122        err = kstrtoul(buf, 10, &val);
 123        if (err)
 124                return err;
 125
 126        val /= 10;
 127        if (val > 255)
 128                return -EINVAL;
 129
 130        data->aout = val;
 131        i2c_smbus_write_byte_data(client, data->control, data->aout);
 132        return count;
 133}
 134
 135static DEVICE_ATTR_RW(out0_output);
 136
 137static ssize_t out0_enable_show(struct device *dev,
 138                                struct device_attribute *attr, char *buf)
 139{
 140        struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
 141        return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
 142}
 143
 144static ssize_t out0_enable_store(struct device *dev,
 145                                 struct device_attribute *attr,
 146                                 const char *buf, size_t count)
 147{
 148        struct i2c_client *client = to_i2c_client(dev);
 149        struct pcf8591_data *data = i2c_get_clientdata(client);
 150        unsigned long val;
 151        int err;
 152
 153        err = kstrtoul(buf, 10, &val);
 154        if (err)
 155                return err;
 156
 157        mutex_lock(&data->update_lock);
 158        if (val)
 159                data->control |= PCF8591_CONTROL_AOEF;
 160        else
 161                data->control &= ~PCF8591_CONTROL_AOEF;
 162        i2c_smbus_write_byte(client, data->control);
 163        mutex_unlock(&data->update_lock);
 164        return count;
 165}
 166
 167static DEVICE_ATTR_RW(out0_enable);
 168
 169static struct attribute *pcf8591_attributes[] = {
 170        &dev_attr_out0_enable.attr,
 171        &dev_attr_out0_output.attr,
 172        &dev_attr_in0_input.attr,
 173        &dev_attr_in1_input.attr,
 174        NULL
 175};
 176
 177static const struct attribute_group pcf8591_attr_group = {
 178        .attrs = pcf8591_attributes,
 179};
 180
 181static struct attribute *pcf8591_attributes_opt[] = {
 182        &dev_attr_in2_input.attr,
 183        &dev_attr_in3_input.attr,
 184        NULL
 185};
 186
 187static const struct attribute_group pcf8591_attr_group_opt = {
 188        .attrs = pcf8591_attributes_opt,
 189};
 190
 191/*
 192 * Real code
 193 */
 194
 195static int pcf8591_probe(struct i2c_client *client,
 196                         const struct i2c_device_id *id)
 197{
 198        struct pcf8591_data *data;
 199        int err;
 200
 201        data = devm_kzalloc(&client->dev, sizeof(struct pcf8591_data),
 202                            GFP_KERNEL);
 203        if (!data)
 204                return -ENOMEM;
 205
 206        i2c_set_clientdata(client, data);
 207        mutex_init(&data->update_lock);
 208
 209        /* Initialize the PCF8591 chip */
 210        pcf8591_init_client(client);
 211
 212        /* Register sysfs hooks */
 213        err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group);
 214        if (err)
 215                return err;
 216
 217        /* Register input2 if not in "two differential inputs" mode */
 218        if (input_mode != 3) {
 219                err = device_create_file(&client->dev, &dev_attr_in2_input);
 220                if (err)
 221                        goto exit_sysfs_remove;
 222        }
 223
 224        /* Register input3 only in "four single ended inputs" mode */
 225        if (input_mode == 0) {
 226                err = device_create_file(&client->dev, &dev_attr_in3_input);
 227                if (err)
 228                        goto exit_sysfs_remove;
 229        }
 230
 231        data->hwmon_dev = hwmon_device_register(&client->dev);
 232        if (IS_ERR(data->hwmon_dev)) {
 233                err = PTR_ERR(data->hwmon_dev);
 234                goto exit_sysfs_remove;
 235        }
 236
 237        return 0;
 238
 239exit_sysfs_remove:
 240        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
 241        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
 242        return err;
 243}
 244
 245static int pcf8591_remove(struct i2c_client *client)
 246{
 247        struct pcf8591_data *data = i2c_get_clientdata(client);
 248
 249        hwmon_device_unregister(data->hwmon_dev);
 250        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
 251        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
 252        return 0;
 253}
 254
 255/* Called when we have found a new PCF8591. */
 256static void pcf8591_init_client(struct i2c_client *client)
 257{
 258        struct pcf8591_data *data = i2c_get_clientdata(client);
 259        data->control = PCF8591_INIT_CONTROL;
 260        data->aout = PCF8591_INIT_AOUT;
 261
 262        i2c_smbus_write_byte_data(client, data->control, data->aout);
 263
 264        /*
 265         * The first byte transmitted contains the conversion code of the
 266         * previous read cycle. FLUSH IT!
 267         */
 268        i2c_smbus_read_byte(client);
 269}
 270
 271static int pcf8591_read_channel(struct device *dev, int channel)
 272{
 273        u8 value;
 274        struct i2c_client *client = to_i2c_client(dev);
 275        struct pcf8591_data *data = i2c_get_clientdata(client);
 276
 277        mutex_lock(&data->update_lock);
 278
 279        if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) {
 280                data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
 281                              | channel;
 282                i2c_smbus_write_byte(client, data->control);
 283
 284                /*
 285                 * The first byte transmitted contains the conversion code of
 286                 * the previous read cycle. FLUSH IT!
 287                 */
 288                i2c_smbus_read_byte(client);
 289        }
 290        value = i2c_smbus_read_byte(client);
 291
 292        mutex_unlock(&data->update_lock);
 293
 294        if ((channel == 2 && input_mode == 2) ||
 295            (channel != 3 && (input_mode == 1 || input_mode == 3)))
 296                return 10 * REG_TO_SIGNED(value);
 297        else
 298                return 10 * value;
 299}
 300
 301static const struct i2c_device_id pcf8591_id[] = {
 302        { "pcf8591", 0 },
 303        { }
 304};
 305MODULE_DEVICE_TABLE(i2c, pcf8591_id);
 306
 307static struct i2c_driver pcf8591_driver = {
 308        .driver = {
 309                .name   = "pcf8591",
 310        },
 311        .probe          = pcf8591_probe,
 312        .remove         = pcf8591_remove,
 313        .id_table       = pcf8591_id,
 314};
 315
 316static int __init pcf8591_init(void)
 317{
 318        if (input_mode < 0 || input_mode > 3) {
 319                pr_warn("invalid input_mode (%d)\n", input_mode);
 320                input_mode = 0;
 321        }
 322        return i2c_add_driver(&pcf8591_driver);
 323}
 324
 325static void __exit pcf8591_exit(void)
 326{
 327        i2c_del_driver(&pcf8591_driver);
 328}
 329
 330MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
 331MODULE_DESCRIPTION("PCF8591 driver");
 332MODULE_LICENSE("GPL");
 333
 334module_init(pcf8591_init);
 335module_exit(pcf8591_exit);
 336