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 */ 40 struct mcp3021_data { 41 struct device *hwmon_dev; 42 u32 vdd; /* device power supply */ 43 }; 44 45 static 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 69 static 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 80 static 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 95 static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); 96 97 static 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 134 exit_remove: 135 sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); 136 exit_free: 137 kfree(data); 138 return err; 139 } 140 141 static 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 152 static const struct i2c_device_id mcp3021_id[] = { 153 { "mcp3021", 0 }, 154 { } 155 }; 156 MODULE_DEVICE_TABLE(i2c, mcp3021_id); 157 158 static 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 167 module_i2c_driver(mcp3021_driver); 168 169 MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>"); 170 MODULE_DESCRIPTION("Microchip MCP3021 driver"); 171 MODULE_LICENSE("GPL"); 172