hwmon: (dme1737) Add support for the SMSC SCH5027

Add support for the SCH5027. The differences to the DME1737 are:
- No support for programmable temp offsets
- In auto mode, PWM outputs stay on min value if temp goes below low threshold
  and can't be programmed to fully turn off
- Different voltage scaling
- No VID input

Signed-off-by: Juerg Haefliger <juergh@gmail.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
Juerg Haefliger 2008-08-06 22:41:03 +02:00 committed by Jean Delvare
parent 55d68d75ab
commit 549edb8332
3 changed files with 169 additions and 81 deletions

View file

@ -10,6 +10,10 @@ Supported chips:
Prefix: 'sch311x' Prefix: 'sch311x'
Addresses scanned: none, address read from Super-I/O config space Addresses scanned: none, address read from Super-I/O config space
Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
* SMSC SCH5027
Prefix: 'sch5027'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
Datasheet: Provided by SMSC upon request and under NDA
Authors: Authors:
Juerg Haefliger <juergh@gmail.com> Juerg Haefliger <juergh@gmail.com>
@ -27,33 +31,31 @@ Module Parameters
following boards: following boards:
- VIA EPIA SN18000 - VIA EPIA SN18000
Note that there is no need to use this parameter if the driver loads without
complaining. The driver will say so if it is necessary.
Description Description
----------- -----------
This driver implements support for the hardware monitoring capabilities of the This driver implements support for the hardware monitoring capabilities of the
SMSC DME1737 and Asus A8000 (which are the same) and SMSC SCH311x Super-I/O SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, and SMSC
chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote SCH311x Super-I/O chips. These chips feature monitoring of 3 temp sensors
diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM 1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
outputs pwm[1-3,5-6] for controlling fan speeds both manually and up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
automatically. automatically.
For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. Fan[3-6] For the DME1737, A8000 and SCH5027, fan[1-2] and pwm[1-2] are always present.
and pwm[3,5-6] are optional features and their availability depends on the Fan[3-6] and pwm[3,5-6] are optional features and their availability depends on
configuration of the chip. The driver will detect which features are present the configuration of the chip. The driver will detect which features are
during initialization and create the sysfs attributes accordingly. present during initialization and create the sysfs attributes accordingly.
For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
pwm[5-6] don't exist. pwm[5-6] don't exist.
The hardware monitoring features of the DME1737 and A8000 are only accessible The hardware monitoring features of the DME1737, A8000, and SCH5027 are only
via SMBus, while the SCH311x only provides access via the ISA bus. The driver accessible via SMBus, while the SCH311x only provides access via the ISA bus.
will therefore register itself as an I2C client driver if it detects a DME1737 The driver will therefore register itself as an I2C client driver if it detects
or A8000 and as a platform driver if it detects a SCH311x chip. a DME1737, A8000, or SCH5027 and as a platform driver if it detects a SCH311x
chip.
Voltage Monitoring Voltage Monitoring
@ -64,6 +66,7 @@ scaling resistors. The values returned by the driver therefore reflect true
millivolts and don't need scaling. The voltage inputs are mapped as follows millivolts and don't need scaling. The voltage inputs are mapped as follows
(the last column indicates the input ranges): (the last column indicates the input ranges):
DME1737, A8000:
in0: +5VTR (+5V standby) 0V - 6.64V in0: +5VTR (+5V standby) 0V - 6.64V
in1: Vccp (processor core) 0V - 3V in1: Vccp (processor core) 0V - 3V
in2: VCC (internal +3.3V) 0V - 4.38V in2: VCC (internal +3.3V) 0V - 4.38V
@ -72,6 +75,24 @@ millivolts and don't need scaling. The voltage inputs are mapped as follows
in5: VTR (+3.3V standby) 0V - 4.38V in5: VTR (+3.3V standby) 0V - 4.38V
in6: Vbat (+3.0V) 0V - 4.38V in6: Vbat (+3.0V) 0V - 4.38V
SCH311x:
in0: +2.5V 0V - 6.64V
in1: Vccp (processor core) 0V - 2V
in2: VCC (internal +3.3V) 0V - 4.38V
in3: +5V 0V - 6.64V
in4: +12V 0V - 16V
in5: VTR (+3.3V standby) 0V - 4.38V
in6: Vbat (+3.0V) 0V - 4.38V
SCH5027:
in0: +5VTR (+5V standby) 0V - 6.64V
in1: Vccp (processor core) 0V - 3V
in2: VCC (internal +3.3V) 0V - 4.38V
in3: V2_IN 0V - 1.5V
in4: V1_IN 0V - 1.5V
in5: VTR (+3.3V standby) 0V - 4.38V
in6: Vbat (+3.0V) 0V - 4.38V
Each voltage input has associated min and max limits which trigger an alarm Each voltage input has associated min and max limits which trigger an alarm
when crossed. when crossed.

