Commit c9ebf6ef authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.6

into kroah.com:/home/greg/linux/BK/i2c-2.6
parents 7b51a623 a1eefc49
......@@ -922,12 +922,10 @@ M: drivers@neukum.org
S: Maintained
I2C AND SENSORS DRIVERS
P: Frodo Looijaard
M: frodol@dds.nl
P: Philip Edelbrock
M: phil@netroedge.com
P: Greg Kroah-Hartman
M: greg@kroah.com
P: Philip Edelbrock
M: phil@netroedge.com
L: sensors@stimpy.netroedge.com
W: http://www.lm-sensors.nu/
S: Maintained
......
......@@ -69,6 +69,18 @@ config I2C_ELV
This support is also available as a module. If so, the module
will be called i2c-elv.
config I2C_HYDRA
tristate "CHRP Apple Hydra Mac I/O I2C interface"
depends on I2C && PCI && PPC_CHRP && EXPERIMENTAL
select I2C_ALGOBIT
help
This supports the use of the I2C interface in the Apple Hydra Mac
I/O chip on some CHRP machines (e.g. the LongTrail). Say Y if you
have such a machine.
This support is also available as a module. If so, the module
will be called i2c-hydra.
config I2C_I801
tristate "Intel 801"
depends on I2C && PCI && EXPERIMENTAL
......
......@@ -8,6 +8,7 @@ obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_ELV) += i2c-elv.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_I810) += i2c-i810.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
......
/*
i2c-hydra.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
i2c Support for the Apple `Hydra' Mac I/O
Copyright (c) 1999-2004 Geert Uytterhoeven <geert@linux-m68k.org>
Based on i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Kysti Mlkki <kmalkki@cc.hut.fi>
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/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/hydra.h>
#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */
#define HYDRA_CPD_PD1 0x00000002
#define HYDRA_CPD_PD2 0x00000004
#define HYDRA_CPD_PD3 0x00000008
#define HYDRA_SCLK HYDRA_CPD_PD0
#define HYDRA_SDAT HYDRA_CPD_PD1
#define HYDRA_SCLK_OE 0x00000010
#define HYDRA_SDAT_OE 0x00000020
static inline void pdregw(void *data, u32 val)
{
struct Hydra *hydra = (struct Hydra *)data;
writel(val, &hydra->CachePD);
}
static inline u32 pdregr(void *data)
{
struct Hydra *hydra = (struct Hydra *)data;
return readl(&hydra->CachePD);
}
static void hydra_bit_setscl(void *data, int state)
{
u32 val = pdregr(data);
if (state)
val &= ~HYDRA_SCLK_OE;
else {
val &= ~HYDRA_SCLK;
val |= HYDRA_SCLK_OE;
}
pdregw(data, val);
}
static void hydra_bit_setsda(void *data, int state)
{
u32 val = pdregr(data);
if (state)
val &= ~HYDRA_SDAT_OE;
else {
val &= ~HYDRA_SDAT;
val |= HYDRA_SDAT_OE;
}
pdregw(data, val);
}
static int hydra_bit_getscl(void *data)
{
return (pdregr(data) & HYDRA_SCLK) != 0;
}
static int hydra_bit_getsda(void *data)
{
return (pdregr(data) & HYDRA_SDAT) != 0;
}
/* ------------------------------------------------------------------------ */
static struct i2c_algo_bit_data hydra_bit_data = {
.setsda = hydra_bit_setsda,
.setscl = hydra_bit_setscl,
.getsda = hydra_bit_getsda,
.getscl = hydra_bit_getscl,
.udelay = 5,
.mdelay = 5,
.timeout = HZ
};
static struct i2c_adapter hydra_adap = {
.owner = THIS_MODULE,
.name = "Hydra i2c",
.id = I2C_HW_B_HYDRA,
.algo_data = &hydra_bit_data,
};
static struct pci_device_id hydra_ids[] = {
{
.vendor = PCI_VENDOR_ID_APPLE,
.device = PCI_DEVICE_ID_APPLE_HYDRA,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit hydra_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned long base = pci_resource_start(dev, 0);
int res;
if (!request_mem_region(base+offsetof(struct Hydra, CachePD), 4,
hydra_adap.name))
return -EBUSY;
hydra_bit_data.data = ioremap(base, pci_resource_len(dev, 0));
if (hydra_bit_data.data == NULL) {
release_mem_region(base+offsetof(struct Hydra, CachePD), 4);
return -ENODEV;
}
pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */
hydra_adap.dev.parent = &dev->dev;
res = i2c_bit_add_bus(&hydra_adap);
if (res < 0) {
iounmap(hydra_bit_data.data);
release_mem_region(base+offsetof(struct Hydra, CachePD), 4);
return res;
}
return 0;
}
static void __devexit hydra_remove(struct pci_dev *dev)
{
pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */
i2c_bit_del_bus(&hydra_adap);
iounmap(hydra_bit_data.data);
release_mem_region(pci_resource_start(dev, 0)+
offsetof(struct Hydra, CachePD), 4);
}
static struct pci_driver hydra_driver = {
.name = "hydra smbus",
.id_table = hydra_ids,
.probe = hydra_probe,
.remove = __devexit_p(hydra_remove),
};
static int __init i2c_hydra_init(void)
{
return pci_module_init(&hydra_driver);
}
static void __exit i2c_hydra_exit(void)
{
pci_unregister_driver(&hydra_driver);
}
MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
MODULE_LICENSE("GPL");
module_init(i2c_hydra_init);
module_exit(i2c_hydra_exit);
......@@ -45,6 +45,28 @@ config SENSORS_EEPROM
This driver can also be built as a module. If so, the module
will be called eeprom.
config SENSORS_FSCHER
tristate "FSC Hermes"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Fujitsu Siemens
Computers Hermes sensor chips.
This driver can also be built as a module. If so, the module
will be called fscher.
config SENSORS_GL518SM
tristate "Genesys Logic GL518SM"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Genesys Logic GL518SM
sensor chips.
This driver can also be built as a module. If so, the module
will be called gl518sm.
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
depends on I2C && EXPERIMENTAL
......
......@@ -8,6 +8,8 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o
......
/*
* fscher.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
*
* 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.
*/
/*
* fujitsu siemens hermes chip,
* module based on fscpos.c
* Copyright (C) 2000 Hermann Jung <hej@odn.de>
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
* and Philip Edelbrock <phil@netroedge.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(fscher);
/*
* The FSCHER registers
*/
/* chip identification */
#define FSCHER_REG_IDENT_0 0x00
#define FSCHER_REG_IDENT_1 0x01
#define FSCHER_REG_IDENT_2 0x02
#define FSCHER_REG_REVISION 0x03
/* global control and status */
#define FSCHER_REG_EVENT_STATE 0x04
#define FSCHER_REG_CONTROL 0x05
/* watchdog */
#define FSCHER_REG_WDOG_PRESET 0x28
#define FSCHER_REG_WDOG_STATE 0x23
#define FSCHER_REG_WDOG_CONTROL 0x21
/* fan 0 */
#define FSCHER_REG_FAN0_MIN 0x55
#define FSCHER_REG_FAN0_ACT 0x0e
#define FSCHER_REG_FAN0_STATE 0x0d
#define FSCHER_REG_FAN0_RIPPLE 0x0f
/* fan 1 */
#define FSCHER_REG_FAN1_MIN 0x65
#define FSCHER_REG_FAN1_ACT 0x6b
#define FSCHER_REG_FAN1_STATE 0x62
#define FSCHER_REG_FAN1_RIPPLE 0x6f
/* fan 2 */
#define FSCHER_REG_FAN2_MIN 0xb5
#define FSCHER_REG_FAN2_ACT 0xbb
#define FSCHER_REG_FAN2_STATE 0xb2
#define FSCHER_REG_FAN2_RIPPLE 0xbf
/* voltage supervision */
#define FSCHER_REG_VOLT_12 0x45
#define FSCHER_REG_VOLT_5 0x42
#define FSCHER_REG_VOLT_BATT 0x48
/* temperature 0 */
#define FSCHER_REG_TEMP0_ACT 0x64
#define FSCHER_REG_TEMP0_STATE 0x71
/* temperature 1 */
#define FSCHER_REG_TEMP1_ACT 0x32
#define FSCHER_REG_TEMP1_STATE 0x81
/* temperature 2 */
#define FSCHER_REG_TEMP2_ACT 0x35
#define FSCHER_REG_TEMP2_STATE 0x91
/*
* Functions declaration
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter);
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind);
static int fscher_detach_client(struct i2c_client *client);
static void fscher_update_client(struct i2c_client *client);
static void fscher_init_client(struct i2c_client *client);
static int fscher_read_value(struct i2c_client *client, u8 reg);
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver fscher_driver = {
.owner = THIS_MODULE,
.name = "fscher",
.id = I2C_DRIVERID_FSCHER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = fscher_attach_adapter,
.detach_client = fscher_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct fscher_data {
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 revision; /* revision of chip */
u8 global_event; /* global event status */
u8 global_control; /* global control register */
u8 watchdog[3]; /* watchdog */
u8 volt[3]; /* 12, 5, battery voltage */
u8 temp_act[3]; /* temperature */
u8 temp_status[3]; /* status of sensor */
u8 fan_act[3]; /* fans revolutions per second */
u8 fan_status[3]; /* fan status */
u8 fan_min[3]; /* fan min value for rps */
u8 fan_ripple[3]; /* divider for rps */
};
/*
* Internal variables
*/
static int fscher_id = 0;
/*
* Sysfs stuff
*/
#define sysfs_r(kind, offset, reg) \
static ssize_t show_##kind (struct fscher_data *, char *, int); \
static ssize_t show_##kind##offset (struct device *, char *); \
static ssize_t show_##kind##offset (struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscher_data *data = i2c_get_clientdata(client); \
fscher_update_client(client); \
return show_##kind(data, buf, (offset)); \
}
#define sysfs_w(kind, offset, reg) \
static ssize_t set_##kind (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \
static ssize_t set_##kind##offset (struct device *, const char *, size_t); \
static ssize_t set_##kind##offset (struct device *dev, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscher_data *data = i2c_get_clientdata(client); \
return set_##kind(client, data, buf, count, (offset), reg); \
}
#define sysfs_rw_n(kind, offset, reg) \
sysfs_r(kind, offset, reg) \
sysfs_w(kind, offset, reg) \
static DEVICE_ATTR(kind##offset, S_IRUGO | S_IWUSR, show_##kind##offset, set_##kind##offset);
#define sysfs_rw(kind, reg) \
sysfs_r(kind, 0, reg) \
sysfs_w(kind, 0, reg) \
static DEVICE_ATTR(kind, S_IRUGO | S_IWUSR, show_##kind##0, set_##kind##0);
#define sysfs_ro_n(kind, offset, reg) \
sysfs_r(kind, offset, reg) \
static DEVICE_ATTR(kind##offset, S_IRUGO, show_##kind##offset, NULL);
#define sysfs_ro(kind, reg) \
sysfs_r(kind, 0, reg) \
static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0, NULL);
#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \
sysfs_rw_n(pwm , offset, reg_min) \
sysfs_rw_n(fan_status, offset, reg_status) \
sysfs_rw_n(fan_div , offset, reg_ripple) \
sysfs_ro_n(fan_input , offset, reg_act)
#define sysfs_temp(offset, reg_status, reg_act) \
sysfs_rw_n(temp_status, offset, reg_status) \
sysfs_ro_n(temp_input , offset, reg_act)
#define sysfs_in(offset, reg_act) \
sysfs_ro_n(in_input, offset, reg_act)
#define sysfs_revision(reg_revision) \
sysfs_ro(revision, reg_revision)
#define sysfs_alarms(reg_events) \
sysfs_ro(alarms, reg_events)
#define sysfs_control(reg_control) \
sysfs_rw(control, reg_control)
#define sysfs_watchdog(reg_control, reg_status, reg_preset) \
sysfs_rw(watchdog_control, reg_control) \
sysfs_rw(watchdog_status , reg_status) \
sysfs_rw(watchdog_preset , reg_preset)
sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN,
FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT)
sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN,
FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT)
sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN,
FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT)
sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT)
sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT)
sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT)
sysfs_in(0, FSCHER_REG_VOLT_12)
sysfs_in(1, FSCHER_REG_VOLT_5)
sysfs_in(2, FSCHER_REG_VOLT_BATT)
sysfs_revision(FSCHER_REG_REVISION)
sysfs_alarms(FSCHER_REG_EVENTS)
sysfs_control(FSCHER_REG_CONTROL)
sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET)
#define device_create_file_fan(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_fan_status##offset); \
device_create_file(&client->dev, &dev_attr_pwm##offset); \
device_create_file(&client->dev, &dev_attr_fan_div##offset); \
device_create_file(&client->dev, &dev_attr_fan_input##offset); \
} while (0)
#define device_create_file_temp(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_temp_status##offset); \
device_create_file(&client->dev, &dev_attr_temp_input##offset); \
} while (0)
#define device_create_file_in(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_in_input##offset); \
} while (0)
#define device_create_file_revision(client) \
do { \
device_create_file(&client->dev, &dev_attr_revision); \
} while (0)
#define device_create_file_alarms(client) \
do { \
device_create_file(&client->dev, &dev_attr_alarms); \
} while (0)
#define device_create_file_control(client) \
do { \
device_create_file(&client->dev, &dev_attr_control); \
} while (0)
#define device_create_file_watchdog(client) \
do { \
device_create_file(&client->dev, &dev_attr_watchdog_status); \
device_create_file(&client->dev, &dev_attr_watchdog_control); \
device_create_file(&client->dev, &dev_attr_watchdog_preset); \
} while (0)
/*
* Real code
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
return 0;
return i2c_detect(adapter, &addr_data, fscher_detect);
}
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct fscher_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
* client structure, even though we cannot fill it completely yet.
* But it allows us to access i2c_smbus_read_byte_data. */
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct fscher_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct fscher_data));
/* The Hermes-specific data is placed right after the common I2C
* client data. */
data = (struct fscher_data *) (new_client + 1);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &fscher_driver;
new_client->flags = 0;
/* Do the remaining detection unless force or force_fscher parameter */
if (kind < 0) {
if ((i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_0) != 0x48) /* 'H' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_1) != 0x45) /* 'E' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */
goto exit_free;
}
/* Fill in the remaining client fields and put it into the
* global list */
strlcpy(new_client->name, "fscher", I2C_NAME_SIZE);
new_client->id = fscher_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
fscher_init_client(new_client);
/* Register sysfs hooks */
device_create_file_revision(new_client);
device_create_file_alarms(new_client);
device_create_file_control(new_client);
device_create_file_watchdog(new_client);
device_create_file_in(new_client, 0);
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
device_create_file_temp(new_client, 3);
return 0;
exit_free:
kfree(new_client);
exit:
return err;
}
static int fscher_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client);
return 0;
}
static int fscher_read_value(struct i2c_client *client, u8 reg)
{
dev_dbg(&client->dev, "read reg 0x%02x\n", reg);
return i2c_smbus_read_byte_data(client, reg);
}
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value)
{
dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n",
reg, value);
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new FSC Hermes. */
static void fscher_init_client(struct i2c_client *client)
{
struct fscher_data *data = i2c_get_clientdata(client);
/* Read revision from chip */
data->revision = fscher_read_value(client, FSCHER_REG_REVISION);
}
static void fscher_update_client(struct i2c_client *client)
{
struct fscher_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((jiffies - data->last_updated > 2 * HZ) ||
(jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting fscher update\n");
data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT);
data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT);
data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT);
data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE);
data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE);
data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE);
data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12);
data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5);
data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT);
data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT);
data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT);
data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT);
data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE);
data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE);
data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE);
data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN);
data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN);
data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN);
data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE);
data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE);
data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE);
data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET);
data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE);
data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL);
data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04;
data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
return count;
}
static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04);
}
static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
data->fan_min[FAN_INDEX_FROM_NUM(nr)] = simple_strtoul(buf, NULL, 10) & 0xff;
fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
return count;
}
static ssize_t show_pwm (struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
}
static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* supported values: 2, 4, 8 */
unsigned long v = simple_strtoul(buf, NULL, 10);
switch (v) {
case 2: v = 1; break;
case 4: v = 2; break;
case 8: v = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 2, 4 or 8!\n", v);
return -1;
}
/* bits 2..7 reserved => mask with 0x03 */
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03;
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v;
fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]);
return count;
}
static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03));
}
#define RPM_FROM_REG(val) (val*60)
static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)]));
}
#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 2..7 reserved, 0 read only => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
return count;
}
static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03);
}
#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)]));
}
/*
* The final conversion is specified in sensors.conf, as it depends on
* mainboard specific values. We export the registers contents as
* pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much
* sense per se, but it minimizes the conversions count and keeps the
* values within a usual range.
*/
#define VOLT_FROM_REG(val) ((val) * 10)
static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr]));
}
static ssize_t show_revision(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->revision);
}
static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr)
{
/* bits 2, 5..6 reserved => mask with 0x9b */
return sprintf(buf, "%u\n", data->global_event & 0x9b);
}
static ssize_t set_control(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 1..7 reserved => mask with 0x01 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01;
data->global_control &= ~v;
fscher_write_value(client, reg, v);
return count;
}
static ssize_t show_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 1..7 reserved => mask with 0x01 */
return sprintf(buf, "%u\n", data->global_control & 0x01);
}
static ssize_t set_watchdog_control(struct i2c_client *client, struct
fscher_data *data, const char *buf, size_t count,
int nr, int reg)
{
/* bits 0..3 reserved => mask with 0xf0 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
data->watchdog[2] &= ~0xf0;
data->watchdog[2] |= v;
fscher_write_value(client, reg, data->watchdog[2]);
return count;
}
static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */
return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0);
}
static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
data->watchdog[1] &= ~v;
fscher_write_value(client, reg, v);
return count;
}
static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
return sprintf(buf, "%u\n", data->watchdog[1] & 0x02);
}
static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
data->watchdog[0] = simple_strtoul(buf, NULL, 10) & 0xff;
fscher_write_value(client, reg, data->watchdog[0]);
return count;
}
static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->watchdog[0]);
}
static int __init sensors_fscher_init(void)
{
return i2c_add_driver(&fscher_driver);
}
static void __exit sensors_fscher_exit(void)
{
i2c_del_driver(&fscher_driver);
}
MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de>");
MODULE_DESCRIPTION("FSC Hermes driver");
MODULE_LICENSE("GPL");
module_init(sensors_fscher_init);
module_exit(sensors_fscher_exit);
/*
* gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
* Kyosti Malkki <kmalkki@cc.hut.fi>
* Copyright (C) 2004 Hong-Gunn Chew <hglinux@gunnet.org> and
* Jean Delvare <khali@linux-fr.org>
*
* 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.
*
* Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare
* and advice of Greg Kroah-Hartman.
*
* Notes about the port:
* Release 0x00 of the GL518SM chipset doesn't support reading of in0,
* in1 nor in2. The original driver had an ugly workaround to get them
* anyway (changing limits and watching alarms trigger and wear off).
* We did not keep that part of the original driver in the Linux 2.6
* version, since it was making the driver significantly more complex
* with no real benefit.
*
* History:
* 2004-01-28 Original port. (Hong-Gunn Chew)
* 2004-01-31 Code review and approval. (Jean Delvare)
*/
#include <linux/config.h>
#ifdef CONFIG_I2C_DEBUG_CHIP
#define DEBUG 1
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80);
/* Many GL518 constants specified below */
/* The GL518 registers */
#define GL518_REG_CHIP_ID 0x00
#define GL518_REG_REVISION 0x01
#define GL518_REG_VENDOR_ID 0x02
#define GL518_REG_CONF 0x03
#define GL518_REG_TEMP_IN 0x04
#define GL518_REG_TEMP_MAX 0x05
#define GL518_REG_TEMP_HYST 0x06
#define GL518_REG_FAN_COUNT 0x07
#define GL518_REG_FAN_LIMIT 0x08
#define GL518_REG_VIN1_LIMIT 0x09
#define GL518_REG_VIN2_LIMIT 0x0a
#define GL518_REG_VIN3_LIMIT 0x0b
#define GL518_REG_VDD_LIMIT 0x0c
#define GL518_REG_VIN3 0x0d
#define GL518_REG_MISC 0x0f
#define GL518_REG_ALARM 0x10
#define GL518_REG_MASK 0x11
#define GL518_REG_INT 0x12
#define GL518_REG_VIN2 0x13
#define GL518_REG_VIN1 0x14
#define GL518_REG_VDD 0x15
/*
* Conversions. Rounding and limit checking is only done on the TO_REG
* variants. Note that you should be a bit careful with which arguments
* these macros are called: arguments may be evaluated more than once.
* Fixing this is just not worth it.
*/
#define RAW_FROM_REG(val) val
#define BOOL_FROM_REG(val) ((val)?0:1)
#define BOOL_TO_REG(val) ((val)?0:1)
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \
(val)-500:(val)+500)/1000)+119),0,255))
#define TEMP_FROM_REG(val) (((val) - 119) * 1000)
static inline u8 FAN_TO_REG(long rpm, int div)
{
long rpmdiv;
if (rpm == 0)
return 0;
rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div;
return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255);
}
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div))))
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
#define IN_FROM_REG(val) ((val)*19)
#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
#define VDD_FROM_REG(val) (((val)*95+2)/4)
#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3)
#define DIV_FROM_REG(val) (1 << (val))
#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask)
#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f)
/* Each client has this additional data */
struct gl518_data {
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 voltage_in[4]; /* Register values; [0] = VDD */
u8 voltage_min[4]; /* Register values; [0] = VDD */
u8 voltage_max[4]; /* Register values; [0] = VDD */
u8 iter_voltage_in[4]; /* Register values; [0] = VDD */
u8 fan_in[2];
u8 fan_min[2];
u8 fan_div[2]; /* Register encoding, shifted right */
u8 fan_auto1; /* Boolean */
u8 temp_in; /* Register values */
u8 temp_max; /* Register values */
u8 temp_hyst; /* Register values */
u8 alarms; /* Register value */
u8 alarm_mask; /* Register value */
u8 beep_mask; /* Register value */
u8 beep_enable; /* Boolean */
};
static int gl518_attach_adapter(struct i2c_adapter *adapter);
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind);
static void gl518_init_client(struct i2c_client *client);
static int gl518_detach_client(struct i2c_client *client);
static int gl518_read_value(struct i2c_client *client, u8 reg);
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
static void gl518_update_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver gl518_driver = {
.owner = THIS_MODULE,
.name = "gl518sm",
.id = I2C_DRIVERID_GL518,
.flags = I2C_DF_NOTIFY,
.attach_adapter = gl518_attach_adapter,
.detach_client = gl518_detach_client,
};
/*
* Internal variables
*/
static int gl518_id = 0;
/*
* Sysfs stuff
*/
#define show(type, suffix, value) \
static ssize_t show_##suffix(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
gl518_update_client(client); \
return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \
}
#define show_fan(suffix, value, index) \
static ssize_t show_##suffix(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
gl518_update_client(client); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \
DIV_FROM_REG(data->fan_div[index]))); \
}
show(TEMP, temp_input1, temp_in);
show(TEMP, temp_max1, temp_max);
show(TEMP, temp_hyst1, temp_hyst);
show(BOOL, fan_auto1, fan_auto1);
show_fan(fan_input1, fan_in, 0);
show_fan(fan_input2, fan_in, 1);
show_fan(fan_min1, fan_min, 0);
show_fan(fan_min2, fan_min, 1);
show(DIV, fan_div1, fan_div[0]);
show(DIV, fan_div2, fan_div[1]);
show(VDD, in_input0, voltage_in[0]);
show(IN, in_input1, voltage_in[1]);
show(IN, in_input2, voltage_in[2]);
show(IN, in_input3, voltage_in[3]);
show(VDD, in_min0, voltage_min[0]);
show(IN, in_min1, voltage_min[1]);
show(IN, in_min2, voltage_min[2]);
show(IN, in_min3, voltage_min[3]);
show(VDD, in_max0, voltage_max[0]);
show(IN, in_max1, voltage_max[1]);
show(IN, in_max2, voltage_max[2]);
show(IN, in_max3, voltage_max[3]);
show(RAW, alarms, alarms);
show(BOOL, beep_enable, beep_enable);
show(BEEP_MASK, beep_mask, beep_mask);
#define set(type, suffix, value, reg) \
static ssize_t set_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
data->value = type##_TO_REG(simple_strtol(buf, NULL, 10)); \
gl518_write_value(client, reg, data->value); \
return count; \
}
#define set_bits(type, suffix, value, reg, mask, shift) \
static ssize_t set_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
int regvalue = gl518_read_value(client, reg); \
data->value = type##_TO_REG(simple_strtoul(buf, NULL, 10)); \
regvalue = (regvalue & ~mask) | (data->value << shift); \
gl518_write_value(client, reg, regvalue); \
return count; \
}
#define set_low(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0x00ff, 0)
#define set_high(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0xff00, 8)
set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX);
set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST);
set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3);
set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6);
set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4);
set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT);
set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT);
set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT);
set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT);
set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT);
set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT);
set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT);
set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT);
set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2);
set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM);
static ssize_t set_fan_min1(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = FAN_TO_REG(simple_strtoul(buf, NULL, 10),
DIV_FROM_REG(data->fan_div[0]));
regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8);
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[0] == 0)
data->alarm_mask &= ~0x20;
else
data->alarm_mask |= 0x20;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
return count;
}
static ssize_t set_fan_min2(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[1] = FAN_TO_REG(simple_strtoul(buf, NULL, 10),
DIV_FROM_REG(data->fan_div[1]));
regvalue = (regvalue & 0xff00) | data->fan_min[1];
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[1] == 0)
data->alarm_mask &= ~0x40;
else
data->alarm_mask |= 0x40;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
return count;
}
static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp_max1, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1);
static DEVICE_ATTR(temp_hyst1, S_IWUSR|S_IRUGO,
show_temp_hyst1, set_temp_hyst1);
static DEVICE_ATTR(fan_auto1, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1);
static DEVICE_ATTR(fan_input1, S_IRUGO, show_fan_input1, NULL);
static DEVICE_ATTR(fan_input2, S_IRUGO, show_fan_input2, NULL);
static DEVICE_ATTR(fan_min1, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1);
static DEVICE_ATTR(fan_min2, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2);
static DEVICE_ATTR(fan_div1, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1);
static DEVICE_ATTR(fan_div2, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2);
static DEVICE_ATTR(in_input0, S_IRUGO, show_in_input0, NULL);
static DEVICE_ATTR(in_input1, S_IRUGO, show_in_input1, NULL);
static DEVICE_ATTR(in_input2, S_IRUGO, show_in_input2, NULL);
static DEVICE_ATTR(in_input3, S_IRUGO, show_in_input3, NULL);
static DEVICE_ATTR(in_min0, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0);
static DEVICE_ATTR(in_min1, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1);
static DEVICE_ATTR(in_min2, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2);
static DEVICE_ATTR(in_min3, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3);
static DEVICE_ATTR(in_max0, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0);
static DEVICE_ATTR(in_max1, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1);
static DEVICE_ATTR(in_max2, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2);
static DEVICE_ATTR(in_max3, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO,
show_beep_enable, set_beep_enable);
static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO,
show_beep_mask, set_beep_mask);
/*
* Real code
*/
static int gl518_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
return 0;
return i2c_detect(adapter, &addr_data, gl518_detect);
}
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct gl518_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access gl518_{read,write}_value. */
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct gl518_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct gl518_data));
data = (struct gl518_data *) (new_client + 1);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl518_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80)
|| (gl518_read_value(new_client, GL518_REG_CONF) & 0x80))
goto exit_free;
}
/* Determine the chip type. */
if (kind <= 0) {
i = gl518_read_value(new_client, GL518_REG_REVISION);
if (i == 0x00) {
kind = gl518sm_r00;
name = "gl518sm";
} else if (i == 0x80) {
kind = gl518sm_r80;
name = "gl518sm";
} else {
if (kind <= 0)
dev_info(&adapter->dev,
"Ignoring 'force' parameter for unknown "
"chip at adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto exit_free;
}
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
new_client->id = gl518_id++;
data->type = kind;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the GL518SM chip */
data->alarm_mask = 0xff;
data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0;
gl518_init_client((struct i2c_client *) new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in_input0);
device_create_file(&new_client->dev, &dev_attr_in_input1);
device_create_file(&new_client->dev, &dev_attr_in_input2);
device_create_file(&new_client->dev, &dev_attr_in_input3);
device_create_file(&new_client->dev, &dev_attr_in_min0);
device_create_file(&new_client->dev, &dev_attr_in_min1);
device_create_file(&new_client->dev, &dev_attr_in_min2);
device_create_file(&new_client->dev, &dev_attr_in_min3);
device_create_file(&new_client->dev, &dev_attr_in_max0);
device_create_file(&new_client->dev, &dev_attr_in_max1);
device_create_file(&new_client->dev, &dev_attr_in_max2);
device_create_file(&new_client->dev, &dev_attr_in_max3);
device_create_file(&new_client->dev, &dev_attr_fan_auto1);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_hyst1);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_beep_enable);
device_create_file(&new_client->dev, &dev_attr_beep_mask);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(new_client);
exit:
return err;
}
/* Called when we have found a new GL518SM.
Note that we preserve D4:NoFan2 and D2:beep_enable. */
static void gl518_init_client(struct i2c_client *client)
{
/* Make sure we leave D7:Reset untouched */
u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f;
/* Comparator mode (D3=0), standby mode (D6=0) */
gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37));
/* Never interrupts */
gl518_write_value(client, GL518_REG_MASK, 0x00);
/* Clear status register (D5=1), start (D6=1) */
gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue);
gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue);
}
static int gl518_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client);
return 0;
}
static inline u16 swap_bytes(u16 val)
{
return (val >> 8) | (val << 8);
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return swap_bytes(i2c_smbus_read_word_data(client, reg));
else
return i2c_smbus_read_byte_data(client, reg);
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return i2c_smbus_write_word_data(client, reg,
swap_bytes(value));
else
return i2c_smbus_write_byte_data(client, reg, value);
}
static void gl518_update_client(struct i2c_client *client)
{
struct gl518_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting gl518 update\n");
data->alarms = gl518_read_value(client, GL518_REG_INT);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
data->voltage_min[0] = val & 0xff;
data->voltage_max[0] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
data->voltage_min[1] = val & 0xff;
data->voltage_max[1] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
data->voltage_min[2] = val & 0xff;
data->voltage_max[2] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
data->voltage_min[3] = val & 0xff;
data->voltage_max[3] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_COUNT);
data->fan_in[0] = (val >> 8) & 0xff;
data->fan_in[1] = val & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = (val >> 8) & 0xff;
data->fan_min[1] = val & 0xff;
data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN);
data->temp_max =
gl518_read_value(client, GL518_REG_TEMP_MAX);
data->temp_hyst =
gl518_read_value(client, GL518_REG_TEMP_HYST);
val = gl518_read_value(client, GL518_REG_MISC);
data->fan_div[0] = (val >> 6) & 0x03;
data->fan_div[1] = (val >> 4) & 0x03;
data->fan_auto1 = (val >> 3) & 0x01;
data->alarms &= data->alarm_mask;
val = gl518_read_value(client, GL518_REG_CONF);
data->beep_enable = (val >> 2) & 1;
if (data->type != gl518sm_r00) {
data->voltage_in[0] =
gl518_read_value(client, GL518_REG_VDD);
data->voltage_in[1] =
gl518_read_value(client, GL518_REG_VIN1);
data->voltage_in[2] =
gl518_read_value(client, GL518_REG_VIN2);
}
data->voltage_in[3] =
gl518_read_value(client, GL518_REG_VIN3);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
static int __init sensors_gl518sm_init(void)
{
return i2c_add_driver(&gl518_driver);
}
static void __exit sensors_gl518sm_exit(void)
{
i2c_del_driver(&gl518_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Kyosti Malkki <kmalkki@cc.hut.fi> and "
"Hong-Gunn Chew <hglinux@gunnet.org>");
MODULE_DESCRIPTION("GL518SM driver");
MODULE_LICENSE("GPL");
module_init(sensors_gl518sm_init);
module_exit(sensors_gl518sm_exit);
......@@ -816,7 +816,7 @@ int lm85_detect(struct i2c_adapter *adapter, int address,
kind = lm85b ;
} else if( company == LM85_COMPANY_NATIONAL
&& (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) {
dev_err(&adapter->dev, "Unrecgonized version/stepping 0x%02x"
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
" Defaulting to LM85.\n", verstep);
kind = any_chip ;
} else if( company == LM85_COMPANY_ANALOG_DEV
......@@ -827,7 +827,7 @@ int lm85_detect(struct i2c_adapter *adapter, int address,
kind = adt7463 ;
} else if( company == LM85_COMPANY_ANALOG_DEV
&& (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) {
dev_err(&adapter->dev, "Unrecgonized version/stepping 0x%02x"
dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x"
" Defaulting to ADM1027.\n", verstep);
kind = adm1027 ;
} else if( kind == 0 && (verstep & 0xf0) == 0x60) {
......@@ -1204,7 +1204,7 @@ static void __exit sm_lm85_exit(void)
/* Thanks to Richard Barrington for adding the LM85 to sensors-detect.
* Thanks to Margit Schubert-While <margitsw@t-online.de> for help with
* post 2.7.0 CVS changes
* post 2.7.0 CVS changes.
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, Margit Schubert-While <margitsw@t-online.de>");
......
......@@ -38,6 +38,9 @@
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* How many retries on register read error */
#define MAX_RETRIES 5
/*
* Address to scan
* Address is fully defined internally and cannot be changed.
......@@ -82,6 +85,7 @@ static int w83l785ts_attach_adapter(struct i2c_adapter *adapter);
static int w83l785ts_detect(struct i2c_adapter *adapter, int address,
int kind);
static int w83l785ts_detach_client(struct i2c_client *client);
static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval);
static void w83l785ts_update_client(struct i2c_client *client);
/*
......@@ -137,8 +141,8 @@ static ssize_t show_temp_over(struct device *dev, char *buf)
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL)
static DEVICE_ATTR(temp_max, S_IRUGO, show_temp_over, NULL)
static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp, NULL)
static DEVICE_ATTR(temp_max1, S_IRUGO, show_temp_over, NULL)
/*
* Real code
......@@ -196,10 +200,10 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
* are skipped.
*/
if (kind < 0) { /* detection */
if (((i2c_smbus_read_byte_data(new_client,
W83L785TS_REG_CONFIG) & 0x80) != 0x00)
|| ((i2c_smbus_read_byte_data(new_client,
W83L785TS_REG_TYPE) & 0xFC) != 0x00)) {
if (((w83l785ts_read_value(new_client,
W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00)
|| ((w83l785ts_read_value(new_client,
W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) {
dev_dbg(&adapter->dev,
"W83L785TS-S detection failed at 0x%02x.\n",
address);
......@@ -211,12 +215,12 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
u16 man_id;
u8 chip_id;
man_id = (i2c_smbus_read_byte_data(new_client,
W83L785TS_REG_MAN_ID1) << 8) +
i2c_smbus_read_byte_data(new_client,
W83L785TS_REG_MAN_ID2);
chip_id = i2c_smbus_read_byte_data(new_client,
W83L785TS_REG_CHIP_ID);
man_id = (w83l785ts_read_value(new_client,
W83L785TS_REG_MAN_ID1, 0) << 8) +
w83l785ts_read_value(new_client,
W83L785TS_REG_MAN_ID2, 0);
chip_id = w83l785ts_read_value(new_client,
W83L785TS_REG_CHIP_ID, 0);
if (man_id == 0x5CA3) { /* Winbond */
if (chip_id == 0x70) { /* W83L785TS-S */
......@@ -239,6 +243,9 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Default values in case the first read fails (unlikely). */
data->temp_over = data->temp = 0;
/* Tell the I2C layer a new client has arrived. */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
......@@ -249,8 +256,8 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
*/
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp_input);
device_create_file(&new_client->dev, &dev_attr_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_max1);
return 0;
......@@ -274,6 +281,26 @@ static int w83l785ts_detach_client(struct i2c_client *client)
return 0;
}
static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval)
{
int value, i;
/* Frequent read errors have been reported on Asus boards, so we
* retry on read errors. If it still fails (unlikely), return the
* default value requested by the caller. */
for (i = 1; i <= MAX_RETRIES; i++) {
value = i2c_smbus_read_byte_data(client, reg);
if (value >= 0)
return value;
dev_dbg(&client->dev, "Read failed, will retry in %d.\n", i);
i2c_delay(i);
}
dev_err(&client->dev, "Couldn't read value from register. "
"Please report.\n");
return defval;
}
static void w83l785ts_update_client(struct i2c_client *client)
{
struct w83l785ts_data *data = i2c_get_clientdata(client);
......@@ -284,10 +311,10 @@ static void w83l785ts_update_client(struct i2c_client *client)
|| (jiffies - data->last_updated > HZ * 2)
|| (jiffies < data->last_updated)) {
dev_dbg(&client->dev, "Updating w83l785ts data.\n");
data->temp = i2c_smbus_read_byte_data(client,
W83L785TS_REG_TEMP);
data->temp_over = i2c_smbus_read_byte_data(client,
W83L785TS_REG_TEMP_OVER);
data->temp = w83l785ts_read_value(client,
W83L785TS_REG_TEMP, data->temp);
data->temp_over = w83l785ts_read_value(client,
W83L785TS_REG_TEMP_OVER, data->temp_over);
data->last_updated = jiffies;
data->valid = 1;
......
......@@ -598,7 +598,7 @@ int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
ret = adap->algo->master_xfer(adap,&msg,1);
up(&adap->bus_lock);
dev_dbg(&client->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n",
dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n",
ret, count, client->addr);
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
......
......@@ -156,6 +156,7 @@
#define I2C_DRIVERID_LM83 1040
#define I2C_DRIVERID_LM90 1042
#define I2C_DRIVERID_ASB100 1043
#define I2C_DRIVERID_FSCHER 1046
#define I2C_DRIVERID_W83L785TS 1047
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment