* [lm-sensors] [PATCH] driver for SMSC SCH311x
2006-07-24 10:55 [lm-sensors] [PATCH] driver for SMSC SCH311x Marcin Dawidowicz
` (3 preceding siblings ...)
2006-11-27 9:15 ` [lm-sensors] FW: " Alan Pettitt
@ 2006-12-03 12:21 ` Jean Delvare
2007-06-15 15:18 ` Juerg Haefliger
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Jean Delvare @ 2006-12-03 12:21 UTC (permalink / raw)
To: lm-sensors
Hi Marcin,
On Fri, 24 Nov 2006 19:19:05 +0100, Marcin Dawidowicz wrote:
> Here is my patch for SVN lm-sensors repository to add SMSC SCH311x support for
> user space from some time ago. I think, there won't be a big problem to apply
> it to current repository state.
Maybe we can merge the user-space support already, even if I don't have
the time to review the driver itself. In particular the detection would
be nice to have. Here's my review:
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/lib/chips.c ./lm-sensors_SVN_modif/lib/chips.c
> --- ./lm-sensors_SVN/lib/chips.c 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/lib/chips.c 2006-07-21 17:19:45.000000000 +0200
> @@ -5840,6 +5840,127 @@ static sensors_chip_feature abituguru_fe
> { 0 }
> };
>
> +static sensors_chip_feature sch311x_features[] > + {
> + { SENSORS_SCH311X_IN(0), "2.5V", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in0_input", 3 },
Please use standard feature names ("in0", "in1", "in0_max", etc.) It
is way less confusing if the standard wiring isn't used on a given
motherboard (which happens all the time.) Another benefit is that you
won't have to explicitly list the Linux 2.6 file names and magnitudes,
libsensors will guess them for you.
> + { SENSORS_SCH311X_IN(1), "Vccp", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in1_input", 3 },
> + { SENSORS_SCH311X_IN(2), "VCC", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in2_input", 3 },
> + { SENSORS_SCH311X_IN(3), "5V", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in3_input", 3 },
> + { SENSORS_SCH311X_IN(4), "12V", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in4_input", 3 },
> + { SENSORS_SCH311X_IN(5), "VRT", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in5_input", 3 },
> + { SENSORS_SCH311X_IN(6), "Vbat", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "in6_input", 3 },
> + { SENSORS_SCH311X_IN_MIN(0), "2.5V_min", SENSORS_SCH311X_IN(0),
> + SENSORS_SCH311X_IN(0), RW,
> + NOSYSCTL, VALUE(1), 2, "in0_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(1), "Vccp_min", SENSORS_SCH311X_IN(1),
> + SENSORS_SCH311X_IN(1), RW,
> + NOSYSCTL, VALUE(1), 2, "in1_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(2), "VCC_min", SENSORS_SCH311X_IN(2),
> + SENSORS_SCH311X_IN(2), RW,
> + NOSYSCTL, VALUE(1), 2, "in2_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(3), "5V_min", SENSORS_SCH311X_IN(3),
> + SENSORS_SCH311X_IN(3), RW,
> + NOSYSCTL, VALUE(1), 2, "in3_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(4), "12V_min", SENSORS_SCH311X_IN(4),
> + SENSORS_SCH311X_IN(4), RW,
> + NOSYSCTL, VALUE(1), 2, "in4_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(5), "VRT_min", SENSORS_SCH311X_IN(5),
> + SENSORS_SCH311X_IN(5), RW,
> + NOSYSCTL, VALUE(1), 2, "in5_min", 3 },
> + { SENSORS_SCH311X_IN_MIN(6), "Vbat_min", SENSORS_SCH311X_IN(6),
> + SENSORS_SCH311X_IN(6), RW,
> + NOSYSCTL, VALUE(1), 2, "in6_min", 3 },
> + { SENSORS_SCH311X_IN_MAX(0), "2.5V_max", SENSORS_SCH311X_IN(0),
> + SENSORS_SCH311X_IN(0), RW,
> + NOSYSCTL, VALUE(2), 2, "in0_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(1), "Vccp_max", SENSORS_SCH311X_IN(1),
> + SENSORS_SCH311X_IN(1), RW,
> + NOSYSCTL, VALUE(2), 2, "in1_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(2), "VCC_max", SENSORS_SCH311X_IN(2),
> + SENSORS_SCH311X_IN(2), RW,
> + NOSYSCTL, VALUE(2), 2, "in2_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(3), "5V_max", SENSORS_SCH311X_IN(3),
> + SENSORS_SCH311X_IN(3), RW,
> + NOSYSCTL, VALUE(2), 2, "in3_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(4), "12V_max", SENSORS_SCH311X_IN(4),
> + SENSORS_SCH311X_IN(4), RW,
> + NOSYSCTL, VALUE(2), 2, "in4_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(5), "VRT_max", SENSORS_SCH311X_IN(5),
> + SENSORS_SCH311X_IN(5), RW,
> + NOSYSCTL, VALUE(2), 2, "in5_max", 3 },
> + { SENSORS_SCH311X_IN_MAX(6), "Vbat_max", SENSORS_SCH311X_IN(6),
> + SENSORS_SCH311X_IN(6), RW,
> + NOSYSCTL, VALUE(2), 2, "in6_max", 3 },
> + { SENSORS_SCH311X_IN_ALARM(0), "2.5V_alarm", SENSORS_SCH311X_IN(0),
> + SENSORS_SCH311X_IN(0), RW,
> + NOSYSCTL, VALUE(3), 2, "in0_alarm", 3 },
Alarms are not scaled, so the second mapping field shouldn't be set to
SENSORS_SCH311X_IN(0), but to NOMAP. Same for all other alarms, of
course. Also, magnitude is definitely 0 for alarms, and I doubt they
are writable.
> + { SENSORS_SCH311X_IN_ALARM(1), "Vccp_alarm", SENSORS_SCH311X_IN(1),
> + SENSORS_SCH311X_IN(1), RW,
> + NOSYSCTL, VALUE(3), 2, "in1_alarm", 3 },
> + { SENSORS_SCH311X_IN_ALARM(2), "VCC_alarm", SENSORS_SCH311X_IN(2),
> + SENSORS_SCH311X_IN(2), RW,
> + NOSYSCTL, VALUE(3), 2, "in2_alarm", 3 },
> + { SENSORS_SCH311X_IN_ALARM(3), "5V_alarm", SENSORS_SCH311X_IN(3),
> + SENSORS_SCH311X_IN(3), RW,
> + NOSYSCTL, VALUE(3), 2, "in3_alarm", 3 },
> + { SENSORS_SCH311X_IN_ALARM(4), "12V_alarm", SENSORS_SCH311X_IN(4),
> + SENSORS_SCH311X_IN(4), RW,
> + NOSYSCTL, VALUE(3), 2, "in4_alarm", 3 },
> + { SENSORS_SCH311X_IN_ALARM(5), "VRT_alarm", SENSORS_SCH311X_IN(5),
> + SENSORS_SCH311X_IN(5), RW,
> + NOSYSCTL, VALUE(3), 2, "in5_alarm", 3 },
> + { SENSORS_SCH311X_IN_ALARM(6), "Vbat_alarm", SENSORS_SCH311X_IN(6),
> + SENSORS_SCH311X_IN(6), RW,
> + NOSYSCTL, VALUE(3), 2, "in6_alarm", 3 },
> + { SENSORS_SCH311X_TEMP(1), "temp1", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(5), 2, "temp1_input", 0 },
Magnitude for temperatures in Linux 2.6 is fixed to 3. You don't need
to mention the Linux 2.6 name and magnitude, BTW, libsensors will guess
them.
> + { SENSORS_SCH311X_TEMP(2), "temp2", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(4), 2, "temp2_input", 0 },
> + { SENSORS_SCH311X_TEMP(3), "temp3", NOMAP, NOMAP,
> + R, NOSYSCTL, VALUE(5), 2, "temp3_input", 0 },
> + { SENSORS_SCH311X_TEMP_MIN(1), "temp1_min", SENSORS_SCH311X_TEMP(1),
> + SENSORS_SCH311X_TEMP(1), RW,
> + NOSYSCTL, VALUE(1), 2, "temp1_low", 0 },
Sysfs file name should be "temp1_min", not "temp1_low".
> + { SENSORS_SCH311X_TEMP_MAX(1), "temp1_max", SENSORS_SCH311X_TEMP(1),
> + SENSORS_SCH311X_TEMP(1), RW,
> + NOSYSCTL, VALUE(2), 2, "temp1_high", 0 },
And here "temp1_max" instead of "temp1_high". But again you don't need
to mention them, libsensors will guess them from the symbol name.
> + { SENSORS_SCH311X_TEMP_ALARM(1), "temp1_alarm", SENSORS_SCH311X_TEMP(1),
> + SENSORS_SCH311X_TEMP(1), RW,
> + NOSYSCTL, VALUE(3), 2, "temp1_alarm", 0 },
> + { SENSORS_SCH311X_TEMP_ERROR(1), "temp1_error", SENSORS_SCH311X_TEMP(1),
> + SENSORS_SCH311X_TEMP(1), RW,
> + NOSYSCTL, VALUE(4), 2, "temp1_error", 0 },
This name doesn't exist in our Linux 2.6 sysfs file name standard. If
this "error" refers to an invalid wiring of the thermal sensor (i.e.
input value is invalid) the file should be named temp1_input_fault. I
also doubt the alarm and error files are writable, and the Linux 2.4
magnitude is wrong (should be 0).
> + { SENSORS_SCH311X_TEMP_MIN(2), "temp2_min", SENSORS_SCH311X_TEMP(2),
> + SENSORS_SCH311X_TEMP(2), RW,
> + NOSYSCTL, VALUE(1), 2, "temp2_low", 0 },
> + { SENSORS_SCH311X_TEMP_MAX(2), "temp2_max", SENSORS_SCH311X_TEMP(2),
> + SENSORS_SCH311X_TEMP(2), RW,
> + NOSYSCTL, VALUE(2), 2, "temp2_high", 0 },
> + { SENSORS_SCH311X_TEMP_ALARM(2), "temp2_alarm", SENSORS_SCH311X_TEMP(2),
> + SENSORS_SCH311X_TEMP(2), RW,
> + NOSYSCTL, VALUE(3), 2, "temp2_alarm", 0 },
> + { SENSORS_SCH311X_TEMP_MIN(3), "temp3_min", SENSORS_SCH311X_TEMP(3),
> + SENSORS_SCH311X_TEMP(3), RW,
> + NOSYSCTL, VALUE(1), 2, "temp3_low", 0 },
> + { SENSORS_SCH311X_TEMP_MAX(3), "temp3_max", SENSORS_SCH311X_TEMP(3),
> + SENSORS_SCH311X_TEMP(3), RW,
> + NOSYSCTL, VALUE(2), 2, "temp3_high", 0 },
> + { SENSORS_SCH311X_TEMP_ALARM(3), "temp3_alarm", SENSORS_SCH311X_TEMP(2),
> + SENSORS_SCH311X_TEMP(2), RW,
> + NOSYSCTL, VALUE(3), 2, "temp3_alarm", 0 },
Copy'n'paste error, should be SENSORS_SCH311X_TEMP(3).
> + { SENSORS_SCH311X_TEMP_ERROR(3), "temp3_error", SENSORS_SCH311X_TEMP(3),
> + SENSORS_SCH311X_TEMP(3), RW,
> + NOSYSCTL, VALUE(4), 2, "temp3_error", 0 },
> + { 0 }
> + };
> +
>
> sensors_chip_features sensors_chip_features_list[] > {
> @@ -5943,5 +6064,6 @@ sensors_chip_features sensors_chip_featu
> { SENSORS_SMSC47B397_PREFIX, smsc47b397_features },
> { SENSORS_F71805F_PREFIX, f71805f_features },
> { SENSORS_ABITUGURU_PREFIX, abituguru_features },
> + { SENSORS_SCH311X_PREFIX, sch311x_features },
> { 0 }
> };
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/lib/chips.h ./lm-sensors_SVN_modif/lib/chips.h
> --- ./lm-sensors_SVN/lib/chips.h 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/lib/chips.h 2006-07-21 17:18:37.000000000 +0200
> @@ -2187,4 +2187,20 @@
> #define SENSORS_ABITUGURU_FAN_ALARM(n) (0xA0 + (n)) /* R */
> #define SENSORS_ABITUGURU_FAN_MIN(n) (0xB0 + (n)) /* RW */
>
> +/* SMSC SCH311x chips */
> +#define SENSORS_SCH311X_PREFIX "sch311x"
> +
> +/* for n from 0 to 6 */
> +#define SENSORS_SCH311X_IN(n) (1 + (n)) /* R */
> +#define SENSORS_SCH311X_IN_MIN(n) (10 + (n)) /* RW */
> +#define SENSORS_SCH311X_IN_MAX(n) (20 + (n)) /* RW */
> +#define SENSORS_SCH311X_IN_ALARM(n) (30 + (n)) /* R */
> +
> +/* for n from 1 to 3 */
> +#define SENSORS_SCH311X_TEMP(n) (50 + (n)) /* R */
> +#define SENSORS_SCH311X_TEMP_MIN(n) (60 + (n)) /* RW */
> +#define SENSORS_SCH311X_TEMP_MAX(n) (70 + (n)) /* RW */
> +#define SENSORS_SCH311X_TEMP_ALARM(n) (80 + (n)) /* R */
> +#define SENSORS_SCH311X_TEMP_ERROR(n) (90 + (n)) /* R */
> +
> #endif /* def LIB_SENSORS_CHIPS_H */
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/prog/detect/sensors-detect ./lm-sensors_SVN_modif/prog/detect/sensors-detect
> --- ./lm-sensors_SVN/prog/detect/sensors-detect 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/prog/detect/sensors-detect 2006-07-17 17:54:13.000000000 +0200
> @@ -1926,6 +1926,24 @@ $chip_kern26_w83791d = {
> logdev => 0x08,
> },
> {
> + name => "SMSC SCH3112 Super IO",
> + driver => "sch311x",
> + devid => 0x7c,
> + logdev => 0x0a,
> + },
> + {
> + name => "SMSC SCH3114 Super IO",
> + driver => "sch311x",
> + devid => 0x7d,
> + logdev => 0x0a,
> + },
> + {
> + name => "SMSC SCH3116 Super IO",
> + driver => "sch311x",
> + devid => 0x7f,
> + logdev => 0x0a,
> + },
> + {
> name => "SMSC LPC47M584-NC Super IO",
> # No datasheet
> devid => 0x76,
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/prog/sensors/chips.c ./lm-sensors_SVN_modif/prog/sensors/chips.c
> --- ./lm-sensors_SVN/prog/sensors/chips.c 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/prog/sensors/chips.c 2006-07-21 17:26:11.000000000 +0200
> @@ -6041,6 +6041,52 @@ void print_abituguru(const sensors_chip_
> SENSORS_ABITUGURU_FAN_ALARM(i), SENSORS_ABITUGURU_FAN_MIN(i));
> }
>
> +void print_sch311x(const sensors_chip_name *name)
> +{
> + char *label;
> + double cur, min, max, alarm;
> + int valid, i;
> +
> + for (i = 0; i < 7; i++) {
> + if (!sensors_get_label_and_valid(*name, SENSORS_SCH311X_IN(i),
> + &label, &valid)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_IN(i), &cur)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_IN_MIN(i), &min)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_IN_MAX(i), &max)) {
> + if (valid) {
> + print_label(label, 10);
> + printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
> + cur, min, max, sensors_get_feature(*name,
> + SENSORS_SCH311X_IN_ALARM(i), &alarm) ? "NOT SUPPORTED" :
Why don't you simply get the alarm value together with the other
values? The code would be more readable.
> + alarm ? "ALARM" : "");
> + }
> + } else
> + printf("Can't get in%d data!\n", i);
We usually prefix these messages with "ERROR: ".
> + free(label);
> + }
> +
> + for (i = 1; i <= 3; i++) {
> + if (!sensors_get_label_and_valid(*name, SENSORS_SCH311X_TEMP(i),
> + &label, &valid)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_TEMP(i), &cur)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_TEMP_MAX(i), &max)
> + && !sensors_get_feature(*name, SENSORS_SCH311X_TEMP_MIN(i), &min)) {
> + if (valid) {
> + print_label(label, 10);
> + print_temp_info(cur, max, min, HYST, 0, 0);
Is the low temperature limit really an hysteresis value? You named it
everywhere as if it were a min limit. Please clarify. If it's an
hysteresis value, the symbold and file names need to be changed. If
it's a min value, please use MINMAX instead of HYST here.
> + printf("%6s", sensors_get_feature(*name,
> + SENSORS_SCH311X_TEMP_ALARM(i), &alarm) ? "NOT SUPPORTED" :
> + alarm ? "ALARM" : "");
> + printf("%6s", sensors_get_feature(*name, SENSORS_SCH311X_TEMP_ERROR(i),
> + &alarm) ? "" : (alarm) ? "ERROR" : "" );
Same here, getting the alarm and error/fault values beforehand would
make the code much more readable IMHO. You should also check the fault
condition first, as it doesn't make much sense to display an alarm if
the input has a fault condition to start with.
BTW you're reading the error/fault value even for i=2, while this file
doesn't exist, this needs to be fixed.
> + printf("\n");
> + }
> + } else
> + printf("Can't get temp%d data!\n", i);
> + free(label);
> + }
> +}
> +
> void print_unknown_chip(const sensors_chip_name *name)
> {
> int a,b,valid;
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/prog/sensors/chips.h ./lm-sensors_SVN_modif/prog/sensors/chips.h
> --- ./lm-sensors_SVN/prog/sensors/chips.h 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/prog/sensors/chips.h 2006-07-17 12:50:53.000000000 +0200
> @@ -73,5 +73,6 @@ extern void print_adm1031(const sensors_
> extern void print_smsc47b397(const sensors_chip_name *name);
> extern void print_f71805f(const sensors_chip_name *name);
> extern void print_abituguru(const sensors_chip_name *name);
> +extern void print_sch311x(const sensors_chip_name *name);
>
> #endif /* def PROG_SENSORS_CHIPS_H */
> diff -uprN -X dontdiff_my ./lm-sensors_SVN/prog/sensors/main.c ./lm-sensors_SVN_modif/prog/sensors/main.c
> --- ./lm-sensors_SVN/prog/sensors/main.c 2006-07-17 12:31:42.000000000 +0200
> +++ ./lm-sensors_SVN_modif/prog/sensors/main.c 2006-07-17 13:28:04.000000000 +0200
> @@ -416,6 +416,7 @@ struct match matches[] = {
> { "smsc47b397", print_smsc47b397 },
> { "f71805f", print_f71805f },
> { "abituguru", print_abituguru },
> + { "sch311x", print_sch311x },
> { NULL, NULL }
> };
>
>
Care to provide an updated patch against the current lm_sensors SVN
tree?
Thanks,
--
Jean Delvare
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [lm-sensors] [PATCH] driver for SMSC SCH311x
2006-07-24 10:55 [lm-sensors] [PATCH] driver for SMSC SCH311x Marcin Dawidowicz
` (4 preceding siblings ...)
2006-12-03 12:21 ` [lm-sensors] " Jean Delvare
@ 2007-06-15 15:18 ` Juerg Haefliger
2007-06-20 12:49 ` Marcin Dawidowicz
2007-06-21 4:23 ` Juerg Haefliger
7 siblings, 0 replies; 9+ messages in thread
From: Juerg Haefliger @ 2007-06-15 15:18 UTC (permalink / raw)
To: lm-sensors
Marcin,
Are you still interested in support for the SMSC sch311x family? I
discovered that the dme1737, which I is supported, is pretty
identical. It wouldn't take a lot to modify the dme1737 driver to
support the sch311x. Let me know if you're still interested and if you
have a system to test and I'll give it a shot. Or you can, if you want
to.
...juerg
> Hello,
>
> Below you find my SMSC SCH311x chips driver. Currently, it supports voltage, temperature inputs and alarms.
> There is not implemented support for fans and pwm control (I couldn't test it on my hardware).
>
> Thanks for your feedback,
> Marcin
>
> Signed-off-by: Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>
>
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/Kconfig linux-2.6.18-rc2-sch311x/drivers/hwmon/Kconfig
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/Kconfig 2006-07-15 23:53:08.000000000 +0200
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/Kconfig 2006-07-20 17:57:01.000000000 +0200
> @@ -396,6 +396,17 @@ config SENSORS_SMSC47B397
> This driver can also be built as a module. If so, the module
> will be called smsc47b397.
>
> +config SENSORS_SCH311X
> + tristate "SMSC SCH311x family"
> + depends on HWMON && I2C && EXPERIMENTAL
> + select I2C_ISA
> + help
> + If you say yes here you get support for the SMSC SCH311x family
> + sensor chip.
> +
> + This driver can also be built as a module. If so, the module
> + will be called sch311x.
> +
> config SENSORS_VIA686A
> tristate "VIA686A"
> depends on HWMON && I2C && PCI
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/Makefile linux-2.6.18-rc2-sch311x/drivers/hwmon/Makefile
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/Makefile 2006-07-15 23:53:08.000000000 +0200
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/Makefile 2006-07-20 17:57:41.000000000 +0200
> @@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_SIS5595) += sis5595
> obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
> obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
> obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
> +obj-$(CONFIG_SENSORS_SCH311X) += sch311x.o
> obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
> obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
> obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/sch311x.c linux-2.6.18-rc2-sch311x/drivers/hwmon/sch311x.c
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/sch311x.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/sch311x.c 2006-07-24 12:36:02.000000000 +0200
> @@ -0,0 +1,629 @@
> +/*
> + sch311x.c - Part of lm_sensors, Linux kernel modules
> + for hardware monitoring
> +
> + Supports the SMSC SCH311x Super-I/O chips.
> +
> + Author: Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>
> + Copyright (C) 2006 Kontron Modular Computers
> +
> + derived in part from smsc47m1.c and smsc47b397:
> + Copyright (C) 2002 Mark D. Studebaker <mdsxyz123 at yahoo.com>
> + Copyright (C) 2004 Jean Delvare <khali at linux-fr.org>
> + Copyright (C) 2004 Mark M. Hoffman <mhoffman at lightlink.com>
> +
> + 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/slab.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-isa.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/mutex.h>
> +#include <asm/io.h>
> +
> +/* Address is autodetected, there is no default value */
> +static unsigned short address;
> +
> +/* Miscellaneous defines */
> +
> +#define SUPERIO_REG_DEVID 0x20
> +#define SUPERIO_REG_DEVREV 0x21
> +#define SUPERIO_REG_BASE_MSB 0x60
> +#define SUPERIO_REG_BASE_LSB 0x61
> +#define SUPERIO_REG_LD 0x0A
> +
> +#define SMSC_EXTENT 0x02 /* hw mon registers size */
> +
> +#define SCH311X_REG_TEMP(nr) (0x25 + nr)
> +#define SCH311X_REG_TEMP_HIGH(nr) (0x4f + (nr*2))
> +#define SCH311X_REG_TEMP_LOW(nr) (0x4e + (nr*2))
> +#define SCH311X_REG_FANTACH_LSB(nr) (0x28 + (nr*2))
> +#define SCH311X_REG_FANTACH_MSB(nr) (0x29 + (nr*2))
> +#define SCH311X_REG_FANTACH_MIN_LSB(nr) (0x54 + (nr*2))
> +#define SCH311X_REG_FANTACH_MIN_MSB(nr) (0x55 + (nr*2))
> +
> +static const u8 sch311x_volt_regs[7] = {
> + 0x20 /* 2.5 V */,
> + 0x21 /* Vccp 1.5 V */,
> + 0x22 /* VCC 3.3 V */,
> + 0x23 /* 5 V */,
> + 0x24 /* 12 V */,
> + 0x99 /* VTR 3.3 V */,
> + 0x9a /* Vbat 3.0 V */
> +};
> +
> +/* max volt values multiplied by 1000 */
> +static const u16 sch311x_volt_max_vals[7] = {
> + 6640 /* 2.5 V */,
> + 3000 /* Vccp 1.5 V */,
> + 4380 /* VCC 3.3 V */,
> + 6640 /* 5 V */,
> + 16000 /* 12 V */,
> + 4380 /* VTR 3.3 V */,
> + 4380 /* Vbat 3.0 V */
> +};
> +
> +#define SCH311X_REG_IN(nr) (sch311x_volt_regs[nr])
> +
> +static const u8 sch311x_volt_lo_regs[7] = {
> + 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x9b, 0x9d
> +};
> +
> +#define SCH311X_REG_MIN(nr) (sch311x_volt_lo_regs[nr])
> +
> +static const u8 sch311x_volt_hi_regs[7] = {
> + 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x9c, 0x9e
> +};
> +
> +#define SCH311X_REG_MAX(nr) (sch311x_volt_hi_regs[nr])
> +
> +/* Interrupt enable voltages */
> +#define SCH311X_REG_INT_VOLT_EN (0x7e)
> +
> +#define VOLT_EN_2_5V (1<<2)
> +#define VOLT_EN_VCPP (1<<3)
> +#define VOLT_EN_VCC (1<<7)
> +#define VOLT_EN_5V (1<<5)
> +#define VOLT_EN_12V (1<<6)
> +#define VOLT_EN_VRT (1<<4)
> +#define VOLT_EN_VBAT (1<<1)
> +
> +/* Interrupt status registers */
> +#define SCH311X_REG_ISR1 (0x41)
> +#define SCH311X_REG_ISR2 (0x42)
> +#define SCH311X_REG_ISR3 (0x83)
> +
> +/* Alarm bits meaning */
> +#define ISR_TEMP3 (6)
> +#define ISR_TEMP2 (5)
> +#define ISR_TEMP1 (4)
> +#define ISR_5V (3)
> +#define ISR_VCC (2)
> +#define ISR_Vccp (1)
> +#define ISR_2_5V (0)
> +
> +#define ISR_ERR3 (15)
> +#define ISR_ERR1 (14)
> +#define ISR_FAN_3 (12)
> +#define ISR_FAN_2 (11)
> +#define ISR_FAN_1 (10)
> +#define ISR_12V (8)
> +
> +#define ISR_VRT (16)
> +#define ISR_Vbat (17)
> +
> +/* logical device for hardware monitoring is 0x0A */
> +#define superio_select() superio_outb(0x07, SUPERIO_REG_LD)
> +
> +/* Super-I/0 registers and commands */
> +#define REG 0x2e /* The register to read/write */
> +#define VAL 0x2f /* The value to read/write */
> +
> +static inline void
> +superio_outb(int reg, int val)
> +{
> + outb(reg, REG);
> + outb(val, VAL);
> +}
> +
> +static inline int
> +superio_inb(int reg)
> +{
> + outb(reg, REG);
> + return inb(VAL);
> +}
> +
> +static inline void
> +superio_enter(void)
> +{
> + outb(0x55, REG);
> +}
> +
> +static inline void
> +superio_exit(void)
> +{
> + outb(0xAA, REG);
> +}
> +
> +struct sch311x_data {
> + struct i2c_client client;
> + struct class_device *class_dev;
> + struct mutex lock;
> +
> + struct mutex update_lock;
> + unsigned long last_updated; /* In jiffies */
> + int valid;
> +
> + u32 alarms; /* Register value of interrupt status */
> +
> + s8 temp[3]; /* register value */
> + s8 temp_high[3]; /* register value */
> + s8 temp_low[3]; /* register value */
> +
> + u8 in[7]; /* register value */
> + u8 in_max[7]; /* register value */
> + u8 in_min[7]; /* register value */
> +};
> +
> +static int sch311x_detect(struct i2c_adapter *adapter);
> +static int sch311x_detach_client(struct i2c_client *client);
> +
> +static struct i2c_driver sch311x_driver = {
> + .driver = {
> + .name = "sch311x",
> + },
> + .attach_adapter = sch311x_detect,
> + .detach_client = sch311x_detach_client,
> +};
> +
> +static int sch311x_read_value(struct i2c_client *client, u8 reg)
> +{
> + struct sch311x_data *data = i2c_get_clientdata(client);
> + int res;
> +
> + mutex_lock(&data->lock);
> +
> + outb(reg, client->addr);
> + res = inb_p(client->addr + 1);
> +
> + mutex_unlock(&data->lock);
> + return res;
> +}
> +
> +static void sch311x_write_value(struct i2c_client *client, u8 reg, u8 value)
> +{
> + struct sch311x_data *data = i2c_get_clientdata(client);
> +
> + mutex_lock(&data->lock);
> +
> + outb(reg, client->addr);
> + outb(value, client->addr + 1);
> +
> + mutex_unlock(&data->lock);
> +}
> +
> +static struct sch311x_data *sch311x_update_device(struct device *dev)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct sch311x_data *data = i2c_get_clientdata(client);
> + int i;
> +
> + mutex_lock(&data->update_lock);
> +
> + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
> + dev_dbg(&client->dev, "starting device update...\n");
> +
> + /* read alarms */
> + data->alarms = sch311x_read_value(client,
> + SCH311X_REG_ISR1);
> + data->alarms|= sch311x_read_value(client,
> + SCH311X_REG_ISR2) << 8;
> + data->alarms|= sch311x_read_value(client,
> + SCH311X_REG_ISR3) << 16;
> +
> + /* 3 temperature inputs */
> + for (i = 0; i < 3; i++) {
> + data->temp[i] = sch311x_read_value(client,
> + SCH311X_REG_TEMP(i));
> + data->temp_high[i] = sch311x_read_value(client,
> + SCH311X_REG_TEMP_HIGH(i));
> + data->temp_low[i] = sch311x_read_value(client,
> + SCH311X_REG_TEMP_LOW(i));
> + }
> + /* 7 voltage inputs */
> + for (i = 0; i < 7; i++) {
> + data->in[i] = sch311x_read_value(client,
> + SCH311X_REG_IN(i));
> + data->in_max[i] = sch311x_read_value(client,
> + SCH311X_REG_MAX(i));
> + data->in_min[i] = sch311x_read_value(client,
> + SCH311X_REG_MIN(i));
> + }
> +
> + /* clear alarms */
> + sch311x_write_value(client, SCH311X_REG_ISR1, 0xff);
> + sch311x_write_value(client, SCH311X_REG_ISR2, 0xff);
> +
> + data->last_updated = jiffies;
> + data->valid = 1;
> +
> + dev_dbg(&client->dev, "... device update complete\n");
> + }
> +
> + mutex_unlock(&data->update_lock);
> +
> + return data;
> +}
> +
> +static int temp_from_reg(u8 reg)
> +{
> + return (s8)reg * 100;
> +}
> +
> +/* 0 <= nr <= 3 */
> +static ssize_t show_temp(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
> +}
> +
> +static ssize_t show_temp_high(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[nr]));
> +}
> +
> +static ssize_t show_temp_low(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", temp_from_reg(data->temp_low[nr]));
> +}
> +
> +#define sysfs_temp(num) \
> +static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_temp(dev, buf, num-1); \
> +} \
> +static ssize_t show_temp_high##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_temp_high(dev, buf, num-1); \
> +} \
> +static ssize_t show_temp_low##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_temp_low(dev, buf, num-1); \
> +} \
> +static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
> +
> +sysfs_temp(1);
> +sysfs_temp(2);
> +sysfs_temp(3);
> +
> +static void set_temp_high(struct device *dev, const char *buf, int nr)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + s32 val;
> +
> + val = simple_strtol(buf, NULL, 10);
> + sch311x_write_value(client, SCH311X_REG_TEMP_HIGH(nr), val/100);
> +}
> +
> +static void set_temp_low(struct device *dev, const char *buf, int nr)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + s32 val;
> +
> + val = simple_strtol(buf, NULL, 10);
> + sch311x_write_value(client, SCH311X_REG_TEMP_LOW(nr), val/100);
> +}
> +
> +#define sysfs_set_temp(num) \
> +static ssize_t set_temp_high##num(struct device *dev, struct device_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + set_temp_high(dev, buf, num - 1); \
> + return count; \
> +} \
> +static ssize_t set_temp_low##num(struct device *dev, struct device_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + set_temp_low(dev, buf, num - 1); \
> + return count; \
> +} \
> +static DEVICE_ATTR(temp##num##_low, S_IRUGO | S_IWUSR, \
> + show_temp_low##num, set_temp_low##num); \
> +static DEVICE_ATTR(temp##num##_high, S_IRUGO | S_IWUSR, \
> + show_temp_high##num, set_temp_high##num)
> +
> +sysfs_set_temp(1);
> +sysfs_set_temp(2);
> +sysfs_set_temp(3);
> +
> +#define device_create_file_temp(client, num) \
> + device_create_file(&client->dev, &dev_attr_temp##num##_input); \
> + device_create_file(&client->dev, &dev_attr_temp##num##_high); \
> + device_create_file(&client->dev, &dev_attr_temp##num##_low)
> +
> +static int in_from_reg(u8 reg, u16 max_val)
> +{
> + return (int) reg * max_val / 255;
> +}
> +
> +/* 0 <= nr <= 7 */
> +static ssize_t show_in(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", in_from_reg(data->in[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +static ssize_t show_max(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", in_from_reg(data->in_max[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +static ssize_t show_min(struct device *dev, char *buf, int nr)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + return sprintf(buf, "%d\n", in_from_reg(data->in_min[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +#define sysfs_in(num) \
> +static ssize_t show_in##num##_input(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_in(dev, buf, num); \
> +} \
> +static ssize_t show_in##num##_max(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_max(dev, buf, num); \
> +} \
> +static ssize_t show_in##num##_min(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> + return show_min(dev, buf, num); \
> +} \
> +static DEVICE_ATTR(in##num##_input, S_IRUGO, show_in##num##_input, NULL)
> +
> +sysfs_in(0);
> +sysfs_in(1);
> +sysfs_in(2);
> +sysfs_in(3);
> +sysfs_in(4);
> +sysfs_in(5);
> +sysfs_in(6);
> +
> +static void set_min(struct device *dev, const char *buf, int nr)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + u32 val;
> +
> + val = simple_strtoul(buf, NULL, 10) * 255 / sch311x_volt_max_vals[nr];
> + sch311x_write_value(client, SCH311X_REG_MIN(nr), val);
> +}
> +
> +static void set_max(struct device *dev, const char *buf, int nr)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + u32 val;
> +
> + val = simple_strtoul(buf, NULL, 10) * 255 / sch311x_volt_max_vals[nr];
> + sch311x_write_value(client, SCH311X_REG_MAX(nr), val);
> +}
> +
> +#define sysfs_set_in(num) \
> +static ssize_t set_in##num##_min(struct device *dev, struct device_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + set_min(dev, buf, num); \
> + return count; \
> +} \
> +static ssize_t set_in##num##_max(struct device *dev, struct device_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + set_max(dev, buf, num); \
> + return count; \
> +} \
> +static DEVICE_ATTR(in##num##_min, S_IRUGO | S_IWUSR, \
> + show_in##num##_min, set_in##num##_min); \
> +static DEVICE_ATTR(in##num##_max, S_IRUGO | S_IWUSR, \
> + show_in##num##_max, set_in##num##_max)
> +
> +#define device_create_file_in(client, num) \
> + device_create_file(&client->dev, &dev_attr_in##num##_input); \
> + device_create_file(&client->dev, &dev_attr_in##num##_max); \
> + device_create_file(&client->dev, &dev_attr_in##num##_min); \
> + device_create_file(&client->dev, &sensor_dev_attr_in##num##_alarm.dev_attr)
> +
> +sysfs_set_in(0);
> +sysfs_set_in(1);
> +sysfs_set_in(2);
> +sysfs_set_in(3);
> +sysfs_set_in(4);
> +sysfs_set_in(5);
> +sysfs_set_in(6);
> +
> +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct sch311x_data *data = sch311x_update_device(dev);
> + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> + int nr = sensor_attr->index;
> + return sprintf(buf, "%u\n", (data->alarms & (1<<nr)) ? 1 : 0);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP1);
> +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP2);
> +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP3);
> +static SENSOR_DEVICE_ATTR(temp1_error, S_IRUGO, show_alarm, NULL, ISR_ERR1);
> +static SENSOR_DEVICE_ATTR(temp3_error, S_IRUGO, show_alarm, NULL, ISR_ERR3);
> +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, ISR_2_5V);
> +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, ISR_Vccp);
> +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, ISR_VCC);
> +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, ISR_5V);
> +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, ISR_12V);
> +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, ISR_VRT);
> +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, ISR_Vbat);
> +
> +static int sch311x_detach_client(struct i2c_client *client)
> +{
> + struct sch311x_data *data = i2c_get_clientdata(client);
> + int err;
> +
> + hwmon_device_unregister(data->class_dev);
> +
> + if ((err = i2c_detach_client(client)))
> + return err;
> +
> + release_region(client->addr, SMSC_EXTENT);
> + kfree(data);
> +
> + return 0;
> +}
> +
> +static int sch311x_detect(struct i2c_adapter *adapter)
> +{
> + struct i2c_client *new_client;
> + struct sch311x_data *data;
> + int err = 0;
> + u8 mon_volt;
> +
> + if (!request_region(address, SMSC_EXTENT,
> + sch311x_driver.driver.name)) {
> + dev_err(&adapter->dev, "Region 0x%x already in use!\n",
> + address);
> + return -EBUSY;
> + }
> +
> + if (!(data = kzalloc(sizeof(struct sch311x_data), GFP_KERNEL))) {
> + err = -ENOMEM;
> + goto error_release;
> + }
> +
> + new_client = &data->client;
> + i2c_set_clientdata(new_client, data);
> + new_client->addr = address;
> + mutex_init(&data->lock);
> + new_client->adapter = adapter;
> + new_client->driver = &sch311x_driver;
> + new_client->flags = 0;
> +
> + strlcpy(new_client->name, "sch311x", I2C_NAME_SIZE);
> +
> + mutex_init(&data->update_lock);
> +
> + if ((err = i2c_attach_client(new_client)))
> + goto error_free;
> +
> + mon_volt = superio_inb(SCH311X_REG_INT_VOLT_EN);
> +
> + data->class_dev = hwmon_device_register(&new_client->dev);
> + if (IS_ERR(data->class_dev)) {
> + err = PTR_ERR(data->class_dev);
> + goto error_detach;
> + }
> +
> + /* temp values */
> + device_create_file_temp(new_client, 1);
> + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_alarm.dev_attr);
> + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_error.dev_attr);
> + device_create_file_temp(new_client, 2);
> + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_alarm.dev_attr);
> + device_create_file_temp(new_client, 3);
> + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_alarm.dev_attr);
> + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_error.dev_attr);
> +
> + /* voltage values */
> + if (mon_volt & VOLT_EN_2_5V) device_create_file_in(new_client, 0);
> + if (mon_volt & VOLT_EN_VCPP) device_create_file_in(new_client, 1);
> + if (mon_volt & VOLT_EN_VCC) device_create_file_in(new_client, 2);
> + if (mon_volt & VOLT_EN_5V) device_create_file_in(new_client, 3);
> + if (mon_volt & VOLT_EN_12V) device_create_file_in(new_client, 4);
> + if (mon_volt & VOLT_EN_VRT) device_create_file_in(new_client, 5);
> + if (mon_volt & VOLT_EN_VBAT) device_create_file_in(new_client, 6);
> +
> + return 0;
> +
> +error_detach:
> + i2c_detach_client(new_client);
> +error_free:
> + kfree(data);
> +error_release:
> + release_region(address, SMSC_EXTENT);
> + return err;
> +}
> +
> +static int __init sch311x_find(unsigned short *addr)
> +{
> + u8 id, rev;
> +
> + superio_enter();
> + id = superio_inb(SUPERIO_REG_DEVID);
> +
> + if ((id & 0xfc) != 0x7c) {
> + superio_exit();
> + return -ENODEV;
> + }
> +
> + rev = superio_inb(SUPERIO_REG_DEVREV);
> +
> + superio_select();
> +
> + /*
> + * base address to "Runtime Registers"
> + * plus offset to HWM Index/Data
> + * registers
> + */
> + *addr = ((superio_inb(SUPERIO_REG_BASE_MSB) << 8)
> + | superio_inb(SUPERIO_REG_BASE_LSB))
> + + 0x70;
> +
> + printk(KERN_INFO "sch311x: found SMSC %s "
> + "(base address 0x%04x, revision %u)\n",
> + id = 0x7c ? "SCH3112" :
> + id = 0x7d ? "SCH3114" :
> + id = 0x7f ? "SCH3116" :
> + "SCH311X id=0x7e <Reserved>"
> + , *addr, rev);
> +
> + superio_exit();
> +
> + return 0;
> +}
> +
> +static int __init sch311x_init(void)
> +{
> + int ret;
> +
> + if ((ret = sch311x_find(&address)))
> + return ret;
> +
> + return i2c_isa_add_driver(&sch311x_driver);
> +}
> +
> +static void __exit sch311x_exit(void)
> +{
> + i2c_isa_del_driver(&sch311x_driver);
> +}
> +
> +MODULE_AUTHOR("Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>");
> +MODULE_DESCRIPTION("SMSC SCH311x driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(sch311x_init);
> +module_exit(sch311x_exit);
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 9+ messages in thread