Blame view

drivers/hwmon/emc2103.c 18.3 KB
74ba9207e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
9df7305b5   Steve Glendinning   hwmon: Add driver...
2
  /*
bf0f3a043   Guenter Roeck   hwmon: (emc2103) ...
3
4
   * emc2103.c - Support for SMSC EMC2103
   * Copyright (c) 2010 SMSC
bf0f3a043   Guenter Roeck   hwmon: (emc2103) ...
5
   */
9df7305b5   Steve Glendinning   hwmon: Add driver...
6
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
  
  #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>
  
  /* Addresses scanned */
  static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
  
  static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
  static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
  static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
  
  #define REG_CONF1		0x20
  #define REG_TEMP_MAX_ALARM	0x24
  #define REG_TEMP_MIN_ALARM	0x25
  #define REG_FAN_CONF1		0x42
  #define REG_FAN_TARGET_LO	0x4c
  #define REG_FAN_TARGET_HI	0x4d
  #define REG_FAN_TACH_HI		0x4e
  #define REG_FAN_TACH_LO		0x4f
  #define REG_PRODUCT_ID		0xfd
  #define REG_MFG_ID		0xfe
  
  /* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
  #define FAN_RPM_FACTOR		3932160
bf0f3a043   Guenter Roeck   hwmon: (emc2103) ...
37
38
  /*
   * 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
9df7305b5   Steve Glendinning   hwmon: Add driver...
39
40
41
42
   * in anti-parallel mode, and in this configuration both can be read
   * independently (so we have 4 temperature inputs).  The device can't
   * detect if it's connected in this mode, so we have to manually enable
   * it.  Default is to leave the device in the state it's already in (-1).
bf0f3a043   Guenter Roeck   hwmon: (emc2103) ...
43
44
   * This parameter allows APD mode to be optionally forced on or off
   */
9df7305b5   Steve Glendinning   hwmon: Add driver...
45
  static int apd = -1;
69116f279   Rusty Russell   module_param: avo...
46
  module_param(apd, bint, 0);
54f0ffc4e   Dan Carpenter   hwmon: (emc2103) ...
47
  MODULE_PARM_DESC(apd, "Set to zero to disable anti-parallel diode mode");
9df7305b5   Steve Glendinning   hwmon: Add driver...
48
49
50
51
52
53
54
  
  struct temperature {
  	s8	degrees;
  	u8	fraction;	/* 0-7 multiples of 0.125 */
  };
  
  struct emc2103_data {
9dd304f86   Axel Lin   hwmon: (emc2103) ...
55
56
  	struct i2c_client	*client;
  	const struct		attribute_group *groups[4];
9df7305b5   Steve Glendinning   hwmon: Add driver...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  	struct mutex		update_lock;
  	bool			valid;		/* registers are valid */
  	bool			fan_rpm_control;
  	int			temp_count;	/* num of temp sensors */
  	unsigned long		last_updated;	/* in jiffies */
  	struct temperature	temp[4];	/* internal + 3 external */
  	s8			temp_min[4];	/* no fractional part */
  	s8			temp_max[4];    /* no fractional part */
  	u8			temp_min_alarm;
  	u8			temp_max_alarm;
  	u8			fan_multiplier;
  	u16			fan_tach;
  	u16			fan_target;
  };
  
  static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
  {
  	int status = i2c_smbus_read_byte_data(client, i2c_reg);
  	if (status < 0) {
  		dev_warn(&client->dev, "reg 0x%02x, err %d
  ",
  			i2c_reg, status);
  	} else {
  		*output = status;
  	}
  	return status;
  }
  
  static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
  			       struct temperature *temp)
  {
  	u8 degrees, fractional;
  
  	if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
  		return;
  
  	if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
  		return;
  
  	temp->degrees = degrees;
  	temp->fraction = (fractional & 0xe0) >> 5;
  }
  
  static void read_fan_from_i2c(struct i2c_client *client, u16 *output,
  			      u8 hi_addr, u8 lo_addr)
  {
  	u8 high_byte, lo_byte;
  
  	if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
  		return;
  
  	if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
  		return;
  
  	*output = ((u16)high_byte << 5) | (lo_byte >> 3);
  }
  
  static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
  {
  	u8 high_byte = (new_target & 0x1fe0) >> 5;
  	u8 low_byte = (new_target & 0x001f) << 3;
  	i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
  	i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
  }
  
  static void read_fan_config_from_i2c(struct i2c_client *client)
  
  {
  	struct emc2103_data *data = i2c_get_clientdata(client);
  	u8 conf1;
  
  	if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
  		return;
  
  	data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
  	data->fan_rpm_control = (conf1 & 0x80) != 0;
  }
  
  static struct emc2103_data *emc2103_update_device(struct device *dev)
  {
9dd304f86   Axel Lin   hwmon: (emc2103) ...
137
138
  	struct emc2103_data *data = dev_get_drvdata(dev);
  	struct i2c_client *client = data->client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
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
171
172
173
174
  
  	mutex_lock(&data->update_lock);
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
  	    || !data->valid) {
  		int i;
  
  		for (i = 0; i < data->temp_count; i++) {
  			read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
  			read_u8_from_i2c(client, REG_TEMP_MIN[i],
  				&data->temp_min[i]);
  			read_u8_from_i2c(client, REG_TEMP_MAX[i],
  				&data->temp_max[i]);
  		}
  
  		read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
  			&data->temp_min_alarm);
  		read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
  			&data->temp_max_alarm);
  
  		read_fan_from_i2c(client, &data->fan_tach,
  			REG_FAN_TACH_HI, REG_FAN_TACH_LO);
  		read_fan_from_i2c(client, &data->fan_target,
  			REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
  		read_fan_config_from_i2c(client);
  
  		data->last_updated = jiffies;
  		data->valid = true;
  	}
  
  	mutex_unlock(&data->update_lock);
  
  	return data;
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
175
  temp_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
