Blame view

drivers/hwmon/ads7828.c 7.56 KB
5812f9283   Steve Hardy   hwmon: Add suppor...
1
2
3
4
5
  /*
  	ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC
  	(C) 2007 EADS Astrium
  
  	This driver is based on the lm75 and other lm_sensors/hwmon drivers
5d84291da   Steven Hardy   hwmon: (ads7828) ...
6
  	Written by Steve Hardy <shardy@redhat.com>
5812f9283   Steve Hardy   hwmon: Add suppor...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  
  	Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.pdf
  
  	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.
  */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/jiffies.h>
  #include <linux/i2c.h>
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/err.h>
  #include <linux/mutex.h>
  
  /* The ADS7828 registers */
  #define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */
  #define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
  #define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */
  #define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */
  #define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */
  #define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */
  #define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */
  #define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
  
  /* Addresses to scan */
25e9c86d5   Mark M. Hoffman   hwmon: normal_i2c...
46
  static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
5812f9283   Steve Hardy   hwmon: Add suppor...
47
  	I2C_CLIENT_END };
1f86df49d   Jean Delvare   i2c: Drop I2C_CLI...
48
  /* Module parameters */
90ab5ee94   Rusty Russell   module_param: mak...
49
50
  static bool se_input = 1; /* Default is SE, 0 == diff */
  static bool int_vref = 1; /* Default is internal ref ON */
5812f9283   Steve Hardy   hwmon: Add suppor...
51
52
53
54
55
56
57
58
59
60
61
  static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */
  module_param(se_input, bool, S_IRUGO);
  module_param(int_vref, bool, S_IRUGO);
  module_param(vref_mv, int, S_IRUGO);
  
  /* Global Variables */
  static u8 ads7828_cmd_byte; /* cmd byte without channel bits */
  static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */
  
  /* Each client has this additional data */
  struct ads7828_data {
5812f9283   Steve Hardy   hwmon: Add suppor...
62
63
64
65
66
67
68
69
  	struct device *hwmon_dev;
  	struct mutex update_lock; /* mutex protect updates */
  	char valid; /* !=0 if following fields are valid */
  	unsigned long last_updated; /* In jiffies */
  	u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */
  };
  
  /* Function declaration - necessary due to function dependencies */
310ec7921   Jean Delvare   i2c: Drop the kin...
70
  static int ads7828_detect(struct i2c_client *client,
7347cb388   Jean Delvare   hwmon: (ads7828) ...
71
72
73
  			  struct i2c_board_info *info);
  static int ads7828_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id);
5812f9283   Steve Hardy   hwmon: Add suppor...
74

5812f9283   Steve Hardy   hwmon: Add suppor...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  static inline u8 channel_cmd_byte(int ch)
  {
  	/* cmd byte C2,C1,C0 - see datasheet */
  	u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4);
  	cmd |= ads7828_cmd_byte;
  	return cmd;
  }
  
  /* 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++) {
  			u8 cmd = channel_cmd_byte(ch);
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
99
100
  			data->adc_input[ch] =
  				i2c_smbus_read_word_swapped(client, cmd);
5812f9283   Steve Hardy   hwmon: Add suppor...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  		}
  		data->last_updated = jiffies;
  		data->valid = 1;
  	}
  
  	mutex_unlock(&data->update_lock);
  
  	return data;
  }
  
  /* sysfs callback function */
  static ssize_t show_in(struct device *dev, struct device_attribute *da,
  	char *buf)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  	struct ads7828_data *data = ads7828_update_device(dev);
  	/* Print value (in mV as specified in sysfs-interface documentation) */
  	return sprintf(buf, "%d
  ", (data->adc_input[attr->index] *
  		ads7828_lsb_resol)/1000);
  }
  
  #define in_reg(offset)\
  static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\
  	NULL, offset)
  
  in_reg(0);
  in_reg(1);
  in_reg(2);
  in_reg(3);
  in_reg(4);
  in_reg(5);
  in_reg(6);
  in_reg(7);
  
  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) ...
151
  static int ads7828_remove(struct i2c_client *client)
5812f9283   Steve Hardy   hwmon: Add suppor...
152
153
154
155
  {
  	struct ads7828_data *data = i2c_get_clientdata(client);
  	hwmon_device_unregister(data->hwmon_dev);
  	sysfs_remove_group(&client->dev.kobj, &ads7828_group);
5812f9283   Steve Hardy   hwmon: Add suppor...
156
157
158
  	kfree(i2c_get_clientdata(client));
  	return 0;
  }
7347cb388   Jean Delvare   hwmon: (ads7828) ...
159
  static const struct i2c_device_id ads7828_id[] = {
1f86df49d   Jean Delvare   i2c: Drop I2C_CLI...
160
  	{ "ads7828", 0 },
7347cb388   Jean Delvare   hwmon: (ads7828) ...
161
162
163
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ads7828_id);
5812f9283   Steve Hardy   hwmon: Add suppor...
164
165
  /* This is the driver that will be inserted */
  static struct i2c_driver ads7828_driver = {
7347cb388   Jean Delvare   hwmon: (ads7828) ...
166
  	.class = I2C_CLASS_HWMON,
5812f9283   Steve Hardy   hwmon: Add suppor...
167
168
169
  	.driver = {
  		.name = "ads7828",
  	},
7347cb388   Jean Delvare   hwmon: (ads7828) ...
170
171
172
173
  	.probe = ads7828_probe,
  	.remove = ads7828_remove,
  	.id_table = ads7828_id,
  	.detect = ads7828_detect,
c3813d6af   Jean Delvare   i2c: Get rid of s...
174
  	.address_list = normal_i2c,
5812f9283   Steve Hardy   hwmon: Add suppor...
175
  };
