Blame view

drivers/hwmon/ad7418.c 7.74 KB
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * An hwmon driver for the Analog Devices AD7416/17/18
   * Copyright (C) 2006-07 Tower Technologies
   *
   * Author: Alessandro Zummo <a.zummo@towertech.it>
   *
   * Based on lm75.c
   * Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
   *
   * 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 - version 2.
   */
  
  #include <linux/module.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>
  #include <linux/delay.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
23
  #include <linux/slab.h>
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
24
25
  
  #include "lm75.h"
369932f6f   Jean Delvare   hwmon: (ad7418) C...
26
  #define DRV_VERSION "0.4"
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
27

369932f6f   Jean Delvare   hwmon: (ad7418) C...
28
  enum chips { ad7416, ad7417, ad7418 };
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  
  /* AD7418 registers */
  #define AD7418_REG_TEMP_IN	0x00
  #define AD7418_REG_CONF		0x01
  #define AD7418_REG_TEMP_HYST	0x02
  #define AD7418_REG_TEMP_OS	0x03
  #define AD7418_REG_ADC		0x04
  #define AD7418_REG_CONF2	0x05
  
  #define AD7418_REG_ADC_CH(x)	((x) << 5)
  #define AD7418_CH_TEMP		AD7418_REG_ADC_CH(0)
  
  static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
  					AD7418_REG_TEMP_HYST,
  					AD7418_REG_TEMP_OS };
  
  struct ad7418_data {
1beeffe43   Tony Jones   hwmon: Convert fr...
46
  	struct device		*hwmon_dev;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
47
48
49
50
51
52
53
54
55
  	struct attribute_group	attrs;
  	enum chips		type;
  	struct mutex		lock;
  	int			adc_max;	/* number of ADC channels */
  	char			valid;
  	unsigned long		last_updated;	/* In jiffies */
  	s16			temp[3];	/* Register values */
  	u16			in[4];
  };
369932f6f   Jean Delvare   hwmon: (ad7418) C...
56
57
58
59
60
61
62
63
64
65
66
  static int ad7418_probe(struct i2c_client *client,
  			const struct i2c_device_id *id);
  static int ad7418_remove(struct i2c_client *client);
  
  static const struct i2c_device_id ad7418_id[] = {
  	{ "ad7416", ad7416 },
  	{ "ad7417", ad7417 },
  	{ "ad7418", ad7418 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ad7418_id);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
67
68
69
70
71
  
  static struct i2c_driver ad7418_driver = {
  	.driver = {
  		.name	= "ad7418",
  	},
369932f6f   Jean Delvare   hwmon: (ad7418) C...
72
73
74
  	.probe		= ad7418_probe,
  	.remove		= ad7418_remove,
  	.id_table	= ad7418_id,
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
75
  };
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  static void ad7418_init_client(struct i2c_client *client)
  {
  	struct ad7418_data *data = i2c_get_clientdata(client);
  
  	int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
  	if (reg < 0) {
  		dev_err(&client->dev, "cannot read configuration register
  ");
  	} else {
  		dev_info(&client->dev, "configuring for mode 1
  ");
  		i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
  
  		if (data->type == ad7417 || data->type == ad7418)
  			i2c_smbus_write_byte_data(client,
  						AD7418_REG_CONF2, 0x00);
  	}
  }
  
  static struct ad7418_data *ad7418_update_device(struct device *dev)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ad7418_data *data = i2c_get_clientdata(client);
  
  	mutex_lock(&data->lock);
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  		|| !data->valid) {
  		u8 cfg;
  		int i, ch;
  
  		/* read config register and clear channel bits */
  		cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
  		cfg &= 0x1F;
  
  		i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
  						cfg | AD7418_CH_TEMP);
  		udelay(30);
  
  		for (i = 0; i < 3; i++) {
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
116
117
118
  			data->temp[i] =
  				i2c_smbus_read_word_swapped(client,
  						AD7418_REG_TEMP[i]);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
119
120
121
122
123
124
125
126
127
  		}
  
  		for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
  			i2c_smbus_write_byte_data(client,
  					AD7418_REG_CONF,
  					cfg | AD7418_REG_ADC_CH(ch));
  
  			udelay(15);
  			data->in[data->adc_max - 1 - i] =
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
128
129
  				i2c_smbus_read_word_swapped(client,
  						AD7418_REG_ADC);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
130
131
132
  		}
  
  		/* restore old configuration value */
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
133
  		i2c_smbus_write_word_swapped(client, AD7418_REG_CONF, cfg);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  
  		data->last_updated = jiffies;
  		data->valid = 1;
  	}
  
  	mutex_unlock(&data->lock);
  
  	return data;
  }
  
  static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
  			char *buf)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
  	struct ad7418_data *data = ad7418_update_device(dev);
  	return sprintf(buf, "%d
  ",
  		LM75_TEMP_FROM_REG(data->temp[attr->index]));
  }
  
  static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
  			char *buf)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
  	struct ad7418_data *data = ad7418_update_device(dev);
  
  	return sprintf(buf, "%d
  ",
  		((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
  }
  
  static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
  			const char *buf, size_t count)
  {
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ad7418_data *data = i2c_get_clientdata(client);
5bfedac04   Christian Hohnstaedt   hwmon: Allow writ...
171
  	long temp = simple_strtol(buf, NULL, 10);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
172
173
174
  
  	mutex_lock(&data->lock);
  	data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
90f4102ce   Jean Delvare   hwmon: Use i2c_sm...
175
176
177
  	i2c_smbus_write_word_swapped(client,
  				     AD7418_REG_TEMP[attr->index],
  				     data->temp[attr->index]);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  	mutex_unlock(&data->lock);
  	return count;
  }
  
  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
  static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
  				show_temp, set_temp, 1);
  static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
  				show_temp, set_temp, 2);
  
  static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0);
  static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1);
  static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2);
  static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  static struct attribute *ad7416_attributes[] = {
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	NULL
  };
  
  static struct attribute *ad7417_attributes[] = {
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
  	&sensor_dev_attr_temp1_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,
  	NULL
  };
  
  static struct attribute *ad7418_attributes[] = {
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	&sensor_dev_attr_in1_input.dev_attr.attr,
  	NULL
  };
