Commit c0046867 authored by Dirk Eibach's avatar Dirk Eibach Committed by Jean Delvare

hwmon: (ads1015) Make gain and datarate configurable

Configuration for ads1015 gain and datarate is possible via
devicetree or platform data.

This is a followup patch to previous ads1015 patches on Jean Delvares
tree.
Signed-off-by: default avatarDirk Eibach <eibach@gdsys.de>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent fdf241a8
...@@ -5,25 +5,69 @@ This device is a 12-bit A-D converter with 4 inputs. ...@@ -5,25 +5,69 @@ This device is a 12-bit A-D converter with 4 inputs.
The inputs can be used single ended or in certain differential combinations. The inputs can be used single ended or in certain differential combinations.
For configuration all possible combinations are mapped to 8 channels: For configuration all possible combinations are mapped to 8 channels:
0: Voltage over AIN0 and AIN1. 0: Voltage over AIN0 and AIN1.
1: Voltage over AIN0 and AIN3. 1: Voltage over AIN0 and AIN3.
2: Voltage over AIN1 and AIN3. 2: Voltage over AIN1 and AIN3.
3: Voltage over AIN2 and AIN3. 3: Voltage over AIN2 and AIN3.
4: Voltage over AIN0 and GND. 4: Voltage over AIN0 and GND.
5: Voltage over AIN1 and GND. 5: Voltage over AIN1 and GND.
6: Voltage over AIN2 and GND. 6: Voltage over AIN2 and GND.
7: Voltage over AIN3 and GND. 7: Voltage over AIN3 and GND.
Optional properties: Each channel can be configured individually:
- pga is the programmable gain amplifier (values are full scale)
- exported-channels : exported_channels is a bitmask that specifies which 0: +/- 6.144 V
channels should be accessable by the user. 1: +/- 4.096 V
2: +/- 2.048 V (default)
Example: 3: +/- 1.024 V
ads1015@49 { 4: +/- 0.512 V
compatible = "ti,ads1015"; 5: +/- 0.256 V
reg = <0x49>; - data_rate in samples per second
exported-channels = <0x14>; 0: 128
}; 1: 250
2: 490
In this example only channel 2 and 4 would be accessable by the user. 3: 920
4: 1600 (default)
5: 2400
6: 3300
1) The /ads1015 node
Required properties:
- compatible : must be "ti,ads1015"
- reg : I2C bus address of the device
- #address-cells : must be <1>
- #size-cells : must be <0>
The node contains child nodes for each channel that the platform uses.
Example ADS1015 node:
ads1015@49 {
compatible = "ti,ads1015";
reg = <0x49>;
#address-cells = <1>;
#size-cells = <0>;
[ child node definitions... ]
}
2) channel nodes
Required properties:
- reg : the channel number
Optional properties:
- ti,gain : the programmable gain amplifier setting
- ti,datarate : the converter data rate
Example ADS1015 channel node:
channel@4 {
reg = <4>;
ti,gain = <3>;
ti,datarate = <5>;
};
...@@ -19,7 +19,7 @@ This device is a 12-bit A-D converter with 4 inputs. ...@@ -19,7 +19,7 @@ This device is a 12-bit A-D converter with 4 inputs.
The inputs can be used single ended or in certain differential combinations. The inputs can be used single ended or in certain differential combinations.
The inputs can be exported to 8 sysfs input files in0_input - in7_input: The inputs can be made available by 8 sysfs input files in0_input - in7_input:
in0: Voltage over AIN0 and AIN1. in0: Voltage over AIN0 and AIN1.
in1: Voltage over AIN0 and AIN3. in1: Voltage over AIN0 and AIN3.
in2: Voltage over AIN1 and AIN3. in2: Voltage over AIN1 and AIN3.
...@@ -29,39 +29,44 @@ in5: Voltage over AIN1 and GND. ...@@ -29,39 +29,44 @@ in5: Voltage over AIN1 and GND.
in6: Voltage over AIN2 and GND. in6: Voltage over AIN2 and GND.
in7: Voltage over AIN3 and GND. in7: Voltage over AIN3 and GND.
Which inputs are exported can be configured using platform data or devicetree. Which inputs are available can be configured using platform data or devicetree.
By default all inputs are exported. By default all inputs are exported.
Platform Data Platform Data
------------- -------------
In linux/i2c/ads1015.h platform data is defined as: In linux/i2c/ads1015.h platform data is defined, channel_data contains
configuration data for the used input combinations:
struct ads1015_platform_data { - pga is the programmable gain amplifier (values are full scale)
unsigned int exported_channels; 0: +/- 6.144 V
}; 1: +/- 4.096 V
2: +/- 2.048 V
exported_channels is a bitmask that specifies which inputs should be exported. 3: +/- 1.024 V
4: +/- 0.512 V
5: +/- 0.256 V
- data_rate in samples per second
0: 128
1: 250
2: 490
3: 920
4: 1600
5: 2400
6: 3300
Example: Example:
struct ads1015_platform_data data = { struct ads1015_platform_data data = {
.exported_channels = (1 << 2) | (1 << 4) .channel_data = {
[2] = { .enabled = true, .pga = 1, .data_rate = 0 },
[4] = { .enabled = true, .pga = 4, .data_rate = 5 },
}
}; };
In this case only in2_input and in4_input would be created. In this case only in2_input (FS +/- 4.096 V, 128 SPS) and in4_input
(FS +/- 0.512 V, 2400 SPS) would be created.
Devicetree Devicetree
---------- ----------
The ads1015 node may have an "exported-channels" property. Configuration is also possible via devicetree:
exported_channels is a bitmask that specifies which inputs should be exported. Documentation/devicetree/bindings/hwmon/ads1015.txt
Example:
ads1015@49 {
compatible = "ti,ads1015";
reg = <0x49>;
exported-channels = < 0x14 >;
};
In this case only in2_input and in4_input would be created.
...@@ -45,12 +45,18 @@ enum { ...@@ -45,12 +45,18 @@ enum {
static const unsigned int fullscale_table[8] = { static const unsigned int fullscale_table[8] = {
6144, 4096, 2048, 1024, 512, 256, 256, 256 }; 6144, 4096, 2048, 1024, 512, 256, 256, 256 };
#define ADS1015_CONFIG_CHANNELS 8 /* Data rates in samples per second */
static const unsigned int data_rate_table[8] = {
128, 250, 490, 920, 1600, 2400, 3300, 3300 };
#define ADS1015_DEFAULT_CHANNELS 0xff #define ADS1015_DEFAULT_CHANNELS 0xff
#define ADS1015_DEFAULT_PGA 2
#define ADS1015_DEFAULT_DATA_RATE 4
struct ads1015_data { struct ads1015_data {
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex update_lock; /* mutex protect updates */ struct mutex update_lock; /* mutex protect updates */
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
}; };
static s32 ads1015_read_reg(struct i2c_client *client, unsigned int reg) 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,40 +77,42 @@ static int ads1015_read_value(struct i2c_client *client, unsigned int channel,
{ {
u16 config; u16 config;
s16 conversion; s16 conversion;
unsigned int pga;
int fullscale;
unsigned int k;
struct ads1015_data *data = i2c_get_clientdata(client); struct ads1015_data *data = i2c_get_clientdata(client);
unsigned int pga = data->channel_data[channel].pga;
int fullscale;
unsigned int data_rate = data->channel_data[channel].data_rate;
unsigned int conversion_time_ms;
int res; int res;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
/* get fullscale voltage */ /* get channel parameters */
res = ads1015_read_reg(client, ADS1015_CONFIG); res = ads1015_read_reg(client, ADS1015_CONFIG);
if (res < 0) if (res < 0)
goto err_unlock; goto err_unlock;
config = res; config = res;
pga = (config >> 9) & 0x0007;
fullscale = fullscale_table[pga]; fullscale = fullscale_table[pga];
conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]);
/* set channel and start single conversion */ /* setup and start single conversion */
config &= ~(0x0007 << 12); config &= 0x001f;
config |= (1 << 15) | (1 << 8) | (channel & 0x0007) << 12; config |= (1 << 15) | (1 << 8);
config |= (channel & 0x0007) << 12;
config |= (pga & 0x0007) << 9;
config |= (data_rate & 0x0007) << 5;
/* wait until conversion finished */
res = ads1015_write_reg(client, ADS1015_CONFIG, config); res = ads1015_write_reg(client, ADS1015_CONFIG, config);
if (res < 0) if (res < 0)
goto err_unlock; goto err_unlock;
for (k = 0; k < 5; ++k) {
msleep(1); /* wait until conversion finished */
res = ads1015_read_reg(client, ADS1015_CONFIG); msleep(conversion_time_ms);
if (res < 0) res = ads1015_read_reg(client, ADS1015_CONFIG);
goto err_unlock; if (res < 0)
config = res; goto err_unlock;
if (config & (1 << 15)) config = res;
break; if (!(config & (1 << 15))) {
} /* conversion not finished in time */
if (k == 5) {
res = -EIO; res = -EIO;
goto err_unlock; goto err_unlock;
} }
...@@ -160,35 +168,97 @@ static int ads1015_remove(struct i2c_client *client) ...@@ -160,35 +168,97 @@ static int ads1015_remove(struct i2c_client *client)
int k; int k;
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr); device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
kfree(data); kfree(data);
return 0; return 0;
} }
static unsigned int ads1015_get_exported_channels(struct i2c_client *client)
{
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
#ifdef CONFIG_OF #ifdef CONFIG_OF
struct device_node *np = client->dev.of_node; static int ads1015_get_channels_config_of(struct i2c_client *client)
const __be32 *of_channels; {
int of_channels_size; struct ads1015_data *data = i2c_get_clientdata(client);
struct device_node *node;
if (!client->dev.of_node
|| !of_get_next_child(client->dev.of_node, NULL))
return -EINVAL;
for_each_child_of_node(client->dev.of_node, node) {
const __be32 *property;
int len;
unsigned int channel;
unsigned int pga = ADS1015_DEFAULT_PGA;
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
property = of_get_property(node, "reg", &len);
if (!property || len != sizeof(int)) {
dev_err(&client->dev, "invalid reg on %s\n",
node->full_name);
continue;
}
channel = be32_to_cpup(property);
if (channel > ADS1015_CHANNELS) {
dev_err(&client->dev,
"invalid channel index %d on %s\n",
channel, node->full_name);
continue;
}
property = of_get_property(node, "ti,gain", &len);
if (property && len == sizeof(int)) {
pga = be32_to_cpup(property);
if (pga > 6) {
dev_err(&client->dev,
"invalid gain on %s\n",
node->full_name);
}
}
property = of_get_property(node, "ti,datarate", &len);
if (property && len == sizeof(int)) {
data_rate = be32_to_cpup(property);
if (data_rate > 7) {
dev_err(&client->dev,
"invalid data_rate on %s\n",
node->full_name);
}
}
data->channel_data[channel].enabled = true;
data->channel_data[channel].pga = pga;
data->channel_data[channel].data_rate = data_rate;
}
return 0;
}
#endif #endif
static void ads1015_get_channels_config(struct i2c_client *client)
{
unsigned int k;
struct ads1015_data *data = i2c_get_clientdata(client);
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
/* prefer platform data */ /* prefer platform data */
if (pdata) if (pdata) {
return pdata->exported_channels; memcpy(data->channel_data, pdata->channel_data,
sizeof(data->channel_data));
return;
}
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* fallback on OF */ if (!ads1015_get_channels_config_of(client))
of_channels = of_get_property(np, "exported-channels", return;
&of_channels_size);
if (of_channels && (of_channels_size == sizeof(*of_channels)))
return be32_to_cpup(of_channels);
#endif #endif
/* fallback on default configuration */ /* fallback on default configuration */
return ADS1015_DEFAULT_CHANNELS; for (k = 0; k < ADS1015_CHANNELS; ++k) {
data->channel_data[k].enabled = true;
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
}
} }
static int ads1015_probe(struct i2c_client *client, static int ads1015_probe(struct i2c_client *client,
...@@ -196,7 +266,6 @@ static int ads1015_probe(struct i2c_client *client, ...@@ -196,7 +266,6 @@ static int ads1015_probe(struct i2c_client *client,
{ {
struct ads1015_data *data; struct ads1015_data *data;
int err; int err;
unsigned int exported_channels;
unsigned int k; unsigned int k;
data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL); data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL);
...@@ -209,9 +278,9 @@ static int ads1015_probe(struct i2c_client *client, ...@@ -209,9 +278,9 @@ static int ads1015_probe(struct i2c_client *client,
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* build sysfs attribute group */ /* build sysfs attribute group */
exported_channels = ads1015_get_exported_channels(client); ads1015_get_channels_config(client);
for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) { for (k = 0; k < ADS1015_CHANNELS; ++k) {
if (!(exported_channels & (1<<k))) if (!data->channel_data[k].enabled)
continue; continue;
err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); err = device_create_file(&client->dev, &ads1015_in[k].dev_attr);
if (err) if (err)
...@@ -227,7 +296,7 @@ static int ads1015_probe(struct i2c_client *client, ...@@ -227,7 +296,7 @@ static int ads1015_probe(struct i2c_client *client,
return 0; return 0;
exit_remove: exit_remove:
for (k = 0; k < ADS1015_CONFIG_CHANNELS; ++k) for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr); device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
exit_free: exit_free:
kfree(data); kfree(data);
......
...@@ -21,8 +21,16 @@ ...@@ -21,8 +21,16 @@
#ifndef LINUX_ADS1015_H #ifndef LINUX_ADS1015_H
#define LINUX_ADS1015_H #define LINUX_ADS1015_H
#define ADS1015_CHANNELS 8
struct ads1015_channel_data {
bool enabled;
unsigned int pga;
unsigned int data_rate;
};
struct ads1015_platform_data { struct ads1015_platform_data {
unsigned int exported_channels; struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
}; };
#endif /* LINUX_ADS1015_H */ #endif /* LINUX_ADS1015_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment