diff options
-rw-r--r-- | Documentation/devicetree/bindings/hwmon/ads1015.txt | 88 | ||||
-rw-r--r-- | Documentation/hwmon/ads1015 | 49 | ||||
-rw-r--r-- | drivers/hwmon/ads1015.c | 149 | ||||
-rw-r--r-- | include/linux/i2c/ads1015.h | 10 |
4 files changed, 211 insertions, 85 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ads1015.txt b/Documentation/devicetree/bindings/hwmon/ads1015.txt index 0f30616384c5..918a507d1159 100644 --- a/Documentation/devicetree/bindings/hwmon/ads1015.txt +++ b/Documentation/devicetree/bindings/hwmon/ads1015.txt | |||
@@ -5,25 +5,69 @@ This device is a 12-bit A-D converter with 4 inputs. | |||
5 | The inputs can be used single ended or in certain differential combinations. | 5 | The inputs can be used single ended or in certain differential combinations. |
6 | 6 | ||
7 | For configuration all possible combinations are mapped to 8 channels: | 7 | For configuration all possible combinations are mapped to 8 channels: |
8 | 0: Voltage over AIN0 and AIN1. | 8 | 0: Voltage over AIN0 and AIN1. |
9 | 1: Voltage over AIN0 and AIN3. | 9 | 1: Voltage over AIN0 and AIN3. |
10 | 2: Voltage over AIN1 and AIN3. | 10 | 2: Voltage over AIN1 and AIN3. |
11 | 3: Voltage over AIN2 and AIN3. | 11 | 3: Voltage over AIN2 and AIN3. |
12 | 4: Voltage over AIN0 and GND. | 12 | 4: Voltage over AIN0 and GND. |
13 | 5: Voltage over AIN1 and GND. | 13 | 5: Voltage over AIN1 and GND. |
14 | 6: Voltage over AIN2 and GND. | 14 | 6: Voltage over AIN2 and GND. |
15 | 7: Voltage over AIN3 and GND. | 15 | 7: Voltage over AIN3 and GND. |
16 | 16 | ||
17 | Optional properties: | 17 | Each channel can be configured individually: |
18 | 18 | - pga is the programmable gain amplifier (values are full scale) | |
19 | - exported-channels : exported_channels is a bitmask that specifies which | 19 | 0: +/- 6.144 V |
20 | channels should be accessable by the user. | 20 | 1: +/- 4.096 V |
21 | 21 | 2: +/- 2.048 V (default) | |
22 | Example: | 22 | 3: +/- 1.024 V |
23 | ads1015@49 { | 23 | 4: +/- 0.512 V |
24 | compatible = "ti,ads1015"; | 24 | 5: +/- 0.256 V |
25 | reg = <0x49>; | 25 | - data_rate in samples per second |
26 | exported-channels = <0x14>; | 26 | 0: 128 |
27 | }; | 27 | 1: 250 |
28 | 28 | 2: 490 | |
29 | In this example only channel 2 and 4 would be accessable by the user. | 29 | 3: 920 |
30 | 4: 1600 (default) | ||
31 | 5: 2400 | ||
32 | 6: 3300 | ||
33 | |||
34 | 1) The /ads1015 node | ||
35 | |||
36 | Required properties: | ||
37 | |||
38 | - compatible : must be "ti,ads1015" | ||
39 | - reg : I2C bus address of the device | ||
40 | - #address-cells : must be <1> | ||
41 | - #size-cells : must be <0> | ||
42 | |||
43 | The node contains child nodes for each channel that the platform uses. | ||
44 | |||
45 | Example ADS1015 node: | ||
46 | |||
47 | ads1015@49 { | ||
48 | compatible = "ti,ads1015"; | ||
49 | reg = <0x49>; | ||
50 | #address-cells = <1>; | ||
51 | #size-cells = <0>; | ||
52 | |||
53 | [ child node definitions... ] | ||
54 | } | ||
55 | |||
56 | 2) channel nodes | ||
57 | |||
58 | Required properties: | ||
59 | |||
60 | - reg : the channel number | ||
61 | |||
62 | Optional properties: | ||
63 | |||
64 | - ti,gain : the programmable gain amplifier setting | ||
65 | - ti,datarate : the converter data rate | ||
66 | |||
67 | Example ADS1015 channel node: | ||
68 | |||
69 | channel@4 { | ||
70 | reg = <4>; | ||
71 | ti,gain = <3>; | ||
72 | ti,datarate = <5>; | ||
73 | }; | ||
diff --git a/Documentation/hwmon/ads1015 b/Documentation/hwmon/ads1015 index 56ee7977b1a8..f6fe9c203733 100644 --- a/Documentation/hwmon/ads1015 +++ b/Documentation/hwmon/ads1015 | |||
@@ -19,7 +19,7 @@ This device is a 12-bit A-D converter with 4 inputs. | |||
19 | 19 | ||
20 | The inputs can be used single ended or in certain differential combinations. | 20 | The inputs can be used single ended or in certain differential combinations. |
21 | 21 | ||
22 | The inputs can be exported to 8 sysfs input files in0_input - in7_input: | 22 | The inputs can be made available by 8 sysfs input files in0_input - in7_input: |
23 | in0: Voltage over AIN0 and AIN1. | 23 | in0: Voltage over AIN0 and AIN1. |
24 | in1: Voltage over AIN0 and AIN3. | 24 | in1: Voltage over AIN0 and AIN3. |
25 | in2: Voltage over AIN1 and AIN3. | 25 | in2: Voltage over AIN1 and AIN3. |
@@ -29,39 +29,44 @@ in5: Voltage over AIN1 and GND. | |||
29 | in6: Voltage over AIN2 and GND. | 29 | in6: Voltage over AIN2 and GND. |
30 | in7: Voltage over AIN3 and GND. | 30 | in7: Voltage over AIN3 and GND. |
31 | 31 | ||
32 | Which inputs are exported can be configured using platform data or devicetree. | 32 | Which inputs are available can be configured using platform data or devicetree. |
33 | 33 | ||
34 | By default all inputs are exported. | 34 | By default all inputs are exported. |
35 | 35 | ||
36 | Platform Data | 36 | Platform Data |
37 | ------------- | 37 | ------------- |
38 | 38 | ||
39 | In linux/i2c/ads1015.h platform data is defined as: | 39 | In linux/i2c/ads1015.h platform data is defined, channel_data contains |
40 | 40 | configuration data for the used input combinations: | |
41 | struct ads1015_platform_data { | 41 | - pga is the programmable gain amplifier (values are full scale) |
42 | unsigned int exported_channels; | 42 | 0: +/- 6.144 V |
43 | }; | 43 | 1: +/- 4.096 V |
44 | 44 | 2: +/- 2.048 V | |
45 | exported_channels is a bitmask that specifies which inputs should be exported. | 45 | 3: +/- 1.024 V |
46 | 4: +/- 0.512 V | ||
47 | 5: +/- 0.256 V | ||
48 | - data_rate in samples per second | ||
49 | 0: 128 | ||
50 | 1: 250 | ||
51 | 2: 490 | ||
52 | 3: 920 | ||
53 | 4: 1600 | ||
54 | 5: 2400 | ||
55 | 6: 3300 | ||
46 | 56 | ||
47 | Example: | 57 | Example: |
48 | struct ads1015_platform_data data = { | 58 | struct ads1015_platform_data data = { |
49 | .exported_channels = (1 << 2) | (1 << 4) | 59 | .channel_data = { |
60 | [2] = { .enabled = true, .pga = 1, .data_rate = 0 }, | ||
61 | [4] = { .enabled = true, .pga = 4, .data_rate = 5 }, | ||
62 | } | ||
50 | }; | 63 | }; |
51 | 64 | ||
52 | In this case only in2_input and in4_input would be created. | 65 | In this case only in2_input (FS +/- 4.096 V, 128 SPS) and in4_input |
66 | (FS +/- 0.512 V, 2400 SPS) would be created. | ||
53 | 67 | ||
54 | Devicetree | 68 | Devicetree |
55 | ---------- | 69 | ---------- |
56 | 70 | ||
57 | The ads1015 node may have an "exported-channels" property. | 71 | Configuration is also possible via devicetree: |
58 | exported_channels is a bitmask that specifies which inputs should be exported. | 72 | Documentation/devicetree/bindings/hwmon/ads1015.txt |
59 | |||
60 | Example: | ||
61 | ads1015@49 { | ||
62 | compatible = "ti,ads1015"; | ||
63 | reg = <0x49>; | ||
64 | exported-channels = < 0x14 >; | ||
65 | }; | ||
66 | |||
67 | In this case only in2_input and in4_input would be created. | ||
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index fa02f20b79ff..e9beeda4cbe5 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c | |||
@@ -45,12 +45,18 @@ enum { | |||
45 | static const unsigned int fullscale_table[8] = { | 45 | static const unsigned int fullscale_table[8] = { |
46 | 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; | 46 | 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; |
47 | 47 | ||
48 | #define ADS1015_CONFIG_CHANNELS 8 | 48 | /* Data rates in samples per second */ |
49 | static const unsigned int data_rate_table[8] = { | ||
50 | 128, 250, 490, 920, 1600, 2400, 3300, 3300 }; | ||
51 | |||
49 | #define ADS1015_DEFAULT_CHANNELS 0xff | 52 | #define ADS1015_DEFAULT_CHANNELS 0xff |
53 | #define ADS1015_DEFAULT_PGA 2 | ||
54 | #define ADS1015_DEFAULT_DATA_RATE 4 | ||
50 | 55 | ||
51 | struct ads1015_data { | 56 | struct ads1015_data { |
52 | struct device *hwmon_dev; | 57 | struct device *hwmon_dev; |
53 | struct mutex update_lock; /* mutex protect updates */ | 58 | struct mutex update_lock; /* mutex protect updates */ |
59 | struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; | ||
54 | }; | 60 | }; |
55 | 61 | ||
56 | static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg) | 62 | static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg) |
@@ -71,40 +77,42 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel, | |||
71 | { | 77 | { |
72 | u16 config; | 78 | u16 config; |
73 | s16 conversion; | 79 | s16 conversion; |
74 | unsigned int pga; | ||
75 | int fullscale; | ||
76 | unsigned int k; | ||
77 | struct ads1015_data *data = i2c_get_clientdata(client); | 80 | struct ads1015_data *data = i2c_get_clientdata(client); |
81 | unsigned int pga = data->channel_data[channel].pga; | ||
82 | int fullscale; | ||
83 | unsigned int data_rate = data->channel_data[channel].data_rate; | ||
84 | unsigned int conversion_time_ms; | ||
78 | int res; | 85 | int res; |
79 | 86 | ||
80 | mutex_lock(&data->update_lock); | 87 | mutex_lock(&data->update_lock); |
81 | 88 | ||
82 | /* get fullscale voltage */ | 89 | /* get channel parameters */ |
83 | res = ads1015_read_reg(client, ADS1015_CONFIG); | 90 | res = ads1015_read_reg(client, ADS1015_CONFIG); |
84 | if (res < 0) | 91 | if (res < 0) |
85 | goto err_unlock; | 92 | goto err_unlock; |
86 | config = res; | 93 | config = res; |
87 | pga = (config >> 9) & 0x0007; | ||
88 | fullscale = fullscale_table[pga]; | 94 | fullscale = fullscale_table[pga]; |
95 | conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]); | ||
89 | 96 | ||
90 | /* set channel and start single conversion */ | 97 | /* setup and start single conversion */ |
91 | config &= ~(0x0007 << 12); | 98 | config &= 0x001f; |
92 | config |= (1 << 15) | (1 << 8) | (channel & 0x0007) << 12; | 99 | config |= (1 << 15) | (1 << 8); |
100 | config |= (channel & 0x0007) << 12; | ||
101 | config |= (pga & 0x0007) << 9; | ||
102 | config |= (data_rate & 0x0007) << 5; | ||
93 | 103 | ||
94 | /* wait until conversion finished */ | ||
95 | res = ads1015_write_reg(client, ADS1015_CONFIG, config); | 104 | res = ads1015_write_reg(client, ADS1015_CONFIG, config); |
96 | if (res < 0) | 105 | if (res < 0) |
97 | goto err_unlock; | 106 | goto err_unlock; |
98 | for (k = 0; k < 5; ++k) { | 107 | |
99 | msleep(1); | 108 | /* wait until conversion finished */ |
100 | res = ads1015_read_reg(client, ADS1015_CONFIG); | 109 | msleep(conversion_time_ms); |
101 | if (res < 0) | 110 | res = ads1015_read_reg(client, ADS1015_CONFIG); |
102 | goto err_unlock; | 111 | if (res < 0) |
103 | config = res; | 112 | goto err_unlock; |
104 | if (config & (1 << 15)) | 113 | config = res; |
105 | break; | 114 | if (!(config & (1 << 15))) { |
106 | } | 115 | /* conversion not finished in time */ |
107 | if (k == 5) { | ||
108 | res = -EIO; | 116 | res = -EIO; |
109 | goto err_unlock; | 117 | goto err_unlock; |
110 | } | 118 | } |
@@ -160,35 +168,97 @@ static int ads1015_remove(struct i2c_client *client) | |||
160 | int k; | 168 | int k; |
161 | 169 | ||
162 | hwmon_device_unregister(data->hwmon_dev); | 170 | hwmon_device_unregister(data->hwmon_dev); |
163 | for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) | 171 | for (k = 0; k < ADS1015_CHANNELS; ++k) |
164 | device_remove_file(&client->dev, &ads1015_in[k].dev_attr); | 172 | device_remove_file(&client->dev, &ads1015_in[k].dev_attr); |
165 | kfree(data); | 173 | kfree(data); |
166 | return 0; | 174 | return 0; |
167 | } | 175 | } |
168 | 176 | ||
169 | static unsigned int ads1015_get_exported_channels(struct i2c_client *client) | ||
170 | { | ||
171 | struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); | ||
172 | #ifdef CONFIG_OF | 177 | #ifdef CONFIG_OF |
173 | struct device_node *np = client->dev.of_node; | 178 | static int ads1015_get_channels_config_of(struct i2c_client *client) |
174 | const __be32 *of_channels; | 179 | { |
175 | int of_channels_size; | 180 | struct ads1015_data *data = i2c_get_clientdata(client); |
181 | struct device_node *node; | ||
182 | |||
183 | if (!client->dev.of_node | ||
184 | || !of_get_next_child(client->dev.of_node, NULL)) | ||
185 | return -EINVAL; | ||
186 | |||
187 | for_each_child_of_node(client->dev.of_node, node) { | ||
188 | const __be32 *property; | ||
189 | int len; | ||
190 | unsigned int channel; | ||
191 | unsigned int pga = ADS1015_DEFAULT_PGA; | ||
192 | unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; | ||
193 | |||
194 | property = of_get_property(node, "reg", &len); | ||
195 | if (!property || len != sizeof(int)) { | ||
196 | dev_err(&client->dev, "invalid reg on %s\n", | ||
197 | node->full_name); | ||
198 | continue; | ||
199 | } | ||
200 | |||
201 | channel = be32_to_cpup(property); | ||
202 | if (channel > ADS1015_CHANNELS) { | ||
203 | dev_err(&client->dev, | ||
204 | "invalid channel index %d on %s\n", | ||
205 | channel, node->full_name); | ||
206 | continue; | ||
207 | } | ||
208 | |||
209 | property = of_get_property(node, "ti,gain", &len); | ||
210 | if (property && len == sizeof(int)) { | ||
211 | pga = be32_to_cpup(property); | ||
212 | if (pga > 6) { | ||
213 | dev_err(&client->dev, | ||
214 | "invalid gain on %s\n", | ||
215 | node->full_name); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | property = of_get_property(node, "ti,datarate", &len); | ||
220 | if (property && len == sizeof(int)) { | ||
221 | data_rate = be32_to_cpup(property); | ||
222 | if (data_rate > 7) { | ||
223 | dev_err(&client->dev, | ||
224 | "invalid data_rate on %s\n", | ||
225 | node->full_name); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | data->channel_data[channel].enabled = true; | ||
230 | data->channel_data[channel].pga = pga; | ||
231 | data->channel_data[channel].data_rate = data_rate; | ||
232 | } | ||
233 | |||
234 | return 0; | ||
235 | } | ||
176 | #endif | 236 | #endif |
177 | 237 | ||
238 | static void ads1015_get_channels_config(struct i2c_client *client) | ||
239 | { | ||
240 | unsigned int k; | ||
241 | struct ads1015_data *data = i2c_get_clientdata(client); | ||
242 | struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); | ||
243 | |||
178 | /* prefer platform data */ | 244 | /* prefer platform data */ |
179 | if (pdata) | 245 | if (pdata) { |
180 | return pdata->exported_channels; | 246 | memcpy(data->channel_data, pdata->channel_data, |
247 | sizeof(data->channel_data)); | ||
248 | return; | ||
249 | } | ||
181 | 250 | ||
182 | #ifdef CONFIG_OF | 251 | #ifdef CONFIG_OF |
183 | /* fallback on OF */ | 252 | if (!ads1015_get_channels_config_of(client)) |
184 | of_channels = of_get_property(np, "exported-channels", | 253 | return; |
185 | &of_channels_size); | ||
186 | if (of_channels && (of_channels_size == sizeof(*of_channels))) | ||
187 | return be32_to_cpup(of_channels); | ||
188 | #endif | 254 | #endif |
189 | 255 | ||
190 | /* fallback on default configuration */ | 256 | /* fallback on default configuration */ |
191 | return ADS1015_DEFAULT_CHANNELS; | 257 | for (k = 0; k < ADS1015_CHANNELS; ++k) { |
258 | data->channel_data[k].enabled = true; | ||
259 | data->channel_data[k].pga = ADS1015_DEFAULT_PGA; | ||
260 | data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; | ||
261 | } | ||
192 | } | 262 | } |
193 | 263 | ||
194 | static int ads1015_probe(struct i2c_client *client, | 264 | static int ads1015_probe(struct i2c_client *client, |
@@ -196,7 +266,6 @@ static int ads1015_probe(struct i2c_client *client, | |||
196 | { | 266 | { |
197 | struct ads1015_data *data; | 267 | struct ads1015_data *data; |
198 | int err; | 268 | int err; |
199 | unsigned int exported_channels; | ||
200 | unsigned int k; | 269 | unsigned int k; |
201 | 270 | ||
202 | data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL); | 271 | data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL); |
@@ -209,9 +278,9 @@ static int ads1015_probe(struct i2c_client *client, | |||
209 | mutex_init(&data->update_lock); | 278 | mutex_init(&data->update_lock); |
210 | 279 | ||
211 | /* build sysfs attribute group */ | 280 | /* build sysfs attribute group */ |
212 | exported_channels = ads1015_get_exported_channels(client); | 281 | ads1015_get_channels_config(client); |
213 | for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) { | 282 | for (k = 0; k < ADS1015_CHANNELS; ++k) { |
214 | if (!(exported_channels & (1<<k))) | 283 | if (!data->channel_data[k].enabled) |
215 | continue; | 284 | continue; |
216 | err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); | 285 | err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); |
217 | if (err) | 286 | if (err) |
@@ -227,7 +296,7 @@ static int ads1015_probe(struct i2c_client *client, | |||
227 | return 0; | 296 | return 0; |
228 | 297 | ||
229 | exit_remove: | 298 | exit_remove: |
230 | for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) | 299 | for (k = 0; k < ADS1015_CHANNELS; ++k) |
231 | device_remove_file(&client->dev, &ads1015_in[k].dev_attr); | 300 | device_remove_file(&client->dev, &ads1015_in[k].dev_attr); |
232 | exit_free: | 301 | exit_free: |
233 | kfree(data); | 302 | kfree(data); |
diff --git a/include/linux/i2c/ads1015.h b/include/linux/i2c/ads1015.h index 8541c6acfafd..d5aa2a045669 100644 --- a/include/linux/i2c/ads1015.h +++ b/include/linux/i2c/ads1015.h | |||
@@ -21,8 +21,16 @@ | |||
21 | #ifndef LINUX_ADS1015_H | 21 | #ifndef LINUX_ADS1015_H |
22 | #define LINUX_ADS1015_H | 22 | #define LINUX_ADS1015_H |
23 | 23 | ||
24 | #define ADS1015_CHANNELS 8 | ||
25 | |||
26 | struct ads1015_channel_data { | ||
27 | bool enabled; | ||
28 | unsigned int pga; | ||
29 | unsigned int data_rate; | ||
30 | }; | ||
31 | |||
24 | struct ads1015_platform_data { | 32 | struct ads1015_platform_data { |
25 | unsigned int exported_channels; | 33 | struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; |
26 | }; | 34 | }; |
27 | 35 | ||
28 | #endif /* LINUX_ADS1015_H */ | 36 | #endif /* LINUX_ADS1015_H */ |