176
177
178
179
180
181
182
183
184
185
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int millidegrees = data->temp[nr].degrees * 1000
  		+ data->temp[nr].fraction * 125;
  	return sprintf(buf, "%d
  ", millidegrees);
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
186
  temp_min_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
187
188
189
190
191
192
193
194
195
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int millidegrees = data->temp_min[nr] * 1000;
  	return sprintf(buf, "%d
  ", millidegrees);
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
196
  temp_max_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
197
198
199
200
201
202
203
204
205
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int millidegrees = data->temp_max[nr] * 1000;
  	return sprintf(buf, "%d
  ", millidegrees);
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
206
  temp_fault_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
207
208
209
210
211
212
213
214
215
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	bool fault = (data->temp[nr].degrees == -128);
  	return sprintf(buf, "%d
  ", fault ? 1 : 0);
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
216
217
  temp_min_alarm_show(struct device *dev, struct device_attribute *da,
  		    char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
218
219
220
221
222
223
224
225
226
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	bool alarm = data->temp_min_alarm & (1 << nr);
  	return sprintf(buf, "%d
  ", alarm ? 1 : 0);
  }
  
  static ssize_t
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
227
228
  temp_max_alarm_show(struct device *dev, struct device_attribute *da,
  		    char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
229
230
231
232
233
234
235
  {
  	int nr = to_sensor_dev_attr(da)->index;
  	struct emc2103_data *data = emc2103_update_device(dev);
  	bool alarm = data->temp_max_alarm & (1 << nr);
  	return sprintf(buf, "%d
  ", alarm ? 1 : 0);
  }
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
236
237
  static ssize_t temp_min_store(struct device *dev, struct device_attribute *da,
  			      const char *buf, size_t count)
9df7305b5   Steve Glendinning   hwmon: Add driver...
238
239
  {
  	int nr = to_sensor_dev_attr(da)->index;
9dd304f86   Axel Lin   hwmon: (emc2103) ...
240
241
  	struct emc2103_data *data = dev_get_drvdata(dev);
  	struct i2c_client *client = data->client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
242
  	long val;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
243
  	int result = kstrtol(buf, 10, &val);
9df7305b5   Steve Glendinning   hwmon: Add driver...
244
  	if (result < 0)
1a3abbd0b   Sachin Kamat   hwmon: (emc2103) ...
245
  		return result;
9df7305b5   Steve Glendinning   hwmon: Add driver...
246

ca1b10b82   Guenter Roeck   hwmon: (emc2103) ...
247
  	val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
9df7305b5   Steve Glendinning   hwmon: Add driver...
248
249
250
251
252
253
254
255
  
  	mutex_lock(&data->update_lock);
  	data->temp_min[nr] = val;
  	i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
  	mutex_unlock(&data->update_lock);
  
  	return count;
  }
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
256
257
  static ssize_t temp_max_store(struct device *dev, struct device_attribute *da,
  			      const char *buf, size_t count)
9df7305b5   Steve Glendinning   hwmon: Add driver...
258
259
  {
  	int nr = to_sensor_dev_attr(da)->index;
9dd304f86   Axel Lin   hwmon: (emc2103) ...
260
261
  	struct emc2103_data *data = dev_get_drvdata(dev);
  	struct i2c_client *client = data->client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
262
  	long val;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
263
  	int result = kstrtol(buf, 10, &val);
9df7305b5   Steve Glendinning   hwmon: Add driver...
264
  	if (result < 0)
1a3abbd0b   Sachin Kamat   hwmon: (emc2103) ...
265
  		return result;
9df7305b5   Steve Glendinning   hwmon: Add driver...
266

ca1b10b82   Guenter Roeck   hwmon: (emc2103) ...
267
  	val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
9df7305b5   Steve Glendinning   hwmon: Add driver...
268
269
270
271
272
273
274
275
276
277
  
  	mutex_lock(&data->update_lock);
  	data->temp_max[nr] = val;
  	i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
  	mutex_unlock(&data->update_lock);
  
  	return count;
  }
  
  static ssize_t
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
278
  fan1_input_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
279
280
281
282
283
284
285
286
287
288
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int rpm = 0;
  	if (data->fan_tach != 0)
  		rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
  	return sprintf(buf, "%d
  ", rpm);
  }
  
  static ssize_t
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
289
  fan1_div_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
290
291
292
293
294
295
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int fan_div = 8 / data->fan_multiplier;
  	return sprintf(buf, "%d
  ", fan_div);
  }
bf0f3a043   Guenter Roeck   hwmon: (emc2103) ...
296
297
298
299
300
301
  /*
   * Note: we also update the fan target here, because its value is
   * determined in part by the fan clock divider.  This follows the principle
   * of least surprise; the user doesn't expect the fan target to change just
   * because the divider changed.
   */
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
302
303
  static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
  			      const char *buf, size_t count)
9df7305b5   Steve Glendinning   hwmon: Add driver...
304
305
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
9dd304f86   Axel Lin   hwmon: (emc2103) ...
306
  	struct i2c_client *client = data->client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
307
308
  	int new_range_bits, old_div = 8 / data->fan_multiplier;
  	long new_div;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
309
  	int status = kstrtol(buf, 10, &new_div);
9df7305b5   Steve Glendinning   hwmon: Add driver...
310
  	if (status < 0)
1a3abbd0b   Sachin Kamat   hwmon: (emc2103) ...
311
  		return status;
9df7305b5   Steve Glendinning   hwmon: Add driver...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  
  	if (new_div == old_div) /* No change */
  		return count;
  
  	switch (new_div) {
  	case 1:
  		new_range_bits = 3;
  		break;
  	case 2:
  		new_range_bits = 2;
  		break;
  	case 4:
  		new_range_bits = 1;
  		break;
  	case 8:
  		new_range_bits = 0;
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	mutex_lock(&data->update_lock);
  
  	status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
  	if (status < 0) {
  		dev_dbg(&client->dev, "reg 0x%02x, err %d
  ",
  			REG_FAN_CONF1, status);
  		mutex_unlock(&data->update_lock);
9bf3babf3   Guenter Roeck   hwmon: (emc2103) ...
341
  		return status;
9df7305b5   Steve Glendinning   hwmon: Add driver...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  	}
  	status &= 0x9F;
  	status |= (new_range_bits << 5);
  	i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
  
  	data->fan_multiplier = 8 / new_div;
  
  	/* update fan target if high byte is not disabled */
  	if ((data->fan_target & 0x1fe0) != 0x1fe0) {
  		u16 new_target = (data->fan_target * old_div) / new_div;
  		data->fan_target = min(new_target, (u16)0x1fff);
  		write_fan_target_to_i2c(client, data->fan_target);
  	}
  
  	/* invalidate data to force re-read from hardware */
  	data->valid = false;
  
  	mutex_unlock(&data->update_lock);
  	return count;
  }
  
  static ssize_t
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
364
  fan1_target_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
365
366
367
368
369
370
371
372
373
374
375
376
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
  	int rpm = 0;
  
  	/* high byte of 0xff indicates disabled so return 0 */
  	if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
  		rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
  			/ data->fan_target;
  
  	return sprintf(buf, "%d
  ", rpm);
  }
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
377
378
379
  static ssize_t fan1_target_store(struct device *dev,
  				 struct device_attribute *da, const char *buf,
  				 size_t count)
9df7305b5   Steve Glendinning   hwmon: Add driver...
380
381
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
9dd304f86   Axel Lin   hwmon: (emc2103) ...
382
  	struct i2c_client *client = data->client;
f6c2dd201   Guenter Roeck   hwmon: (emc2103) ...
383
  	unsigned long rpm_target;
9df7305b5   Steve Glendinning   hwmon: Add driver...
384

f6c2dd201   Guenter Roeck   hwmon: (emc2103) ...
385
  	int result = kstrtoul(buf, 10, &rpm_target);
9df7305b5   Steve Glendinning   hwmon: Add driver...
386
  	if (result < 0)
1a3abbd0b   Sachin Kamat   hwmon: (emc2103) ...
387
  		return result;
9df7305b5   Steve Glendinning   hwmon: Add driver...
388
389
  
  	/* Datasheet states 16384 as maximum RPM target (table 3.2) */
f6c2dd201   Guenter Roeck   hwmon: (emc2103) ...
390
  	rpm_target = clamp_val(rpm_target, 0, 16384);
9df7305b5   Steve Glendinning   hwmon: Add driver...
391
392
393
394
395
396
  
  	mutex_lock(&data->update_lock);
  
  	if (rpm_target == 0)
  		data->fan_target = 0x1fff;
  	else
2a844c148   Guenter Roeck   hwmon: Replace SE...
397
  		data->fan_target = clamp_val(
9df7305b5   Steve Glendinning   hwmon: Add driver...
398
399
400
401
402
403
404
405
406
407
  			(FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
  			0, 0x1fff);
  
  	write_fan_target_to_i2c(client, data->fan_target);
  
  	mutex_unlock(&data->update_lock);
  	return count;
  }
  
  static ssize_t
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
408
  fan1_fault_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
409
410
411
412
413
414
415
416
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
  	bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
  	return sprintf(buf, "%d
  ", fault ? 1 : 0);
  }
  
  static ssize_t
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
417
  pwm1_enable_show(struct device *dev, struct device_attribute *da, char *buf)
9df7305b5   Steve Glendinning   hwmon: Add driver...
418
419
420
421
422
  {
  	struct emc2103_data *data = emc2103_update_device(dev);
  	return sprintf(buf, "%d
  ", data->fan_rpm_control ? 3 : 0);
  }
4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
423
424
425
  static ssize_t pwm1_enable_store(struct device *dev,
  				 struct device_attribute *da, const char *buf,
  				 size_t count)
9df7305b5   Steve Glendinning   hwmon: Add driver...
426
  {
9dd304f86   Axel Lin   hwmon: (emc2103) ...
427
428
  	struct emc2103_data *data = dev_get_drvdata(dev);
  	struct i2c_client *client = data->client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
429
430
  	long new_value;
  	u8 conf_reg;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
431
  	int result = kstrtol(buf, 10, &new_value);
9df7305b5   Steve Glendinning   hwmon: Add driver...
432
  	if (result < 0)
1a3abbd0b   Sachin Kamat   hwmon: (emc2103) ...
433
  		return result;
9df7305b5   Steve Glendinning   hwmon: Add driver...
434
435
436
437
438
439
440
441
442
443
  
  	mutex_lock(&data->update_lock);
  	switch (new_value) {
  	case 0:
  		data->fan_rpm_control = false;
  		break;
  	case 3:
  		data->fan_rpm_control = true;
  		break;
  	default:
2355375ef   Guenter Roeck   hwmon: (emc2103) ...
444
445
  		count = -EINVAL;
  		goto err;
9df7305b5   Steve Glendinning   hwmon: Add driver...
446
  	}
2355375ef   Guenter Roeck   hwmon: (emc2103) ...
447
  	result = read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
14b0e83dc   Vishwas M   hwmon: (emc2103) ...
448
  	if (result < 0) {
2355375ef   Guenter Roeck   hwmon: (emc2103) ...
449
450
451
  		count = result;
  		goto err;
  	}
9df7305b5   Steve Glendinning   hwmon: Add driver...
452
453
454
455
456
457
458
  
  	if (data->fan_rpm_control)
  		conf_reg |= 0x80;
  	else
  		conf_reg &= ~0x80;
  
  	i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
2355375ef   Guenter Roeck   hwmon: (emc2103) ...
459
  err:
9df7305b5   Steve Glendinning   hwmon: Add driver...
460
461
462
  	mutex_unlock(&data->update_lock);
  	return count;
  }
94bf70da8   Guenter Roeck   hwmon: (emc2103) ...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
  static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
  static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
  static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
  static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0);
  static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0);
  
  static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
  static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
  static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
  static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
  static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1);
  static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1);
  
  static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
  static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
  static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
  static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
  static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2);
  static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2);
  
  static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
  static SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3);
  static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3);
  static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
  static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, temp_min_alarm, 3);
  static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, temp_max_alarm, 3);
