Blame view

drivers/hwmon/ads7828.c 7 KB
5812f9283   Steve Hardy   hwmon: Add suppor...
1
  /*
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
2
   * ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles
d13d6232d   Guenter Roeck   hwmon: (ads7828) ...
3
4
5
6
7
8
   * (C) 2007 EADS Astrium
   *
   * This driver is based on the lm75 and other lm_sensors/hwmon drivers
   *
   * Written by Steve Hardy <shardy@redhat.com>
   *
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
9
10
   * ADS7830 support, by Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
   *
46d784629   Vivien Didelot   hwmon: (ads7828) ...
11
   * For further information, see the Documentation/hwmon/ads7828 file.
d13d6232d   Guenter Roeck   hwmon: (ads7828) ...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
5812f9283   Steve Hardy   hwmon: Add suppor...
27

46d784629   Vivien Didelot   hwmon: (ads7828) ...
28
  #include <linux/err.h>
5812f9283   Steve Hardy   hwmon: Add suppor...
29
30
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
46d784629   Vivien Didelot   hwmon: (ads7828) ...
31
32
33
34
  #include <linux/i2c.h>
  #include <linux/init.h>
  #include <linux/jiffies.h>
  #include <linux/module.h>
5812f9283   Steve Hardy   hwmon: Add suppor...
35
  #include <linux/mutex.h>
46d784629   Vivien Didelot   hwmon: (ads7828) ...
36
37
  #include <linux/platform_data/ads7828.h>
  #include <linux/slab.h>
5812f9283   Steve Hardy   hwmon: Add suppor...
38
39
  
  /* The ADS7828 registers */
46d784629   Vivien Didelot   hwmon: (ads7828) ...
40
41
42
43
44
45
46
  #define ADS7828_NCH		8	/* 8 channels supported */
  #define ADS7828_CMD_SD_SE	0x80	/* Single ended inputs */
  #define ADS7828_CMD_PD1		0x04	/* Internal vref OFF && A/D ON */
  #define ADS7828_CMD_PD3		0x0C	/* Internal vref ON && A/D ON */
  #define ADS7828_INT_VREF_MV	2500	/* Internal vref is 2.5V, 2500mV */
  #define ADS7828_EXT_VREF_MV_MIN	50	/* External vref min value 0.05V */
  #define ADS7828_EXT_VREF_MV_MAX	5250	/* External vref max value 5.25V */
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
47
48
  /* List of supported devices */
  enum ads7828_chips { ads7828, ads7830 };
46d784629   Vivien Didelot   hwmon: (ads7828) ...
49
  /* Client specific data */
5812f9283   Steve Hardy   hwmon: Add suppor...
50
  struct ads7828_data {
5812f9283   Steve Hardy   hwmon: Add suppor...
51
  	struct device *hwmon_dev;
46d784629   Vivien Didelot   hwmon: (ads7828) ...
52
53
54
55
56
57
58
59
60
  	struct mutex update_lock;	/* Mutex protecting updates */
  	unsigned long last_updated;	/* Last updated time (in jiffies) */
  	u16 adc_input[ADS7828_NCH];	/* ADS7828_NCH samples */
  	bool valid;			/* Validity flag */
  	bool diff_input;		/* Differential input */
  	bool ext_vref;			/* External voltage reference */
  	unsigned int vref_mv;		/* voltage reference value */
  	u8 cmd_byte;			/* Command byte without channel bits */
  	unsigned int lsb_resol;		/* Resolution of the ADC sample LSB */
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
61
  	s32 (*read_channel)(const struct i2c_client *client, u8 command);
5812f9283   Steve Hardy   hwmon: Add suppor...
62
  };
46d784629   Vivien Didelot   hwmon: (ads7828) ...
63
64
  /* Command byte C2,C1,C0 - see datasheet */
  static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
