aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorHans de Goede <j.w.r.degoede@hhs.nl>2007-10-11 08:06:29 -0400
committerMark M. Hoffman <mhoffman@lightlink.com>2007-10-11 08:11:24 -0400
commit569ff1022662a88591b0a89ea316a3c44306d199 (patch)
treea0420271733bae76afce1a13c8a3942a41607635 /drivers/hwmon
parent4cfdbe7f6cfdc07c27ae8ac490cd3453440dd9d8 (diff)
hwmon: Add new combined driver for FSC chips
This patch adds a new merged driver for FSC sensor chips, it merges the fscher and fscpos drivers and adds support for the FSC Scylla, Heracles and Heimdall chips. Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> Acked-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig14
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/fschmd.c778
3 files changed, 793 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index c69de6c56d97..e47f88170806 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -266,6 +266,20 @@ config SENSORS_FSCPOS
266 This driver can also be built as a module. If so, the module 266 This driver can also be built as a module. If so, the module
267 will be called fscpos. 267 will be called fscpos.
268 268
269config SENSORS_FSCHMD
270 tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles"
271 depends on X86 && I2C && EXPERIMENTAL
272 help
273 If you say yes here you get support for various Fujitsu Siemens
274 Computers sensor chips.
275
276 This is a new merged driver for FSC sensor chips which is intended
277 as a replacment for the fscpos, fscscy and fscher drivers and adds
278 support for several other FCS sensor chips.
279
280 This driver can also be built as a module. If so, the module
281 will be called fschmd.
282
269config SENSORS_GL518SM 283config SENSORS_GL518SM
270 tristate "Genesys Logic GL518SM" 284 tristate "Genesys Logic GL518SM"
271 depends on I2C 285 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2f592e2e01b3..6da3eef94306 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
33obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o 33obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
34obj-$(CONFIG_SENSORS_F75375S) += f75375s.o 34obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
35obj-$(CONFIG_SENSORS_FSCHER) += fscher.o 35obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
36obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
36obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o 37obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
37obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o 38obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
38obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o 39obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
new file mode 100644
index 000000000000..63a4df0580db
--- /dev/null
+++ b/drivers/hwmon/fschmd.c
@@ -0,0 +1,778 @@
1/* fschmd.c
2 *
3 * Copyright (C) 2007 Hans de Goede <j.w.r.degoede@hhs.nl>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20/*
21 * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes,
22 * Scylla, Heracles and Heimdall chips
23 *
24 * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6
25 * (candidate) fschmd drivers:
26 * Copyright (C) 2006 Thilo Cestonaro
27 * <thilo.cestonaro.external@fujitsu-siemens.com>
28 * Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
29 * Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
30 * Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de>
31 * Copyright (C) 2000 Hermann Jung <hej@odn.de>
32 */
33
34#include <linux/module.h>
35#include <linux/init.h>
36#include <linux/slab.h>
37#include <linux/jiffies.h>
38#include <linux/i2c.h>
39#include <linux/hwmon.h>
40#include <linux/hwmon-sysfs.h>
41#include <linux/err.h>
42#include <linux/mutex.h>
43#include <linux/sysfs.h>
44
45/* Addresses to scan */
46static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
47
48/* Insmod parameters */
49I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
50
51/*
52 * The FSCHMD registers and other defines
53 */
54
55/* chip identification */
56#define FSCHMD_REG_IDENT_0 0x00
57#define FSCHMD_REG_IDENT_1 0x01
58#define FSCHMD_REG_IDENT_2 0x02
59#define FSCHMD_REG_REVISION 0x03
60
61/* global control and status */
62#define FSCHMD_REG_EVENT_STATE 0x04
63#define FSCHMD_REG_CONTROL 0x05
64
65#define FSCHMD_CONTROL_ALERT_LED_MASK 0x01
66
67/* watchdog (support to be implemented) */
68#define FSCHMD_REG_WDOG_PRESET 0x28
69#define FSCHMD_REG_WDOG_STATE 0x23
70#define FSCHMD_REG_WDOG_CONTROL 0x21
71
72/* voltages, weird order is to keep the same order as the old drivers */
73static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 };
74
75/* minimum pwm at which the fan is driven (pwm can by increased depending on
76 the temp. Notice that for the scy some fans share there minimum speed.
77 Also notice that with the scy the sensor order is different then with the
78 other chips, this order was in the 2.4 driver and kept for consistency. */
79static const u8 FSCHMD_REG_FAN_MIN[5][6] = {
80 { 0x55, 0x65 }, /* pos */
81 { 0x55, 0x65, 0xb5 }, /* her */
82 { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */
83 { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */
84 { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */
85};
86
87/* actual fan speed */
88static const u8 FSCHMD_REG_FAN_ACT[5][6] = {
89 { 0x0e, 0x6b, 0xab }, /* pos */
90 { 0x0e, 0x6b, 0xbb }, /* her */
91 { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */
92 { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */
93 { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */
94};
95
96/* fan status registers */
97static const u8 FSCHMD_REG_FAN_STATE[5][6] = {
98 { 0x0d, 0x62, 0xa2 }, /* pos */
99 { 0x0d, 0x62, 0xb2 }, /* her */
100 { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */
101 { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */
102 { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */
103};
104
105/* fan ripple / divider registers */
106static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = {
107 { 0x0f, 0x6f, 0xaf }, /* pos */
108 { 0x0f, 0x6f, 0xbf }, /* her */
109 { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */
110 { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */
111 { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */
112};
113
114static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 };
115
116/* Fan status register bitmasks */
117#define FSCHMD_FAN_ALARM_MASK 0x04 /* called fault by FSC! */
118#define FSCHMD_FAN_NOT_PRESENT_MASK 0x08 /* not documented */
119
120
121/* actual temperature registers */
122static const u8 FSCHMD_REG_TEMP_ACT[5][5] = {
123 { 0x64, 0x32, 0x35 }, /* pos */
124 { 0x64, 0x32, 0x35 }, /* her */
125 { 0x64, 0xD0, 0x32, 0x35 }, /* scy */
126 { 0x64, 0x32, 0x35 }, /* hrc */
127 { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */
128};
129
130/* temperature state registers */
131static const u8 FSCHMD_REG_TEMP_STATE[5][5] = {
132 { 0x71, 0x81, 0x91 }, /* pos */
133 { 0x71, 0x81, 0x91 }, /* her */
134 { 0x71, 0xd1, 0x81, 0x91 }, /* scy */
135 { 0x71, 0x81, 0x91 }, /* hrc */
136 { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */
137};
138
139/* temperature high limit registers, FSC does not document these. Proven to be
140 there with field testing on the fscher and fschrc, already supported / used
141 in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers
142 at these addresses, but doesn't want to confirm they are the same as with
143 the fscher?? */
144static const u8 FSCHMD_REG_TEMP_LIMIT[5][5] = {
145 { 0, 0, 0 }, /* pos */
146 { 0x76, 0x86, 0x96 }, /* her */
147 { 0x76, 0xd6, 0x86, 0x96 }, /* scy */
148 { 0x76, 0x86, 0x96 }, /* hrc */
149 { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */
150};
151
152/* These were found through experimenting with an fscher, currently they are
153 not used, but we keep them around for future reference.
154static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 };
155static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */
156
157static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 };
158
159/* temp status register bitmasks */
160#define FSCHMD_TEMP_WORKING_MASK 0x01
161#define FSCHMD_TEMP_ALERT_MASK 0x02
162/* there only really is an alarm if the sensor is working and alert == 1 */
163#define FSCHMD_TEMP_ALARM_MASK \
164 (FSCHMD_TEMP_WORKING_MASK | FSCHMD_TEMP_ALERT_MASK)
165
166/* our driver name */
167#define FSCHMD_NAME "fschmd"
168
169/*
170 * Functions declarations
171 */
172
173static int fschmd_attach_adapter(struct i2c_adapter *adapter);
174static int fschmd_detach_client(struct i2c_client *client);
175static struct fschmd_data *fschmd_update_device(struct device *dev);
176
177/*
178 * Driver data (common to all clients)
179 */
180
181static struct i2c_driver fschmd_driver = {
182 .driver = {
183 .name = FSCHMD_NAME,
184 },
185 .attach_adapter = fschmd_attach_adapter,
186 .detach_client = fschmd_detach_client,
187};
188
189/*
190 * Client data (each client gets its own)
191 */
192
193struct fschmd_data {
194 struct i2c_client client;
195 struct device *hwmon_dev;
196 struct mutex update_lock;
197 int kind;
198 char valid; /* zero until following fields are valid */
199 unsigned long last_updated; /* in jiffies */
200
201 /* register values */
202 u8 global_control; /* global control register */
203 u8 volt[3]; /* 12, 5, battery voltage */
204 u8 temp_act[5]; /* temperature */
205 u8 temp_status[5]; /* status of sensor */
206 u8 temp_max[5]; /* high temp limit, notice: undocumented! */
207 u8 fan_act[6]; /* fans revolutions per second */
208 u8 fan_status[6]; /* fan status */
209 u8 fan_min[6]; /* fan min value for rps */
210 u8 fan_ripple[6]; /* divider for rps */
211};
212
213/*
214 * Sysfs attr show / store functions
215 */
216
217static ssize_t show_in_value(struct device *dev,
218 struct device_attribute *devattr, char *buf)
219{
220 const int max_reading[3] = { 14200, 6600, 3300 };
221 int index = to_sensor_dev_attr(devattr)->index;
222 struct fschmd_data *data = fschmd_update_device(dev);
223
224 return sprintf(buf, "%d\n", (data->volt[index] *
225 max_reading[index] + 128) / 255);
226}
227
228
229#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
230
231static ssize_t show_temp_value(struct device *dev,
232 struct device_attribute *devattr, char *buf)
233{
234 int index = to_sensor_dev_attr(devattr)->index;
235 struct fschmd_data *data = fschmd_update_device(dev);
236
237 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[index]));
238}
239
240static ssize_t show_temp_max(struct device *dev,
241 struct device_attribute *devattr, char *buf)
242{
243 int index = to_sensor_dev_attr(devattr)->index;
244 struct fschmd_data *data = fschmd_update_device(dev);
245
246 return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[index]));
247}
248
249static ssize_t store_temp_max(struct device *dev, struct device_attribute
250 *devattr, const char *buf, size_t count)
251{
252 int index = to_sensor_dev_attr(devattr)->index;
253 struct fschmd_data *data = dev_get_drvdata(dev);
254 long v = simple_strtol(buf, NULL, 10) / 1000;
255
256 v = SENSORS_LIMIT(v, -128, 127) + 128;
257
258 mutex_lock(&data->update_lock);
259 i2c_smbus_write_byte_data(&data->client,
260 FSCHMD_REG_TEMP_LIMIT[data->kind][index], v);
261 data->temp_max[index] = v;
262 mutex_unlock(&data->update_lock);
263
264 return count;
265}
266
267static ssize_t show_temp_fault(struct device *dev,
268 struct device_attribute *devattr, char *buf)
269{
270 int index = to_sensor_dev_attr(devattr)->index;
271 struct fschmd_data *data = fschmd_update_device(dev);
272
273 /* bit 0 set means sensor working ok, so no fault! */
274 if (data->temp_status[index] & FSCHMD_TEMP_WORKING_MASK)
275 return sprintf(buf, "0\n");
276 else
277 return sprintf(buf, "1\n");
278}
279
280static ssize_t show_temp_alarm(struct device *dev,
281 struct device_attribute *devattr, char *buf)
282{
283 int index = to_sensor_dev_attr(devattr)->index;
284 struct fschmd_data *data = fschmd_update_device(dev);
285
286 if ((data->temp_status[index] & FSCHMD_TEMP_ALARM_MASK) ==
287 FSCHMD_TEMP_ALARM_MASK)
288 return sprintf(buf, "1\n");
289 else
290 return sprintf(buf, "0\n");
291}
292
293
294#define RPM_FROM_REG(val) ((val) * 60)
295
296static ssize_t show_fan_value(struct device *dev,
297 struct device_attribute *devattr, char *buf)
298{
299 int index = to_sensor_dev_attr(devattr)->index;
300 struct fschmd_data *data = fschmd_update_device(dev);
301
302 return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[index]));
303}
304
305static ssize_t show_fan_div(struct device *dev,
306 struct device_attribute *devattr, char *buf)
307{
308 int index = to_sensor_dev_attr(devattr)->index;
309 struct fschmd_data *data = fschmd_update_device(dev);
310
311 /* bits 2..7 reserved => mask with 3 */
312 return sprintf(buf, "%d\n", 1 << (data->fan_ripple[index] & 3));
313}
314
315static ssize_t store_fan_div(struct device *dev, struct device_attribute
316 *devattr, const char *buf, size_t count)
317{
318 u8 reg;
319 int index = to_sensor_dev_attr(devattr)->index;
320 struct fschmd_data *data = dev_get_drvdata(dev);
321 /* supported values: 2, 4, 8 */
322 unsigned long v = simple_strtoul(buf, NULL, 10);
323
324 switch (v) {
325 case 2: v = 1; break;
326 case 4: v = 2; break;
327 case 8: v = 3; break;
328 default:
329 dev_err(dev, "fan_div value %lu not supported. "
330 "Choose one of 2, 4 or 8!\n", v);
331 return -EINVAL;
332 }
333
334 mutex_lock(&data->update_lock);
335
336 reg = i2c_smbus_read_byte_data(&data->client,
337 FSCHMD_REG_FAN_RIPPLE[data->kind][index]);
338
339 /* bits 2..7 reserved => mask with 0x03 */
340 reg &= ~0x03;
341 reg |= v;
342
343 i2c_smbus_write_byte_data(&data->client,
344 FSCHMD_REG_FAN_RIPPLE[data->kind][index], reg);
345
346 data->fan_ripple[index] = reg;
347
348 mutex_unlock(&data->update_lock);
349
350 return count;
351}
352
353static ssize_t show_fan_alarm(struct device *dev,
354 struct device_attribute *devattr, char *buf)
355{
356 int index = to_sensor_dev_attr(devattr)->index;
357 struct fschmd_data *data = fschmd_update_device(dev);
358
359 if (data->fan_status[index] & FSCHMD_FAN_ALARM_MASK)
360 return sprintf(buf, "1\n");
361 else
362 return sprintf(buf, "0\n");
363}
364
365static ssize_t show_fan_fault(struct device *dev,
366 struct device_attribute *devattr, char *buf)
367{
368 int index = to_sensor_dev_attr(devattr)->index;
369 struct fschmd_data *data = fschmd_update_device(dev);
370
371 if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT_MASK)
372 return sprintf(buf, "1\n");
373 else
374 return sprintf(buf, "0\n");
375}
376
377
378static ssize_t show_pwm_auto_point1_pwm(struct device *dev,
379 struct device_attribute *devattr, char *buf)
380{
381 int index = to_sensor_dev_attr(devattr)->index;
382 int val = fschmd_update_device(dev)->fan_min[index];
383
384 /* 0 = allow turning off, 1-255 = 50-100% */
385 if (val)
386 val = val / 2 + 128;
387
388 return sprintf(buf, "%d\n", val);
389}
390
391static ssize_t store_pwm_auto_point1_pwm(struct device *dev,
392 struct device_attribute *devattr, const char *buf, size_t count)
393{
394 int index = to_sensor_dev_attr(devattr)->index;
395 struct fschmd_data *data = dev_get_drvdata(dev);
396 unsigned long v = simple_strtoul(buf, NULL, 10);
397
398 /* register: 0 = allow turning off, 1-255 = 50-100% */
399 if (v) {
400 v = SENSORS_LIMIT(v, 128, 255);
401 v = (v - 128) * 2 + 1;
402 }
403
404 mutex_lock(&data->update_lock);
405
406 i2c_smbus_write_byte_data(&data->client,
407 FSCHMD_REG_FAN_MIN[data->kind][index], v);
408 data->fan_min[index] = v;
409
410 mutex_unlock(&data->update_lock);
411
412 return count;
413}
414
415
416/* The FSC hwmon family has the ability to force an attached alert led to flash
417 from software, we export this as an alert_led sysfs attr */
418static ssize_t show_alert_led(struct device *dev,
419 struct device_attribute *devattr, char *buf)
420{
421 struct fschmd_data *data = fschmd_update_device(dev);
422
423 if (data->global_control & FSCHMD_CONTROL_ALERT_LED_MASK)
424 return sprintf(buf, "1\n");
425 else
426 return sprintf(buf, "0\n");
427}
428
429static ssize_t store_alert_led(struct device *dev,
430 struct device_attribute *devattr, const char *buf, size_t count)
431{
432 u8 reg;
433 struct fschmd_data *data = dev_get_drvdata(dev);
434 unsigned long v = simple_strtoul(buf, NULL, 10);
435
436 mutex_lock(&data->update_lock);
437
438 reg = i2c_smbus_read_byte_data(&data->client, FSCHMD_REG_CONTROL);
439
440 if (v)
441 reg |= FSCHMD_CONTROL_ALERT_LED_MASK;
442 else
443 reg &= ~FSCHMD_CONTROL_ALERT_LED_MASK;
444
445 i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_CONTROL, reg);
446
447 data->global_control = reg;
448
449 mutex_unlock(&data->update_lock);
450
451 return count;
452}
453
454static struct sensor_device_attribute fschmd_attr[] = {
455 SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
456 SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1),
457 SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2),
458 SENSOR_ATTR(alert_led, 0644, show_alert_led, store_alert_led, 0),
459};
460
461static struct sensor_device_attribute fschmd_temp_attr[] = {
462 SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
463 SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
464 SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0),
465 SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0),
466 SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
467 SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
468 SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1),
469 SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1),
470 SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2),
471 SENSOR_ATTR(temp3_max, 0644, show_temp_max, store_temp_max, 2),
472 SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2),
473 SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2),
474 SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3),
475 SENSOR_ATTR(temp4_max, 0644, show_temp_max, store_temp_max, 3),
476 SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3),
477 SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3),
478 SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4),
479 SENSOR_ATTR(temp5_max, 0644, show_temp_max, store_temp_max, 4),
480 SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4),
481 SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4),
482};
483
484static struct sensor_device_attribute fschmd_fan_attr[] = {
485 SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0),
486 SENSOR_ATTR(fan1_div, 0644, show_fan_div, store_fan_div, 0),
487 SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0),
488 SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0),
489 SENSOR_ATTR(pwm1_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
490 store_pwm_auto_point1_pwm, 0),
491 SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1),
492 SENSOR_ATTR(fan2_div, 0644, show_fan_div, store_fan_div, 1),
493 SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1),
494 SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1),
495 SENSOR_ATTR(pwm2_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
496 store_pwm_auto_point1_pwm, 1),
497 SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2),
498 SENSOR_ATTR(fan3_div, 0644, show_fan_div, store_fan_div, 2),
499 SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2),
500 SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2),
501 SENSOR_ATTR(pwm3_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
502 store_pwm_auto_point1_pwm, 2),
503 SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3),
504 SENSOR_ATTR(fan4_div, 0644, show_fan_div, store_fan_div, 3),
505 SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3),
506 SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3),
507 SENSOR_ATTR(pwm4_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
508 store_pwm_auto_point1_pwm, 3),
509 SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4),
510 SENSOR_ATTR(fan5_div, 0644, show_fan_div, store_fan_div, 4),
511 SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4),
512 SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4),
513 SENSOR_ATTR(pwm5_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
514 store_pwm_auto_point1_pwm, 4),
515 SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5),
516 SENSOR_ATTR(fan6_div, 0644, show_fan_div, store_fan_div, 5),
517 SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5),
518 SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5),
519 SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
520 store_pwm_auto_point1_pwm, 5),
521};
522
523
524/*
525 * Real code
526 */
527
528static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind)
529{
530 struct i2c_client *client;
531 struct fschmd_data *data;
532 u8 revision;
533 const char * const names[5] = { "Poseidon", "Hermes", "Scylla",
534 "Heracles", "Heimdall" };
535 const char * const client_names[5] = { "fscpos", "fscher", "fscscy",
536 "fschrc", "fschmd" };
537 int i, err = 0;
538
539 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
540 return 0;
541
542 /* OK. For now, we presume we have a valid client. We now create the
543 * client structure, even though we cannot fill it completely yet.
544 * But it allows us to access i2c_smbus_read_byte_data. */
545 if (!(data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL)))
546 return -ENOMEM;
547
548 client = &data->client;
549 i2c_set_clientdata(client, data);
550 client->addr = address;
551 client->adapter = adapter;
552 client->driver = &fschmd_driver;
553 mutex_init(&data->update_lock);
554
555 /* Detect & Identify the chip */
556 if (kind <= 0) {
557 char id[4];
558
559 id[0] = i2c_smbus_read_byte_data(client,
560 FSCHMD_REG_IDENT_0);
561 id[1] = i2c_smbus_read_byte_data(client,
562 FSCHMD_REG_IDENT_1);
563 id[2] = i2c_smbus_read_byte_data(client,
564 FSCHMD_REG_IDENT_2);
565 id[3] = '\0';
566
567 if (!strcmp(id, "PEG"))
568 kind = fscpos;
569 else if (!strcmp(id, "HER"))
570 kind = fscher;
571 else if (!strcmp(id, "SCY"))
572 kind = fscscy;
573 else if (!strcmp(id, "HRC"))
574 kind = fschrc;
575 else if (!strcmp(id, "HMD"))
576 kind = fschmd;
577 else
578 goto exit_free;
579 }
580
581 if (kind == fscpos) {
582 /* The Poseidon has hardwired temp limits, fill these
583 in for the alarm resetting code */
584 data->temp_max[0] = 70 + 128;
585 data->temp_max[1] = 50 + 128;
586 data->temp_max[2] = 50 + 128;
587 }
588
589 /* i2c kind goes from 1-5, we want from 0-4 to address arrays */
590 data->kind = kind - 1;
591 strlcpy(client->name, client_names[data->kind], I2C_NAME_SIZE);
592
593 /* Tell the I2C layer a new client has arrived */
594 if ((err = i2c_attach_client(client)))
595 goto exit_free;
596
597 for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) {
598 err = device_create_file(&client->dev,
599 &fschmd_attr[i].dev_attr);
600 if (err)
601 goto exit_detach;
602 }
603
604 for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) {
605 /* Poseidon doesn't have TEMP_LIMIT registers */
606 if (kind == fscpos && fschmd_temp_attr[i].dev_attr.show ==
607 show_temp_max)
608 continue;
609
610 err = device_create_file(&client->dev,
611 &fschmd_temp_attr[i].dev_attr);
612 if (err)
613 goto exit_detach;
614 }
615
616 for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) {
617 /* Poseidon doesn't have a FAN_MIN register for its 3rd fan */
618 if (kind == fscpos &&
619 !strcmp(fschmd_fan_attr[i].dev_attr.attr.name,
620 "pwm3_auto_point1_pwm"))
621 continue;
622
623 err = device_create_file(&client->dev,
624 &fschmd_fan_attr[i].dev_attr);
625 if (err)
626 goto exit_detach;
627 }
628
629 data->hwmon_dev = hwmon_device_register(&client->dev);
630 if (IS_ERR(data->hwmon_dev)) {
631 err = PTR_ERR(data->hwmon_dev);
632 data->hwmon_dev = NULL;
633 goto exit_detach;
634 }
635
636 revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
637 printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n",
638 names[data->kind], (int) revision);
639
640 return 0;
641
642exit_detach:
643 fschmd_detach_client(client); /* will also free data for us */
644 return err;
645
646exit_free:
647 kfree(data);
648 return err;
649}
650
651static int fschmd_attach_adapter(struct i2c_adapter *adapter)
652{
653 if (!(adapter->class & I2C_CLASS_HWMON))
654 return 0;
655 return i2c_probe(adapter, &addr_data, fschmd_detect);
656}
657
658static int fschmd_detach_client(struct i2c_client *client)
659{
660 struct fschmd_data *data = i2c_get_clientdata(client);
661 int i, err;
662
663 /* Check if registered in case we're called from fschmd_detect
664 to cleanup after an error */
665 if (data->hwmon_dev)
666 hwmon_device_unregister(data->hwmon_dev);
667
668 for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++)
669 device_remove_file(&client->dev, &fschmd_attr[i].dev_attr);
670 for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++)
671 device_remove_file(&client->dev,
672 &fschmd_temp_attr[i].dev_attr);
673 for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++)
674 device_remove_file(&client->dev,
675 &fschmd_fan_attr[i].dev_attr);
676
677 if ((err = i2c_detach_client(client)))
678 return err;
679
680 kfree(data);
681 return 0;
682}
683
684static struct fschmd_data *fschmd_update_device(struct device *dev)
685{
686 struct i2c_client *client = to_i2c_client(dev);
687 struct fschmd_data *data = i2c_get_clientdata(client);
688 int i;
689
690 mutex_lock(&data->update_lock);
691
692 if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
693
694 for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) {
695 data->temp_act[i] = i2c_smbus_read_byte_data(client,
696 FSCHMD_REG_TEMP_ACT[data->kind][i]);
697 data->temp_status[i] = i2c_smbus_read_byte_data(client,
698 FSCHMD_REG_TEMP_STATE[data->kind][i]);
699
700 /* The fscpos doesn't have TEMP_LIMIT registers */
701 if (FSCHMD_REG_TEMP_LIMIT[data->kind][i])
702 data->temp_max[i] = i2c_smbus_read_byte_data(
703 client,
704 FSCHMD_REG_TEMP_LIMIT[data->kind][i]);
705
706 /* reset alarm if the alarm condition is gone,
707 the chip doesn't do this itself */
708 if ((data->temp_status[i] & FSCHMD_TEMP_ALARM_MASK) ==
709 FSCHMD_TEMP_ALARM_MASK &&
710 data->temp_act[i] < data->temp_max[i])
711 i2c_smbus_write_byte_data(client,
712 FSCHMD_REG_TEMP_STATE[data->kind][i],
713 FSCHMD_TEMP_ALERT_MASK);
714 }
715
716 for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
717 data->fan_act[i] = i2c_smbus_read_byte_data(client,
718 FSCHMD_REG_FAN_ACT[data->kind][i]);
719 data->fan_status[i] = i2c_smbus_read_byte_data(client,
720 FSCHMD_REG_FAN_STATE[data->kind][i]);
721 data->fan_ripple[i] = i2c_smbus_read_byte_data(client,
722 FSCHMD_REG_FAN_RIPPLE[data->kind][i]);
723
724 /* The fscpos third fan doesn't have a fan_min */
725 if (FSCHMD_REG_FAN_MIN[data->kind][i])
726 data->fan_min[i] = i2c_smbus_read_byte_data(
727 client,
728 FSCHMD_REG_FAN_MIN[data->kind][i]);
729
730 /* reset fan status if speed is back to > 0 */
731 if ((data->fan_status[i] & FSCHMD_FAN_ALARM_MASK) &&
732 data->fan_act[i])
733 i2c_smbus_write_byte_data(client,
734 FSCHMD_REG_FAN_STATE[data->kind][i],
735 FSCHMD_FAN_ALARM_MASK);
736 }
737
738 for (i = 0; i < 3; i++)
739 data->volt[i] = i2c_smbus_read_byte_data(client,
740 FSCHMD_REG_VOLT[i]);
741
742 data->global_control = i2c_smbus_read_byte_data(client,
743 FSCHMD_REG_CONTROL);
744
745 /* To be implemented in the future
746 data->watchdog[0] = i2c_smbus_read_byte_data(client,
747 FSCHMD_REG_WDOG_PRESET);
748 data->watchdog[1] = i2c_smbus_read_byte_data(client,
749 FSCHMD_REG_WDOG_STATE);
750 data->watchdog[2] = i2c_smbus_read_byte_data(client,
751 FSCHMD_REG_WDOG_CONTROL); */
752
753 data->last_updated = jiffies;
754 data->valid = 1;
755 }
756
757 mutex_unlock(&data->update_lock);
758
759 return data;
760}
761
762static int __init fschmd_init(void)
763{
764 return i2c_add_driver(&fschmd_driver);
765}
766
767static void __exit fschmd_exit(void)
768{
769 i2c_del_driver(&fschmd_driver);
770}
771
772MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
773MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and "
774 "Heimdall driver");
775MODULE_LICENSE("GPL");
776
777module_init(fschmd_init);
778module_exit(fschmd_exit);