Commit fb4504fe84b09cbf49fda19e6630a1003d79656a
1 parent
a157d06d4d
Exists in
master
and in
39 other branches
Move the pcf8591 driver to hwmon
Directory drivers/i2c/chips is going away, so drivers there must find new homes. For the pcf8591 driver, the best choice seems to be the hwmon subsystem. While the Philips PCF8591 device isn't a typical hardware monitoring chip, its DAC interface is compatible with the hwmon one, so it fits somewhat. If a better subsystem is ever created for ADC/DAC chips, the driver could be moved there. Signed-off-by: Jean Delvare <khali@linux-fr.org> Cc: Aurelien Jarno <aurelien@aurel32.net>
Showing 8 changed files with 430 additions and 429 deletions Side-by-side Diff
Documentation/hwmon/pcf8591
1 | +Kernel driver pcf8591 | |
2 | +===================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Philips PCF8591 | |
6 | + Prefix: 'pcf8591' | |
7 | + Addresses scanned: I2C 0x48 - 0x4f | |
8 | + Datasheet: Publicly available at the Philips Semiconductor website | |
9 | + http://www.semiconductors.philips.com/pip/PCF8591P.html | |
10 | + | |
11 | +Authors: | |
12 | + Aurelien Jarno <aurelien@aurel32.net> | |
13 | + valuable contributions by Jan M. Sendler <sendler@sendler.de>, | |
14 | + Jean Delvare <khali@linux-fr.org> | |
15 | + | |
16 | + | |
17 | +Description | |
18 | +----------- | |
19 | +The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one | |
20 | +analog output) for the I2C bus produced by Philips Semiconductors. It | |
21 | +is designed to provide a byte I2C interface to up to 4 separate devices. | |
22 | + | |
23 | +The PCF8591 has 4 analog inputs programmable as single-ended or | |
24 | +differential inputs : | |
25 | +- mode 0 : four single ended inputs | |
26 | + Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 | |
27 | + | |
28 | +- mode 1 : three differential inputs | |
29 | + Pins AIN3 is the common negative differential input | |
30 | + Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 | |
31 | + | |
32 | +- mode 2 : single ended and differential mixed | |
33 | + Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 | |
34 | + Pins AIN2 is the positive differential input for channel 3 | |
35 | + Pins AIN3 is the negative differential input for channel 3 | |
36 | + | |
37 | +- mode 3 : two differential inputs | |
38 | + Pins AIN0 is the positive differential input for channel 0 | |
39 | + Pins AIN1 is the negative differential input for channel 0 | |
40 | + Pins AIN2 is the positive differential input for channel 1 | |
41 | + Pins AIN3 is the negative differential input for channel 1 | |
42 | + | |
43 | +See the datasheet for details. | |
44 | + | |
45 | +Module parameters | |
46 | +----------------- | |
47 | + | |
48 | +* input_mode int | |
49 | + | |
50 | + Analog input mode: | |
51 | + 0 = four single ended inputs | |
52 | + 1 = three differential inputs | |
53 | + 2 = single ended and differential mixed | |
54 | + 3 = two differential inputs | |
55 | + | |
56 | + | |
57 | +Accessing PCF8591 via /sys interface | |
58 | +------------------------------------- | |
59 | + | |
60 | +! Be careful ! | |
61 | +The PCF8591 is plainly impossible to detect ! Stupid chip. | |
62 | +So every chip with address in the interval [48..4f] is | |
63 | +detected as PCF8591. If you have other chips in this address | |
64 | +range, the workaround is to load this module after the one | |
65 | +for your others chips. | |
66 | + | |
67 | +On detection (i.e. insmod, modprobe et al.), directories are being | |
68 | +created for each detected PCF8591: | |
69 | + | |
70 | +/sys/bus/devices/<0>-<1>/ | |
71 | +where <0> is the bus the chip was detected on (e. g. i2c-0) | |
72 | +and <1> the chip address ([48..4f]) | |
73 | + | |
74 | +Inside these directories, there are such files: | |
75 | +in0, in1, in2, in3, out0_enable, out0_output, name | |
76 | + | |
77 | +Name contains chip name. | |
78 | + | |
79 | +The in0, in1, in2 and in3 files are RO. Reading gives the value of the | |
80 | +corresponding channel. Depending on the current analog inputs configuration, | |
81 | +files in2 and/or in3 do not exist. Values range are from 0 to 255 for single | |
82 | +ended inputs and -128 to +127 for differential inputs (8-bit ADC). | |
83 | + | |
84 | +The out0_enable file is RW. Reading gives "1" for analog output enabled and | |
85 | +"0" for analog output disabled. Writing accepts "0" and "1" accordingly. | |
86 | + | |
87 | +The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send | |
88 | +the value to the digital-to-analog converter. Note that a voltage will | |
89 | +only appears on AOUT pin if aout0_enable equals 1. Reading returns the last | |
90 | +value written. |
Documentation/i2c/chips/pcf8591
1 | -Kernel driver pcf8591 | |
2 | -===================== | |
3 | - | |
4 | -Supported chips: | |
5 | - * Philips PCF8591 | |
6 | - Prefix: 'pcf8591' | |
7 | - Addresses scanned: I2C 0x48 - 0x4f | |
8 | - Datasheet: Publicly available at the Philips Semiconductor website | |
9 | - http://www.semiconductors.philips.com/pip/PCF8591P.html | |
10 | - | |
11 | -Authors: | |
12 | - Aurelien Jarno <aurelien@aurel32.net> | |
13 | - valuable contributions by Jan M. Sendler <sendler@sendler.de>, | |
14 | - Jean Delvare <khali@linux-fr.org> | |
15 | - | |
16 | - | |
17 | -Description | |
18 | ------------ | |
19 | -The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one | |
20 | -analog output) for the I2C bus produced by Philips Semiconductors. It | |
21 | -is designed to provide a byte I2C interface to up to 4 separate devices. | |
22 | - | |
23 | -The PCF8591 has 4 analog inputs programmable as single-ended or | |
24 | -differential inputs : | |
25 | -- mode 0 : four single ended inputs | |
26 | - Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 | |
27 | - | |
28 | -- mode 1 : three differential inputs | |
29 | - Pins AIN3 is the common negative differential input | |
30 | - Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 | |
31 | - | |
32 | -- mode 2 : single ended and differential mixed | |
33 | - Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 | |
34 | - Pins AIN2 is the positive differential input for channel 3 | |
35 | - Pins AIN3 is the negative differential input for channel 3 | |
36 | - | |
37 | -- mode 3 : two differential inputs | |
38 | - Pins AIN0 is the positive differential input for channel 0 | |
39 | - Pins AIN1 is the negative differential input for channel 0 | |
40 | - Pins AIN2 is the positive differential input for channel 1 | |
41 | - Pins AIN3 is the negative differential input for channel 1 | |
42 | - | |
43 | -See the datasheet for details. | |
44 | - | |
45 | -Module parameters | |
46 | ------------------ | |
47 | - | |
48 | -* input_mode int | |
49 | - | |
50 | - Analog input mode: | |
51 | - 0 = four single ended inputs | |
52 | - 1 = three differential inputs | |
53 | - 2 = single ended and differential mixed | |
54 | - 3 = two differential inputs | |
55 | - | |
56 | - | |
57 | -Accessing PCF8591 via /sys interface | |
58 | -------------------------------------- | |
59 | - | |
60 | -! Be careful ! | |
61 | -The PCF8591 is plainly impossible to detect ! Stupid chip. | |
62 | -So every chip with address in the interval [48..4f] is | |
63 | -detected as PCF8591. If you have other chips in this address | |
64 | -range, the workaround is to load this module after the one | |
65 | -for your others chips. | |
66 | - | |
67 | -On detection (i.e. insmod, modprobe et al.), directories are being | |
68 | -created for each detected PCF8591: | |
69 | - | |
70 | -/sys/bus/devices/<0>-<1>/ | |
71 | -where <0> is the bus the chip was detected on (e. g. i2c-0) | |
72 | -and <1> the chip address ([48..4f]) | |
73 | - | |
74 | -Inside these directories, there are such files: | |
75 | -in0, in1, in2, in3, out0_enable, out0_output, name | |
76 | - | |
77 | -Name contains chip name. | |
78 | - | |
79 | -The in0, in1, in2 and in3 files are RO. Reading gives the value of the | |
80 | -corresponding channel. Depending on the current analog inputs configuration, | |
81 | -files in2 and/or in3 do not exist. Values range are from 0 to 255 for single | |
82 | -ended inputs and -128 to +127 for differential inputs (8-bit ADC). | |
83 | - | |
84 | -The out0_enable file is RW. Reading gives "1" for analog output enabled and | |
85 | -"0" for analog output disabled. Writing accepts "0" and "1" accordingly. | |
86 | - | |
87 | -The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send | |
88 | -the value to the digital-to-analog converter. Note that a voltage will | |
89 | -only appears on AOUT pin if aout0_enable equals 1. Reading returns the last | |
90 | -value written. |
drivers/hwmon/Kconfig
... | ... | @@ -635,6 +635,20 @@ |
635 | 635 | This driver can also be built as a module. If so, the module |
636 | 636 | will be called pc87427. |
637 | 637 | |
638 | +config SENSORS_PCF8591 | |
639 | + tristate "Philips PCF8591 ADC/DAC" | |
640 | + depends on I2C | |
641 | + default n | |
642 | + help | |
643 | + If you say yes here you get support for Philips PCF8591 4-channel | |
644 | + ADC, 1-channel DAC chips. | |
645 | + | |
646 | + This driver can also be built as a module. If so, the module | |
647 | + will be called pcf8591. | |
648 | + | |
649 | + These devices are hard to detect and rarely found on mainstream | |
650 | + hardware. If unsure, say N. | |
651 | + | |
638 | 652 | config SENSORS_SIS5595 |
639 | 653 | tristate "Silicon Integrated Systems Corp. SiS5595" |
640 | 654 | depends on PCI |
drivers/hwmon/Makefile
... | ... | @@ -70,6 +70,7 @@ |
70 | 70 | obj-$(CONFIG_SENSORS_MAX6650) += max6650.o |
71 | 71 | obj-$(CONFIG_SENSORS_PC87360) += pc87360.o |
72 | 72 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o |
73 | +obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o | |
73 | 74 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o |
74 | 75 | obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o |
75 | 76 | obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o |
drivers/hwmon/pcf8591.c
1 | +/* | |
2 | + Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> | |
3 | + Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with | |
4 | + the help of Jean Delvare <khali@linux-fr.org> | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 2 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + You should have received a copy of the GNU General Public License | |
17 | + along with this program; if not, write to the Free Software | |
18 | + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | +*/ | |
20 | + | |
21 | +#include <linux/module.h> | |
22 | +#include <linux/init.h> | |
23 | +#include <linux/slab.h> | |
24 | +#include <linux/i2c.h> | |
25 | +#include <linux/mutex.h> | |
26 | + | |
27 | +/* Addresses to scan */ | |
28 | +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, | |
29 | + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; | |
30 | + | |
31 | +/* Insmod parameters */ | |
32 | +I2C_CLIENT_INSMOD_1(pcf8591); | |
33 | + | |
34 | +static int input_mode; | |
35 | +module_param(input_mode, int, 0); | |
36 | +MODULE_PARM_DESC(input_mode, | |
37 | + "Analog input mode:\n" | |
38 | + " 0 = four single ended inputs\n" | |
39 | + " 1 = three differential inputs\n" | |
40 | + " 2 = single ended and differential mixed\n" | |
41 | + " 3 = two differential inputs\n"); | |
42 | + | |
43 | +/* The PCF8591 control byte | |
44 | + 7 6 5 4 3 2 1 0 | |
45 | + | 0 |AOEF| AIP | 0 |AINC| AICH | */ | |
46 | + | |
47 | +/* Analog Output Enable Flag (analog output active if 1) */ | |
48 | +#define PCF8591_CONTROL_AOEF 0x40 | |
49 | + | |
50 | +/* Analog Input Programming | |
51 | + 0x00 = four single ended inputs | |
52 | + 0x10 = three differential inputs | |
53 | + 0x20 = single ended and differential mixed | |
54 | + 0x30 = two differential inputs */ | |
55 | +#define PCF8591_CONTROL_AIP_MASK 0x30 | |
56 | + | |
57 | +/* Autoincrement Flag (switch on if 1) */ | |
58 | +#define PCF8591_CONTROL_AINC 0x04 | |
59 | + | |
60 | +/* Channel selection | |
61 | + 0x00 = channel 0 | |
62 | + 0x01 = channel 1 | |
63 | + 0x02 = channel 2 | |
64 | + 0x03 = channel 3 */ | |
65 | +#define PCF8591_CONTROL_AICH_MASK 0x03 | |
66 | + | |
67 | +/* Initial values */ | |
68 | +#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) | |
69 | +#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ | |
70 | + | |
71 | +/* Conversions */ | |
72 | +#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) | |
73 | + | |
74 | +struct pcf8591_data { | |
75 | + struct mutex update_lock; | |
76 | + | |
77 | + u8 control; | |
78 | + u8 aout; | |
79 | +}; | |
80 | + | |
81 | +static void pcf8591_init_client(struct i2c_client *client); | |
82 | +static int pcf8591_read_channel(struct device *dev, int channel); | |
83 | + | |
84 | +/* following are the sysfs callback functions */ | |
85 | +#define show_in_channel(channel) \ | |
86 | +static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ | |
87 | +{ \ | |
88 | + return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ | |
89 | +} \ | |
90 | +static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ | |
91 | + show_in##channel##_input, NULL); | |
92 | + | |
93 | +show_in_channel(0); | |
94 | +show_in_channel(1); | |
95 | +show_in_channel(2); | |
96 | +show_in_channel(3); | |
97 | + | |
98 | +static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) | |
99 | +{ | |
100 | + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); | |
101 | + return sprintf(buf, "%d\n", data->aout * 10); | |
102 | +} | |
103 | + | |
104 | +static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
105 | +{ | |
106 | + unsigned int value; | |
107 | + struct i2c_client *client = to_i2c_client(dev); | |
108 | + struct pcf8591_data *data = i2c_get_clientdata(client); | |
109 | + if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { | |
110 | + data->aout = value; | |
111 | + i2c_smbus_write_byte_data(client, data->control, data->aout); | |
112 | + return count; | |
113 | + } | |
114 | + return -EINVAL; | |
115 | +} | |
116 | + | |
117 | +static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, | |
118 | + show_out0_ouput, set_out0_output); | |
119 | + | |
120 | +static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) | |
121 | +{ | |
122 | + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); | |
123 | + return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); | |
124 | +} | |
125 | + | |
126 | +static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
127 | +{ | |
128 | + struct i2c_client *client = to_i2c_client(dev); | |
129 | + struct pcf8591_data *data = i2c_get_clientdata(client); | |
130 | + unsigned long val = simple_strtoul(buf, NULL, 10); | |
131 | + | |
132 | + mutex_lock(&data->update_lock); | |
133 | + if (val) | |
134 | + data->control |= PCF8591_CONTROL_AOEF; | |
135 | + else | |
136 | + data->control &= ~PCF8591_CONTROL_AOEF; | |
137 | + i2c_smbus_write_byte(client, data->control); | |
138 | + mutex_unlock(&data->update_lock); | |
139 | + return count; | |
140 | +} | |
141 | + | |
142 | +static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, | |
143 | + show_out0_enable, set_out0_enable); | |
144 | + | |
145 | +static struct attribute *pcf8591_attributes[] = { | |
146 | + &dev_attr_out0_enable.attr, | |
147 | + &dev_attr_out0_output.attr, | |
148 | + &dev_attr_in0_input.attr, | |
149 | + &dev_attr_in1_input.attr, | |
150 | + NULL | |
151 | +}; | |
152 | + | |
153 | +static const struct attribute_group pcf8591_attr_group = { | |
154 | + .attrs = pcf8591_attributes, | |
155 | +}; | |
156 | + | |
157 | +static struct attribute *pcf8591_attributes_opt[] = { | |
158 | + &dev_attr_in2_input.attr, | |
159 | + &dev_attr_in3_input.attr, | |
160 | + NULL | |
161 | +}; | |
162 | + | |
163 | +static const struct attribute_group pcf8591_attr_group_opt = { | |
164 | + .attrs = pcf8591_attributes_opt, | |
165 | +}; | |
166 | + | |
167 | +/* | |
168 | + * Real code | |
169 | + */ | |
170 | + | |
171 | +/* Return 0 if detection is successful, -ENODEV otherwise */ | |
172 | +static int pcf8591_detect(struct i2c_client *client, int kind, | |
173 | + struct i2c_board_info *info) | |
174 | +{ | |
175 | + struct i2c_adapter *adapter = client->adapter; | |
176 | + | |
177 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | |
178 | + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
179 | + return -ENODEV; | |
180 | + | |
181 | + /* Now, we would do the remaining detection. But the PCF8591 is plainly | |
182 | + impossible to detect! Stupid chip. */ | |
183 | + | |
184 | + strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); | |
185 | + | |
186 | + return 0; | |
187 | +} | |
188 | + | |
189 | +static int pcf8591_probe(struct i2c_client *client, | |
190 | + const struct i2c_device_id *id) | |
191 | +{ | |
192 | + struct pcf8591_data *data; | |
193 | + int err; | |
194 | + | |
195 | + if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { | |
196 | + err = -ENOMEM; | |
197 | + goto exit; | |
198 | + } | |
199 | + | |
200 | + i2c_set_clientdata(client, data); | |
201 | + mutex_init(&data->update_lock); | |
202 | + | |
203 | + /* Initialize the PCF8591 chip */ | |
204 | + pcf8591_init_client(client); | |
205 | + | |
206 | + /* Register sysfs hooks */ | |
207 | + err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); | |
208 | + if (err) | |
209 | + goto exit_kfree; | |
210 | + | |
211 | + /* Register input2 if not in "two differential inputs" mode */ | |
212 | + if (input_mode != 3) { | |
213 | + if ((err = device_create_file(&client->dev, | |
214 | + &dev_attr_in2_input))) | |
215 | + goto exit_sysfs_remove; | |
216 | + } | |
217 | + | |
218 | + /* Register input3 only in "four single ended inputs" mode */ | |
219 | + if (input_mode == 0) { | |
220 | + if ((err = device_create_file(&client->dev, | |
221 | + &dev_attr_in3_input))) | |
222 | + goto exit_sysfs_remove; | |
223 | + } | |
224 | + | |
225 | + return 0; | |
226 | + | |
227 | +exit_sysfs_remove: | |
228 | + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); | |
229 | + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); | |
230 | +exit_kfree: | |
231 | + kfree(data); | |
232 | +exit: | |
233 | + return err; | |
234 | +} | |
235 | + | |
236 | +static int pcf8591_remove(struct i2c_client *client) | |
237 | +{ | |
238 | + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); | |
239 | + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); | |
240 | + kfree(i2c_get_clientdata(client)); | |
241 | + return 0; | |
242 | +} | |
243 | + | |
244 | +/* Called when we have found a new PCF8591. */ | |
245 | +static void pcf8591_init_client(struct i2c_client *client) | |
246 | +{ | |
247 | + struct pcf8591_data *data = i2c_get_clientdata(client); | |
248 | + data->control = PCF8591_INIT_CONTROL; | |
249 | + data->aout = PCF8591_INIT_AOUT; | |
250 | + | |
251 | + i2c_smbus_write_byte_data(client, data->control, data->aout); | |
252 | + | |
253 | + /* The first byte transmitted contains the conversion code of the | |
254 | + previous read cycle. FLUSH IT! */ | |
255 | + i2c_smbus_read_byte(client); | |
256 | +} | |
257 | + | |
258 | +static int pcf8591_read_channel(struct device *dev, int channel) | |
259 | +{ | |
260 | + u8 value; | |
261 | + struct i2c_client *client = to_i2c_client(dev); | |
262 | + struct pcf8591_data *data = i2c_get_clientdata(client); | |
263 | + | |
264 | + mutex_lock(&data->update_lock); | |
265 | + | |
266 | + if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { | |
267 | + data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) | |
268 | + | channel; | |
269 | + i2c_smbus_write_byte(client, data->control); | |
270 | + | |
271 | + /* The first byte transmitted contains the conversion code of | |
272 | + the previous read cycle. FLUSH IT! */ | |
273 | + i2c_smbus_read_byte(client); | |
274 | + } | |
275 | + value = i2c_smbus_read_byte(client); | |
276 | + | |
277 | + mutex_unlock(&data->update_lock); | |
278 | + | |
279 | + if ((channel == 2 && input_mode == 2) || | |
280 | + (channel != 3 && (input_mode == 1 || input_mode == 3))) | |
281 | + return (10 * REG_TO_SIGNED(value)); | |
282 | + else | |
283 | + return (10 * value); | |
284 | +} | |
285 | + | |
286 | +static const struct i2c_device_id pcf8591_id[] = { | |
287 | + { "pcf8591", 0 }, | |
288 | + { } | |
289 | +}; | |
290 | +MODULE_DEVICE_TABLE(i2c, pcf8591_id); | |
291 | + | |
292 | +static struct i2c_driver pcf8591_driver = { | |
293 | + .driver = { | |
294 | + .name = "pcf8591", | |
295 | + }, | |
296 | + .probe = pcf8591_probe, | |
297 | + .remove = pcf8591_remove, | |
298 | + .id_table = pcf8591_id, | |
299 | + | |
300 | + .class = I2C_CLASS_HWMON, /* Nearest choice */ | |
301 | + .detect = pcf8591_detect, | |
302 | + .address_data = &addr_data, | |
303 | +}; | |
304 | + | |
305 | +static int __init pcf8591_init(void) | |
306 | +{ | |
307 | + if (input_mode < 0 || input_mode > 3) { | |
308 | + printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", | |
309 | + input_mode); | |
310 | + input_mode = 0; | |
311 | + } | |
312 | + return i2c_add_driver(&pcf8591_driver); | |
313 | +} | |
314 | + | |
315 | +static void __exit pcf8591_exit(void) | |
316 | +{ | |
317 | + i2c_del_driver(&pcf8591_driver); | |
318 | +} | |
319 | + | |
320 | +MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); | |
321 | +MODULE_DESCRIPTION("PCF8591 driver"); | |
322 | +MODULE_LICENSE("GPL"); | |
323 | + | |
324 | +module_init(pcf8591_init); | |
325 | +module_exit(pcf8591_exit); |
drivers/i2c/chips/Kconfig
... | ... | @@ -64,19 +64,6 @@ |
64 | 64 | This driver is deprecated and will be dropped soon. Use |
65 | 65 | drivers/gpio/pca953x.c instead. |
66 | 66 | |
67 | -config SENSORS_PCF8591 | |
68 | - tristate "Philips PCF8591" | |
69 | - depends on EXPERIMENTAL | |
70 | - default n | |
71 | - help | |
72 | - If you say yes here you get support for Philips PCF8591 chips. | |
73 | - | |
74 | - This driver can also be built as a module. If so, the module | |
75 | - will be called pcf8591. | |
76 | - | |
77 | - These devices are hard to detect and rarely found on mainstream | |
78 | - hardware. If unsure, say N. | |
79 | - | |
80 | 67 | config SENSORS_MAX6875 |
81 | 68 | tristate "Maxim MAX6875 Power supply supervisor" |
82 | 69 | depends on EXPERIMENTAL |
drivers/i2c/chips/Makefile
... | ... | @@ -15,7 +15,6 @@ |
15 | 15 | obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o |
16 | 16 | obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o |
17 | 17 | obj-$(CONFIG_PCF8575) += pcf8575.o |
18 | -obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o | |
19 | 18 | obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o |
20 | 19 | |
21 | 20 | ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) |
drivers/i2c/chips/pcf8591.c
1 | -/* | |
2 | - Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> | |
3 | - Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with | |
4 | - the help of Jean Delvare <khali@linux-fr.org> | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 2 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - You should have received a copy of the GNU General Public License | |
17 | - along with this program; if not, write to the Free Software | |
18 | - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | -*/ | |
20 | - | |
21 | -#include <linux/module.h> | |
22 | -#include <linux/init.h> | |
23 | -#include <linux/slab.h> | |
24 | -#include <linux/i2c.h> | |
25 | -#include <linux/mutex.h> | |
26 | - | |
27 | -/* Addresses to scan */ | |
28 | -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, | |
29 | - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; | |
30 | - | |
31 | -/* Insmod parameters */ | |
32 | -I2C_CLIENT_INSMOD_1(pcf8591); | |
33 | - | |
34 | -static int input_mode; | |
35 | -module_param(input_mode, int, 0); | |
36 | -MODULE_PARM_DESC(input_mode, | |
37 | - "Analog input mode:\n" | |
38 | - " 0 = four single ended inputs\n" | |
39 | - " 1 = three differential inputs\n" | |
40 | - " 2 = single ended and differential mixed\n" | |
41 | - " 3 = two differential inputs\n"); | |
42 | - | |
43 | -/* The PCF8591 control byte | |
44 | - 7 6 5 4 3 2 1 0 | |
45 | - | 0 |AOEF| AIP | 0 |AINC| AICH | */ | |
46 | - | |
47 | -/* Analog Output Enable Flag (analog output active if 1) */ | |
48 | -#define PCF8591_CONTROL_AOEF 0x40 | |
49 | - | |
50 | -/* Analog Input Programming | |
51 | - 0x00 = four single ended inputs | |
52 | - 0x10 = three differential inputs | |
53 | - 0x20 = single ended and differential mixed | |
54 | - 0x30 = two differential inputs */ | |
55 | -#define PCF8591_CONTROL_AIP_MASK 0x30 | |
56 | - | |
57 | -/* Autoincrement Flag (switch on if 1) */ | |
58 | -#define PCF8591_CONTROL_AINC 0x04 | |
59 | - | |
60 | -/* Channel selection | |
61 | - 0x00 = channel 0 | |
62 | - 0x01 = channel 1 | |
63 | - 0x02 = channel 2 | |
64 | - 0x03 = channel 3 */ | |
65 | -#define PCF8591_CONTROL_AICH_MASK 0x03 | |
66 | - | |
67 | -/* Initial values */ | |
68 | -#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) | |
69 | -#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ | |
70 | - | |
71 | -/* Conversions */ | |
72 | -#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) | |
73 | - | |
74 | -struct pcf8591_data { | |
75 | - struct mutex update_lock; | |
76 | - | |
77 | - u8 control; | |
78 | - u8 aout; | |
79 | -}; | |
80 | - | |
81 | -static void pcf8591_init_client(struct i2c_client *client); | |
82 | -static int pcf8591_read_channel(struct device *dev, int channel); | |
83 | - | |
84 | -/* following are the sysfs callback functions */ | |
85 | -#define show_in_channel(channel) \ | |
86 | -static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ | |
87 | -{ \ | |
88 | - return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ | |
89 | -} \ | |
90 | -static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ | |
91 | - show_in##channel##_input, NULL); | |
92 | - | |
93 | -show_in_channel(0); | |
94 | -show_in_channel(1); | |
95 | -show_in_channel(2); | |
96 | -show_in_channel(3); | |
97 | - | |
98 | -static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) | |
99 | -{ | |
100 | - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); | |
101 | - return sprintf(buf, "%d\n", data->aout * 10); | |
102 | -} | |
103 | - | |
104 | -static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
105 | -{ | |
106 | - unsigned int value; | |
107 | - struct i2c_client *client = to_i2c_client(dev); | |
108 | - struct pcf8591_data *data = i2c_get_clientdata(client); | |
109 | - if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { | |
110 | - data->aout = value; | |
111 | - i2c_smbus_write_byte_data(client, data->control, data->aout); | |
112 | - return count; | |
113 | - } | |
114 | - return -EINVAL; | |
115 | -} | |
116 | - | |
117 | -static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, | |
118 | - show_out0_ouput, set_out0_output); | |
119 | - | |
120 | -static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) | |
121 | -{ | |
122 | - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); | |
123 | - return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); | |
124 | -} | |
125 | - | |
126 | -static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
127 | -{ | |
128 | - struct i2c_client *client = to_i2c_client(dev); | |
129 | - struct pcf8591_data *data = i2c_get_clientdata(client); | |
130 | - unsigned long val = simple_strtoul(buf, NULL, 10); | |
131 | - | |
132 | - mutex_lock(&data->update_lock); | |
133 | - if (val) | |
134 | - data->control |= PCF8591_CONTROL_AOEF; | |
135 | - else | |
136 | - data->control &= ~PCF8591_CONTROL_AOEF; | |
137 | - i2c_smbus_write_byte(client, data->control); | |
138 | - mutex_unlock(&data->update_lock); | |
139 | - return count; | |
140 | -} | |
141 | - | |
142 | -static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, | |
143 | - show_out0_enable, set_out0_enable); | |
144 | - | |
145 | -static struct attribute *pcf8591_attributes[] = { | |
146 | - &dev_attr_out0_enable.attr, | |
147 | - &dev_attr_out0_output.attr, | |
148 | - &dev_attr_in0_input.attr, | |
149 | - &dev_attr_in1_input.attr, | |
150 | - NULL | |
151 | -}; | |
152 | - | |
153 | -static const struct attribute_group pcf8591_attr_group = { | |
154 | - .attrs = pcf8591_attributes, | |
155 | -}; | |
156 | - | |
157 | -static struct attribute *pcf8591_attributes_opt[] = { | |
158 | - &dev_attr_in2_input.attr, | |
159 | - &dev_attr_in3_input.attr, | |
160 | - NULL | |
161 | -}; | |
162 | - | |
163 | -static const struct attribute_group pcf8591_attr_group_opt = { | |
164 | - .attrs = pcf8591_attributes_opt, | |
165 | -}; | |
166 | - | |
167 | -/* | |
168 | - * Real code | |
169 | - */ | |
170 | - | |
171 | -/* Return 0 if detection is successful, -ENODEV otherwise */ | |
172 | -static int pcf8591_detect(struct i2c_client *client, int kind, | |
173 | - struct i2c_board_info *info) | |
174 | -{ | |
175 | - struct i2c_adapter *adapter = client->adapter; | |
176 | - | |
177 | - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | |
178 | - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
179 | - return -ENODEV; | |
180 | - | |
181 | - /* Now, we would do the remaining detection. But the PCF8591 is plainly | |
182 | - impossible to detect! Stupid chip. */ | |
183 | - | |
184 | - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); | |
185 | - | |
186 | - return 0; | |
187 | -} | |
188 | - | |
189 | -static int pcf8591_probe(struct i2c_client *client, | |
190 | - const struct i2c_device_id *id) | |
191 | -{ | |
192 | - struct pcf8591_data *data; | |
193 | - int err; | |
194 | - | |
195 | - if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { | |
196 | - err = -ENOMEM; | |
197 | - goto exit; | |
198 | - } | |
199 | - | |
200 | - i2c_set_clientdata(client, data); | |
201 | - mutex_init(&data->update_lock); | |
202 | - | |
203 | - /* Initialize the PCF8591 chip */ | |
204 | - pcf8591_init_client(client); | |
205 | - | |
206 | - /* Register sysfs hooks */ | |
207 | - err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); | |
208 | - if (err) | |
209 | - goto exit_kfree; | |
210 | - | |
211 | - /* Register input2 if not in "two differential inputs" mode */ | |
212 | - if (input_mode != 3) { | |
213 | - if ((err = device_create_file(&client->dev, | |
214 | - &dev_attr_in2_input))) | |
215 | - goto exit_sysfs_remove; | |
216 | - } | |
217 | - | |
218 | - /* Register input3 only in "four single ended inputs" mode */ | |
219 | - if (input_mode == 0) { | |
220 | - if ((err = device_create_file(&client->dev, | |
221 | - &dev_attr_in3_input))) | |
222 | - goto exit_sysfs_remove; | |
223 | - } | |
224 | - | |
225 | - return 0; | |
226 | - | |
227 | -exit_sysfs_remove: | |
228 | - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); | |
229 | - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); | |
230 | -exit_kfree: | |
231 | - kfree(data); | |
232 | -exit: | |
233 | - return err; | |
234 | -} | |
235 | - | |
236 | -static int pcf8591_remove(struct i2c_client *client) | |
237 | -{ | |
238 | - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); | |
239 | - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); | |
240 | - kfree(i2c_get_clientdata(client)); | |
241 | - return 0; | |
242 | -} | |
243 | - | |
244 | -/* Called when we have found a new PCF8591. */ | |
245 | -static void pcf8591_init_client(struct i2c_client *client) | |
246 | -{ | |
247 | - struct pcf8591_data *data = i2c_get_clientdata(client); | |
248 | - data->control = PCF8591_INIT_CONTROL; | |
249 | - data->aout = PCF8591_INIT_AOUT; | |
250 | - | |
251 | - i2c_smbus_write_byte_data(client, data->control, data->aout); | |
252 | - | |
253 | - /* The first byte transmitted contains the conversion code of the | |
254 | - previous read cycle. FLUSH IT! */ | |
255 | - i2c_smbus_read_byte(client); | |
256 | -} | |
257 | - | |
258 | -static int pcf8591_read_channel(struct device *dev, int channel) | |
259 | -{ | |
260 | - u8 value; | |
261 | - struct i2c_client *client = to_i2c_client(dev); | |
262 | - struct pcf8591_data *data = i2c_get_clientdata(client); | |
263 | - | |
264 | - mutex_lock(&data->update_lock); | |
265 | - | |
266 | - if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { | |
267 | - data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) | |
268 | - | channel; | |
269 | - i2c_smbus_write_byte(client, data->control); | |
270 | - | |
271 | - /* The first byte transmitted contains the conversion code of | |
272 | - the previous read cycle. FLUSH IT! */ | |
273 | - i2c_smbus_read_byte(client); | |
274 | - } | |
275 | - value = i2c_smbus_read_byte(client); | |
276 | - | |
277 | - mutex_unlock(&data->update_lock); | |
278 | - | |
279 | - if ((channel == 2 && input_mode == 2) || | |
280 | - (channel != 3 && (input_mode == 1 || input_mode == 3))) | |
281 | - return (10 * REG_TO_SIGNED(value)); | |
282 | - else | |
283 | - return (10 * value); | |
284 | -} | |
285 | - | |
286 | -static const struct i2c_device_id pcf8591_id[] = { | |
287 | - { "pcf8591", 0 }, | |
288 | - { } | |
289 | -}; | |
290 | -MODULE_DEVICE_TABLE(i2c, pcf8591_id); | |
291 | - | |
292 | -static struct i2c_driver pcf8591_driver = { | |
293 | - .driver = { | |
294 | - .name = "pcf8591", | |
295 | - }, | |
296 | - .probe = pcf8591_probe, | |
297 | - .remove = pcf8591_remove, | |
298 | - .id_table = pcf8591_id, | |
299 | - | |
300 | - .class = I2C_CLASS_HWMON, /* Nearest choice */ | |
301 | - .detect = pcf8591_detect, | |
302 | - .address_data = &addr_data, | |
303 | -}; | |
304 | - | |
305 | -static int __init pcf8591_init(void) | |
306 | -{ | |
307 | - if (input_mode < 0 || input_mode > 3) { | |
308 | - printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", | |
309 | - input_mode); | |
310 | - input_mode = 0; | |
311 | - } | |
312 | - return i2c_add_driver(&pcf8591_driver); | |
313 | -} | |
314 | - | |
315 | -static void __exit pcf8591_exit(void) | |
316 | -{ | |
317 | - i2c_del_driver(&pcf8591_driver); | |
318 | -} | |
319 | - | |
320 | -MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); | |
321 | -MODULE_DESCRIPTION("PCF8591 driver"); | |
322 | -MODULE_LICENSE("GPL"); | |
323 | - | |
324 | -module_init(pcf8591_init); | |
325 | -module_exit(pcf8591_exit); |