369932f6f   Jean Delvare   hwmon: (ad7418) C...
217
218
  static int ad7418_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
219
  {
369932f6f   Jean Delvare   hwmon: (ad7418) C...
220
  	struct i2c_adapter *adapter = client->adapter;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
221
  	struct ad7418_data *data;
369932f6f   Jean Delvare   hwmon: (ad7418) C...
222
  	int err;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
223
224
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
369932f6f   Jean Delvare   hwmon: (ad7418) C...
225
226
  					I2C_FUNC_SMBUS_WORD_DATA)) {
  		err = -EOPNOTSUPP;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
227
  		goto exit;
369932f6f   Jean Delvare   hwmon: (ad7418) C...
228
  	}
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
229
230
231
232
233
  
  	if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) {
  		err = -ENOMEM;
  		goto exit;
  	}
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
234
235
236
  	i2c_set_clientdata(client, data);
  
  	mutex_init(&data->lock);
369932f6f   Jean Delvare   hwmon: (ad7418) C...
237
  	data->type = id->driver_data;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
238
239
  
  	switch (data->type) {
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
240
241
242
  	case ad7416:
  		data->adc_max = 0;
  		data->attrs.attrs = ad7416_attributes;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
243
244
245
246
247
  		break;
  
  	case ad7417:
  		data->adc_max = 4;
  		data->attrs.attrs = ad7417_attributes;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
248
249
250
251
252
  		break;
  
  	case ad7418:
  		data->adc_max = 1;
  		data->attrs.attrs = ad7418_attributes;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
253
254
  		break;
  	}
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
255
256
257
258
259
260
261
262
  	dev_info(&client->dev, "%s chip found
  ", client->name);
  
  	/* Initialize the AD7418 chip */
  	ad7418_init_client(client);
  
  	/* Register sysfs hooks */
  	if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
369932f6f   Jean Delvare   hwmon: (ad7418) C...
263
  		goto exit_free;
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
264

1beeffe43   Tony Jones   hwmon: Convert fr...
265
266
267
  	data->hwmon_dev = hwmon_device_register(&client->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
268
269
270
271
272
273
274
  		goto exit_remove;
  	}
  
  	return 0;
  
  exit_remove:
  	sysfs_remove_group(&client->dev.kobj, &data->attrs);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
275
276
277
278
279
  exit_free:
  	kfree(data);
  exit:
  	return err;
  }
369932f6f   Jean Delvare   hwmon: (ad7418) C...
280
  static int ad7418_remove(struct i2c_client *client)
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
281
282
  {
  	struct ad7418_data *data = i2c_get_clientdata(client);
1beeffe43   Tony Jones   hwmon: Convert fr...
283
  	hwmon_device_unregister(data->hwmon_dev);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
284
  	sysfs_remove_group(&client->dev.kobj, &data->attrs);
2d8dd65fc   Alessandro Zummo   hwmon: New AD7416...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  	kfree(data);
  	return 0;
  }
  
  static int __init ad7418_init(void)
  {
  	return i2c_add_driver(&ad7418_driver);
  }
  
  static void __exit ad7418_exit(void)
  {
  	i2c_del_driver(&ad7418_driver);
  }
  
  MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
  MODULE_DESCRIPTION("AD7416/17/18 driver");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(DRV_VERSION);
  
  module_init(ad7418_init);
  module_exit(ad7418_exit);