9df7305b5   Steve Glendinning   hwmon: Add driver...
490

4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
491
492
493
494
  static DEVICE_ATTR_RO(fan1_input);
  static DEVICE_ATTR_RW(fan1_div);
  static DEVICE_ATTR_RW(fan1_target);
  static DEVICE_ATTR_RO(fan1_fault);
9df7305b5   Steve Glendinning   hwmon: Add driver...
495

4cd0183dc   Julia Lawall   hwmon: (emc2103) ...
496
  static DEVICE_ATTR_RW(pwm1_enable);
9df7305b5   Steve Glendinning   hwmon: Add driver...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
  
  /* sensors present on all models */
  static struct attribute *emc2103_attributes[] = {
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	&sensor_dev_attr_temp1_min.dev_attr.attr,
  	&sensor_dev_attr_temp1_max.dev_attr.attr,
  	&sensor_dev_attr_temp1_fault.dev_attr.attr,
  	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp2_input.dev_attr.attr,
  	&sensor_dev_attr_temp2_min.dev_attr.attr,
  	&sensor_dev_attr_temp2_max.dev_attr.attr,
  	&sensor_dev_attr_temp2_fault.dev_attr.attr,
  	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
  	&dev_attr_fan1_input.attr,
  	&dev_attr_fan1_div.attr,
  	&dev_attr_fan1_target.attr,
  	&dev_attr_fan1_fault.attr,
  	&dev_attr_pwm1_enable.attr,
  	NULL
  };
  
  /* extra temperature sensors only present on 2103-2 and 2103-4 */
  static struct attribute *emc2103_attributes_temp3[] = {
  	&sensor_dev_attr_temp3_input.dev_attr.attr,
  	&sensor_dev_attr_temp3_min.dev_attr.attr,
  	&sensor_dev_attr_temp3_max.dev_attr.attr,
  	&sensor_dev_attr_temp3_fault.dev_attr.attr,
  	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
  	NULL
  };
  
  /* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
  static struct attribute *emc2103_attributes_temp4[] = {
  	&sensor_dev_attr_temp4_input.dev_attr.attr,
  	&sensor_dev_attr_temp4_min.dev_attr.attr,
  	&sensor_dev_attr_temp4_max.dev_attr.attr,
  	&sensor_dev_attr_temp4_fault.dev_attr.attr,
  	&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
  	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
  	NULL
  };
  
  static const struct attribute_group emc2103_group = {
  	.attrs = emc2103_attributes,
  };
  
  static const struct attribute_group emc2103_temp3_group = {
  	.attrs = emc2103_attributes_temp3,
  };
  
  static const struct attribute_group emc2103_temp4_group = {
  	.attrs = emc2103_attributes_temp4,
  };
  
  static int
9bf5dd8b2   Stephen Kitt   hwmon: (emc2103) ...
555
  emc2103_probe(struct i2c_client *client)
9df7305b5   Steve Glendinning   hwmon: Add driver...
556
557
  {
  	struct emc2103_data *data;
9dd304f86   Axel Lin   hwmon: (emc2103) ...
558
559
  	struct device *hwmon_dev;
  	int status, idx = 0;
9df7305b5   Steve Glendinning   hwmon: Add driver...
560
561
562
  
  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		return -EIO;
59da32d8d   Guenter Roeck   hwmon: (emc2103) ...
563
564
  	data = devm_kzalloc(&client->dev, sizeof(struct emc2103_data),
  			    GFP_KERNEL);
9df7305b5   Steve Glendinning   hwmon: Add driver...
565
566
567
568
  	if (!data)
  		return -ENOMEM;
  
  	i2c_set_clientdata(client, data);
9dd304f86   Axel Lin   hwmon: (emc2103) ...
569
  	data->client = client;
9df7305b5   Steve Glendinning   hwmon: Add driver...
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  	mutex_init(&data->update_lock);
  
  	/* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
  	status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
  	if (status == 0x24) {
  		/* 2103-1 only has 1 external diode */
  		data->temp_count = 2;
  	} else {
  		/* 2103-2 and 2103-4 have 3 or 4 external diodes */
  		status = i2c_smbus_read_byte_data(client, REG_CONF1);
  		if (status < 0) {
  			dev_dbg(&client->dev, "reg 0x%02x, err %d
  ", REG_CONF1,
  				status);
59da32d8d   Guenter Roeck   hwmon: (emc2103) ...
584
  			return status;
9df7305b5   Steve Glendinning   hwmon: Add driver...
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  		}
  
  		/* detect current state of hardware */
  		data->temp_count = (status & 0x01) ? 4 : 3;
  
  		/* force APD state if module parameter is set */
  		if (apd == 0) {
  			/* force APD mode off */
  			data->temp_count = 3;
  			status &= ~(0x01);
  			i2c_smbus_write_byte_data(client, REG_CONF1, status);
  		} else if (apd == 1) {
  			/* force APD mode on */
  			data->temp_count = 4;
  			status |= 0x01;
  			i2c_smbus_write_byte_data(client, REG_CONF1, status);
  		}
  	}
