Commit a11227f6 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 73d7a5fa f265df5a
...@@ -51,6 +51,19 @@ config I2C_AMD756 ...@@ -51,6 +51,19 @@ config I2C_AMD756
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 i2c-amd756. will be called i2c-amd756.
config I2C_AMD756_S4882
tristate "SMBus multiplexing on the Tyan S4882"
depends on I2C_AMD756 && EXPERIMENTAL
help
Enabling this option will add specific SMBus support for the Tyan
S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed
over 8 different channels, where the various memory module EEPROMs
and temperature sensors live. Saying yes here will give you access
to these in addition to the trunk.
This driver can also be built as a module. If so, the module
will be called i2c-amd756-s4882.
config I2C_AMD8111 config I2C_AMD8111
tristate "AMD 8111" tristate "AMD 8111"
depends on I2C && PCI && EXPERIMENTAL depends on I2C && PCI && EXPERIMENTAL
......
...@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o ...@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o
obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
static unsigned short ali1535_smba; static unsigned short ali1535_smba;
DECLARE_MUTEX(i2c_ali1535_sem); static DECLARE_MUTEX(i2c_ali1535_sem);
/* Detect whether a ALI1535 can be found, and initialize it, where necessary. /* Detect whether a ALI1535 can be found, and initialize it, where necessary.
Note the differences between kernels with the old PCI BIOS interface and Note the differences between kernels with the old PCI BIOS interface and
...@@ -465,7 +465,7 @@ static s32 ali1535_access(struct i2c_adapter *adap, u16 addr, ...@@ -465,7 +465,7 @@ static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
} }
u32 ali1535_func(struct i2c_adapter *adapter) static u32 ali1535_func(struct i2c_adapter *adapter)
{ {
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
......
/*
* i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
*
* Copyright (C) 2004 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.
*/
/*
* We select the channels by sending commands to the Philips
* PCA9556 chip at I2C address 0x18. The main adapter is used for
* the non-multiplexed part of the bus, and 4 virtual adapters
* are defined for the multiplexed addresses: 0x50-0x53 (memory
* module EEPROM) located on channels 1-4, and 0x4c (LM63)
* located on multiplexed channels 0 and 5-7. We define one
* virtual adapter per CPU, which corresponds to two multiplexed
* channels:
* CPU0: virtual adapter 1, channels 1 and 0
* CPU1: virtual adapter 2, channels 2 and 5
* CPU2: virtual adapter 3, channels 3 and 6
* CPU3: virtual adapter 4, channels 4 and 7
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
extern struct i2c_adapter amd756_smbus;
static struct i2c_adapter *s4882_adapter;
static struct i2c_algorithm *s4882_algo;
/* Wrapper access functions for multiplexed SMBus */
static struct semaphore amd756_lock;
static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
int error;
/* We exclude the multiplexed addresses */
if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
|| addr == 0x18)
return -1;
down(&amd756_lock);
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
command, size, data);
up(&amd756_lock);
return error;
}
/* We remember the last used channels combination so as to only switch
channels when it is really needed. This greatly reduces the SMBus
overhead, but also assumes that nobody will be writing to the PCA9556
in our back. */
static u8 last_channels;
static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data,
u8 channels)
{
int error;
/* We exclude the non-multiplexed addresses */
if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
return -1;
down(&amd756_lock);
if (last_channels != channels) {
union i2c_smbus_data mplxdata;
mplxdata.byte = channels;
error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
I2C_SMBUS_WRITE, 0x01,
I2C_SMBUS_BYTE_DATA,
&mplxdata);
if (error)
goto UNLOCK;
last_channels = channels;
}
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
command, size, data);
UNLOCK:
up(&amd756_lock);
return error;
}
static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU0: channels 1 and 0 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x03);
}
static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU1: channels 2 and 5 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x24);
}
static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU2: channels 3 and 6 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x48);
}
static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU3: channels 4 and 7 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x90);
}
static int __init amd756_s4882_init(void)
{
int i, error;
union i2c_smbus_data ioconfig;
/* Unregister physical bus */
error = i2c_del_adapter(&amd756_smbus);
if (error) {
if (error != -EINVAL)
dev_err(&amd756_smbus.dev, "Physical bus removal "
"failed\n");
goto ERROR0;
}
printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
init_MUTEX(&amd756_lock);
/* Define the 5 virtual adapters and algorithms structures */
if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
GFP_KERNEL))) {
error = -ENOMEM;
goto ERROR1;
}
if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
GFP_KERNEL))) {
error = -ENOMEM;
goto ERROR2;
}
/* Fill in the new structures */
s4882_algo[0] = *(amd756_smbus.algo);
s4882_algo[0].smbus_xfer = amd756_access_virt0;
s4882_adapter[0] = amd756_smbus;
s4882_adapter[0].algo = s4882_algo;
for (i = 1; i < 5; i++) {
s4882_algo[i] = *(amd756_smbus.algo);
s4882_adapter[i] = amd756_smbus;
sprintf(s4882_adapter[i].name,
"SMBus 8111 adapter (CPU%d)", i-1);
s4882_adapter[i].algo = s4882_algo+i;
}
s4882_algo[1].smbus_xfer = amd756_access_virt1;
s4882_algo[2].smbus_xfer = amd756_access_virt2;
s4882_algo[3].smbus_xfer = amd756_access_virt3;
s4882_algo[4].smbus_xfer = amd756_access_virt4;
/* Configure the PCA9556 multiplexer */
ioconfig.byte = 0x00; /* All I/O to output mode */
error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
I2C_SMBUS_WRITE, 0x03,
I2C_SMBUS_BYTE_DATA, &ioconfig);
if (error) {
dev_dbg(&amd756_smbus.dev, "PCA9556 configuration failed\n");
error = -EIO;
goto ERROR3;
}
/* Register virtual adapters */
for (i = 0; i < 5; i++) {
error = i2c_add_adapter(s4882_adapter+i);
if (error) {
dev_err(&amd756_smbus.dev,
"Virtual adapter %d registration "
"failed, module not inserted\n", i);
for (i--; i >= 0; i--)
i2c_del_adapter(s4882_adapter+i);
goto ERROR3;
}
}
return 0;
ERROR3:
kfree(s4882_algo);
s4882_algo = NULL;
ERROR2:
kfree(s4882_adapter);
s4882_adapter = NULL;
ERROR1:
i2c_del_adapter(&amd756_smbus);
ERROR0:
return error;
}
static void __exit amd756_s4882_exit(void)
{
if (s4882_adapter) {
int i;
for (i = 0; i < 5; i++)
i2c_del_adapter(s4882_adapter+i);
kfree(s4882_adapter);
s4882_adapter = NULL;
}
if (s4882_algo) {
kfree(s4882_algo);
s4882_algo = NULL;
}
/* Restore physical bus */
if (i2c_add_adapter(&amd756_smbus))
dev_err(&amd756_smbus.dev, "Physical bus restoration "
"failed\n");
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("S4882 SMBus multiplexing");
MODULE_LICENSE("GPL");
module_init(amd756_s4882_init);
module_exit(amd756_s4882_exit);
...@@ -302,7 +302,7 @@ static struct i2c_algorithm smbus_algorithm = { ...@@ -302,7 +302,7 @@ static struct i2c_algorithm smbus_algorithm = {
.functionality = amd756_func, .functionality = amd756_func,
}; };
static struct i2c_adapter amd756_adapter = { struct i2c_adapter amd756_smbus = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.class = I2C_CLASS_HWMON, .class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm, .algo = &smbus_algorithm,
...@@ -374,12 +374,12 @@ static int __devinit amd756_probe(struct pci_dev *pdev, ...@@ -374,12 +374,12 @@ static int __devinit amd756_probe(struct pci_dev *pdev,
dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport);
/* set up the driverfs linkage to our parent device */ /* set up the driverfs linkage to our parent device */
amd756_adapter.dev.parent = &pdev->dev; amd756_smbus.dev.parent = &pdev->dev;
sprintf(amd756_adapter.name, "SMBus %s adapter at %04x", sprintf(amd756_smbus.name, "SMBus %s adapter at %04x",
chipname[id->driver_data], amd756_ioport); chipname[id->driver_data], amd756_ioport);
error = i2c_add_adapter(&amd756_adapter); error = i2c_add_adapter(&amd756_smbus);
if (error) { if (error) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Adapter registration failed, module not inserted\n"); "Adapter registration failed, module not inserted\n");
...@@ -395,7 +395,7 @@ static int __devinit amd756_probe(struct pci_dev *pdev, ...@@ -395,7 +395,7 @@ static int __devinit amd756_probe(struct pci_dev *pdev,
static void __devexit amd756_remove(struct pci_dev *dev) static void __devexit amd756_remove(struct pci_dev *dev)
{ {
i2c_del_adapter(&amd756_adapter); i2c_del_adapter(&amd756_smbus);
release_region(amd756_ioport, SMB_IOSIZE); release_region(amd756_ioport, SMB_IOSIZE);
} }
...@@ -420,5 +420,7 @@ MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>"); ...@@ -420,5 +420,7 @@ MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
EXPORT_SYMBOL(amd756_smbus);
module_init(amd756_init) module_init(amd756_init)
module_exit(amd756_exit) module_exit(amd756_exit)
...@@ -67,7 +67,7 @@ struct amd_smbus { ...@@ -67,7 +67,7 @@ struct amd_smbus {
* ACPI 2.0 chapter 13 access of registers of the EC * ACPI 2.0 chapter 13 access of registers of the EC
*/ */
unsigned int amd_ec_wait_write(struct amd_smbus *smbus) static unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
{ {
int timeout = 500; int timeout = 500;
...@@ -82,7 +82,7 @@ unsigned int amd_ec_wait_write(struct amd_smbus *smbus) ...@@ -82,7 +82,7 @@ unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
return 0; return 0;
} }
unsigned int amd_ec_wait_read(struct amd_smbus *smbus) static unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
{ {
int timeout = 500; int timeout = 500;
...@@ -97,7 +97,7 @@ unsigned int amd_ec_wait_read(struct amd_smbus *smbus) ...@@ -97,7 +97,7 @@ unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
return 0; return 0;
} }
unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data) static unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data)
{ {
if (amd_ec_wait_write(smbus)) if (amd_ec_wait_write(smbus))
return -1; return -1;
...@@ -114,7 +114,7 @@ unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigne ...@@ -114,7 +114,7 @@ unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigne
return 0; return 0;
} }
unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data) static unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data)
{ {
if (amd_ec_wait_write(smbus)) if (amd_ec_wait_write(smbus))
return -1; return -1;
...@@ -174,7 +174,7 @@ unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsign ...@@ -174,7 +174,7 @@ unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsign
#define AMD_SMB_PRTCL_PEC 0x80 #define AMD_SMB_PRTCL_PEC 0x80
s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data * data) char read_write, u8 command, int size, union i2c_smbus_data * data)
{ {
struct amd_smbus *smbus = adap->algo_data; struct amd_smbus *smbus = adap->algo_data;
...@@ -315,7 +315,7 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, ...@@ -315,7 +315,7 @@ s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
} }
u32 amd8111_func(struct i2c_adapter *adapter) static u32 amd8111_func(struct i2c_adapter *adapter)
{ {
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
......
...@@ -412,18 +412,12 @@ static int iic_wait_for_tc(struct ibm_iic_private* dev){ ...@@ -412,18 +412,12 @@ static int iic_wait_for_tc(struct ibm_iic_private* dev){
if (dev->irq >= 0){ if (dev->irq >= 0){
/* Interrupt mode */ /* Interrupt mode */
wait_queue_t wait; ret = wait_event_interruptible_timeout(dev->wq,
init_waitqueue_entry(&wait, current); !(in_8(&iic->sts) & STS_PT), dev->adap.timeout * HZ);
add_wait_queue(&dev->wq, &wait); if (unlikely(ret < 0))
if (in_8(&iic->sts) & STS_PT)
msleep_interruptible(dev->adap.timeout * 1000);
remove_wait_queue(&dev->wq, &wait);
if (unlikely(signal_pending(current))){
DBG("%d: wait interrupted\n", dev->idx); DBG("%d: wait interrupted\n", dev->idx);
ret = -ERESTARTSYS; else if (unlikely(in_8(&iic->sts) & STS_PT)){
} else if (unlikely(in_8(&iic->sts) & STS_PT)){
DBG("%d: wait timeout\n", dev->idx); DBG("%d: wait timeout\n", dev->idx);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/clock.h> #include <asm/hardware/clock.h>
#include <asm/arch/regs-gpio.h> #include <asm/arch/regs-gpio.h>
...@@ -71,7 +72,7 @@ struct s3c24xx_i2c { ...@@ -71,7 +72,7 @@ struct s3c24xx_i2c {
struct i2c_adapter adap; struct i2c_adapter adap;
}; };
/* default platform data to use if not supplied in the platfrom_device /* default platform data to use if not supplied in the platform_device
*/ */
static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = { static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
...@@ -79,8 +80,21 @@ static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = { ...@@ -79,8 +80,21 @@ static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
.slave_addr = 0x10, .slave_addr = 0x10,
.bus_freq = 100*1000, .bus_freq = 100*1000,
.max_freq = 400*1000, .max_freq = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
}; };
/* s3c24xx_i2c_is2440()
*
* return true is this is an s3c2440
*/
static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
{
struct platform_device *pdev = to_platform_device(i2c->dev);
return !strcmp(pdev->name, "s3c2440-i2c");
}
/* s3c24xx_i2c_get_platformdata /* s3c24xx_i2c_get_platformdata
* *
...@@ -163,10 +177,9 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, ...@@ -163,10 +177,9 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
{ {
unsigned int addr = (msg->addr & 0x7f) << 1; unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat; unsigned long stat;
unsigned long iiccon;
stat = readl(i2c->regs + S3C2410_IICSTAT); stat = 0;
stat &= ~S3C2410_IICSTAT_MODEMASK;
stat |= S3C2410_IICSTAT_START;
stat |= S3C2410_IICSTAT_TXRXEN; stat |= S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) { if (msg->flags & I2C_M_RD) {
...@@ -178,8 +191,18 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, ...@@ -178,8 +191,18 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
// todo - check for wether ack wanted or not // todo - check for wether ack wanted or not
s3c24xx_i2c_enable_ack(i2c); s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS); writeb(addr, i2c->regs + S3C2410_IICDS);
// delay a bit and reset iiccon before setting start (per samsung)
udelay(1);
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT); writel(stat, i2c->regs + S3C2410_IICSTAT);
} }
...@@ -264,6 +287,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) ...@@ -264,6 +287,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */ /* ack was not received... */
dev_err(i2c->dev, "ack was not received\n" );
s3c24xx_i2c_stop(i2c, -EREMOTEIO); s3c24xx_i2c_stop(i2c, -EREMOTEIO);
goto out_ack; goto out_ack;
} }
...@@ -407,6 +431,7 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id, ...@@ -407,6 +431,7 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
if (status & S3C2410_IICSTAT_ARBITR) { if (status & S3C2410_IICSTAT_ARBITR) {
// deal with arbitration loss // deal with arbitration loss
dev_err(i2c->dev, "deal with arbitration loss\n");
} }
if (i2c->state == STATE_IDLE) { if (i2c->state == STATE_IDLE) {
...@@ -534,7 +559,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, ...@@ -534,7 +559,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
static struct i2c_algorithm s3c24xx_i2c_algorithm = { static struct i2c_algorithm s3c24xx_i2c_algorithm = {
.name = "S3C2410-I2C-Algorithm", .name = "S3C2410-I2C-Algorithm",
.id = I2C_ALGO_S3C2410,
.master_xfer = s3c24xx_i2c_xfer, .master_xfer = s3c24xx_i2c_xfer,
}; };
...@@ -543,7 +567,6 @@ static struct s3c24xx_i2c s3c24xx_i2c = { ...@@ -543,7 +567,6 @@ static struct s3c24xx_i2c s3c24xx_i2c = {
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
.adap = { .adap = {
.name = "s3c2410-i2c", .name = "s3c2410-i2c",
.id = I2C_ALGO_S3C2410,
.algo = &s3c24xx_i2c_algorithm, .algo = &s3c24xx_i2c_algorithm,
.retries = 2, .retries = 2,
}, },
...@@ -576,7 +599,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, ...@@ -576,7 +599,7 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
*divs = calc_divs; *divs = calc_divs;
*div1 = calc_div1; *div1 = calc_div1;
return clkin / (calc_divs + calc_div1); return clkin / (calc_divs * calc_div1);
} }
/* freq_acceptable /* freq_acceptable
...@@ -684,6 +707,17 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) ...@@ -684,6 +707,17 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* todo - check that the i2c lines aren't being dragged anywhere */ /* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
writel(iicon, i2c->regs + S3C2410_IICCON);
/* check for s3c2440 i2c controller */
if (s3c24xx_i2c_is2440(i2c)) {
dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
}
return 0; return 0;
} }
...@@ -851,7 +885,7 @@ static int s3c24xx_i2c_resume(struct device *dev, u32 level) ...@@ -851,7 +885,7 @@ static int s3c24xx_i2c_resume(struct device *dev, u32 level)
/* device driver for platform bus bits */ /* device driver for platform bus bits */
static struct device_driver s3c24xx_i2c_driver = { static struct device_driver s3c2410_i2c_driver = {
.name = "s3c2410-i2c", .name = "s3c2410-i2c",
.bus = &platform_bus_type, .bus = &platform_bus_type,
.probe = s3c24xx_i2c_probe, .probe = s3c24xx_i2c_probe,
...@@ -859,14 +893,29 @@ static struct device_driver s3c24xx_i2c_driver = { ...@@ -859,14 +893,29 @@ static struct device_driver s3c24xx_i2c_driver = {
.resume = s3c24xx_i2c_resume, .resume = s3c24xx_i2c_resume,
}; };
static struct device_driver s3c2440_i2c_driver = {
.name = "s3c2440-i2c",
.bus = &platform_bus_type,
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.resume = s3c24xx_i2c_resume,
};
static int __init i2c_adap_s3c_init(void) static int __init i2c_adap_s3c_init(void)
{ {
return driver_register(&s3c24xx_i2c_driver); int ret;
ret = driver_register(&s3c2410_i2c_driver);
if (ret == 0)
ret = driver_register(&s3c2440_i2c_driver);
return ret;
} }
static void i2c_adap_s3c_exit(void) static void __exit i2c_adap_s3c_exit(void)
{ {
return driver_unregister(&s3c24xx_i2c_driver); driver_unregister(&s3c2410_i2c_driver);
driver_unregister(&s3c2440_i2c_driver);
} }
module_init(i2c_adap_s3c_init); module_init(i2c_adap_s3c_init);
......
...@@ -402,9 +402,9 @@ static struct i2c_algorithm scx200_acb_algorithm = { ...@@ -402,9 +402,9 @@ static struct i2c_algorithm scx200_acb_algorithm = {
.functionality = scx200_acb_func, .functionality = scx200_acb_func,
}; };
struct scx200_acb_iface *scx200_acb_list; static struct scx200_acb_iface *scx200_acb_list;
int scx200_acb_probe(struct scx200_acb_iface *iface) static int scx200_acb_probe(struct scx200_acb_iface *iface)
{ {
u8 val; u8 val;
......
...@@ -86,7 +86,7 @@ static struct i2c_adapter scx200_i2c_ops = { ...@@ -86,7 +86,7 @@ static struct i2c_adapter scx200_i2c_ops = {
.name = "NatSemi SCx200 I2C", .name = "NatSemi SCx200 I2C",
}; };
int scx200_i2c_init(void) static int scx200_i2c_init(void)
{ {
pr_debug(NAME ": NatSemi SCx200 I2C Driver\n"); pr_debug(NAME ": NatSemi SCx200 I2C Driver\n");
...@@ -115,7 +115,7 @@ int scx200_i2c_init(void) ...@@ -115,7 +115,7 @@ int scx200_i2c_init(void)
return 0; return 0;
} }
void scx200_i2c_cleanup(void) static void scx200_i2c_cleanup(void)
{ {
i2c_bit_del_bus(&scx200_i2c_ops); i2c_bit_del_bus(&scx200_i2c_ops);
} }
......
...@@ -97,6 +97,19 @@ config SENSORS_IT87 ...@@ -97,6 +97,19 @@ config SENSORS_IT87
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 it87. will be called it87.
config SENSORS_LM63
tristate "National Semiconductor LM63"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for the National Semiconductor
LM63 remote diode digital temperature sensor with integrated fan
control. Such chips are found on the Tyan S4882 (Thunder K8QS Pro)
motherboard, among others.
This driver can also be built as a module. If so, the module
will be called lm63.
config SENSORS_LM75 config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles" tristate "National Semiconductor LM75 and compatibles"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
...@@ -202,6 +215,21 @@ config SENSORS_MAX1619 ...@@ -202,6 +215,21 @@ config SENSORS_MAX1619
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 max1619. will be called max1619.
config SENSORS_PC87360
tristate "National Semiconductor PC87360 family"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get access to the hardware monitoring
functions of the National Semiconductor PC8736x Super-I/O chips.
The PC87360, PC87363 and PC87364 only have fan monitoring and
control. The PC87365 and PC87366 additionally have voltage and
temperature monitoring.
This driver can also be built as a module. If so, the module
will be called pc87360.
config SENSORS_SMSC47M1 config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles" tristate "SMSC LPC47M10x and compatibles"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
......
...@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o ...@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM77) += lm77.o obj-$(CONFIG_SENSORS_LM77) += lm77.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o obj-$(CONFIG_SENSORS_LM78) += lm78.o
...@@ -24,6 +25,7 @@ obj-$(CONFIG_SENSORS_LM85) += lm85.o ...@@ -24,6 +25,7 @@ obj-$(CONFIG_SENSORS_LM85) += lm85.o
obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o
......
...@@ -40,12 +40,11 @@ ...@@ -40,12 +40,11 @@
#define ADM1021_ALARM_RTEMP_NA 0x04 #define ADM1021_ALARM_RTEMP_NA 0x04
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, 0x29, 0x2a, 0x2b,
0x4c, 0x4e, I2C_CLIENT_END 0x4c, 0x4d, 0x4e,
}; I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066);
......
...@@ -59,10 +59,8 @@ ...@@ -59,10 +59,8 @@
* NE1619 has two possible addresses: 0x2c and 0x2d. * NE1619 has two possible addresses: 0x2c and 0x2d.
*/ */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -57,10 +57,8 @@ ...@@ -57,10 +57,8 @@
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan)) #define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_2(adm1030, adm1031); SENSORS_INSMOD_2(adm1030, adm1031);
......
...@@ -56,12 +56,11 @@ ...@@ -56,12 +56,11 @@
#define ASB100_VERSION "1.0.0" #define ASB100_VERSION "1.0.0"
/* I2C addresses to scan */ /* I2C addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
static unsigned short normal_i2c_range[] = { 0x28, 0x2f, I2C_CLIENT_END }; 0x2e, 0x2f, I2C_CLIENT_END };
/* ISA addresses to scan (none) */ /* ISA addresses to scan (none) */
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(asb100); SENSORS_INSMOD_1(asb100);
......
...@@ -29,10 +29,9 @@ ...@@ -29,10 +29,9 @@
#include "lm75.h" #include "lm75.h"
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
static unsigned short normal_i2c_range[] = { 0x48, 0x4f, I2C_CLIENT_END }; 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(ds1621); SENSORS_INSMOD_1(ds1621);
......
...@@ -36,10 +36,9 @@ ...@@ -36,10 +36,9 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
static unsigned short normal_i2c_range[] = { 0x50, 0x57, I2C_CLIENT_END }; 0x55, 0x56, 0x57, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(eeprom); SENSORS_INSMOD_1(eeprom);
......
...@@ -38,9 +38,7 @@ ...@@ -38,9 +38,7 @@
*/ */
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; 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[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -45,9 +45,7 @@ ...@@ -45,9 +45,7 @@
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; 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[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80);
......
...@@ -42,10 +42,11 @@ ...@@ -42,10 +42,11 @@
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24,
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END }; 0x25, 0x26, 0x27, 0x28, 0x29,
0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_2(it87, it8712); SENSORS_INSMOD_2(it87, it8712);
...@@ -56,13 +57,6 @@ SENSORS_INSMOD_2(it87, it8712); ...@@ -56,13 +57,6 @@ SENSORS_INSMOD_2(it87, it8712);
#define PME 0x04 /* The device with the fan registers in it */ #define PME 0x04 /* The device with the fan registers in it */
#define DEVID 0x20 /* Register: Device ID */ #define DEVID 0x20 /* Register: Device ID */
static inline void
superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int static inline int
superio_inb(int reg) superio_inb(int reg)
{ {
......
/*
* lm63.c - driver for the National Semiconductor LM63 temperature sensor
* with integrated fan control
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
* Based on the lm90 driver.
*
* The LM63 is a sensor chip made by National Semiconductor. It measures
* two temperatures (its own and one external one) and the speed of one
* fan, those speed it can additionally control. Complete datasheet can be
* obtained from National's website at:
* http://www.national.com/pf/LM/LM63.html
*
* The LM63 is basically an LM86 with fan speed monitoring and control
* capabilities added. It misses some of the LM86 features though:
* - No low limit for local temperature.
* - No critical limit for local temperature.
* - Critical limit for remote temperature can be changed only once. We
* will consider that the critical limit is read-only.
*
* The datasheet isn't very clear about what the tachometer reading is.
* I had a explanation from National Semiconductor though. The two lower
* bits of the read value have to be masked out. The value is still 16 bit
* in width.
*
* 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/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
* Address is fully defined internally and cannot be changed.
*/
static unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm63);
/*
* The LM63 registers
*/
#define LM63_REG_CONFIG1 0x03
#define LM63_REG_CONFIG2 0xBF
#define LM63_REG_CONFIG_FAN 0x4A
#define LM63_REG_TACH_COUNT_MSB 0x47
#define LM63_REG_TACH_COUNT_LSB 0x46
#define LM63_REG_TACH_LIMIT_MSB 0x49
#define LM63_REG_TACH_LIMIT_LSB 0x48
#define LM63_REG_PWM_VALUE 0x4C
#define LM63_REG_PWM_FREQ 0x4D
#define LM63_REG_LOCAL_TEMP 0x00
#define LM63_REG_LOCAL_HIGH 0x05
#define LM63_REG_REMOTE_TEMP_MSB 0x01
#define LM63_REG_REMOTE_TEMP_LSB 0x10
#define LM63_REG_REMOTE_OFFSET_MSB 0x11
#define LM63_REG_REMOTE_OFFSET_LSB 0x12
#define LM63_REG_REMOTE_HIGH_MSB 0x07
#define LM63_REG_REMOTE_HIGH_LSB 0x13
#define LM63_REG_REMOTE_LOW_MSB 0x08
#define LM63_REG_REMOTE_LOW_LSB 0x14
#define LM63_REG_REMOTE_TCRIT 0x19
#define LM63_REG_REMOTE_TCRIT_HYST 0x21
#define LM63_REG_ALERT_STATUS 0x02
#define LM63_REG_ALERT_MASK 0x16
#define LM63_REG_MAN_ID 0xFE
#define LM63_REG_CHIP_ID 0xFF
/*
* Conversions and various macros
* For tachometer counts, the LM63 uses 16-bit values.
* For local temperature and high limit, remote critical limit and hysteresis
* value, it uses signed 8-bit values with LSB = 1 degree Celcius.
* For remote temperature, low and high limits, it uses signed 11-bit values
* with LSB = 0.125 degree Celcius, left-justified in 16-bit registers.
*/
#define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \
5400000 / (reg))
#define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \
(5400000 / (val)) & 0xFFFC)
#define TEMP8_FROM_REG(reg) ((reg) * 1000)
#define TEMP8_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
#define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
(val) >= 127875 ? 0x7FE0 : \
(val) < 0 ? ((val) - 62) / 125 * 32 : \
((val) + 62) / 125 * 32)
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
/*
* Functions declaration
*/
static int lm63_attach_adapter(struct i2c_adapter *adapter);
static int lm63_detach_client(struct i2c_client *client);
static struct lm63_data *lm63_update_device(struct device *dev);
static int lm63_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm63_init_client(struct i2c_client *client);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm63_driver = {
.owner = THIS_MODULE,
.name = "lm63",
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm63_attach_adapter,
.detach_client = lm63_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm63_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u8 config, config_fan;
u16 fan1_input;
u16 fan1_low;
u8 pwm1_freq;
u8 pwm1_value;
s8 temp1_input;
s8 temp1_high;
s16 temp2_input;
s16 temp2_high;
s16 temp2_low;
s8 temp2_crit;
u8 temp2_crit_hyst;
u8 alarms;
};
/*
* Sysfs callback functions and files
*/
#define show_fan(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm63_data *data = lm63_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value)); \
}
show_fan(fan1_input);
show_fan(fan1_low);
static ssize_t set_fan1_low(struct device *dev, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
data->fan1_low = FAN_TO_REG(val);
i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
data->fan1_low & 0xFF);
i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
data->fan1_low >> 8);
return count;
}
static ssize_t show_pwm1(struct device *dev, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ?
255 : (data->pwm1_value * 255 + data->pwm1_freq) /
(2 * data->pwm1_freq));
}
static ssize_t set_pwm1(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
unsigned long val;
if (!(data->config_fan & 0x20)) /* register is read-only */
return -EPERM;
val = simple_strtoul(buf, NULL, 10);
data->pwm1_value = val <= 0 ? 0 :
val >= 255 ? 2 * data->pwm1_freq :
(val * data->pwm1_freq * 2 + 127) / 255;
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
return count;
}
static ssize_t show_pwm1_enable(struct device *dev, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
}
#define show_temp8(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm63_data *data = lm63_update_device(dev); \
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->value)); \
}
#define show_temp11(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm63_data *data = lm63_update_device(dev); \
return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->value)); \
}
show_temp8(temp1_input);
show_temp8(temp1_high);
show_temp11(temp2_input);
show_temp11(temp2_high);
show_temp11(temp2_low);
show_temp8(temp2_crit);
#define set_temp8(value, reg) \
static ssize_t set_##value(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm63_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->value = TEMP8_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
return count; \
}
#define set_temp11(value, reg_msb, reg_lsb) \
static ssize_t set_##value(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm63_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->value = TEMP11_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg_msb, data->value >> 8); \
i2c_smbus_write_byte_data(client, reg_lsb, data->value & 0xff); \
return count; \
}
set_temp8(temp1_high, LM63_REG_LOCAL_HIGH);
set_temp11(temp2_high, LM63_REG_REMOTE_HIGH_MSB, LM63_REG_REMOTE_HIGH_LSB);
set_temp11(temp2_low, LM63_REG_REMOTE_LOW_MSB, LM63_REG_REMOTE_LOW_LSB);
/* Hysteresis register holds a relative value, while we want to present
an absolute to user-space */
static ssize_t show_temp2_crit_hyst(struct device *dev, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp2_crit)
- TEMP8_FROM_REG(data->temp2_crit_hyst));
}
/* And now the other way around, user-space provides an absolute
hysteresis value and we have to store a relative one */
static ssize_t set_temp2_crit_hyst(struct device *dev, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
int hyst = TEMP8_FROM_REG(data->temp2_crit) -
simple_strtol(buf, NULL, 10);
i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
HYST_TO_REG(hyst));
return count;
}
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan1_input, NULL);
static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan1_low,
set_fan1_low);
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1);
static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_high,
set_temp1_high);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp2_input, NULL);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp2_low,
set_temp2_low);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp2_high,
set_temp2_high);
static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp2_crit, NULL);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
set_temp2_crit_hyst);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm63_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm63_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm63_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm63_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm63_data));
/* The common I2C client data is placed right before the
LM63-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm63_driver;
new_client->flags = 0;
/* Default to an LM63 if forced */
if (kind == 0)
kind = lm63;
if (kind < 0) { /* must identify */
u8 man_id, chip_id, reg_config1, reg_config2;
u8 reg_alert_status, reg_alert_mask;
man_id = i2c_smbus_read_byte_data(new_client,
LM63_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM63_REG_CHIP_ID);
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM63_REG_CONFIG1);
reg_config2 = i2c_smbus_read_byte_data(new_client,
LM63_REG_CONFIG2);
reg_alert_status = i2c_smbus_read_byte_data(new_client,
LM63_REG_ALERT_STATUS);
reg_alert_mask = i2c_smbus_read_byte_data(new_client,
LM63_REG_ALERT_MASK);
if (man_id == 0x01 /* National Semiconductor */
&& chip_id == 0x41 /* LM63 */
&& (reg_config1 & 0x18) == 0x00
&& (reg_config2 & 0xF8) == 0x00
&& (reg_alert_status & 0x20) == 0x00
&& (reg_alert_mask & 0xA4) == 0xA4) {
kind = lm63;
} else { /* failed */
dev_dbg(&adapter->dev, "Unsupported chip "
"(man_id=0x%02X, chip_id=0x%02X).\n",
man_id, chip_id);
goto exit_free;
}
}
strlcpy(new_client->name, "lm63", I2C_NAME_SIZE);
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 LM63 chip */
lm63_init_client(new_client);
/* Register sysfs hooks */
if (data->config & 0x04) { /* tachometer enabled */
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
}
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
/* Idealy we shouldn't have to initialize anything, since the BIOS
should have taken care of everything */
static void lm63_init_client(struct i2c_client *client)
{
struct lm63_data *data = i2c_get_clientdata(client);
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
data->config_fan = i2c_smbus_read_byte_data(client,
LM63_REG_CONFIG_FAN);
/* Start converting if needed */
if (data->config & 0x40) { /* standby */
dev_dbg(&client->dev, "Switching to operational mode");
data->config &= 0xA7;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
data->config);
}
/* We may need pwm1_freq before ever updating the client data */
data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
/* Show some debug info about the LM63 configuration */
dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
(data->config & 0x04) ? "tachometer input" :
"alert output");
dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
(data->config_fan & 0x04) ? "1.4" : "360",
((data->config_fan & 0x04) ? 700 : 180000) / data->pwm1_freq);
dev_dbg(&client->dev, "PWM output active %s, %s mode\n",
(data->config_fan & 0x10) ? "low" : "high",
(data->config_fan & 0x20) ? "manual" : "auto");
}
static int lm63_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(i2c_get_clientdata(client));
return 0;
}
static struct lm63_data *lm63_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ) ||
(jiffies < data->last_updated) ||
!data->valid) {
if (data->config & 0x04) { /* tachometer enabled */
/* order matters for fan1_input */
data->fan1_input = i2c_smbus_read_byte_data(client,
LM63_REG_TACH_COUNT_LSB) & 0xFC;
data->fan1_input |= i2c_smbus_read_byte_data(client,
LM63_REG_TACH_COUNT_MSB) << 8;
data->fan1_low = (i2c_smbus_read_byte_data(client,
LM63_REG_TACH_LIMIT_LSB) & 0xFC)
| (i2c_smbus_read_byte_data(client,
LM63_REG_TACH_LIMIT_MSB) << 8);
}
data->pwm1_freq = i2c_smbus_read_byte_data(client,
LM63_REG_PWM_FREQ);
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
data->pwm1_value = i2c_smbus_read_byte_data(client,
LM63_REG_PWM_VALUE);
data->temp1_input = i2c_smbus_read_byte_data(client,
LM63_REG_LOCAL_TEMP);
data->temp1_high = i2c_smbus_read_byte_data(client,
LM63_REG_LOCAL_HIGH);
/* order matters for temp2_input */
data->temp2_input = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TEMP_MSB) << 8;
data->temp2_input |= i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TEMP_LSB);
data->temp2_high = (i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_HIGH_MSB) << 8)
| i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_HIGH_LSB);
data->temp2_low = (i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_LOW_MSB) << 8)
| i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_LOW_LSB);
data->temp2_crit = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TCRIT);
data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TCRIT_HYST);
data->alarms = i2c_smbus_read_byte_data(client,
LM63_REG_ALERT_STATUS) & 0x7F;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm63_init(void)
{
return i2c_add_driver(&lm63_driver);
}
static void __exit sensors_lm63_exit(void)
{
i2c_del_driver(&lm63_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM63 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm63_init);
module_exit(sensors_lm63_exit);
...@@ -28,10 +28,9 @@ ...@@ -28,10 +28,9 @@
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
static unsigned short normal_i2c_range[] = { 0x48, 0x4f, I2C_CLIENT_END }; 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(lm75); SENSORS_INSMOD_1(lm75);
......
...@@ -34,10 +34,8 @@ ...@@ -34,10 +34,8 @@
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x48, 0x4b, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(lm77); SENSORS_INSMOD_1(lm77);
......
...@@ -27,10 +27,11 @@ ...@@ -27,10 +27,11 @@
#include <asm/io.h> #include <asm/io.h>
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24,
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END }; 0x25, 0x26, 0x27, 0x28, 0x29,
0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_3(lm78, lm78j, lm79); SENSORS_INSMOD_3(lm78, lm78j, lm79);
......
...@@ -29,10 +29,9 @@ ...@@ -29,10 +29,9 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c,
static unsigned short normal_i2c_range[] = { 0x28, 0x2f, I2C_CLIENT_END }; 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(lm80); SENSORS_INSMOD_1(lm80);
......
...@@ -40,11 +40,11 @@ ...@@ -40,11 +40,11 @@
* addresses. * addresses.
*/ */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, 0x29, 0x2a, 0x2b,
0x4c, 0x4e, I2C_CLIENT_END }; 0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
Chip details at <http://www.national.com/ds/LM/LM85.pdf> Chip details at <http://www.national.com/ds/LM/LM85.pdf>
...@@ -29,17 +30,10 @@ ...@@ -29,17 +30,10 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h> #include <linux/i2c-vid.h>
/*
#include <asm/io.h>
*/
#undef LM85EXTENDEDFUNC /* Extended functionality */
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 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[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_4(lm85b, lm85c, adm1027, adt7463); SENSORS_INSMOD_4(lm85b, lm85c, adm1027, adt7463);
...@@ -163,8 +157,6 @@ static int lm85_scaling[] = { /* .001 Volts */ ...@@ -163,8 +157,6 @@ static int lm85_scaling[] = { /* .001 Volts */
#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03) #define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03)
#ifdef LM85EXTENDEDFUNC /* Extended functionality */
/* ZONEs have the following parameters: /* ZONEs have the following parameters:
* Limit (low) temp, 1. degC * Limit (low) temp, 1. degC
* Hysteresis (below limit), 1. degC (0-15) * Hysteresis (below limit), 1. degC (0-15)
...@@ -183,20 +175,32 @@ static int lm85_scaling[] = { /* .001 Volts */ ...@@ -183,20 +175,32 @@ static int lm85_scaling[] = { /* .001 Volts */
* Filter constant (or disabled) .1 seconds * Filter constant (or disabled) .1 seconds
*/ */
/* These are the zone temperature range encodings */ /* These are the zone temperature range encodings in .001 degree C */
static int lm85_range_map[] = { /* .1 degC */ static int lm85_range_map[] = {
20, 25, 33, 40, 50, 66, 2000, 2500, 3300, 4000, 5000, 6600,
80, 100, 133, 160, 200, 266, 8000, 10000, 13300, 16000, 20000, 26600,
320, 400, 533, 800 32000, 40000, 53300, 80000
}; };
static int RANGE_TO_REG( int range ) static int RANGE_TO_REG( int range )
{ {
int i; int i;
if( range >= lm85_range_map[15] ) { return 15 ; } if ( range < lm85_range_map[0] ) {
for( i = 0 ; i < 15 ; ++i ) return 0 ;
if( range <= lm85_range_map[i] ) } else if ( range > lm85_range_map[15] ) {
break ; return 15 ;
} else { /* find closest match */
for ( i = 14 ; i >= 0 ; --i ) {
if ( range > lm85_range_map[i] ) { /* range bracketed */
if ((lm85_range_map[i+1] - range) <
(range - lm85_range_map[i])) {
i++;
break;
}
break;
}
}
}
return( i & 0x0f ); return( i & 0x0f );
} }
#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f]) #define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f])
...@@ -206,41 +210,9 @@ static int RANGE_TO_REG( int range ) ...@@ -206,41 +210,9 @@ static int RANGE_TO_REG( int range )
* MSB (bit 3, value 8). If the enable bit is 0, the encoded value * MSB (bit 3, value 8). If the enable bit is 0, the encoded value
* is ignored, or set to 0. * is ignored, or set to 0.
*/ */
static int lm85_smooth_map[] = { /* .1 sec */
350, 176, 118, 70, 44, 30, 16, 8
/* 35.4 * 1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48 */
};
static int SMOOTH_TO_REG( int smooth )
{
int i;
if( smooth <= 0 ) { return 0 ; } /* Disabled */
for( i = 0 ; i < 7 ; ++i )
if( smooth >= lm85_smooth_map[i] )
break ;
return( (i & 0x07) | 0x08 );
}
#define SMOOTH_FROM_REG(val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0)
/* These are the fan spinup delay time encodings */
static int lm85_spinup_map[] = { /* .1 sec */
0, 1, 2, 4, 7, 10, 20, 40
};
static int SPINUP_TO_REG( int spinup )
{
int i;
if( spinup >= lm85_spinup_map[7] ) { return 7 ; }
for( i = 0 ; i < 7 ; ++i )
if( spinup <= lm85_spinup_map[i] )
break ;
return( i & 0x07 );
}
#define SPINUP_FROM_REG(val) (lm85_spinup_map[(val)&0x07])
/* These are the PWM frequency encodings */ /* These are the PWM frequency encodings */
static int lm85_freq_map[] = { /* .1 Hz */ static int lm85_freq_map[] = { /* .1 Hz */
100, 150, 230, 300, 380, 470, 620, 980 100, 150, 230, 300, 380, 470, 620, 940
}; };
static int FREQ_TO_REG( int freq ) static int FREQ_TO_REG( int freq )
{ {
...@@ -266,13 +238,9 @@ static int FREQ_TO_REG( int freq ) ...@@ -266,13 +238,9 @@ static int FREQ_TO_REG( int freq )
* -2 -- PWM responds to manual control * -2 -- PWM responds to manual control
*/ */
#endif /* Extended functionality */
static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 }; static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 };
#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07]) #define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07])
#ifdef LM85EXTENDEDFUNC /* Extended functionality */
static int ZONE_TO_REG( int zone ) static int ZONE_TO_REG( int zone )
{ {
int i; int i;
...@@ -285,10 +253,8 @@ static int ZONE_TO_REG( int zone ) ...@@ -285,10 +253,8 @@ static int ZONE_TO_REG( int zone )
return( (i & 0x07)<<5 ); return( (i & 0x07)<<5 );
} }
#endif /* Extended functionality */ #define HYST_TO_REG(val) (SENSORS_LIMIT(((val)+500)/1000,0,15))
#define HYST_FROM_REG(val) ((val)*1000)
#define HYST_TO_REG(val) (SENSORS_LIMIT((-(val)+5)/10,0,15))
#define HYST_FROM_REG(val) (-(val)*10)
#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127)) #define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
#define OFFSET_FROM_REG(val) ((val)*25) #define OFFSET_FROM_REG(val) ((val)*25)
...@@ -338,6 +304,14 @@ struct lm85_zone { ...@@ -338,6 +304,14 @@ struct lm85_zone {
u8 hyst; /* Low limit hysteresis. (0-15) */ u8 hyst; /* Low limit hysteresis. (0-15) */
u8 range; /* Temp range, encoded */ u8 range; /* Temp range, encoded */
s8 critical; /* "All fans ON" temp limit */ s8 critical; /* "All fans ON" temp limit */
u8 off_desired; /* Actual "off" temperature specified. Preserved
* to prevent "drift" as other autofan control
* values change.
*/
u8 max_desired; /* Actual "max" temperature specified. Preserved
* to prevent "drift" as other autofan control
* values change.
*/
}; };
struct lm85_autofan { struct lm85_autofan {
...@@ -448,7 +422,8 @@ static ssize_t set_fan_##offset##_min (struct device *dev, \ ...@@ -448,7 +422,8 @@ static ssize_t set_fan_##offset##_min (struct device *dev, \
{ \ { \
return set_fan_min(dev, buf, count, offset - 1); \ return set_fan_min(dev, buf, count, offset - 1); \
} \ } \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \
NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min); show_fan_##offset##_min, set_fan_##offset##_min);
...@@ -615,7 +590,8 @@ static ssize_t set_in_##offset##_max (struct device *dev, \ ...@@ -615,7 +590,8 @@ static ssize_t set_in_##offset##_max (struct device *dev, \
{ \ { \
return set_in_max(dev, buf, count, offset); \ return set_in_max(dev, buf, count, offset); \
} \ } \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, NULL); \ static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, \
NULL); \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in_##offset##_min, set_in_##offset##_min); \ show_in_##offset##_min, set_in_##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
...@@ -695,7 +671,8 @@ static ssize_t set_temp_##offset##_max (struct device *dev, \ ...@@ -695,7 +671,8 @@ static ssize_t set_temp_##offset##_max (struct device *dev, \
{ \ { \
return set_temp_max(dev, buf, count, offset - 1); \ return set_temp_max(dev, buf, count, offset - 1); \
} \ } \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL); \ static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \
NULL); \
static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min); \ show_temp_##offset##_min, set_temp_##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
...@@ -706,6 +683,330 @@ show_temp_reg(2); ...@@ -706,6 +683,330 @@ show_temp_reg(2);
show_temp_reg(3); show_temp_reg(3);
/* Automatic PWM control */
static ssize_t show_pwm_auto_channels(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", ZONE_FROM_REG(data->autofan[nr].config));
}
static ssize_t set_pwm_auto_channels(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->autofan[nr].config = (data->autofan[nr].config & (~0xe0))
| ZONE_TO_REG(val) ;
lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr),
data->autofan[nr].config);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm_auto_pwm_min(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", PWM_FROM_REG(data->autofan[nr].min_pwm));
}
static ssize_t set_pwm_auto_pwm_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->autofan[nr].min_pwm = PWM_TO_REG(val);
lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr),
data->autofan[nr].min_pwm);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm_auto_pwm_minctl(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", data->autofan[nr].min_off);
}
static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->autofan[nr].min_off = val;
lm85_write_value(client, LM85_REG_AFAN_SPIKE1, data->smooth[0]
| data->syncpwm3
| (data->autofan[0].min_off ? 0x20 : 0)
| (data->autofan[1].min_off ? 0x40 : 0)
| (data->autofan[2].min_off ? 0x80 : 0)
);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm_auto_pwm_freq(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", FREQ_FROM_REG(data->autofan[nr].freq));
}
static ssize_t set_pwm_auto_pwm_freq(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->autofan[nr].freq = FREQ_TO_REG(val);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
(data->zone[nr].range << 4)
| data->autofan[nr].freq
);
up(&data->update_lock);
return count;
}
#define pwm_auto(offset) \
static ssize_t show_pwm##offset##_auto_channels (struct device *dev, \
char *buf) \
{ \
return show_pwm_auto_channels(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_auto_channels (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm_auto_channels(dev, buf, count, offset - 1); \
} \
static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, \
char *buf) \
{ \
return show_pwm_auto_pwm_min(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \
} \
static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, \
char *buf) \
{ \
return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1); \
} \
static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, \
char *buf) \
{ \
return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \
show_pwm##offset##_auto_channels, \
set_pwm##offset##_auto_channels); \
static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \
show_pwm##offset##_auto_pwm_min, \
set_pwm##offset##_auto_pwm_min); \
static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \
show_pwm##offset##_auto_pwm_minctl, \
set_pwm##offset##_auto_pwm_minctl); \
static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \
show_pwm##offset##_auto_pwm_freq, \
set_pwm##offset##_auto_pwm_freq);
pwm_auto(1);
pwm_auto(2);
pwm_auto(3);
/* Temperature settings for automatic PWM control */
static ssize_t show_temp_auto_temp_off(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) -
HYST_FROM_REG(data->zone[nr].hyst));
}
static ssize_t set_temp_auto_temp_off(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int min, val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
min = TEMP_FROM_REG(data->zone[nr].limit);
data->zone[nr].off_desired = TEMP_TO_REG(val);
data->zone[nr].hyst = HYST_TO_REG(min - val);
if ( nr == 0 || nr == 1 ) {
lm85_write_value(client, LM85_REG_AFAN_HYST1,
(data->zone[0].hyst << 4)
| data->zone[1].hyst
);
} else {
lm85_write_value(client, LM85_REG_AFAN_HYST2,
(data->zone[2].hyst << 4)
);
}
up(&data->update_lock);
return count;
}
static ssize_t show_temp_auto_temp_min(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) );
}
static ssize_t set_temp_auto_temp_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->zone[nr].limit = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr),
data->zone[nr].limit);
/* Update temp_auto_max and temp_auto_range */
data->zone[nr].range = RANGE_TO_REG(
TEMP_FROM_REG(data->zone[nr].max_desired) -
TEMP_FROM_REG(data->zone[nr].limit));
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
| (data->autofan[nr].freq & 0x07));
/* Update temp_auto_hyst and temp_auto_off */
data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG(
data->zone[nr].limit) - TEMP_FROM_REG(
data->zone[nr].off_desired));
if ( nr == 0 || nr == 1 ) {
lm85_write_value(client, LM85_REG_AFAN_HYST1,
(data->zone[0].hyst << 4)
| data->zone[1].hyst
);
} else {
lm85_write_value(client, LM85_REG_AFAN_HYST2,
(data->zone[2].hyst << 4)
);
}
up(&data->update_lock);
return count;
}
static ssize_t show_temp_auto_temp_max(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) +
RANGE_FROM_REG(data->zone[nr].range));
}
static ssize_t set_temp_auto_temp_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int min, val;
down(&data->update_lock);
min = TEMP_FROM_REG(data->zone[nr].limit);
val = simple_strtol(buf, NULL, 10);
data->zone[nr].max_desired = TEMP_TO_REG(val);
data->zone[nr].range = RANGE_TO_REG(
val - min);
lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
((data->zone[nr].range & 0x0f) << 4)
| (data->autofan[nr].freq & 0x07));
up(&data->update_lock);
return count;
}
static ssize_t show_temp_auto_temp_crit(struct device *dev, char *buf, int nr)
{
struct lm85_data *data = lm85_update_device(dev);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].critical));
}
static ssize_t set_temp_auto_temp_crit(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->zone[nr].critical = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr),
data->zone[nr].critical);
up(&data->update_lock);
return count;
}
#define temp_auto(offset) \
static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, \
char *buf) \
{ \
return show_temp_auto_temp_off(dev, buf, offset - 1); \
} \
static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_auto_temp_off(dev, buf, count, offset - 1); \
} \
static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, \
char *buf) \
{ \
return show_temp_auto_temp_min(dev, buf, offset - 1); \
} \
static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_auto_temp_min(dev, buf, count, offset - 1); \
} \
static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, \
char *buf) \
{ \
return show_temp_auto_temp_max(dev, buf, offset - 1); \
} \
static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_auto_temp_max(dev, buf, count, offset - 1); \
} \
static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, \
char *buf) \
{ \
return show_temp_auto_temp_crit(dev, buf, offset - 1); \
} \
static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_auto_temp_crit(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \
show_temp##offset##_auto_temp_off, \
set_temp##offset##_auto_temp_off); \
static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \
show_temp##offset##_auto_temp_min, \
set_temp##offset##_auto_temp_min); \
static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \
show_temp##offset##_auto_temp_max, \
set_temp##offset##_auto_temp_max); \
static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \
show_temp##offset##_auto_temp_crit, \
set_temp##offset##_auto_temp_crit);
temp_auto(1);
temp_auto(2);
temp_auto(3);
int lm85_attach_adapter(struct i2c_adapter *adapter) int lm85_attach_adapter(struct i2c_adapter *adapter)
{ {
if (!(adapter->class & I2C_CLASS_HWMON)) if (!(adapter->class & I2C_CLASS_HWMON))
...@@ -879,6 +1180,30 @@ int lm85_detect(struct i2c_adapter *adapter, int address, ...@@ -879,6 +1180,30 @@ int lm85_detect(struct i2c_adapter *adapter, int address,
device_create_file(&new_client->dev, &dev_attr_vrm); device_create_file(&new_client->dev, &dev_attr_vrm);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid); device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
device_create_file(&new_client->dev, &dev_attr_alarms); device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels);
device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels);
device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels);
device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min);
device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min);
device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min);
device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl);
device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl);
device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl);
device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq);
device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq);
device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq);
device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off);
device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off);
device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off);
device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit);
device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit);
return 0; return 0;
...@@ -1162,7 +1487,7 @@ static void __exit sm_lm85_exit(void) ...@@ -1162,7 +1487,7 @@ static void __exit sm_lm85_exit(void)
* post 2.7.0 CVS changes. * post 2.7.0 CVS changes.
*/ */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, Margit Schubert-While <margitsw@t-online.de>"); MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, Margit Schubert-While <margitsw@t-online.de>, Justin Thiessen <jthiessen@penguincomputing.com");
MODULE_DESCRIPTION("LM85-B, LM85-C driver"); MODULE_DESCRIPTION("LM85-B, LM85-C driver");
module_init(sm_lm85_init); module_init(sm_lm85_init);
......
...@@ -65,10 +65,8 @@ ...@@ -65,10 +65,8 @@
* LM87 has three possible addresses: 0x2c, 0x2d and 0x2e. * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
*/ */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -76,9 +76,7 @@ ...@@ -76,9 +76,7 @@
*/ */
static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x4c, 0x4d, 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[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -34,11 +34,11 @@ ...@@ -34,11 +34,11 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, 0x29, 0x2a, 0x2b,
0x4c, 0x4e, I2C_CLIENT_END }; 0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
/*
* pc87360.c - Part of lm_sensors, Linux kernel modules
* for hardware monitoring
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
*
* Copied from smsc47m1.c:
* Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Supports the following chips:
*
* Chip #vin #fan #pwm #temp devid
* PC87360 - 2 2 - 0xE1
* PC87363 - 2 2 - 0xE8
* PC87364 - 3 3 - 0xE4
* PC87365 11 3 3 2 0xE5
* PC87366 11 3 3 3-4 0xE9
*
* This driver assumes that no more than one chip is present, and one of
* the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F).
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
#include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{ NULL }};
static u8 devid;
static unsigned int extra_isa[3];
static u8 confreg[4];
enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 };
static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_isa = normal_isa,
.forces = forces,
};
static int init = 1;
module_param(init, int, 0);
MODULE_PARM_DESC(init,
"Chip initialization level:\n"
" 0: None\n"
"*1: Forcibly enable internal voltage and temperature channels, except in9\n"
" 2: Forcibly enable all voltage and temperature channels, except in9\n"
" 3: Forcibly enable all voltage and temperature channels, including in9");
/*
* Super-I/O registers and operations
*/
#define DEV 0x07 /* Register: Logical device select */
#define DEVID 0x20 /* Register: Device ID */
#define ACT 0x30 /* Register: Device activation */
#define BASE 0x60 /* Register: Base address */
#define FSCM 0x09 /* Logical device: fans */
#define VLM 0x0d /* Logical device: voltages */
#define TMS 0x0e /* Logical device: temperatures */
static const u8 logdev[3] = { FSCM, VLM, TMS };
#define LD_FAN 0
#define LD_IN 1
#define LD_TEMP 2
static inline void superio_outb(int sioaddr, int reg, int val)
{
outb(reg, sioaddr);
outb(val, sioaddr+1);
}
static inline int superio_inb(int sioaddr, int reg)
{
outb(reg, sioaddr);
return inb(sioaddr+1);
}
static inline void superio_exit(int sioaddr)
{
outb(0x02, sioaddr);
outb(0x02, sioaddr+1);
}
/*
* Logical devices
*/
#define PC87360_EXTENT 0x10
#define PC87365_REG_BANK 0x09
#define NO_BANK 0xff
/*
* Fan registers and conversions
*/
/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */
#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr))
#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr))
#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr))
#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr))
#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr))
#define FAN_FROM_REG(val,div) ((val) == 0 ? 0: \
480000 / ((val)*(div)))
#define FAN_TO_REG(val,div) ((val) <= 100 ? 0 : \
480000 / ((val)*(div)))
#define FAN_DIV_FROM_REG(val) (1 << ((val >> 5) & 0x03))
#define FAN_STATUS_FROM_REG(val) ((val) & 0x07)
#define FAN_CONFIG_MONITOR(val,nr) (((val) >> (2 + nr * 3)) & 1)
#define FAN_CONFIG_CONTROL(val,nr) (((val) >> (3 + nr * 3)) & 1)
#define FAN_CONFIG_INVERT(val,nr) (((val) >> (4 + nr * 3)) & 1)
#define PWM_FROM_REG(val,inv) ((inv) ? 255 - (val) : (val))
static inline u8 PWM_TO_REG(int val, int inv)
{
if (inv)
val = 255 - val;
if (val < 0)
return 0;
if (val > 255)
return 255;
return val;
}
/*
* Voltage registers and conversions
*/
#define PC87365_REG_IN_CONVRATE 0x07
#define PC87365_REG_IN_CONFIG 0x08
#define PC87365_REG_IN 0x0B
#define PC87365_REG_IN_MIN 0x0D
#define PC87365_REG_IN_MAX 0x0C
#define PC87365_REG_IN_STATUS 0x0A
#define PC87365_REG_IN_ALARMS1 0x00
#define PC87365_REG_IN_ALARMS2 0x01
#define PC87365_REG_VID 0x06
#define IN_FROM_REG(val,ref) (((val) * (ref) + 128) / 256)
#define IN_TO_REG(val,ref) ((val) < 0 ? 0 : \
(val)*256 >= (ref)*255 ? 255: \
((val) * 256 + (ref)/2) / (ref))
/*
* Temperature registers and conversions
*/
#define PC87365_REG_TEMP_CONFIG 0x08
#define PC87365_REG_TEMP 0x0B
#define PC87365_REG_TEMP_MIN 0x0D
#define PC87365_REG_TEMP_MAX 0x0C
#define PC87365_REG_TEMP_CRIT 0x0E
#define PC87365_REG_TEMP_STATUS 0x0A
#define PC87365_REG_TEMP_ALARMS 0x00
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \
(val) > 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
/*
* Client data (each client gets its own)
*/
struct pc87360_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
int address[3];
u8 fannr, innr, tempnr;
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 fan_status[3]; /* Register value */
u8 pwm[3]; /* Register value */
u16 fan_conf; /* Configuration register values, combined */
u16 in_vref; /* 1 mV/bit */
u8 in[14]; /* Register value */
u8 in_min[14]; /* Register value */
u8 in_max[14]; /* Register value */
u8 in_crit[3]; /* Register value */
u8 in_status[14]; /* Register value */
u16 in_alarms; /* Register values, combined, masked */
u8 vid_conf; /* Configuration register value */
u8 vrm;
u8 vid; /* Register value */
s8 temp[3]; /* Register value */
s8 temp_min[3]; /* Register value */
s8 temp_max[3]; /* Register value */
s8 temp_crit[3]; /* Register value */
u8 temp_status[3]; /* Register value */
u8 temp_alarms; /* Register value, masked */
};
/*
* Functions declaration
*/
static int pc87360_attach_adapter(struct i2c_adapter *adapter);
static int pc87360_detect(struct i2c_adapter *adapter, int address, int kind);
static int pc87360_detach_client(struct i2c_client *client);
static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
u8 reg);
static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
u8 reg, u8 value);
static void pc87360_init_client(struct i2c_client *client, int use_thermistors);
static struct pc87360_data *pc87360_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver pc87360_driver = {
.owner = THIS_MODULE,
.name = "pc87360",
.flags = I2C_DF_NOTIFY,
.attach_adapter = pc87360_attach_adapter,
.detach_client = pc87360_detach_client,
};
/*
* Sysfs stuff
*/
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct pc87360_data *data = i2c_get_clientdata(client);
long fan_min = simple_strtol(buf, NULL, 10);
fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[nr]));
/* If it wouldn't fit, change clock divisor */
while (fan_min > 255
&& (data->fan_status[nr] & 0x60) != 0x60) {
fan_min >>= 1;
data->fan[nr] >>= 1;
data->fan_status[nr] += 0x20;
}
data->fan_min[nr] = fan_min > 255 ? 255 : fan_min;
pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(nr),
data->fan_min[nr]);
/* Write new divider, preserve alarm bits */
pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(nr),
data->fan_status[nr] & 0xF9);
return count;
}
#define show_and_set_fan(offset) \
static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[offset-1], \
FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \
} \
static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[offset-1], \
FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \
} \
static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", \
FAN_DIV_FROM_REG(data->fan_status[offset-1])); \
} \
static ssize_t show_fan##offset##_status(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", \
FAN_STATUS_FROM_REG(data->fan_status[offset-1])); \
} \
static ssize_t set_fan##offset##_min(struct device *dev, const char *buf, \
size_t count) \
{ \
return set_fan_min(dev, buf, count, offset-1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
show_fan##offset##_input, NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IWUSR | S_IRUGO, \
show_fan##offset##_min, set_fan##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \
show_fan##offset##_div, NULL); \
static DEVICE_ATTR(fan##offset##_status, S_IRUGO, \
show_fan##offset##_status, NULL);
show_and_set_fan(1)
show_and_set_fan(2)
show_and_set_fan(3)
#define show_and_set_pwm(offset) \
static ssize_t show_pwm##offset(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", \
PWM_FROM_REG(data->pwm[offset-1], \
FAN_CONFIG_INVERT(data->fan_conf, \
offset-1))); \
} \
static ssize_t set_pwm##offset(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->pwm[offset-1] = PWM_TO_REG(val, \
FAN_CONFIG_INVERT(data->fan_conf, offset-1)); \
pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(offset-1), \
data->pwm[offset-1]); \
return count; \
} \
static DEVICE_ATTR(pwm##offset, S_IWUSR | S_IRUGO, \
show_pwm##offset, set_pwm##offset);
show_and_set_pwm(1)
show_and_set_pwm(2)
show_and_set_pwm(3)
#define show_and_set_in(offset) \
static ssize_t show_in##offset##_input(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
data->in_vref)); \
} \
static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
data->in_vref)); \
} \
static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
data->in_vref)); \
} \
static ssize_t show_in##offset##_status(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", data->in_status[offset]); \
} \
static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->in_min[offset] = IN_TO_REG(val, data->in_vref); \
pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MIN, \
data->in_min[offset]); \
return count; \
} \
static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->in_max[offset] = IN_TO_REG(val, \
data->in_vref); \
pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MAX, \
data->in_max[offset]); \
return count; \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset##_input, NULL); \
static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \
show_in##offset##_max, set_in##offset##_max); \
static DEVICE_ATTR(in##offset##_status, S_IRUGO, \
show_in##offset##_status, NULL);
show_and_set_in(0)
show_and_set_in(1)
show_and_set_in(2)
show_and_set_in(3)
show_and_set_in(4)
show_and_set_in(5)
show_and_set_in(6)
show_and_set_in(7)
show_and_set_in(8)
show_and_set_in(9)
show_and_set_in(10)
#define show_and_set_therm(offset) \
static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset+7], \
data->in_vref)); \
} \
static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset+7], \
data->in_vref)); \
} \
static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset+7], \
data->in_vref)); \
} \
static ssize_t show_temp##offset##_crit(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[offset-4], \
data->in_vref)); \
} \
static ssize_t show_temp##offset##_status(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%u\n", data->in_status[offset+7]); \
} \
static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->in_min[offset+7] = IN_TO_REG(val, data->in_vref); \
pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MIN, \
data->in_min[offset+7]); \
return count; \
} \
static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->in_max[offset+7] = IN_TO_REG(val, data->in_vref); \
pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MAX, \
data->in_max[offset+7]); \
return count; \
} \
static ssize_t set_temp##offset##_crit(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->in_crit[offset-4] = IN_TO_REG(val, data->in_vref); \
pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_CRIT, \
data->in_crit[offset-4]); \
return count; \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
show_temp##offset##_input, NULL); \
static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
show_temp##offset##_min, set_temp##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
show_temp##offset##_max, set_temp##offset##_max); \
static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \
show_temp##offset##_crit, set_temp##offset##_crit); \
static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \
show_temp##offset##_status, NULL);
show_and_set_therm(4)
show_and_set_therm(5)
show_and_set_therm(6)
static ssize_t show_vid(struct device *dev, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
static ssize_t show_vrm(struct device *dev, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", data->vrm);
}
static ssize_t set_vrm(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct pc87360_data *data = i2c_get_clientdata(client);
data->vrm = simple_strtoul(buf, NULL, 10);
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
static ssize_t show_in_alarms(struct device *dev, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", data->in_alarms);
}
static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
#define show_and_set_temp(offset) \
static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
} \
static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \
} \
static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
}\
static ssize_t show_temp##offset##_crit(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[offset-1])); \
}\
static ssize_t show_temp##offset##_status(struct device *dev, char *buf) \
{ \
struct pc87360_data *data = pc87360_update_device(dev); \
return sprintf(buf, "%d\n", data->temp_status[offset-1]); \
}\
static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->temp_min[offset-1] = TEMP_TO_REG(val); \
pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MIN, \
data->temp_min[offset-1]); \
return count; \
} \
static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->temp_max[offset-1] = TEMP_TO_REG(val); \
pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MAX, \
data->temp_max[offset-1]); \
return count; \
} \
static ssize_t set_temp##offset##_crit(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct pc87360_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
data->temp_crit[offset-1] = TEMP_TO_REG(val); \
pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_CRIT, \
data->temp_crit[offset-1]); \
return count; \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
show_temp##offset##_input, NULL); \
static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
show_temp##offset##_min, set_temp##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
show_temp##offset##_max, set_temp##offset##_max); \
static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \
show_temp##offset##_crit, set_temp##offset##_crit); \
static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \
show_temp##offset##_status, NULL);
show_and_set_temp(1)
show_and_set_temp(2)
show_and_set_temp(3)
static ssize_t show_temp_alarms(struct device *dev, char *buf)
{
struct pc87360_data *data = pc87360_update_device(dev);
return sprintf(buf, "%u\n", data->temp_alarms);
}
static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL);
/*
* Device detection, registration and update
*/
static int pc87360_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, pc87360_detect);
}
static int pc87360_find(int sioaddr, u8 *devid, int *address)
{
u16 val;
int i;
int nrdev; /* logical device count */
/* No superio_enter */
/* Identify device */
val = superio_inb(sioaddr, DEVID);
switch (val) {
case 0xE1: /* PC87360 */
case 0xE8: /* PC87363 */
case 0xE4: /* PC87364 */
nrdev = 1;
break;
case 0xE5: /* PC87365 */
case 0xE9: /* PC87366 */
nrdev = 3;
break;
default:
superio_exit(sioaddr);
return -ENODEV;
}
/* Remember the device id */
*devid = val;
for (i = 0; i < nrdev; i++) {
/* select logical device */
superio_outb(sioaddr, DEV, logdev[i]);
val = superio_inb(sioaddr, ACT);
if (!(val & 0x01)) {
printk(KERN_INFO "pc87360: Device 0x%02x not "
"activated\n", logdev[i]);
continue;
}
val = (superio_inb(sioaddr, BASE) << 8)
| superio_inb(sioaddr, BASE + 1);
if (!val) {
printk(KERN_INFO "pc87360: Base address not set for "
"device 0x%02x\n", logdev[i]);
continue;
}
address[i] = val;
if (i==0) { /* Fans */
confreg[0] = superio_inb(sioaddr, 0xF0);
confreg[1] = superio_inb(sioaddr, 0xF1);
#ifdef DEBUG
printk(KERN_DEBUG "pc87360: Fan 1: mon=%d "
"ctrl=%d inv=%d\n", (confreg[0]>>2)&1,
(confreg[0]>>3)&1, (confreg[0]>>4)&1);
printk(KERN_DEBUG "pc87360: Fan 2: mon=%d "
"ctrl=%d inv=%d\n", (confreg[0]>>5)&1,
(confreg[0]>>6)&1, (confreg[0]>>7)&1);
printk(KERN_DEBUG "pc87360: Fan 3: mon=%d "
"ctrl=%d inv=%d\n", confreg[1]&1,
(confreg[1]>>1)&1, (confreg[1]>>2)&1);
#endif
} else if (i==1) { /* Voltages */
/* Are we using thermistors? */
if (*devid == 0xE9) { /* PC87366 */
/* These registers are not logical-device
specific, just that we won't need them if
we don't use the VLM device */
confreg[2] = superio_inb(sioaddr, 0x2B);
confreg[3] = superio_inb(sioaddr, 0x25);
if (confreg[2] & 0x40) {
printk(KERN_INFO "pc87360: Using "
"thermistors for temperature "
"monitoring\n");
}
if (confreg[3] & 0xE0) {
printk(KERN_INFO "pc87360: VID "
"inputs routed (mode %u)\n",
confreg[3] >> 5);
}
}
}
}
superio_exit(sioaddr);
return 0;
}
/* We don't really care about the address.
Read from extra_isa instead. */
int pc87360_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct pc87360_data *data;
int err = 0;
const char *name = "pc87360";
int use_thermistors = 0;
if (!i2c_is_isa_adapter(adapter))
return -ENODEV;
if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL)))
return -ENOMEM;
memset(data, 0x00, sizeof(struct pc87360_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
init_MUTEX(&data->lock);
new_client->adapter = adapter;
new_client->driver = &pc87360_driver;
new_client->flags = 0;
data->fannr = 2;
data->innr = 0;
data->tempnr = 0;
switch (devid) {
case 0xe8:
name = "pc87363";
break;
case 0xe4:
name = "pc87364";
data->fannr = 3;
break;
case 0xe5:
name = "pc87365";
data->fannr = extra_isa[0] ? 3 : 0;
data->innr = extra_isa[1] ? 11 : 0;
data->tempnr = extra_isa[2] ? 2 : 0;
break;
case 0xe9:
name = "pc87366";
data->fannr = extra_isa[0] ? 3 : 0;
data->innr = extra_isa[1] ? 14 : 0;
data->tempnr = extra_isa[2] ? 3 : 0;
break;
}
strcpy(new_client->name, name);
data->valid = 0;
init_MUTEX(&data->update_lock);
for (i = 0; i < 3; i++) {
if (((data->address[i] = extra_isa[i]))
&& !request_region(extra_isa[i], PC87360_EXTENT, "pc87360")) {
dev_err(&new_client->dev, "Region 0x%x-0x%x already "
"in use!\n", extra_isa[i],
extra_isa[i]+PC87360_EXTENT-1);
for (i--; i >= 0; i--)
release_region(extra_isa[i], PC87360_EXTENT);
err = -EBUSY;
goto ERROR1;
}
}
/* Retrieve the fans configuration from Super-I/O space */
if (data->fannr)
data->fan_conf = confreg[0] | (confreg[1] << 8);
if ((err = i2c_attach_client(new_client)))
goto ERROR2;
/* Use the correct reference voltage
Unless both the VLM and the TMS logical devices agree to
use an external Vref, the internal one is used. */
if (data->innr) {
i = pc87360_read_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONFIG);
if (data->tempnr) {
i &= pc87360_read_value(data, LD_TEMP, NO_BANK,
PC87365_REG_TEMP_CONFIG);
}
data->in_vref = (i&0x02) ? 3025 : 2966;
dev_dbg(&new_client->dev, "Using %s reference voltage\n",
(i&0x02) ? "external" : "internal");
data->vid_conf = confreg[3];
data->vrm = 90;
}
/* Fan clock dividers may be needed before any data is read */
for (i = 0; i < data->fannr; i++) {
data->fan_status[i] = pc87360_read_value(data, LD_FAN,
NO_BANK, PC87360_REG_FAN_STATUS(i));
}
if (init > 0) {
if (devid == 0xe9 && data->address[1]) /* PC87366 */
use_thermistors = confreg[2] & 0x40;
pc87360_init_client(new_client, use_thermistors);
}
/* Register sysfs hooks */
if (data->innr) {
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_in7_input);
device_create_file(&new_client->dev, &dev_attr_in8_input);
device_create_file(&new_client->dev, &dev_attr_in9_input);
device_create_file(&new_client->dev, &dev_attr_in10_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in7_min);
device_create_file(&new_client->dev, &dev_attr_in8_min);
device_create_file(&new_client->dev, &dev_attr_in9_min);
device_create_file(&new_client->dev, &dev_attr_in10_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_in6_max);
device_create_file(&new_client->dev, &dev_attr_in7_max);
device_create_file(&new_client->dev, &dev_attr_in8_max);
device_create_file(&new_client->dev, &dev_attr_in9_max);
device_create_file(&new_client->dev, &dev_attr_in10_max);
device_create_file(&new_client->dev, &dev_attr_in0_status);
device_create_file(&new_client->dev, &dev_attr_in1_status);
device_create_file(&new_client->dev, &dev_attr_in2_status);
device_create_file(&new_client->dev, &dev_attr_in3_status);
device_create_file(&new_client->dev, &dev_attr_in4_status);
device_create_file(&new_client->dev, &dev_attr_in5_status);
device_create_file(&new_client->dev, &dev_attr_in6_status);
device_create_file(&new_client->dev, &dev_attr_in7_status);
device_create_file(&new_client->dev, &dev_attr_in8_status);
device_create_file(&new_client->dev, &dev_attr_in9_status);
device_create_file(&new_client->dev, &dev_attr_in10_status);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
device_create_file(&new_client->dev, &dev_attr_vrm);
device_create_file(&new_client->dev, &dev_attr_alarms_in);
}
if (data->tempnr) {
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_status);
device_create_file(&new_client->dev, &dev_attr_temp2_status);
device_create_file(&new_client->dev, &dev_attr_alarms_temp);
}
if (data->tempnr == 3) {
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_min);
device_create_file(&new_client->dev, &dev_attr_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp3_crit);
device_create_file(&new_client->dev, &dev_attr_temp3_status);
}
if (data->innr == 14) {
device_create_file(&new_client->dev, &dev_attr_temp4_input);
device_create_file(&new_client->dev, &dev_attr_temp5_input);
device_create_file(&new_client->dev, &dev_attr_temp6_input);
device_create_file(&new_client->dev, &dev_attr_temp4_min);
device_create_file(&new_client->dev, &dev_attr_temp5_min);
device_create_file(&new_client->dev, &dev_attr_temp6_min);
device_create_file(&new_client->dev, &dev_attr_temp4_max);
device_create_file(&new_client->dev, &dev_attr_temp5_max);
device_create_file(&new_client->dev, &dev_attr_temp6_max);
device_create_file(&new_client->dev, &dev_attr_temp4_crit);
device_create_file(&new_client->dev, &dev_attr_temp5_crit);
device_create_file(&new_client->dev, &dev_attr_temp6_crit);
device_create_file(&new_client->dev, &dev_attr_temp4_status);
device_create_file(&new_client->dev, &dev_attr_temp5_status);
device_create_file(&new_client->dev, &dev_attr_temp6_status);
}
if (data->fannr) {
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_fan1_status);
device_create_file(&new_client->dev, &dev_attr_fan2_status);
if (FAN_CONFIG_CONTROL(data->fan_conf, 0))
device_create_file(&new_client->dev, &dev_attr_pwm1);
if (FAN_CONFIG_CONTROL(data->fan_conf, 1))
device_create_file(&new_client->dev, &dev_attr_pwm2);
}
if (data->fannr == 3) {
device_create_file(&new_client->dev, &dev_attr_fan3_input);
device_create_file(&new_client->dev, &dev_attr_fan3_min);
device_create_file(&new_client->dev, &dev_attr_fan3_div);
device_create_file(&new_client->dev, &dev_attr_fan3_status);
if (FAN_CONFIG_CONTROL(data->fan_conf, 2))
device_create_file(&new_client->dev, &dev_attr_pwm3);
}
return 0;
ERROR2:
for (i = 0; i < 3; i++) {
if (data->address[i]) {
release_region(data->address[i], PC87360_EXTENT);
}
}
ERROR1:
kfree(data);
return err;
}
static int pc87360_detach_client(struct i2c_client *client)
{
struct pc87360_data *data = i2c_get_clientdata(client);
int i;
if ((i = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return i;
}
for (i = 0; i < 3; i++) {
if (data->address[i]) {
release_region(data->address[i], PC87360_EXTENT);
}
}
kfree(data);
return 0;
}
/* ldi is the logical device index
bank is for voltages and temperatures only */
static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
u8 reg)
{
int res;
down(&(data->lock));
if (bank != NO_BANK)
outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
res = inb_p(data->address[ldi] + reg);
up(&(data->lock));
return res;
}
static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
u8 reg, u8 value)
{
down(&(data->lock));
if (bank != NO_BANK)
outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
outb_p(value, data->address[ldi] + reg);
up(&(data->lock));
}
static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
{
struct pc87360_data *data = i2c_get_clientdata(client);
int i, nr;
const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 };
const u8 init_temp[3] = { 2, 2, 1 };
u8 reg;
if (init >= 2 && data->innr) {
reg = pc87360_read_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONVRATE);
dev_info(&client->dev, "VLM conversion set to"
"1s period, 160us delay\n");
pc87360_write_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONVRATE,
(reg & 0xC0) | 0x11);
}
nr = data->innr < 11 ? data->innr : 11;
for (i=0; i<nr; i++) {
if (init >= init_in[i]) {
/* Forcibly enable voltage channel */
reg = pc87360_read_value(data, LD_IN, i,
PC87365_REG_IN_STATUS);
if (!(reg & 0x01)) {
dev_dbg(&client->dev, "Forcibly "
"enabling in%d\n", i);
pc87360_write_value(data, LD_IN, i,
PC87365_REG_IN_STATUS,
(reg & 0x68) | 0x87);
}
}
}
/* We can't blindly trust the Super-I/O space configuration bit,
most BIOS won't set it properly */
for (i=11; i<data->innr; i++) {
reg = pc87360_read_value(data, LD_IN, i,
PC87365_REG_TEMP_STATUS);
use_thermistors = use_thermistors || (reg & 0x01);
}
i = use_thermistors ? 2 : 0;
for (; i<data->tempnr; i++) {
if (init >= init_temp[i]) {
/* Forcibly enable temperature channel */
reg = pc87360_read_value(data, LD_TEMP, i,
PC87365_REG_TEMP_STATUS);
if (!(reg & 0x01)) {
dev_dbg(&client->dev, "Forcibly "
"enabling temp%d\n", i+1);
pc87360_write_value(data, LD_TEMP, i,
PC87365_REG_TEMP_STATUS,
0xCF);
}
}
}
if (use_thermistors) {
for (i=11; i<data->innr; i++) {
if (init >= init_in[i]) {
/* The pin may already be used by thermal
diodes */
reg = pc87360_read_value(data, LD_TEMP,
(i-11)/2, PC87365_REG_TEMP_STATUS);
if (reg & 0x01) {
dev_dbg(&client->dev, "Skipping "
"temp%d, pin already in use "
"by temp%d\n", i-7, (i-11)/2);
continue;
}
/* Forcibly enable thermistor channel */
reg = pc87360_read_value(data, LD_IN, i,
PC87365_REG_IN_STATUS);
if (!(reg & 0x01)) {
dev_dbg(&client->dev, "Forcibly "
"enabling temp%d\n", i-7);
pc87360_write_value(data, LD_IN, i,
PC87365_REG_TEMP_STATUS,
(reg & 0x60) | 0x8F);
}
}
}
}
if (data->innr) {
reg = pc87360_read_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONFIG);
if (reg & 0x01) {
dev_dbg(&client->dev, "Forcibly "
"enabling monitoring (VLM)\n");
pc87360_write_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONFIG,
reg & 0xFE);
}
}
if (data->tempnr) {
reg = pc87360_read_value(data, LD_TEMP, NO_BANK,
PC87365_REG_TEMP_CONFIG);
if (reg & 0x01) {
dev_dbg(&client->dev, "Forcibly enabling "
"monitoring (TMS)\n");
pc87360_write_value(data, LD_TEMP, NO_BANK,
PC87365_REG_TEMP_CONFIG,
reg & 0xFE);
}
if (init >= 2) {
/* Chip config as documented by National Semi. */
pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08);
/* We voluntarily omit the bank here, in case the
sequence itself matters. It shouldn't be a problem,
since nobody else is supposed to access the
device at that point. */
pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04);
pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35);
pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05);
pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05);
}
}
}
static void pc87360_autodiv(struct i2c_client *client, int nr)
{
struct pc87360_data *data = i2c_get_clientdata(client);
u8 old_min = data->fan_min[nr];
/* Increase clock divider if needed and possible */
if ((data->fan_status[nr] & 0x04) /* overflow flag */
|| (data->fan[nr] >= 224)) { /* next to overflow */
if ((data->fan_status[nr] & 0x60) != 0x60) {
data->fan_status[nr] += 0x20;
data->fan_min[nr] >>= 1;
data->fan[nr] >>= 1;
dev_dbg(&client->dev, "Increasing "
"clock divider to %d for fan %d\n",
FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1);
}
} else {
/* Decrease clock divider if possible */
while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */
&& data->fan[nr] < 85 /* bad accuracy */
&& (data->fan_status[nr] & 0x60) != 0x00) {
data->fan_status[nr] -= 0x20;
data->fan_min[nr] <<= 1;
data->fan[nr] <<= 1;
dev_dbg(&client->dev, "Decreasing "
"clock divider to %d for fan %d\n",
FAN_DIV_FROM_REG(data->fan_status[nr]),
nr+1);
}
}
/* Write new fan min if it changed */
if (old_min != data->fan_min[nr]) {
pc87360_write_value(data, LD_FAN, NO_BANK,
PC87360_REG_FAN_MIN(nr),
data->fan_min[nr]);
}
}
static struct pc87360_data *pc87360_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct pc87360_data *data = i2c_get_clientdata(client);
u8 i;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ * 2)
|| (jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Data update\n");
/* Fans */
for (i = 0; i < data->fannr; i++) {
if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
data->fan_status[i] =
pc87360_read_value(data, LD_FAN,
NO_BANK, PC87360_REG_FAN_STATUS(i));
data->fan[i] = pc87360_read_value(data, LD_FAN,
NO_BANK, PC87360_REG_FAN(i));
data->fan_min[i] = pc87360_read_value(data,
LD_FAN, NO_BANK,
PC87360_REG_FAN_MIN(i));
/* Change clock divider if needed */
pc87360_autodiv(client, i);
/* Clear bits and write new divider */
pc87360_write_value(data, LD_FAN, NO_BANK,
PC87360_REG_FAN_STATUS(i),
data->fan_status[i]);
}
if (FAN_CONFIG_CONTROL(data->fan_conf, i))
data->pwm[i] = pc87360_read_value(data, LD_FAN,
NO_BANK, PC87360_REG_PWM(i));
}
/* Voltages */
for (i = 0; i < data->innr; i++) {
data->in_status[i] = pc87360_read_value(data, LD_IN, i,
PC87365_REG_IN_STATUS);
/* Clear bits */
pc87360_write_value(data, LD_IN, i,
PC87365_REG_IN_STATUS,
data->in_status[i]);
if ((data->in_status[i] & 0x81) == 0x81) {
data->in[i] = pc87360_read_value(data, LD_IN,
i, PC87365_REG_IN);
}
if (data->in_status[i] & 0x01) {
data->in_min[i] = pc87360_read_value(data,
LD_IN, i,
PC87365_REG_IN_MIN);
data->in_max[i] = pc87360_read_value(data,
LD_IN, i,
PC87365_REG_IN_MAX);
if (i >= 11)
data->in_crit[i-11] =
pc87360_read_value(data, LD_IN,
i, PC87365_REG_TEMP_CRIT);
}
}
if (data->innr) {
data->in_alarms = pc87360_read_value(data, LD_IN,
NO_BANK, PC87365_REG_IN_ALARMS1)
| ((pc87360_read_value(data, LD_IN,
NO_BANK, PC87365_REG_IN_ALARMS2)
& 0x07) << 8);
data->vid = (data->vid_conf & 0xE0) ?
pc87360_read_value(data, LD_IN,
NO_BANK, PC87365_REG_VID) : 0x1F;
}
/* Temperatures */
for (i = 0; i < data->tempnr; i++) {
data->temp_status[i] = pc87360_read_value(data,
LD_TEMP, i,
PC87365_REG_TEMP_STATUS);
/* Clear bits */
pc87360_write_value(data, LD_TEMP, i,
PC87365_REG_TEMP_STATUS,
data->temp_status[i]);
if ((data->temp_status[i] & 0x81) == 0x81) {
data->temp[i] = pc87360_read_value(data,
LD_TEMP, i,
PC87365_REG_TEMP);
}
if (data->temp_status[i] & 0x01) {
data->temp_min[i] = pc87360_read_value(data,
LD_TEMP, i,
PC87365_REG_TEMP_MIN);
data->temp_max[i] = pc87360_read_value(data,
LD_TEMP, i,
PC87365_REG_TEMP_MAX);
data->temp_crit[i] = pc87360_read_value(data,
LD_TEMP, i,
PC87365_REG_TEMP_CRIT);
}
}
if (data->tempnr) {
data->temp_alarms = pc87360_read_value(data, LD_TEMP,
NO_BANK, PC87365_REG_TEMP_ALARMS)
& 0x3F;
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init pc87360_init(void)
{
int i;
if (pc87360_find(0x2e, &devid, extra_isa)
&& pc87360_find(0x4e, &devid, extra_isa)) {
printk(KERN_WARNING "pc87360: PC8736x not detected, "
"module not inserted.\n");
return -ENODEV;
}
/* Arbitrarily pick one of the addresses */
for (i = 0; i < 3; i++) {
if (extra_isa[i] != 0x0000) {
normal_isa[0] = extra_isa[i];
break;
}
}
if (normal_isa[0] == 0x0000) {
printk(KERN_WARNING "pc87360: No active logical device, "
"module not inserted.\n");
return -ENODEV;
}
return i2c_add_driver(&pc87360_driver);
}
static void __exit pc87360_exit(void)
{
i2c_del_driver(&pc87360_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("PC8736x hardware monitor");
MODULE_LICENSE("GPL");
module_init(pc87360_init);
module_exit(pc87360_exit);
...@@ -42,10 +42,10 @@ ...@@ -42,10 +42,10 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
static unsigned short normal_i2c_range[] = { 0x20, 0x27, 0x38, 0x3f, I2C_CLIENT_END }; 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_2(pcf8574, pcf8574a); SENSORS_INSMOD_2(pcf8574, pcf8574a);
......
...@@ -27,10 +27,9 @@ ...@@ -27,10 +27,9 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
static unsigned short normal_i2c_range[] = { 0x48, 0x4f, I2C_CLIENT_END }; 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(pcf8591); SENSORS_INSMOD_1(pcf8591);
......
...@@ -66,11 +66,8 @@ static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END }; ...@@ -66,11 +66,8 @@ static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = { static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr, .normal_i2c = normal_addr,
.normal_i2c_range = ignore,
.probe = ignore, .probe = ignore,
.probe_range = ignore,
.ignore = ignore, .ignore = ignore,
.ignore_range = ignore,
.force = ignore, .force = ignore,
}; };
......
...@@ -34,22 +34,14 @@ ...@@ -34,22 +34,14 @@
#include <asm/io.h> #include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
/* Address is autodetected, there is no default value */ /* Address is autodetected, there is no default value */
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{NULL}}; static struct i2c_force_data forces[] = {{NULL}};
enum chips { any_chip, smsc47m1 }; enum chips { any_chip, smsc47m1 };
static struct i2c_address_data addr_data = { static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c, .normal_i2c = normal_i2c,
.normal_i2c_range = normal_i2c_range,
.normal_isa = normal_isa, .normal_isa = normal_isa,
.normal_isa_range = normal_isa_range,
.probe = normal_i2c, /* cheat */
.probe_range = normal_i2c_range, /* cheat */
.ignore = normal_i2c, /* cheat */
.ignore_range = normal_i2c_range, /* cheat */
.forces = forces, .forces = forces,
}; };
......
...@@ -52,9 +52,7 @@ MODULE_PARM_DESC(force_addr, ...@@ -52,9 +52,7 @@ MODULE_PARM_DESC(force_addr,
Note that we can't determine the ISA address until we have initialized Note that we can't determine the ISA address until we have initialized
our module */ our module */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_1(via686a); SENSORS_INSMOD_1(via686a);
......
...@@ -57,9 +57,7 @@ MODULE_PARM_DESC(force_i2c, ...@@ -57,9 +57,7 @@ MODULE_PARM_DESC(force_i2c,
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf); SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf);
......
...@@ -49,10 +49,10 @@ ...@@ -49,10 +49,10 @@
#define W83781D_RT 1 #define W83781D_RT 1
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END }; 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */ /* Insmod parameters */
SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf); SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf);
......
...@@ -47,9 +47,7 @@ ...@@ -47,9 +47,7 @@
*/ */
static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; static unsigned short normal_i2c[] = { 0x2e, 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[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* /*
* Insmod parameters * Insmod parameters
......
...@@ -177,12 +177,25 @@ int i2c_add_adapter(struct i2c_adapter *adap) ...@@ -177,12 +177,25 @@ int i2c_add_adapter(struct i2c_adapter *adap)
int i2c_del_adapter(struct i2c_adapter *adap) int i2c_del_adapter(struct i2c_adapter *adap)
{ {
struct list_head *item, *_n; struct list_head *item, *_n;
struct i2c_adapter *adap_from_list;
struct i2c_driver *driver; struct i2c_driver *driver;
struct i2c_client *client; struct i2c_client *client;
int res = 0; int res = 0;
down(&core_lists); down(&core_lists);
/* First make sure that this adapter was ever added */
list_for_each_entry(adap_from_list, &adapters, list) {
if (adap_from_list == adap)
break;
}
if (adap_from_list != adap) {
pr_debug("I2C: Attempting to delete an unregistered "
"adapter\n");
res = -EINVAL;
goto out_unlock;
}
list_for_each(item,&drivers) { list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list); driver = list_entry(item, struct i2c_driver, list);
if (driver->detach_adapter) if (driver->detach_adapter)
...@@ -1008,48 +1021,6 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value) ...@@ -1008,48 +1021,6 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
I2C_SMBUS_WORD_DATA,&data); I2C_SMBUS_WORD_DATA,&data);
} }
s32 i2c_smbus_process_call(struct i2c_client *client, u8 command, u16 value)
{
union i2c_smbus_data data;
data.word = value;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_PROC_CALL, &data))
return -1;
else
return 0x0FFFF & data.word;
}
/* Returns the number of read bytes */
s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values)
{
union i2c_smbus_data data;
int i;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,command,
I2C_SMBUS_BLOCK_DATA,&data))
return -1;
else {
for (i = 1; i <= data.block[0]; i++)
values[i-1] = data.block[i];
return data.block[0];
}
}
s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command, u8 length, u8 *values)
{
union i2c_smbus_data data;
int i;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_BLOCK_DATA,&data);
}
/* Returns the number of read bytes */ /* Returns the number of read bytes */
s32 i2c_smbus_block_process_call(struct i2c_client *client, u8 command, u8 length, u8 *values) s32 i2c_smbus_block_process_call(struct i2c_client *client, u8 command, u8 length, u8 *values)
{ {
...@@ -1085,20 +1056,6 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val ...@@ -1085,20 +1056,6 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val
} }
} }
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command, u8 length, u8 *values)
{
union i2c_smbus_data data;
int i;
if (length > I2C_SMBUS_I2C_BLOCK_MAX)
length = I2C_SMBUS_I2C_BLOCK_MAX;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_I2C_BLOCK_DATA,&data);
}
/* Simulate a SMBus command using the i2c protocol /* Simulate a SMBus command using the i2c protocol
No checking of parameters is done! */ No checking of parameters is done! */
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
...@@ -1322,11 +1279,7 @@ EXPORT_SYMBOL(i2c_smbus_read_byte_data); ...@@ -1322,11 +1279,7 @@ EXPORT_SYMBOL(i2c_smbus_read_byte_data);
EXPORT_SYMBOL(i2c_smbus_write_byte_data); EXPORT_SYMBOL(i2c_smbus_write_byte_data);
EXPORT_SYMBOL(i2c_smbus_read_word_data); EXPORT_SYMBOL(i2c_smbus_read_word_data);
EXPORT_SYMBOL(i2c_smbus_write_word_data); EXPORT_SYMBOL(i2c_smbus_write_word_data);
EXPORT_SYMBOL(i2c_smbus_process_call);
EXPORT_SYMBOL(i2c_smbus_read_block_data);
EXPORT_SYMBOL(i2c_smbus_write_block_data);
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
EXPORT_SYMBOL(i2c_get_functionality); EXPORT_SYMBOL(i2c_get_functionality);
EXPORT_SYMBOL(i2c_check_functionality); EXPORT_SYMBOL(i2c_check_functionality);
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <linux/i2c-sensor.h> #include <linux/i2c-sensor.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
static unsigned short empty[] = {I2C_CLIENT_END};
static unsigned int empty_isa[] = {I2C_CLIENT_ISA_END};
/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ /* Very inefficient for ISA detects, and won't work for 10-bit addresses! */
int i2c_detect(struct i2c_adapter *adapter, int i2c_detect(struct i2c_adapter *adapter,
...@@ -42,12 +44,28 @@ int i2c_detect(struct i2c_adapter *adapter, ...@@ -42,12 +44,28 @@ int i2c_detect(struct i2c_adapter *adapter,
int is_isa = i2c_is_isa_adapter(adapter); int is_isa = i2c_is_isa_adapter(adapter);
int adapter_id = int adapter_id =
is_isa ? ANY_I2C_ISA_BUS : i2c_adapter_id(adapter); is_isa ? ANY_I2C_ISA_BUS : i2c_adapter_id(adapter);
unsigned short *normal_i2c;
unsigned int *normal_isa;
unsigned short *probe;
unsigned short *ignore;
/* Forget it if we can't probe using SMBUS_QUICK */ /* Forget it if we can't probe using SMBUS_QUICK */
if ((!is_isa) && if ((!is_isa) &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK))
return -1; return -1;
/* Use default "empty" list if the adapter doesn't specify any */
normal_i2c = probe = ignore = empty;
normal_isa = empty_isa;
if (address_data->normal_i2c)
normal_i2c = address_data->normal_i2c;
if (address_data->normal_isa)
normal_isa = address_data->normal_isa;
if (address_data->probe)
probe = address_data->probe;
if (address_data->ignore)
ignore = address_data->ignore;
for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
if (!is_isa && i2c_check_addr(adapter, addr)) if (!is_isa && i2c_check_addr(adapter, addr))
continue; continue;
...@@ -72,81 +90,46 @@ int i2c_detect(struct i2c_adapter *adapter, ...@@ -72,81 +90,46 @@ int i2c_detect(struct i2c_adapter *adapter,
/* If this address is in one of the ignores, we can forget about it /* If this address is in one of the ignores, we can forget about it
right now */ right now */
for (i = 0; !found && (address_data->ignore[i] != I2C_CLIENT_END); i += 2) { for (i = 0; !found && (ignore[i] != I2C_CLIENT_END); i += 2) {
if ( ((adapter_id == address_data->ignore[i]) || if ( ((adapter_id == ignore[i]) ||
((address_data->ignore[i] == ANY_I2C_BUS) && ((ignore[i] == ANY_I2C_BUS) &&
!is_isa)) && !is_isa)) &&
(addr == address_data->ignore[i + 1])) { (addr == ignore[i + 1])) {
dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr); dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1; found = 1;
} }
} }
for (i = 0; !found && (address_data->ignore_range[i] != I2C_CLIENT_END); i += 3) {
if ( ((adapter_id == address_data->ignore_range[i]) ||
((address_data-> ignore_range[i] == ANY_I2C_BUS) &
!is_isa)) &&
(addr >= address_data->ignore_range[i + 1]) &&
(addr <= address_data->ignore_range[i + 2])) {
dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
if (found) if (found)
continue; continue;
/* Now, we will do a detection, but only if it is in the normal or /* Now, we will do a detection, but only if it is in the normal or
probe entries */ probe entries */
if (is_isa) { if (is_isa) {
for (i = 0; !found && (address_data->normal_isa[i] != I2C_CLIENT_ISA_END); i += 1) { for (i = 0; !found && (normal_isa[i] != I2C_CLIENT_ISA_END); i += 1) {
if (addr == address_data->normal_isa[i]) { if (addr == normal_isa[i]) {
dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr); dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr);
found = 1; found = 1;
} }
} }
for (i = 0; !found && (address_data->normal_isa_range[i] != I2C_CLIENT_ISA_END); i += 3) {
if ((addr >= address_data->normal_isa_range[i]) &&
(addr <= address_data->normal_isa_range[i + 1]) &&
((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) {
dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr);
found = 1;
}
}
} else { } else {
for (i = 0; !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); i += 1) { for (i = 0; !found && (normal_i2c[i] != I2C_CLIENT_END); i += 1) {
if (addr == address_data->normal_i2c[i]) { if (addr == normal_i2c[i]) {
found = 1; found = 1;
dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr); dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr);
} }
} }
for (i = 0; !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); i += 2) {
if ((addr >= address_data->normal_i2c_range[i]) &&
(addr <= address_data->normal_i2c_range[i + 1])) {
dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
} }
for (i = 0; for (i = 0;
!found && (address_data->probe[i] != I2C_CLIENT_END); !found && (probe[i] != I2C_CLIENT_END);
i += 2) { i += 2) {
if (((adapter_id == address_data->probe[i]) || if (((adapter_id == probe[i]) ||
((address_data-> ((probe[i] == ANY_I2C_BUS) && !is_isa))
probe[i] == ANY_I2C_BUS) && !is_isa)) && (addr == probe[i + 1])) {
&& (addr == address_data->probe[i + 1])) {
dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr); dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1; found = 1;
} }
} }
for (i = 0; !found && (address_data->probe_range[i] != I2C_CLIENT_END); i += 3) {
if ( ((adapter_id == address_data->probe_range[i]) ||
((address_data->probe_range[i] == ANY_I2C_BUS) && !is_isa)) &&
(addr >= address_data->probe_range[i + 1]) &&
(addr <= address_data->probe_range[i + 2])) {
found = 1;
dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
}
}
if (!found) if (!found)
continue; continue;
......
...@@ -733,10 +733,8 @@ void ds_disconnect(struct usb_interface *intf) ...@@ -733,10 +733,8 @@ void ds_disconnect(struct usb_interface *intf)
while (atomic_read(&dev->refcnt)) { while (atomic_read(&dev->refcnt)) {
printk(KERN_INFO "Waiting for DS to become free: refcnt=%d.\n", printk(KERN_INFO "Waiting for DS to become free: refcnt=%d.\n",
atomic_read(&dev->refcnt)); atomic_read(&dev->refcnt));
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
if (signal_pending(current)) if (msleep_interruptible(1000))
flush_signals(current); flush_signals(current);
} }
......
...@@ -452,10 +452,8 @@ static void w1_slave_detach(struct w1_slave *sl) ...@@ -452,10 +452,8 @@ static void w1_slave_detach(struct w1_slave *sl)
while (atomic_read(&sl->refcnt)) { while (atomic_read(&sl->refcnt)) {
printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
sl->name, atomic_read(&sl->refcnt)); sl->name, atomic_read(&sl->refcnt));
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
if (signal_pending(current)) if (msleep_interruptible(1000))
flush_signals(current); flush_signals(current);
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/delay.h>
#include "w1_family.h" #include "w1_family.h"
...@@ -87,10 +88,8 @@ void w1_unregister_family(struct w1_family *fent) ...@@ -87,10 +88,8 @@ void w1_unregister_family(struct w1_family *fent)
while (atomic_read(&fent->refcnt)) { while (atomic_read(&fent->refcnt)) {
printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n", printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n",
fent->fid, atomic_read(&fent->refcnt)); fent->fid, atomic_read(&fent->refcnt));
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
if (signal_pending(current)) if (msleep_interruptible(1000))
flush_signals(current); flush_signals(current);
} }
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/delay.h>
#include "w1.h" #include "w1.h"
#include "w1_log.h" #include "w1_log.h"
...@@ -184,10 +185,8 @@ void __w1_remove_master_device(struct w1_master *dev) ...@@ -184,10 +185,8 @@ void __w1_remove_master_device(struct w1_master *dev)
while (atomic_read(&dev->refcnt)) { while (atomic_read(&dev->refcnt)) {
printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
dev->name, atomic_read(&dev->refcnt)); dev->name, atomic_read(&dev->refcnt));
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
if (signal_pending(current)) if (msleep_interruptible(1000))
flush_signals(current); flush_signals(current);
} }
......
...@@ -42,51 +42,25 @@ struct i2c_force_data { ...@@ -42,51 +42,25 @@ struct i2c_force_data {
/* A structure containing the detect information. /* A structure containing the detect information.
normal_i2c: filled in by the module writer. Terminated by I2C_CLIENT_ISA_END. normal_i2c: filled in by the module writer. Terminated by I2C_CLIENT_ISA_END.
A list of I2C addresses which should normally be examined. A list of I2C addresses which should normally be examined.
normal_i2c_range: filled in by the module writer. Terminated by
I2C_CLIENT_ISA_END
A list of pairs of I2C addresses, each pair being an inclusive range of
addresses which should normally be examined.
normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END.
A list of ISA addresses which should normally be examined. A list of ISA addresses which should normally be examined.
normal_isa_range: filled in by the module writer. Terminated by
SENSORS_ISA_END
A list of triples. The first two elements are ISA addresses, being an
range of addresses which should normally be examined. The third is the
modulo parameter: only addresses which are 0 module this value relative
to the first address of the range are actually considered.
probe: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END values. probe: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END values.
A list of pairs. The first value is a bus number (ANY_I2C_ISA_BUS for A list of pairs. The first value is a bus number (ANY_I2C_ISA_BUS for
the ISA bus, -1 for any I2C bus), the second is the address. These the ISA bus, -1 for any I2C bus), the second is the address. These
addresses are also probed, as if they were in the 'normal' list. addresses are also probed, as if they were in the 'normal' list.
probe_range: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END
values.
A list of triples. The first value is a bus number (ANY_I2C_ISA_BUS for
the ISA bus, -1 for any I2C bus), the second and third are addresses.
These form an inclusive range of addresses that are also probed, as
if they were in the 'normal' list.
ignore: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END values. ignore: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END values.
A list of pairs. The first value is a bus number (ANY_I2C_ISA_BUS for A list of pairs. The first value is a bus number (ANY_I2C_ISA_BUS for
the ISA bus, -1 for any I2C bus), the second is the I2C address. These the ISA bus, -1 for any I2C bus), the second is the I2C address. These
addresses are never probed. This parameter overrules 'normal' and addresses are never probed. This parameter overrules 'normal' and
'probe', but not the 'force' lists. 'probe', but not the 'force' lists.
ignore_range: insmod parameter. Initialize this list with I2C_CLIENT_ISA_END
values.
A list of triples. The first value is a bus number (ANY_I2C_ISA_BUS for
the ISA bus, -1 for any I2C bus), the second and third are addresses.
These form an inclusive range of I2C addresses that are never probed.
This parameter overrules 'normal' and 'probe', but not the 'force' lists.
force_data: insmod parameters. A list, ending with an element of which force_data: insmod parameters. A list, ending with an element of which
the force field is NULL. the force field is NULL.
*/ */
struct i2c_address_data { struct i2c_address_data {
unsigned short *normal_i2c; unsigned short *normal_i2c;
unsigned short *normal_i2c_range;
unsigned int *normal_isa; unsigned int *normal_isa;
unsigned int *normal_isa_range;
unsigned short *probe; unsigned short *probe;
unsigned short *probe_range;
unsigned short *ignore; unsigned short *ignore;
unsigned short *ignore_range;
struct i2c_force_data *forces; struct i2c_force_data *forces;
}; };
...@@ -100,23 +74,13 @@ struct i2c_address_data { ...@@ -100,23 +74,13 @@ struct i2c_address_data {
#define SENSORS_INSMOD \ #define SENSORS_INSMOD \
I2C_CLIENT_MODULE_PARM(probe, \ I2C_CLIENT_MODULE_PARM(probe, \
"List of adapter,address pairs to scan additionally"); \ "List of adapter,address pairs to scan additionally"); \
I2C_CLIENT_MODULE_PARM(probe_range, \
"List of adapter,start-addr,end-addr triples to scan " \
"additionally"); \
I2C_CLIENT_MODULE_PARM(ignore, \ I2C_CLIENT_MODULE_PARM(ignore, \
"List of adapter,address pairs not to scan"); \ "List of adapter,address pairs not to scan"); \
I2C_CLIENT_MODULE_PARM(ignore_range, \
"List of adapter,start-addr,end-addr triples not to " \
"scan"); \
static struct i2c_address_data addr_data = { \ static struct i2c_address_data addr_data = { \
.normal_i2c = normal_i2c, \ .normal_i2c = normal_i2c, \
.normal_i2c_range = normal_i2c_range, \
.normal_isa = normal_isa, \ .normal_isa = normal_isa, \
.normal_isa_range = normal_isa_range, \
.probe = probe, \ .probe = probe, \
.probe_range = probe_range, \
.ignore = ignore, \ .ignore = ignore, \
.ignore_range = ignore_range, \
.forces = forces, \ .forces = forces, \
} }
......
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