5812f9283   Steve Hardy   hwmon: Add suppor...
65
  {
46d784629   Vivien Didelot   hwmon: (ads7828) ...
66
  	return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
5812f9283   Steve Hardy   hwmon: Add suppor...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  }
  
  /* Update data for the device (all 8 channels) */
  static struct ads7828_data *ads7828_update_device(struct device *dev)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ads7828_data *data = i2c_get_clientdata(client);
  
  	mutex_lock(&data->update_lock);
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  			|| !data->valid) {
  		unsigned int ch;
  		dev_dbg(&client->dev, "Starting ads7828 update
  ");
  
  		for (ch = 0; ch < ADS7828_NCH; ch++) {
46d784629   Vivien Didelot   hwmon: (ads7828) ...
84
  			u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch);
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
85
  			data->adc_input[ch] = data->read_channel(client, cmd);
5812f9283   Steve Hardy   hwmon: Add suppor...
86
87
  		}
  		data->last_updated = jiffies;
46d784629   Vivien Didelot   hwmon: (ads7828) ...
88
  		data->valid = true;
5812f9283   Steve Hardy   hwmon: Add suppor...
89
90
91
92
93
94
95
96
  	}
  
  	mutex_unlock(&data->update_lock);
  
  	return data;
  }
  
  /* sysfs callback function */
46d784629   Vivien Didelot   hwmon: (ads7828) ...
97
98
  static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da,
  			       char *buf)
5812f9283   Steve Hardy   hwmon: Add suppor...
99
100
101
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  	struct ads7828_data *data = ads7828_update_device(dev);
46d784629   Vivien Didelot   hwmon: (ads7828) ...
102
103
  	unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] *
  					       data->lsb_resol, 1000);
5812f9283   Steve Hardy   hwmon: Add suppor...
104

46d784629   Vivien Didelot   hwmon: (ads7828) ...
105
106
107
  	return sprintf(buf, "%d
  ", value);
  }
5812f9283   Steve Hardy   hwmon: Add suppor...
108

46d784629   Vivien Didelot   hwmon: (ads7828) ...
109
110
111
112
113
114
115
116
  static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0);
  static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1);
  static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2);
  static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3);
  static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4);
  static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5);
  static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6);
  static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7);
5812f9283   Steve Hardy   hwmon: Add suppor...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  
  static struct attribute *ads7828_attributes[] = {
  	&sensor_dev_attr_in0_input.dev_attr.attr,
  	&sensor_dev_attr_in1_input.dev_attr.attr,
  	&sensor_dev_attr_in2_input.dev_attr.attr,
  	&sensor_dev_attr_in3_input.dev_attr.attr,
  	&sensor_dev_attr_in4_input.dev_attr.attr,
  	&sensor_dev_attr_in5_input.dev_attr.attr,
  	&sensor_dev_attr_in6_input.dev_attr.attr,
  	&sensor_dev_attr_in7_input.dev_attr.attr,
  	NULL
  };
  
  static const struct attribute_group ads7828_group = {
  	.attrs = ads7828_attributes,
  };
7347cb388   Jean Delvare   hwmon: (ads7828) ...
133
  static int ads7828_remove(struct i2c_client *client)
5812f9283   Steve Hardy   hwmon: Add suppor...
134
135
  {
  	struct ads7828_data *data = i2c_get_clientdata(client);
46d784629   Vivien Didelot   hwmon: (ads7828) ...
136

5812f9283   Steve Hardy   hwmon: Add suppor...
137
138
  	hwmon_device_unregister(data->hwmon_dev);
  	sysfs_remove_group(&client->dev.kobj, &ads7828_group);
5812f9283   Steve Hardy   hwmon: Add suppor...
139

7347cb388   Jean Delvare   hwmon: (ads7828) ...
140
141
  	return 0;
  }
5812f9283   Steve Hardy   hwmon: Add suppor...
142