9dd304f86   Axel Lin   hwmon: (emc2103) ...
603
604
  	/* sysfs hooks */
  	data->groups[idx++] = &emc2103_group;
9df7305b5   Steve Glendinning   hwmon: Add driver...
605
  	if (data->temp_count >= 3)
9dd304f86   Axel Lin   hwmon: (emc2103) ...
606
  		data->groups[idx++] = &emc2103_temp3_group;
9df7305b5   Steve Glendinning   hwmon: Add driver...
607
  	if (data->temp_count == 4)
9dd304f86   Axel Lin   hwmon: (emc2103) ...
608
  		data->groups[idx++] = &emc2103_temp4_group;
9df7305b5   Steve Glendinning   hwmon: Add driver...
609

9dd304f86   Axel Lin   hwmon: (emc2103) ...
610
611
612
613
614
  	hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
  							   client->name, data,
  							   data->groups);
  	if (IS_ERR(hwmon_dev))
  		return PTR_ERR(hwmon_dev);
9df7305b5   Steve Glendinning   hwmon: Add driver...
615

9dd304f86   Axel Lin   hwmon: (emc2103) ...
616
617
618
  	dev_info(&client->dev, "%s: sensor '%s'
  ",
  		 dev_name(hwmon_dev), client->name);
9df7305b5   Steve Glendinning   hwmon: Add driver...
619

