linux/drivers/hwmon/mcp3021.c
<<
>>
Prefs
   1/*
   2 * mcp3021.c - driver for Microchip MCP3021 and MCP3221
   3 *
   4 * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
   5 * Author: Mingkai Hu <Mingkai.hu@freescale.com>
   6 * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
   7 *
   8 * This driver export the value of analog input voltage to sysfs, the
   9 * voltage unit is mV. Through the sysfs interface, lm-sensors tool
  10 * can also display the input voltage.
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License as published by
  14 * the Free Software Foundation; either version 2 of the License, or
  15 * (at your option) any later version.
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/hwmon.h>
  21#include <linux/slab.h>
  22#include <linux/i2c.h>
  23#include <linux/err.h>
  24#include <linux/device.h>
  25
  26/* Vdd info */
  27#define MCP3021_VDD_MAX         5500
  28#define MCP3021_VDD_MIN         2700
  29#define MCP3021_VDD_REF         3300
  30
  31/* output format */
  32#define MCP3021_SAR_SHIFT       2
  33#define MCP3021_SAR_MASK        0x3ff
  34#define MCP3021_OUTPUT_RES      10      /* 10-bit resolution */
  35
  36#define MCP3221_SAR_SHIFT       0
  37#define MCP3221_SAR_MASK        0xfff
  38#define MCP3221_OUTPUT_RES      12      /* 12-bit resolution */
  39
  40enum chips {
  41        mcp3021,
  42        mcp3221
  43};
  44
  45/*
  46 * Client data (each client gets its own)
  47 */
  48struct mcp3021_data {
  49        struct device *hwmon_dev;
  50        u32 vdd;        /* device power supply */
  51        u16 sar_shift;
  52        u16 sar_mask;
  53        u8 output_res;
  54};
  55
  56static int mcp3021_read16(struct i2c_client *client)
  57{
  58        struct mcp3021_data *data = i2c_get_clientdata(client);
  59        int ret;
  60        u16 reg;
  61        __be16 buf;
  62
  63        ret = i2c_master_recv(client, (char *)&buf, 2);
  64        if (ret < 0)
  65                return ret;
  66        if (ret != 2)
  67                return -EIO;
  68
  69        /* The output code of the MCP3021 is transmitted with MSB first. */
  70        reg = be16_to_cpu(buf);
  71
  72        /*
  73         * The ten-bit output code is composed of the lower 4-bit of the
  74         * first byte and the upper 6-bit of the second byte.
  75         */
  76        reg = (reg >> data->sar_shift) & data->sar_mask;
  77
  78        return reg;
  79}
  80
  81static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
  82{
  83        return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
  84}
  85
  86static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
  87                char *buf)
  88{
  89        struct i2c_client *client = to_i2c_client(dev);
  90        struct mcp3021_data *data = i2c_get_clientdata(client);
  91        int reg, in_input;
  92
  93        reg = mcp3021_read16(client);
  94        if (reg < 0)
  95                return reg;
  96
  97        in_input = volts_from_reg(data, reg);
  98
  99        return sprintf(buf, "%d\n", in_input);
 100}
 101
 102static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL);
 103
 104static int mcp3021_probe(struct i2c_client *client,
 105                                const struct i2c_device_id *id)
 106{
 107        int err;
 108        struct mcp3021_data *data = NULL;
 109
 110        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 111                return -ENODEV;
 112
 113        data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data),
 114                            GFP_KERNEL);
 115        if (!data)
 116                return -ENOMEM;
 117
 118        i2c_set_clientdata(client, data);
 119
 120        switch (id->driver_data) {
 121        case mcp3021:
 122                data->sar_shift = MCP3021_SAR_SHIFT;
 123                data->sar_mask = MCP3021_SAR_MASK;
 124                data->output_res = MCP3021_OUTPUT_RES;
 125                break;
 126
 127        case mcp3221:
 128                data->sar_shift = MCP3221_SAR_SHIFT;
 129                data->sar_mask = MCP3221_SAR_MASK;
 130                data->output_res = MCP3221_OUTPUT_RES;
 131                break;
 132        }
 133
 134        if (dev_get_platdata(&client->dev)) {
 135                data->vdd = *(u32 *)dev_get_platdata(&client->dev);
 136                if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
 137                        return -EINVAL;
 138        } else {
 139                data->vdd = MCP3021_VDD_REF;
 140        }
 141
 142        err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 143        if (err)
 144                return err;
 145
 146        data->hwmon_dev = hwmon_device_register(&client->dev);
 147        if (IS_ERR(data->hwmon_dev)) {
 148                err = PTR_ERR(data->hwmon_dev);
 149                goto exit_remove;
 150        }
 151
 152        return 0;
 153
 154exit_remove:
 155        sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 156        return err;
 157}
 158
 159static int mcp3021_remove(struct i2c_client *client)
 160{
 161        struct mcp3021_data *data = i2c_get_clientdata(client);
 162
 163        hwmon_device_unregister(data->hwmon_dev);
 164        sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 165
 166        return 0;
 167}
 168
 169static const struct i2c_device_id mcp3021_id[] = {
 170        { "mcp3021", mcp3021 },
 171        { "mcp3221", mcp3221 },
 172        { }
 173};
 174MODULE_DEVICE_TABLE(i2c, mcp3021_id);
 175
 176static struct i2c_driver mcp3021_driver = {
 177        .driver = {
 178                .name = "mcp3021",
 179        },
 180        .probe = mcp3021_probe,
 181        .remove = mcp3021_remove,
 182        .id_table = mcp3021_id,
 183};
 184
 185module_i2c_driver(mcp3021_driver);
 186
 187MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
 188MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver");
 189MODULE_LICENSE("GPL");
 190