Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.4-rc3 171 lines 4.0 kB view raw
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");