hwmon: (dme1737) Add sch311x support
This patch adds support for the SMSC SCH3112, SCH3114, and SCH3116 Super-I/O chips. These chips feature identical hardware monitoring capabilites with the expection that some of the fan inputs and pmw outputs don't exist. The hardware monitoring features of the SCH311x chips can only be accessed via the ISA bus. The driver therefore registers as a platform driver, if such a chip is detected. Signed-off-by: Juerg Haefliger <juergh at gmail.com> Acked-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
This commit is contained in:
parent
62ee3e10d3
commit
e95c237d78
@ -6,6 +6,10 @@ Supported chips:
|
|||||||
Prefix: 'dme1737'
|
Prefix: 'dme1737'
|
||||||
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
|
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
|
||||||
Datasheet: Provided by SMSC upon request and under NDA
|
Datasheet: Provided by SMSC upon request and under NDA
|
||||||
|
* SMSC SCH3112, SCH3114, SCH3116
|
||||||
|
Prefix: 'sch311x'
|
||||||
|
Addresses scanned: none, address read from Super-I/O config space
|
||||||
|
Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
|
||||||
|
|
||||||
Authors:
|
Authors:
|
||||||
Juerg Haefliger <juergh@gmail.com>
|
Juerg Haefliger <juergh@gmail.com>
|
||||||
@ -27,16 +31,25 @@ 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) Super-I/O chips. This chip
|
SMSC DME1737 and Asus A8000 (which are the same) and SMSC SCH311x Super-I/O
|
||||||
features monitoring of 3 temp sensors temp[1-3] (2 remote diodes and 1
|
chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote
|
||||||
internal), 7 voltages in[0-6] (6 external and 1 internal) and 6 fan speeds
|
diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up
|
||||||
fan[1-6]. Additionally, the chip implements 5 PWM outputs pwm[1-3,5-6] for
|
to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM
|
||||||
controlling fan speeds both manually and automatically.
|
outputs pwm[1-3,5-6] for controlling fan speeds both manually and
|
||||||
|
automatically.
|
||||||
|
|
||||||
Fan[3-6] and pwm[3,5-6] are optional features and their availability is
|
For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. Fan[3-6]
|
||||||
dependent on the configuration of the chip. The driver will detect which
|
and pwm[3,5-6] are optional features and their availability depends on the
|
||||||
features are present during initialization and create the sysfs attributes
|
configuration of the chip. The driver will detect which features are present
|
||||||
accordingly.
|
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
|
||||||
|
pwm[5-6] don't exist.
|
||||||
|
|
||||||
|
The hardware monitoring features of the DME1737 and A8000 are only accessible
|
||||||
|
via SMBus, while the SCH311x only provides access via the ISA bus. The driver
|
||||||
|
will therefore register itself as an I2C client driver if it detects a DME1737
|
||||||
|
or A8000 and as a platform driver if it detects a SCH311x chip.
|
||||||
|
|
||||||
|
|
||||||
Voltage Monitoring
|
Voltage Monitoring
|
||||||
|
@ -509,13 +509,13 @@ config SENSORS_SIS5595
|
|||||||
will be called sis5595.
|
will be called sis5595.
|
||||||
|
|
||||||
config SENSORS_DME1737
|
config SENSORS_DME1737
|
||||||
tristate "SMSC DME1737 and compatibles"
|
tristate "SMSC DME1737, SCH311x and compatibles"
|
||||||
depends on I2C && EXPERIMENTAL
|
depends on I2C && EXPERIMENTAL
|
||||||
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 (and compatibles
|
||||||
like the Asus A8000) Super-I/O chip.
|
like the Asus A8000) and SCH311x 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.
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* dme1737.c - driver for the SMSC DME1737 and Asus A8000 Super-I/O chips
|
* dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x
|
||||||
* integrated hardware monitoring features.
|
* Super-I/O chips integrated hardware monitoring features.
|
||||||
* Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com>
|
* Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com>
|
||||||
*
|
*
|
||||||
* This driver is based on the LM85 driver. The hardware monitoring
|
* This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
|
||||||
* capabilities of the DME1737 are very similar to the LM85 with some
|
* the chip registers if a DME1737 (or A8000) is found and the ISA bus if a
|
||||||
* additional features. Even though the DME1737 is a Super-I/O chip, the
|
* SCH311x chip is found. Both types of chips have very similar hardware
|
||||||
* hardware monitoring registers are only accessible via SMBus.
|
* 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
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/hwmon-vid.h>
|
#include <linux/hwmon-vid.h>
|
||||||
@ -35,6 +36,9 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
|
/* ISA device, if found */
|
||||||
|
static struct platform_device *pdev;
|
||||||
|
|
||||||
/* Module load parameters */
|
/* Module load parameters */
|
||||||
static int force_start;
|
static int force_start;
|
||||||
module_param(force_start, bool, 0);
|
module_param(force_start, bool, 0);
|
||||||
@ -133,6 +137,7 @@ static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6};
|
|||||||
static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
|
static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
|
||||||
|
|
||||||
/* Miscellaneous registers */
|
/* Miscellaneous registers */
|
||||||
|
#define DME1737_REG_DEVICE 0x3d
|
||||||
#define DME1737_REG_COMPANY 0x3e
|
#define DME1737_REG_COMPANY 0x3e
|
||||||
#define DME1737_REG_VERSTEP 0x3f
|
#define DME1737_REG_VERSTEP 0x3f
|
||||||
#define DME1737_REG_CONFIG 0x40
|
#define DME1737_REG_CONFIG 0x40
|
||||||
@ -148,11 +153,17 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
|
|||||||
#define DME1737_COMPANY_SMSC 0x5c
|
#define DME1737_COMPANY_SMSC 0x5c
|
||||||
#define DME1737_VERSTEP 0x88
|
#define DME1737_VERSTEP 0x88
|
||||||
#define DME1737_VERSTEP_MASK 0xf8
|
#define DME1737_VERSTEP_MASK 0xf8
|
||||||
|
#define SCH311X_DEVICE 0x8c
|
||||||
|
|
||||||
|
/* Length of ISA address segment */
|
||||||
|
#define DME1737_EXTENT 2
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Data structures and manipulation thereof
|
* Data structures and manipulation thereof
|
||||||
* --------------------------------------------------------------------- */
|
* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
|
||||||
|
the driver field to differentiate between I2C and ISA chips. */
|
||||||
struct dme1737_data {
|
struct dme1737_data {
|
||||||
struct i2c_client client;
|
struct i2c_client client;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
@ -465,27 +476,48 @@ static inline int PWM_OFF_TO_REG(int val, int ix, int reg)
|
|||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Device I/O access
|
* Device I/O access
|
||||||
|
*
|
||||||
|
* ISA access is performed through an index/data register pair and needs to
|
||||||
|
* be protected by a mutex during runtime (not required for initialization).
|
||||||
|
* We use data->update_lock for this and need to ensure that we acquire it
|
||||||
|
* before calling dme1737_read or dme1737_write.
|
||||||
* --------------------------------------------------------------------- */
|
* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
static u8 dme1737_read(struct i2c_client *client, u8 reg)
|
static u8 dme1737_read(struct i2c_client *client, u8 reg)
|
||||||
{
|
{
|
||||||
s32 val = i2c_smbus_read_byte_data(client, reg);
|
s32 val;
|
||||||
|
|
||||||
|
if (client->driver) { /* I2C device */
|
||||||
|
val = i2c_smbus_read_byte_data(client, reg);
|
||||||
|
|
||||||
if (val < 0) {
|
if (val < 0) {
|
||||||
dev_warn(&client->dev, "Read from register 0x%02x failed! "
|
dev_warn(&client->dev, "Read from register "
|
||||||
"Please report to the driver maintainer.\n", reg);
|
"0x%02x failed! Please report to the driver "
|
||||||
|
"maintainer.\n", reg);
|
||||||
|
}
|
||||||
|
} else { /* ISA device */
|
||||||
|
outb(reg, client->addr);
|
||||||
|
val = inb(client->addr + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 value)
|
static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val)
|
||||||
{
|
{
|
||||||
s32 res = i2c_smbus_write_byte_data(client, reg, value);
|
s32 res = 0;
|
||||||
|
|
||||||
|
if (client->driver) { /* I2C device */
|
||||||
|
res = i2c_smbus_write_byte_data(client, reg, val);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
dev_warn(&client->dev, "Write to register 0x%02x failed! "
|
dev_warn(&client->dev, "Write to register "
|
||||||
"Please report to the driver maintainer.\n", reg);
|
"0x%02x failed! Please report to the driver "
|
||||||
|
"maintainer.\n", reg);
|
||||||
|
}
|
||||||
|
} else { /* ISA device */
|
||||||
|
outb(reg, client->addr);
|
||||||
|
outb(val, client->addr + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -630,6 +662,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
|
|||||||
DME1737_REG_ALARM3) << 16;
|
DME1737_REG_ALARM3) << 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The ISA chips require explicit clearing of alarm bits.
|
||||||
|
* Don't worry, an alarm will come back if the condition
|
||||||
|
* that causes it still exists */
|
||||||
|
if (!client->driver) {
|
||||||
|
if (data->alarms & 0xff0000) {
|
||||||
|
dme1737_write(client, DME1737_REG_ALARM3,
|
||||||
|
0xff);
|
||||||
|
}
|
||||||
|
if (data->alarms & 0xff00) {
|
||||||
|
dme1737_write(client, DME1737_REG_ALARM2,
|
||||||
|
0xff);
|
||||||
|
}
|
||||||
|
if (data->alarms & 0xff) {
|
||||||
|
dme1737_write(client, DME1737_REG_ALARM1,
|
||||||
|
0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data->last_update = jiffies;
|
data->last_update = jiffies;
|
||||||
data->valid = 1;
|
data->valid = 1;
|
||||||
}
|
}
|
||||||
@ -995,7 +1045,7 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
|
|||||||
/* Only valid for fan[1-4] */
|
/* Only valid for fan[1-4] */
|
||||||
if (!(val == 1 || val == 2 || val == 4)) {
|
if (!(val == 1 || val == 2 || val == 4)) {
|
||||||
count = -EINVAL;
|
count = -EINVAL;
|
||||||
dev_warn(&client->dev, "Fan type value %ld not "
|
dev_warn(dev, "Fan type value %ld not "
|
||||||
"supported. Choose one of 1, 2, or 4.\n",
|
"supported. Choose one of 1, 2, or 4.\n",
|
||||||
val);
|
val);
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -1122,7 +1172,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
|||||||
/* Only valid for pwm[1-3] */
|
/* Only valid for pwm[1-3] */
|
||||||
if (val < 0 || val > 2) {
|
if (val < 0 || val > 2) {
|
||||||
count = -EINVAL;
|
count = -EINVAL;
|
||||||
dev_warn(&client->dev, "PWM enable %ld not "
|
dev_warn(dev, "PWM enable %ld not "
|
||||||
"supported. Choose one of 0, 1, or 2.\n",
|
"supported. Choose one of 0, 1, or 2.\n",
|
||||||
val);
|
val);
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -1223,7 +1273,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
|||||||
if (!(val == 1 || val == 2 || val == 4 ||
|
if (!(val == 1 || val == 2 || val == 4 ||
|
||||||
val == 6 || val == 7)) {
|
val == 6 || val == 7)) {
|
||||||
count = -EINVAL;
|
count = -EINVAL;
|
||||||
dev_warn(&client->dev, "PWM auto channels zone %ld "
|
dev_warn(dev, "PWM auto channels zone %ld "
|
||||||
"not supported. Choose one of 1, 2, 4, 6, "
|
"not supported. Choose one of 1, 2, 4, 6, "
|
||||||
"or 7.\n", val);
|
"or 7.\n", val);
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -1311,6 +1361,14 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
|
|||||||
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
|
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct dme1737_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return sprintf(buf, "%s\n", data->client.name);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Sysfs device attribute defines and structs
|
* Sysfs device attribute defines and structs
|
||||||
* --------------------------------------------------------------------- */
|
* --------------------------------------------------------------------- */
|
||||||
@ -1446,6 +1504,7 @@ SENSOR_DEVICE_ATTR_PWM_5TO6(6);
|
|||||||
|
|
||||||
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
|
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
|
||||||
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
|
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
|
||||||
|
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* for ISA devices */
|
||||||
|
|
||||||
#define SENSOR_DEV_ATTR_IN(ix) \
|
#define SENSOR_DEV_ATTR_IN(ix) \
|
||||||
&sensor_dev_attr_in##ix##_input.dev_attr.attr, \
|
&sensor_dev_attr_in##ix##_input.dev_attr.attr, \
|
||||||
@ -1698,7 +1757,7 @@ static inline void dme1737_sio_outb(int sio_cip, int reg, int val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Device detection, registration and initialization
|
* Device initialization
|
||||||
* --------------------------------------------------------------------- */
|
* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
static int dme1737_i2c_get_features(int, struct dme1737_data*);
|
static int dme1737_i2c_get_features(int, struct dme1737_data*);
|
||||||
@ -1743,6 +1802,10 @@ static void dme1737_remove_files(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sysfs_remove_group(&dev->kobj, &dme1737_group);
|
sysfs_remove_group(&dev->kobj, &dme1737_group);
|
||||||
|
|
||||||
|
if (!data->client.driver) {
|
||||||
|
sysfs_remove_file(&dev->kobj, &dev_attr_name.attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dme1737_create_files(struct device *dev)
|
static int dme1737_create_files(struct device *dev)
|
||||||
@ -1750,9 +1813,15 @@ static int dme1737_create_files(struct device *dev)
|
|||||||
struct dme1737_data *data = dev_get_drvdata(dev);
|
struct dme1737_data *data = dev_get_drvdata(dev);
|
||||||
int err, ix;
|
int err, ix;
|
||||||
|
|
||||||
|
/* Create a name attribute for ISA devices */
|
||||||
|
if (!data->client.driver &&
|
||||||
|
(err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create standard sysfs attributes */
|
/* Create standard sysfs attributes */
|
||||||
if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) {
|
if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) {
|
||||||
goto exit;
|
goto exit_remove;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create fan sysfs attributes */
|
/* Create fan sysfs attributes */
|
||||||
@ -1840,6 +1909,8 @@ static int dme1737_init_device(struct device *dev)
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Determine which optional fan and pwm features are enabled/present */
|
||||||
|
if (client->driver) { /* I2C chip */
|
||||||
data->config2 = dme1737_read(client, DME1737_REG_CONFIG2);
|
data->config2 = dme1737_read(client, DME1737_REG_CONFIG2);
|
||||||
/* Check if optional fan3 input is enabled */
|
/* Check if optional fan3 input is enabled */
|
||||||
if (data->config2 & 0x04) {
|
if (data->config2 & 0x04) {
|
||||||
@ -1847,21 +1918,28 @@ static int dme1737_init_device(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fan4 and pwm3 are only available if the client's I2C address
|
/* Fan4 and pwm3 are only available if the client's I2C address
|
||||||
* is the default 0x2e. Otherwise the I/Os associated with these
|
* is the default 0x2e. Otherwise the I/Os associated with
|
||||||
* functions are used for addr enable/select. */
|
* these functions are used for addr enable/select. */
|
||||||
if (client->addr == 0x2e) {
|
if (data->client.addr == 0x2e) {
|
||||||
data->has_fan |= (1 << 3);
|
data->has_fan |= (1 << 3);
|
||||||
data->has_pwm |= (1 << 2);
|
data->has_pwm |= (1 << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine if the optional fan[5-6] and/or pwm[5-6] are enabled.
|
/* Determine which of the optional fan[5-6] and pwm[5-6]
|
||||||
* For this, we need to query the runtime registers through the
|
* features are enabled. For this, we need to query the runtime
|
||||||
* Super-IO LPC interface. Try both config ports 0x2e and 0x4e. */
|
* registers through the Super-IO LPC interface. Try both
|
||||||
|
* config ports 0x2e and 0x4e. */
|
||||||
if (dme1737_i2c_get_features(0x2e, data) &&
|
if (dme1737_i2c_get_features(0x2e, data) &&
|
||||||
dme1737_i2c_get_features(0x4e, data)) {
|
dme1737_i2c_get_features(0x4e, data)) {
|
||||||
dev_warn(dev, "Failed to query Super-IO for optional "
|
dev_warn(dev, "Failed to query Super-IO for optional "
|
||||||
"features.\n");
|
"features.\n");
|
||||||
}
|
}
|
||||||
|
} else { /* ISA chip */
|
||||||
|
/* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6]
|
||||||
|
* don't exist in the ISA chip. */
|
||||||
|
data->has_fan |= (1 << 2);
|
||||||
|
data->has_pwm |= (1 << 2);
|
||||||
|
}
|
||||||
|
|
||||||
/* Fan1, fan2, pwm1, and pwm2 are always present */
|
/* Fan1, fan2, pwm1, and pwm2 are always present */
|
||||||
data->has_fan |= 0x03;
|
data->has_fan |= 0x03;
|
||||||
@ -1879,13 +1957,19 @@ static int dme1737_init_device(struct device *dev)
|
|||||||
|
|
||||||
reg = dme1737_read(client, DME1737_REG_TACH_PWM);
|
reg = dme1737_read(client, DME1737_REG_TACH_PWM);
|
||||||
/* Inform if fan-to-pwm mapping differs from the default */
|
/* Inform if fan-to-pwm mapping differs from the default */
|
||||||
if (reg != 0xa4) {
|
if (client->driver && reg != 0xa4) { /* I2C chip */
|
||||||
dev_warn(dev, "Non-standard fan to pwm mapping: "
|
dev_warn(dev, "Non-standard fan to pwm mapping: "
|
||||||
"fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, "
|
"fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, "
|
||||||
"fan4->pwm%d. Please report to the driver "
|
"fan4->pwm%d. Please report to the driver "
|
||||||
"maintainer.\n",
|
"maintainer.\n",
|
||||||
(reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
|
(reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
|
||||||
((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
|
((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
|
||||||
|
} else if (!client->driver && reg != 0x24) { /* ISA chip */
|
||||||
|
dev_warn(dev, "Non-standard fan to pwm mapping: "
|
||||||
|
"fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. "
|
||||||
|
"Please report to the driver maintainer.\n",
|
||||||
|
(reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
|
||||||
|
((reg >> 4) & 0x03) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch pwm[1-3] to manual mode if they are currently disabled and
|
/* Switch pwm[1-3] to manual mode if they are currently disabled and
|
||||||
@ -2093,17 +2177,226 @@ static struct i2c_driver dme1737_i2c_driver = {
|
|||||||
.detach_client = dme1737_i2c_detach_client,
|
.detach_client = dme1737_i2c_detach_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
* ISA device detection and registration
|
||||||
|
* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
|
||||||
|
{
|
||||||
|
int err = 0, reg;
|
||||||
|
unsigned short base_addr;
|
||||||
|
|
||||||
|
dme1737_sio_enter(sio_cip);
|
||||||
|
|
||||||
|
/* Check device ID
|
||||||
|
* We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and
|
||||||
|
* SCH3116 (0x7f). */
|
||||||
|
reg = dme1737_sio_inb(sio_cip, 0x20);
|
||||||
|
if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select logical device A (runtime registers) */
|
||||||
|
dme1737_sio_outb(sio_cip, 0x07, 0x0a);
|
||||||
|
|
||||||
|
/* Get the base address of the runtime registers */
|
||||||
|
if (!(base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
|
||||||
|
dme1737_sio_inb(sio_cip, 0x61))) {
|
||||||
|
printk(KERN_ERR "dme1737: Base address not set.\n");
|
||||||
|
err = -ENODEV;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Access to the hwmon registers is through an index/data register
|
||||||
|
* pair located at offset 0x70/0x71. */
|
||||||
|
*addr = base_addr + 0x70;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
dme1737_sio_exit(sio_cip);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init dme1737_isa_device_add(unsigned short addr)
|
||||||
|
{
|
||||||
|
struct resource res = {
|
||||||
|
.start = addr,
|
||||||
|
.end = addr + DME1737_EXTENT - 1,
|
||||||
|
.name = "dme1737",
|
||||||
|
.flags = IORESOURCE_IO,
|
||||||
|
};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!(pdev = platform_device_alloc("dme1737", addr))) {
|
||||||
|
printk(KERN_ERR "dme1737: Failed to allocate device.\n");
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = platform_device_add_resources(pdev, &res, 1))) {
|
||||||
|
printk(KERN_ERR "dme1737: Failed to add device resource "
|
||||||
|
"(err = %d).\n", err);
|
||||||
|
goto exit_device_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = platform_device_add(pdev))) {
|
||||||
|
printk(KERN_ERR "dme1737: Failed to add device (err = %d).\n",
|
||||||
|
err);
|
||||||
|
goto exit_device_put;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_device_put:
|
||||||
|
platform_device_put(pdev);
|
||||||
|
pdev = NULL;
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit dme1737_isa_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
u8 company, device;
|
||||||
|
struct resource *res;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct dme1737_data *data;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||||
|
if (!request_region(res->start, DME1737_EXTENT, "dme1737")) {
|
||||||
|
dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
|
||||||
|
(unsigned short)res->start,
|
||||||
|
(unsigned short)res->start + DME1737_EXTENT - 1);
|
||||||
|
err = -EBUSY;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit_release_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
client = &data->client;
|
||||||
|
i2c_set_clientdata(client, data);
|
||||||
|
client->addr = res->start;
|
||||||
|
platform_set_drvdata(pdev, data);
|
||||||
|
|
||||||
|
company = dme1737_read(client, DME1737_REG_COMPANY);
|
||||||
|
device = dme1737_read(client, DME1737_REG_DEVICE);
|
||||||
|
|
||||||
|
if (!((company == DME1737_COMPANY_SMSC) &&
|
||||||
|
(device == SCH311X_DEVICE))) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto exit_kfree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the remaining client fields and initialize the mutex */
|
||||||
|
strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
|
||||||
|
mutex_init(&data->update_lock);
|
||||||
|
|
||||||
|
dev_info(dev, "Found a SCH311x chip at 0x%04x\n", client->addr);
|
||||||
|
|
||||||
|
/* Initialize the chip */
|
||||||
|
if ((err = dme1737_init_device(dev))) {
|
||||||
|
dev_err(dev, "Failed to initialize device.\n");
|
||||||
|
goto exit_kfree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create sysfs files */
|
||||||
|
if ((err = dme1737_create_files(dev))) {
|
||||||
|
dev_err(dev, "Failed to create sysfs files.\n");
|
||||||
|
goto exit_kfree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register device */
|
||||||
|
data->hwmon_dev = hwmon_device_register(dev);
|
||||||
|
if (IS_ERR(data->hwmon_dev)) {
|
||||||
|
dev_err(dev, "Failed to register device.\n");
|
||||||
|
err = PTR_ERR(data->hwmon_dev);
|
||||||
|
goto exit_remove_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_remove_files:
|
||||||
|
dme1737_remove_files(dev);
|
||||||
|
exit_kfree:
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(data);
|
||||||
|
exit_release_region:
|
||||||
|
release_region(res->start, DME1737_EXTENT);
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit dme1737_isa_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct dme1737_data *data = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
hwmon_device_unregister(data->hwmon_dev);
|
||||||
|
dme1737_remove_files(&pdev->dev);
|
||||||
|
release_region(data->client.addr, DME1737_EXTENT);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver dme1737_isa_driver = {
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "dme1737",
|
||||||
|
},
|
||||||
|
.probe = dme1737_isa_probe,
|
||||||
|
.remove = __devexit_p(dme1737_isa_remove),
|
||||||
|
};
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Module initialization and cleanup
|
* Module initialization and cleanup
|
||||||
* --------------------------------------------------------------------- */
|
* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
static int __init dme1737_init(void)
|
static int __init dme1737_init(void)
|
||||||
{
|
{
|
||||||
return i2c_add_driver(&dme1737_i2c_driver);
|
int err;
|
||||||
|
unsigned short addr;
|
||||||
|
|
||||||
|
if ((err = i2c_add_driver(&dme1737_i2c_driver))) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dme1737_isa_detect(0x2e, &addr) &&
|
||||||
|
dme1737_isa_detect(0x4e, &addr)) {
|
||||||
|
/* Return 0 if we didn't find an ISA device */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = platform_driver_register(&dme1737_isa_driver))) {
|
||||||
|
goto exit_del_i2c_driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets global pdev as a side effect */
|
||||||
|
if ((err = dme1737_isa_device_add(addr))) {
|
||||||
|
goto exit_del_isa_driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_del_isa_driver:
|
||||||
|
platform_driver_unregister(&dme1737_isa_driver);
|
||||||
|
exit_del_i2c_driver:
|
||||||
|
i2c_del_driver(&dme1737_i2c_driver);
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit dme1737_exit(void)
|
static void __exit dme1737_exit(void)
|
||||||
{
|
{
|
||||||
|
if (pdev) {
|
||||||
|
platform_device_unregister(pdev);
|
||||||
|
platform_driver_unregister(&dme1737_isa_driver);
|
||||||
|
}
|
||||||
|
|
||||||
i2c_del_driver(&dme1737_i2c_driver);
|
i2c_del_driver(&dme1737_i2c_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user