7347cb388   Jean Delvare   hwmon: (ads7828) ...
143
144
145
  static int ads7828_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
  {
46d784629   Vivien Didelot   hwmon: (ads7828) ...
146
  	struct ads7828_platform_data *pdata = client->dev.platform_data;
7347cb388   Jean Delvare   hwmon: (ads7828) ...
147
148
  	struct ads7828_data *data;
  	int err;
5812f9283   Steve Hardy   hwmon: Add suppor...
149

34e3f7f5a   Guenter Roeck   hwmon: (ads7828) ...
150
151
152
153
  	data = devm_kzalloc(&client->dev, sizeof(struct ads7828_data),
  			    GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
5812f9283   Steve Hardy   hwmon: Add suppor...
154

46d784629   Vivien Didelot   hwmon: (ads7828) ...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  	if (pdata) {
  		data->diff_input = pdata->diff_input;
  		data->ext_vref = pdata->ext_vref;
  		if (data->ext_vref)
  			data->vref_mv = pdata->vref_mv;
  	}
  
  	/* Bound Vref with min/max values if it was provided */
  	if (data->vref_mv)
  		data->vref_mv = SENSORS_LIMIT(data->vref_mv,
  					      ADS7828_EXT_VREF_MV_MIN,
  					      ADS7828_EXT_VREF_MV_MAX);
  	else
  		data->vref_mv = ADS7828_INT_VREF_MV;
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
169
170
171
172
173
174
175
176
  	/* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */
  	if (id->driver_data == ads7828) {
  		data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096);
  		data->read_channel = i2c_smbus_read_word_swapped;
  	} else {
  		data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256);
  		data->read_channel = i2c_smbus_read_byte_data;
  	}
46d784629   Vivien Didelot   hwmon: (ads7828) ...
177
178
179
180
  
  	data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
  	if (!data->diff_input)
  		data->cmd_byte |= ADS7828_CMD_SD_SE;
7347cb388   Jean Delvare   hwmon: (ads7828) ...
181
  	i2c_set_clientdata(client, data);
5812f9283   Steve Hardy   hwmon: Add suppor...
182
  	mutex_init(&data->update_lock);
5812f9283   Steve Hardy   hwmon: Add suppor...
183
184
  	err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
  	if (err)
34e3f7f5a   Guenter Roeck   hwmon: (ads7828) ...
185
  		return err;
5812f9283   Steve Hardy   hwmon: Add suppor...
186
187
188
189
  
  	data->hwmon_dev = hwmon_device_register(&client->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
46d784629   Vivien Didelot   hwmon: (ads7828) ...
190
  		goto error;
5812f9283   Steve Hardy   hwmon: Add suppor...
191
192
193
  	}
  
  	return 0;
46d784629   Vivien Didelot   hwmon: (ads7828) ...
194
  error:
5812f9283   Steve Hardy   hwmon: Add suppor...
195
  	sysfs_remove_group(&client->dev.kobj, &ads7828_group);
5812f9283   Steve Hardy   hwmon: Add suppor...
196
197
  	return err;
  }
46d784629   Vivien Didelot   hwmon: (ads7828) ...
198
  static const struct i2c_device_id ads7828_device_ids[] = {
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
199
200
  	{ "ads7828", ads7828 },
  	{ "ads7830", ads7830 },
46d784629   Vivien Didelot   hwmon: (ads7828) ...
201
202
203
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
5812f9283   Steve Hardy   hwmon: Add suppor...
204

46d784629   Vivien Didelot   hwmon: (ads7828) ...
205
206
207
208
  static struct i2c_driver ads7828_driver = {
  	.driver = {
  		.name = "ads7828",
  	},
5812f9283   Steve Hardy   hwmon: Add suppor...
209

46d784629   Vivien Didelot   hwmon: (ads7828) ...
210
211
212
213
  	.id_table = ads7828_device_ids,
  	.probe = ads7828_probe,
  	.remove = ads7828_remove,
  };
5812f9283   Steve Hardy   hwmon: Add suppor...
214

46d784629   Vivien Didelot   hwmon: (ads7828) ...
215
  module_i2c_driver(ads7828_driver);
5812f9283   Steve Hardy   hwmon: Add suppor...
216

5812f9283   Steve Hardy   hwmon: Add suppor...
217
  MODULE_LICENSE("GPL");
46d784629   Vivien Didelot   hwmon: (ads7828) ...
218
  MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
0962e0f1a   Guillaume Roguez   hwmon: (ads7828) ...
219
  MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles");