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