9df7305b5   Steve Glendinning   hwmon: Add driver...
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  	return 0;
  }
  
  static const struct i2c_device_id emc2103_ids[] = {
  	{ "emc2103", 0, },
  	{ /* LIST END */ }
  };
  MODULE_DEVICE_TABLE(i2c, emc2103_ids);
  
  /* Return 0 if detection is successful, -ENODEV otherwise */
  static int
  emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
  {
  	struct i2c_adapter *adapter = new_client->adapter;
  	int manufacturer, product;
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		return -ENODEV;
  
  	manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
  	if (manufacturer != 0x5D)
  		return -ENODEV;
  
  	product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
  	if ((product != 0x24) && (product != 0x26))
  		return -ENODEV;
  
  	strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
  
  	return 0;
  }
  
  static struct i2c_driver emc2103_driver = {
  	.class		= I2C_CLASS_HWMON,
  	.driver = {
  		.name	= "emc2103",
  	},
9bf5dd8b2   Stephen Kitt   hwmon: (emc2103) ...
657
  	.probe_new	= emc2103_probe,
9df7305b5   Steve Glendinning   hwmon: Add driver...
658
659
660
661
  	.id_table	= emc2103_ids,
  	.detect		= emc2103_detect,
  	.address_list	= normal_i2c,
  };
f0967eea8   Axel Lin   hwmon: convert dr...
662
  module_i2c_driver(emc2103_driver);
9df7305b5   Steve Glendinning   hwmon: Add driver...
663

90b24cfb4   Steve Glendinning   Change email addr...
664
  MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");
9df7305b5   Steve Glendinning   hwmon: Add driver...
665
666
  MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
  MODULE_LICENSE("GPL");