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
  35#define MCP3021_OUTPUT_RES      10      /* 10-bit resolution */
  36#define MCP3021_OUTPUT_SCALE    4
  37
  38#define MCP3221_SAR_SHIFT       0
  39#define MCP3221_SAR_MASK        0xfff
  40#define MCP3221_OUTPUT_RES      12      /* 12-bit resolution */
  41#define MCP3221_OUTPUT_SCALE    1
  42
  43enum chips {
  44        mcp3021,
  45        mcp3221
  46};
  47
  48/*
  49 * Client data (each client gets its own)
  50 */
  51struct mcp3021_data {
  52        struct device *hwmon_dev;
  53        u32 vdd;        /* device power supply */
  54        u16 sar_shift;
  55        u16 sar_mask;
  56        u8 output_res;
  57        u8 output_scale;
  58};
  59
  60static int mcp3021_read16(struct i2c_client *client)
  61{
  62        struct mcp3021_data *data = i2c_get_clientdata(client);
  63        int ret;
  64        u16 reg;
  65        __be16 buf;
  66
  67        ret = i2c_master_recv(client, (char *)&buf, 2);
  68        if (ret < 0)
  69                return ret;
  70        if (ret != 2)
  71                return -EIO;
  72
  73        /* The output code of the MCP3021 is transmitted with MSB first. */
  74        reg = be16_to_cpu(buf);
  75
  76        /*
  77         * The ten-bit output code is composed of the lower 4-bit of the
  78         * first byte and the upper 6-bit of the second byte.
  79         */
  80        reg = (reg >> data->sar_shift) & data->sar_mask;
  81
  82        return reg;
  83}
  84
  85static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
  86{
  87        if (val == 0)
  88                return 0;
  89
  90        val = val * data->output_scale - data->output_scale / 2;
  91
  92        return val * DIV_ROUND_CLOSEST(data->vdd,
  93                        (1 << data->output_res) * data->output_scale);
  94}
  95
  96static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
  97                char *buf)
  98{
  99        struct i2c_client *client = to_i2c_client(dev);
 100        struct mcp3021_data *data = i2c_get_clientdata(client);
 101        int reg, in_input;
 102
 103        reg = mcp3021_read16(client);
 104        if (reg < 0)
 105                return reg;
 106
 107        in_input = volts_from_reg(data, reg);
 108
 109        return sprintf(buf, "%d\n", in_input);
 110}
 111
 112static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL);
 113
 114static int mcp3021_probe(struct i2c_client *client,
 115                                const struct i2c_device_id *id)
 116{
 117        int err;
 118        struct mcp3021_data *data = NULL;
 119
 120        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 121                return -ENODEV;
 122
 123        data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data),
 124                            GFP_KERNEL);
 125        if (!data)
 126                return -ENOMEM;
 127
 128        i2c_set_clientdata(client, data);
 129
 130        switch (id->driver_data) {
 131        case mcp3021:
 132                data->sar_shift = MCP3021_SAR_SHIFT;
 133                data->sar_mask = MCP3021_SAR_MASK;
 134                data->output_res = MCP3021_OUTPUT_RES;
 135                data->output_scale = MCP3021_OUTPUT_SCALE;
 136                break;
 137
 138        case mcp3221:
 139                data->sar_shift = MCP3221_SAR_SHIFT;
 140                data->sar_mask = MCP3221_SAR_MASK;
 141                data->output_res = MCP3221_OUTPUT_RES;
 142                data->output_scale = MCP3221_OUTPUT_SCALE;
 143                break;
 144        }
 145
 146        if (dev_get_platdata(&client->dev)) {
 147                data->vdd = *(u32 *)dev_get_platdata(&client->dev);
 148                if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
 149                        return -EINVAL;
 150        } else {
 151                data->vdd = MCP3021_VDD_REF;
 152        }
 153
 154        err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 155        if (err)
 156                return err;
 157
 158        data->hwmon_dev = hwmon_device_register(&client->dev);
 159        if (IS_ERR(data->hwmon_dev)) {
 160                err = PTR_ERR(data->hwmon_dev);
 161                goto exit_remove;
 162        }
 163
 164        return 0;
 165
 166exit_remove:
 167        sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 168        return err;
 169}
 170
 171static int mcp3021_remove(struct i2c_client *client)
 172{
 173        struct mcp3021_data *data = i2c_get_clientdata(client);
 174
 175        hwmon_device_unregister(data->hwmon_dev);
 176        sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
 177
 178        return 0;
 179}
 180
 181static const struct i2c_device_id mcp3021_id[] = {
 182        { "mcp3021", mcp3021 },
 183        { "mcp3221", mcp3221 },
 184        { }
 185};
 186MODULE_DEVICE_TABLE(i2c, mcp3021_id);
 187
 188static struct i2c_driver mcp3021_driver = {
 189        .driver = {
 190                .name = "mcp3021",
 191        },
 192        .probe = mcp3021_probe,
 193        .remove = mcp3021_remove,
 194        .id_table = mcp3021_id,
 195};
 196
 197module_i2c_driver(mcp3021_driver);
 198
 199MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
 200MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver");
 201MODULE_LICENSE("GPL");
 202
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.