View file

@ -575,8 +575,8 @@ config SENSORS_DME1737
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the hardware monitoring If you say yes here you get support for the hardware monitoring
and fan control features of the SMSC DME1737 (and compatibles and fan control features of the SMSC DME1737, SCH311x, SCH5027, and
like the Asus A8000) and SCH311x Super-I/O chips. Asus A8000 Super-I/O chips.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called dme1737. will be called dme1737.

View file

@ -1,11 +1,11 @@
/* /*
* dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and
* Super-I/O chips integrated hardware monitoring features. * SCH5027 Super-I/O chips integrated hardware monitoring features.
* Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com> * Copyright (c) 2007, 2008 Juerg Haefliger <juergh@gmail.com>
* *
* This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
* the chip registers if a DME1737 (or A8000) is found and the ISA bus if a * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
* SCH311x chip is found. Both types of chips have very similar hardware * if a SCH311x chip is found. Both types of chips have very similar hardware
* monitoring capabilities but differ in the way they can be accessed. * monitoring capabilities but differ in the way they can be accessed.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -57,7 +57,10 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END}; static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
/* Insmod parameters */ /* Insmod parameters */
I2C_CLIENT_INSMOD_1(dme1737); I2C_CLIENT_INSMOD_2(dme1737, sch5027);
/* ISA chip types */
enum isa_chips { sch311x = sch5027 + 1 };
/* --------------------------------------------------------------------- /* ---------------------------------------------------------------------
* Registers * Registers
@ -163,6 +166,7 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
#define DME1737_VERSTEP 0x88 #define DME1737_VERSTEP 0x88
#define DME1737_VERSTEP_MASK 0xf8 #define DME1737_VERSTEP_MASK 0xf8
#define SCH311X_DEVICE 0x8c #define SCH311X_DEVICE 0x8c
#define SCH5027_VERSTEP 0x69
/* Length of ISA address segment */ /* Length of ISA address segment */
#define DME1737_EXTENT 2 #define DME1737_EXTENT 2
@ -182,6 +186,7 @@ struct dme1737_data {
unsigned long last_update; /* in jiffies */ unsigned long last_update; /* in jiffies */
unsigned long last_vbat; /* in jiffies */ unsigned long last_vbat; /* in jiffies */
enum chips type; enum chips type;
const int *in_nominal; /* pointer to IN_NOMINAL array */
u8 vid; u8 vid;
u8 pwm_rr_en; u8 pwm_rr_en;
@ -220,23 +225,23 @@ static const int IN_NOMINAL_DME1737[] = {5000, 2250, 3300, 5000, 12000, 3300,
3300}; 3300};
static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300, static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
3300}; 3300};
#define IN_NOMINAL(ix, type) (((type) == dme1737) ? \ static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
IN_NOMINAL_DME1737[(ix)] : \ 3300};
IN_NOMINAL_SCH311x[(ix)]) #define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \
(type) == sch5027 ? IN_NOMINAL_SCH5027 : \
IN_NOMINAL_DME1737)
/* Voltage input /* Voltage input
* Voltage inputs have 16 bits resolution, limit values have 8 bits * Voltage inputs have 16 bits resolution, limit values have 8 bits
* resolution. */ * resolution. */
static inline int IN_FROM_REG(int reg, int ix, int res, int type) static inline int IN_FROM_REG(int reg, int nominal, int res)
{ {
return (reg * IN_NOMINAL(ix, type) + (3 << (res - 3))) / return (reg * nominal + (3 << (res - 3))) / (3 << (res - 2));
(3 << (res - 2));
} }
static inline int IN_TO_REG(int val, int ix, int type) static inline int IN_TO_REG(int val, int nominal)
{ {
return SENSORS_LIMIT((val * 192 + IN_NOMINAL(ix, type) / 2) / return SENSORS_LIMIT((val * 192 + nominal / 2) / nominal, 0, 255);
IN_NOMINAL(ix, type), 0, 255);
} }
/* Temperature input /* Temperature input
@ -565,7 +570,10 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
/* Sample register contents every 1 sec */ /* Sample register contents every 1 sec */
if (time_after(jiffies, data->last_update + HZ) || !data->valid) { if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
data->vid = dme1737_read(client, DME1737_REG_VID) & 0x3f; if (data->type != sch5027) {
data->vid = dme1737_read(client, DME1737_REG_VID) &
0x3f;
}
/* In (voltage) registers */ /* In (voltage) registers */
for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
@ -593,8 +601,10 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
DME1737_REG_TEMP_MIN(ix)); DME1737_REG_TEMP_MIN(ix));
data->temp_max[ix] = dme1737_read(client, data->temp_max[ix] = dme1737_read(client,
DME1737_REG_TEMP_MAX(ix)); DME1737_REG_TEMP_MAX(ix));
data->temp_offset[ix] = dme1737_read(client, if (data->type != sch5027) {
DME1737_REG_TEMP_OFFSET(ix)); data->temp_offset[ix] = dme1737_read(client,
DME1737_REG_TEMP_OFFSET(ix));
}
} }
/* In and temp LSB registers /* In and temp LSB registers
@ -669,9 +679,11 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
data->zone_abs[ix] = dme1737_read(client, data->zone_abs[ix] = dme1737_read(client,
DME1737_REG_ZONE_ABS(ix)); DME1737_REG_ZONE_ABS(ix));
} }
for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) { if (data->type != sch5027) {
data->zone_hyst[ix] = dme1737_read(client, for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
data->zone_hyst[ix] = dme1737_read(client,
DME1737_REG_ZONE_HYST(ix)); DME1737_REG_ZONE_HYST(ix));
}
} }
/* Alarm registers */ /* Alarm registers */
@ -735,13 +747,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
switch (fn) { switch (fn) {
case SYS_IN_INPUT: case SYS_IN_INPUT:
res = IN_FROM_REG(data->in[ix], ix, 16, data->type); res = IN_FROM_REG(data->in[ix], data->in_nominal[ix], 16);
break; break;
case SYS_IN_MIN: case SYS_IN_MIN:
res = IN_FROM_REG(data->in_min[ix], ix, 8, data->type); res = IN_FROM_REG(data->in_min[ix], data->in_nominal[ix], 8);
break; break;
case SYS_IN_MAX: case SYS_IN_MAX:
res = IN_FROM_REG(data->in_max[ix], ix, 8, data->type); res = IN_FROM_REG(data->in_max[ix], data->in_nominal[ix], 8);
break; break;
case SYS_IN_ALARM: case SYS_IN_ALARM:
res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01; res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
@ -768,12 +780,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
switch (fn) { switch (fn) {
case SYS_IN_MIN: case SYS_IN_MIN:
data->in_min[ix] = IN_TO_REG(val, ix, data->type); data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]);
dme1737_write(client, DME1737_REG_IN_MIN(ix), dme1737_write(client, DME1737_REG_IN_MIN(ix),
data->in_min[ix]); data->in_min[ix]);
break; break;
case SYS_IN_MAX: case SYS_IN_MAX:
data->in_max[ix] = IN_TO_REG(val, ix, data->type); data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]);
dme1737_write(client, DME1737_REG_IN_MAX(ix), dme1737_write(client, DME1737_REG_IN_MAX(ix),
data->in_max[ix]); data->in_max[ix]);
break; break;
@ -1570,43 +1582,56 @@ static struct attribute *dme1737_attr[] ={
&sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr, &sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_temp1_offset.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_alarm.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_alarm.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
/* Zones */ /* Zones */
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
NULL
};
static const struct attribute_group dme1737_group = {
.attrs = dme1737_attr,
};
/* The following struct holds misc attributes, which are not available in all
* chips. Their creation depends on the chip type which is determined during
* module load. */
static struct attribute *dme1737_misc_attr[] = {
/* Temperatures */
&sensor_dev_attr_temp1_offset.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
/* Zones */
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
/* Misc */ /* Misc */
&dev_attr_vrm.attr, &dev_attr_vrm.attr,
&dev_attr_cpu0_vid.attr, &dev_attr_cpu0_vid.attr,
NULL NULL
}; };
static const struct attribute_group dme1737_group = { static const struct attribute_group dme1737_misc_group = {
.attrs = dme1737_attr, .attrs = dme1737_misc_attr,
}; };
/* The following structs hold the PWM attributes, some of which are optional. /* The following structs hold the PWM attributes, some of which are optional.
@ -1618,7 +1643,6 @@ static struct attribute *dme1737_pwm1_attr[] = {
&sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
NULL NULL
@ -1629,7 +1653,6 @@ static struct attribute *dme1737_pwm2_attr[] = {
&sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
NULL NULL
@ -1640,7 +1663,6 @@ static struct attribute *dme1737_pwm3_attr[] = {
&sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
NULL NULL
@ -1667,6 +1689,15 @@ static const struct attribute_group dme1737_pwm_group[] = {
{ .attrs = dme1737_pwm6_attr }, { .attrs = dme1737_pwm6_attr },
}; };
/* The following struct holds misc PWM attributes, which are not available in
* all chips. Their creation depends on the chip type which is determined
* during module load. */
static struct attribute *dme1737_pwm_misc_attr[] = {
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
};
/* The following structs hold the fan attributes, some of which are optional. /* The following structs hold the fan attributes, some of which are optional.
* Their creation depends on the chip configuration which is determined during * Their creation depends on the chip configuration which is determined during
* module load. */ * module load. */
@ -1722,31 +1753,23 @@ static const struct attribute_group dme1737_fan_group[] = {
{ .attrs = dme1737_fan6_attr }, { .attrs = dme1737_fan6_attr },
}; };
/* The permissions of all of the following attributes are changed to read- /* The permissions of the following zone attributes are changed to read-
* writeable if the chip is *not* locked. Otherwise they stay read-only. */ * writeable if the chip is *not* locked. Otherwise they stay read-only. */
static struct attribute *dme1737_misc_chmod_attr[] = { static struct attribute *dme1737_zone_chmod_attr[] = {
/* Temperatures */
&sensor_dev_attr_temp1_offset.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
/* Zones */
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
NULL NULL
}; };
static const struct attribute_group dme1737_misc_chmod_group = { static const struct attribute_group dme1737_zone_chmod_group = {
.attrs = dme1737_misc_chmod_attr, .attrs = dme1737_zone_chmod_attr,
}; };
/* The permissions of the following PWM attributes are changed to read- /* The permissions of the following PWM attributes are changed to read-
@ -1757,7 +1780,6 @@ static struct attribute *dme1737_pwm1_chmod_attr[] = {
&sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
NULL NULL
}; };
@ -1766,7 +1788,6 @@ static struct attribute *dme1737_pwm2_chmod_attr[] = {
&sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
NULL NULL
}; };
@ -1775,7 +1796,6 @@ static struct attribute *dme1737_pwm3_chmod_attr[] = {
&sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr, &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
NULL NULL
}; };
@ -1875,9 +1895,17 @@ static void dme1737_remove_files(struct device *dev)
if (data->has_pwm & (1 << ix)) { if (data->has_pwm & (1 << ix)) {
sysfs_remove_group(&dev->kobj, sysfs_remove_group(&dev->kobj,
&dme1737_pwm_group[ix]); &dme1737_pwm_group[ix]);
if (data->type != sch5027 && ix < 3) {
sysfs_remove_file(&dev->kobj,
dme1737_pwm_misc_attr[ix]);
}
} }
} }
if (data->type != sch5027) {
sysfs_remove_group(&dev->kobj, &dme1737_misc_group);
}
sysfs_remove_group(&dev->kobj, &dme1737_group); sysfs_remove_group(&dev->kobj, &dme1737_group);
if (!data->client.driver) { if (!data->client.driver) {
@ -1901,6 +1929,13 @@ static int dme1737_create_files(struct device *dev)
goto exit_remove; goto exit_remove;
} }
/* Create misc sysfs attributes */
if ((data->type != sch5027) &&
(err = sysfs_create_group(&dev->kobj,
&dme1737_misc_group))) {
goto exit_remove;
}
/* Create fan sysfs attributes */ /* Create fan sysfs attributes */
for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
if (data->has_fan & (1 << ix)) { if (data->has_fan & (1 << ix)) {
@ -1918,6 +1953,11 @@ static int dme1737_create_files(struct device *dev)
&dme1737_pwm_group[ix]))) { &dme1737_pwm_group[ix]))) {
goto exit_remove; goto exit_remove;
} }
if (data->type != sch5027 && ix < 3 &&
(err = sysfs_create_file(&dev->kobj,
dme1737_pwm_misc_attr[ix]))) {
goto exit_remove;
}
} }
} }
@ -1927,16 +1967,27 @@ static int dme1737_create_files(struct device *dev)
dev_info(dev, "Device is locked. Some attributes " dev_info(dev, "Device is locked. Some attributes "
"will be read-only.\n"); "will be read-only.\n");
} else { } else {
/* Change permissions of standard sysfs attributes */ /* Change permissions of zone sysfs attributes */
dme1737_chmod_group(dev, &dme1737_misc_chmod_group, dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
S_IRUGO | S_IWUSR); S_IRUGO | S_IWUSR);
/* Change permissions of misc sysfs attributes */
if (data->type != sch5027) {
dme1737_chmod_group(dev, &dme1737_misc_group,
S_IRUGO | S_IWUSR);
}
/* Change permissions of PWM sysfs attributes */ /* Change permissions of PWM sysfs attributes */
for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) { for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
if (data->has_pwm & (1 << ix)) { if (data->has_pwm & (1 << ix)) {
dme1737_chmod_group(dev, dme1737_chmod_group(dev,
&dme1737_pwm_chmod_group[ix], &dme1737_pwm_chmod_group[ix],
S_IRUGO | S_IWUSR); S_IRUGO | S_IWUSR);
if (data->type != sch5027 && ix < 3) {
dme1737_chmod_file(dev,
dme1737_pwm_misc_attr[ix],
S_IRUGO | S_IWUSR);
}
} }
} }
@ -1966,6 +2017,9 @@ static int dme1737_init_device(struct device *dev)
int ix; int ix;
u8 reg; u8 reg;
/* Point to the right nominal voltages array */
data->in_nominal = IN_NOMINAL(data->type);
data->config = dme1737_read(client, DME1737_REG_CONFIG); data->config = dme1737_read(client, DME1737_REG_CONFIG);
/* Inform if part is not monitoring/started */ /* Inform if part is not monitoring/started */
if (!(data->config & 0x01)) { if (!(data->config & 0x01)) {
@ -2076,7 +2130,9 @@ static int dme1737_init_device(struct device *dev)
data->pwm_acz[2] = 4; /* pwm3 -> zone3 */ data->pwm_acz[2] = 4; /* pwm3 -> zone3 */
/* Set VRM */ /* Set VRM */
data->vrm = vid_which_vrm(); if (data->type != sch5027) {
data->vrm = vid_which_vrm();
}
return 0; return 0;
} }
@ -2095,9 +2151,10 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
dme1737_sio_enter(sio_cip); dme1737_sio_enter(sio_cip);
/* Check device ID /* Check device ID
* The DME1737 can return either 0x78 or 0x77 as its device ID. */ * The DME1737 can return either 0x78 or 0x77 as its device ID.
* The SCH5027 returns 0x89 as its device ID. */
reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20); reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
if (!(reg == 0x77 || reg == 0x78)) { if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) {
err = -ENODEV; err = -ENODEV;
goto exit; goto exit;
} }
@ -2166,15 +2223,24 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
company = dme1737_read(client, DME1737_REG_COMPANY); company = dme1737_read(client, DME1737_REG_COMPANY);
verstep = dme1737_read(client, DME1737_REG_VERSTEP); verstep = dme1737_read(client, DME1737_REG_VERSTEP);
if (!((company == DME1737_COMPANY_SMSC) && if (company == DME1737_COMPANY_SMSC &&
((verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP))) { (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
kind = dme1737;
} else if (company == DME1737_COMPANY_SMSC &&
verstep == SCH5027_VERSTEP) {
kind = sch5027;
} else {
err = -ENODEV; err = -ENODEV;
goto exit_kfree; goto exit_kfree;
} }
} }
kind = dme1737; if (kind == sch5027) {
name = "dme1737"; name = "sch5027";
} else {
kind = dme1737;
name = "dme1737";
}
data->type = kind; data->type = kind;
/* Fill in the remaining client fields and put it into the global /* Fill in the remaining client fields and put it into the global
@ -2187,8 +2253,9 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
goto exit_kfree; goto exit_kfree;
} }
dev_info(dev, "Found a DME1737 chip at 0x%02x (rev 0x%02x).\n", dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
client->addr, verstep); kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
verstep);
/* Initialize the DME1737 chip */ /* Initialize the DME1737 chip */
if ((err = dme1737_init_device(dev))) { if ((err = dme1737_init_device(dev))) {
@ -2371,7 +2438,7 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
goto exit_kfree; goto exit_kfree;
} }
} }
data->type = -1; data->type = sch311x;
/* Fill in the remaining client fields and initialize the mutex */ /* Fill in the remaining client fields and initialize the mutex */
strlcpy(client->name, "sch311x", I2C_NAME_SIZE); strlcpy(client->name, "sch311x", I2C_NAME_SIZE);