7347cb388   Jean Delvare   hwmon: (ads7828) ...
176
  /* Return 0 if detection is successful, -ENODEV otherwise */
310ec7921   Jean Delvare   i2c: Drop the kin...
177
  static int ads7828_detect(struct i2c_client *client,
7347cb388   Jean Delvare   hwmon: (ads7828) ...
178
  			  struct i2c_board_info *info)
5812f9283   Steve Hardy   hwmon: Add suppor...
179
  {
7347cb388   Jean Delvare   hwmon: (ads7828) ...
180
  	struct i2c_adapter *adapter = client->adapter;
52df6440a   Jean Delvare   hwmon: Clean up d...
181
  	int ch;
5812f9283   Steve Hardy   hwmon: Add suppor...
182
183
184
  
  	/* Check we have a valid client */
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
7347cb388   Jean Delvare   hwmon: (ads7828) ...
185
  		return -ENODEV;
5812f9283   Steve Hardy   hwmon: Add suppor...
186
187
188
189
190
191
192
  
  	/* Now, we do the remaining detection. There is no identification
  	dedicated register so attempt to sanity check using knowledge of
  	the chip
  	- Read from the 8 channel addresses
  	- Check the top 4 bits of each result are not set (12 data bits)
  	*/
52df6440a   Jean Delvare   hwmon: Clean up d...
193
194
195
  	for (ch = 0; ch < ADS7828_NCH; ch++) {
  		u16 in_data;
  		u8 cmd = channel_cmd_byte(ch);
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
196
  		in_data = i2c_smbus_read_word_swapped(client, cmd);
52df6440a   Jean Delvare   hwmon: Clean up d...
197
198
199
200
201
  		if (in_data & 0xF000) {
  			pr_debug("%s : Doesn't look like an ads7828 device
  ",
  				 __func__);
  			return -ENODEV;
5812f9283   Steve Hardy   hwmon: Add suppor...
202
203
  		}
  	}
52df6440a   Jean Delvare   hwmon: Clean up d...
204

7347cb388   Jean Delvare   hwmon: (ads7828) ...
205
  	strlcpy(info->type, "ads7828", I2C_NAME_SIZE);
5812f9283   Steve Hardy   hwmon: Add suppor...
206

7347cb388   Jean Delvare   hwmon: (ads7828) ...
207
208
  	return 0;
  }
5812f9283   Steve Hardy   hwmon: Add suppor...
209

7347cb388   Jean Delvare   hwmon: (ads7828) ...
210
211
212
213
214
  static int ads7828_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
  {
  	struct ads7828_data *data;
  	int err;
5812f9283   Steve Hardy   hwmon: Add suppor...
215

7347cb388   Jean Delvare   hwmon: (ads7828) ...
216
217
218
219
220
  	data = kzalloc(sizeof(struct ads7828_data), GFP_KERNEL);
  	if (!data) {
  		err = -ENOMEM;
  		goto exit;
  	}
5812f9283   Steve Hardy   hwmon: Add suppor...
221

7347cb388   Jean Delvare   hwmon: (ads7828) ...
222
  	i2c_set_clientdata(client, data);
5812f9283   Steve Hardy   hwmon: Add suppor...
223
  	mutex_init(&data->update_lock);
5812f9283   Steve Hardy   hwmon: Add suppor...
224
225
226
  	/* Register sysfs hooks */
  	err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
  	if (err)
7347cb388   Jean Delvare   hwmon: (ads7828) ...
227
  		goto exit_free;
5812f9283   Steve Hardy   hwmon: Add suppor...
228
229
230
231
232
233
234
235
236
237
238
  
  	data->hwmon_dev = hwmon_device_register(&client->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
  		goto exit_remove;
  	}
  
  	return 0;
  
  exit_remove:
  	sysfs_remove_group(&client->dev.kobj, &ads7828_group);
5812f9283   Steve Hardy   hwmon: Add suppor...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  exit_free:
  	kfree(data);
  exit:
  	return err;
  }
  
  static int __init sensors_ads7828_init(void)
  {
  	/* Initialize the command byte according to module parameters */
  	ads7828_cmd_byte = se_input ?
  		ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF;
  	ads7828_cmd_byte |= int_vref ?
  		ADS7828_CMD_PD3 : ADS7828_CMD_PD1;
  
  	/* Calculate the LSB resolution */
  	ads7828_lsb_resol = (vref_mv*1000)/4096;
  
  	return i2c_add_driver(&ads7828_driver);
  }
  
  static void __exit sensors_ads7828_exit(void)
  {
  	i2c_del_driver(&ads7828_driver);
  }
5d84291da   Steven Hardy   hwmon: (ads7828) ...
263
  MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
5812f9283   Steve Hardy   hwmon: Add suppor...
264
265
266
267
268
  MODULE_DESCRIPTION("ADS7828 driver");
  MODULE_LICENSE("GPL");
  
  module_init(sensors_ads7828_init);
  module_exit(sensors_ads7828_exit);