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 <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/*
  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 show_out0_ouput(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 set_out0_output(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(out0_output, S_IWUSR | S_IRUGO,
 136                   show_out0_ouput, set_out0_output);
 137
 138static ssize_t show_out0_enable(struct device *dev,
 139                                struct device_attribute *attr, char *buf)
 140{
 141        struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
 142        return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
 143}
 144
 145static ssize_t set_out0_enable(struct device *dev,
 146                               struct device_attribute *attr,
 147                               const char *buf, size_t count)
 148{
 149        struct i2c_client *client = to_i2c_client(dev);
 150        struct pcf8591_data *data = i2c_get_clientdata(client);
 151        unsigned long val;
 152        int err;
 153
 154        err = kstrtoul(buf, 10, &val);
 155        if (err)
 156                return err;
 157
 158        mutex_lock(&data->update_lock);
 159        if (val)
 160                data->control |= PCF8591_CONTROL_AOEF;
 161        else
 162                data->control &= ~PCF8591_CONTROL_AOEF;
 163        i2c_smbus_write_byte(client, data->control);
 164        mutex_unlock(&data->update_lock);
 165        return count;
 166}
 167
 168static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
 169                   show_out0_enable, set_out0_enable);
 170
 171static struct attribute *pcf8591_attributes[] = {
 172        &dev_attr_out0_enable.attr,
 173        &dev_attr_out0_output.attr,
 174        &dev_attr_in0_input.attr,
 175        &dev_attr_in1_input.attr,
 176        NULL
 177};
 178
 179static const struct attribute_group pcf8591_attr_group = {
 180        .attrs = pcf8591_attributes,
 181};
 182
 183static struct attribute *pcf8591_attributes_opt[] = {
 184        &dev_attr_in2_input.attr,
 185        &dev_attr_in3_input.attr,
 186        NULL
 187};
 188
 189static const struct attribute_group pcf8591_attr_group_opt = {
 190        .attrs = pcf8591_attributes_opt,
 191};
 192
 193/*
 194 * Real code
 195 */
 196
 197static int pcf8591_probe(struct i2c_client *client,
 198                         const struct i2c_device_id *id)
 199{
 200        struct pcf8591_data *data;
 201        int err;
 202
 203        data = devm_kzalloc(&client->dev, sizeof(struct pcf8591_data),
 204                            GFP_KERNEL);
 205        if (!data)
 206                return -ENOMEM;
 207
 208        i2c_set_clientdata(client, data);
 209        mutex_init(&data->update_lock);
 210
 211        /* Initialize the PCF8591 chip */
 212        pcf8591_init_client(client);
 213
 214        /* Register sysfs hooks */
 215        err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group);
 216        if (err)
 217                return err;
 218
 219        /* Register input2 if not in "two differential inputs" mode */
 220        if (input_mode != 3) {
 221                err = device_create_file(&client->dev, &dev_attr_in2_input);
 222                if (err)
 223                        goto exit_sysfs_remove;
 224        }
 225
 226        /* Register input3 only in "four single ended inputs" mode */
 227        if (input_mode == 0) {
 228                err = device_create_file(&client->dev, &dev_attr_in3_input);
 229                if (err)
 230                        goto exit_sysfs_remove;
 231        }
 232
 233        data->hwmon_dev = hwmon_device_register(&client->dev);
 234        if (IS_ERR(data->hwmon_dev)) {
 235                err = PTR_ERR(data->hwmon_dev);
 236                goto exit_sysfs_remove;
 237        }
 238
 239        return 0;
 240
 241exit_sysfs_remove:
 242        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
 243        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
 244        return err;
 245}
 246
 247static int pcf8591_remove(struct i2c_client *client)
 248{
 249        struct pcf8591_data *data = i2c_get_clientdata(client);
 250
 251        hwmon_device_unregister(data->hwmon_dev);
 252        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
 253        sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
 254        return 0;
 255}
 256
 257/* Called when we have found a new PCF8591. */
 258static void pcf8591_init_client(struct i2c_client *client)
 259{
 260        struct pcf8591_data *data = i2c_get_clientdata(client);
 261        data->control = PCF8591_INIT_CONTROL;
 262        data->aout = PCF8591_INIT_AOUT;
 263
 264        i2c_smbus_write_byte_data(client, data->control, data->aout);
 265
 266        /*
 267         * The first byte transmitted contains the conversion code of the
 268         * previous read cycle. FLUSH IT!
 269         */
 270        i2c_smbus_read_byte(client);
 271}
 272
 273static int pcf8591_read_channel(struct device *dev, int channel)
 274{
 275        u8 value;
 276        struct i2c_client *client = to_i2c_client(dev);
 277        struct pcf8591_data *data = i2c_get_clientdata(client);
 278
 279        mutex_lock(&data->update_lock);
 280
 281        if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) {
 282                data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
 283                              | channel;
 284                i2c_smbus_write_byte(client, data->control);
 285
 286                /*
 287                 * The first byte transmitted contains the conversion code of
 288                 * the previous read cycle. FLUSH IT!
 289                 */
 290                i2c_smbus_read_byte(client);
 291        }
 292        value = i2c_smbus_read_byte(client);
 293
 294        mutex_unlock(&data->update_lock);
 295
 296        if ((channel == 2 && input_mode == 2) ||
 297            (channel != 3 && (input_mode == 1 || input_mode == 3)))
 298                return 10 * REG_TO_SIGNED(value);
 299        else
 300                return 10 * value;
 301}
 302
 303static const struct i2c_device_id pcf8591_id[] = {
 304        { "pcf8591", 0 },
 305        { }
 306};
 307MODULE_DEVICE_TABLE(i2c, pcf8591_id);
 308
 309static struct i2c_driver pcf8591_driver = {
 310        .driver = {
 311                .name   = "pcf8591",
 312        },
 313        .probe          = pcf8591_probe,
 314        .remove         = pcf8591_remove,
 315        .id_table       = pcf8591_id,
 316};
 317
 318static int __init pcf8591_init(void)
 319{
 320        if (input_mode < 0 || input_mode > 3) {
 321                pr_warn("invalid input_mode (%d)\n", input_mode);
 322                input_mode = 0;
 323        }
 324        return i2c_add_driver(&pcf8591_driver);
 325}
 326
 327static void __exit pcf8591_exit(void)
 328{
 329        i2c_del_driver(&pcf8591_driver);
 330}
 331
 332MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
 333MODULE_DESCRIPTION("PCF8591 driver");
 334MODULE_LICENSE("GPL");
 335
 336module_init(pcf8591_init);
 337module_exit(pcf8591_exit);
 338
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.