Commit 5510e62a66bad22b104d5d854445523d7f5754f7
Committed by
Jean Delvare
1 parent
0c2732152a
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
hwmon: Add MCP3021 ADC driver
Add I2C driver for MCP3021 that is an ADC chip from Microchip. The MCP3021 is a successive approximation A/D converter (ADC) with 10-bit resolution. The driver export the value of Vin to sysfs, the voltage unit is mV. Through the sysfs interface, lm-sensors tool can also display Vin voltage. Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Showing 4 changed files with 204 additions and 0 deletions Side-by-side Diff
Documentation/hwmon/mcp3021
1 | +Kernel driver MCP3021 | |
2 | +====================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Microchip Technology MCP3021 | |
6 | + Prefix: 'mcp3021' | |
7 | + Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21805a.pdf | |
8 | + | |
9 | +Author: Mingkai Hu | |
10 | + | |
11 | +Description | |
12 | +----------- | |
13 | + | |
14 | +This driver implements support for the Microchip Technology MCP3021 chip. | |
15 | + | |
16 | +The Microchip Technology Inc. MCP3021 is a successive approximation A/D | |
17 | +converter (ADC) with 10-bit resolution. | |
18 | +This device provides one single-ended input with very low power consumption. | |
19 | +Communication to the MCP3021 is performed using a 2-wire I2C compatible | |
20 | +interface. Standard (100 kHz) and Fast (400 kHz) I2C modes are available. | |
21 | +The default I2C device address is 0x4d (contact the Microchip factory for | |
22 | +additional address options). |
drivers/hwmon/Kconfig
... | ... | @@ -813,6 +813,16 @@ |
813 | 813 | This driver can also be built as a module. If so, the module |
814 | 814 | will be called max6650. |
815 | 815 | |
816 | +config SENSORS_MCP3021 | |
817 | + tristate "Microchip MCP3021" | |
818 | + depends on I2C && EXPERIMENTAL | |
819 | + help | |
820 | + If you say yes here you get support for the MCP3021 chip | |
821 | + that is a A/D converter (ADC) with 10-bit resolution. | |
822 | + | |
823 | + This driver can also be built as a module. If so, the module | |
824 | + will be called mcp3021. | |
825 | + | |
816 | 826 | config SENSORS_NTC_THERMISTOR |
817 | 827 | tristate "NTC thermistor support" |
818 | 828 | depends on EXPERIMENTAL |
drivers/hwmon/Makefile
... | ... | @@ -95,6 +95,7 @@ |
95 | 95 | obj-$(CONFIG_SENSORS_MAX6642) += max6642.o |
96 | 96 | obj-$(CONFIG_SENSORS_MAX6650) += max6650.o |
97 | 97 | obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o |
98 | +obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o | |
98 | 99 | obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o |
99 | 100 | obj-$(CONFIG_SENSORS_PC87360) += pc87360.o |
100 | 101 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o |
drivers/hwmon/mcp3021.c
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"); |