added block data read/write support. Minor cleanup.

git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1926 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Alexander Malysh committed 2003-07-30 22:35:17 +00:00
1 parent 1843d33fb7
commit bb6298c2f2
1 file changed
+155 -83
+155 -83
View File
@@ -1,8 +1,8 @@
/*
sis630.c - Part of lm_sensors, Linux kernel modules for hardware
i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2002 Alexander Malysh <amalysh@web.de>
Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,21 +21,21 @@
/*
Changes:
19.11.2002
Fixed logic by restoring of Host Master Clock
24.08.2002
Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
Changed sis630_transaction.(Thanks to Mark M. Hoffman)
18.09.2002
Added SIS730 as supported
21.09.2002
Added high_clock module option.If this option is set
used Host Master Clock 56KHz (default 14KHz).For now we are save old Host
Master Clock and after transaction completed restore (otherwise
it's confuse BIOS and hung Machine).
24.09.2002
Fixed typo in sis630_access
Fixed logical error by restoring of Host Master Clock
21.09.2002
Added high_clock module option.If this option is set
used Host Master Clock 56KHz (default 14KHz).For now we are save old Host
Master Clock and after transaction completed restore (otherwise
it's confuse BIOS and hung Machine).
18.09.2002
Added SIS730 as supported
24.08.2002
Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
Changed sis630_transaction. Now it's 2x faster (Thanks to Mark M. Hoffman)
31.07.2003
Added block data read/write support.
*/
/*
@@ -59,12 +59,12 @@
#include <asm/io.h>
#include "version.h"
struct sd {
const unsigned short mfr;
const unsigned short dev;
const unsigned char fn;
const char *name;
};
#ifdef DEBUG
#define DBG(x...) printk(KERN_DEBUG "i2c-sis630.o: " x)
#else
#define DBG(x...)
#endif
/* SIS630 SMBus registers */
#define SMB_STS 0x80 /* status */
@@ -103,84 +103,67 @@ struct sd {
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
SIS630. DANGEROUS! */
static int high_clock = 0;
/*
static int force = 0;
MODULE_PARM(force, "i");
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
*/
MODULE_PARM(high_clock, "i");
MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
static void sis630_do_pause(unsigned int amount);
static int sis630_transaction(int size);
static u8 sis630_read(u8 reg);
static void sis630_write(u8 reg, u8 data);
#if 0
/* If force is set to anything different from 0, we forcibly enable the
SIS630. DANGEROUS! */
static int force = 0;
MODULE_PARM(force, "i");
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
#endif
static unsigned short acpi_base = 0;
u8 sis630_read(u8 reg) {
static inline u8 sis630_read(u8 reg) {
return inb(acpi_base + reg);
}
void sis630_write(u8 reg, u8 data) {
static inline void sis630_write(u8 reg, u8 data) {
outb(data, acpi_base + reg);
}
/* Internally used pause function */
void sis630_do_pause(unsigned int amount)
static void sis630_do_pause(unsigned int amount)
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(amount);
}
int sis630_transaction(int size) {
static int sis630_transaction_start(int size, u8 *oldclock) {
int temp;
int result = 0;
int timeout = 0;
u8 oldclock;
/*
Make sure the SMBus host is ready to start transmitting.
*/
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: SMBus busy (%02x). "
"Resetting...\n",temp);
#endif
DBG("SMBus busy (%02x).Resetting...\n",temp);
/* kill smbus transaction */
sis630_write(SMBHOST_CNT, 0x20);
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: Failed! (%02x)\n",
temp);
#endif
DBG("Failed! (%02x)\n", temp);
return -1;
} else {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: Successfull!\n");
#endif
DBG("Successfull!\n");
}
}
/* save old clock, so we can prevent machine to hung */
oldclock = sis630_read(SMB_CNT);
/* save old clock, so we can prevent machine for hung */
*oldclock = sis630_read(SMB_CNT);
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: saved clock 0x%02x\n", oldclock);
#endif
DBG("saved clock 0x%02x\n", *oldclock);
/* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
if (high_clock > 0)
sis630_write(SMB_CNT, 0x20);
else
sis630_write(SMB_CNT, (oldclock & ~0x40));
sis630_write(SMB_CNT, (*oldclock & ~0x40));
/* clear all sticky bits */
temp = sis630_read(SMB_STS);
@@ -189,44 +172,52 @@ int sis630_transaction(int size) {
/* start the transaction by setting bit 4 and size */
sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
return 0;
}
static int sis630_transaction_wait(int size) {
int temp, result = 0, timeout = 0;
/* We will always wait for a fraction of a second! */
do {
sis630_do_pause(1);
temp = sis630_read(SMB_STS);
/* check if block transmitted */
if (size == SIS630_BLOCK_DATA && (temp & 0x10))
break;
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: SMBus Timeout!\n");
#endif
DBG("SMBus Timeout!\n");
result = -1;
}
if (temp & 0x02) {
result = -1;
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: Error: Failed bus "
"transaction\n");
#endif
DBG("Error: Failed bus transaction\n");
}
if (temp & 0x04) {
result = -1;
printk(KERN_ERR "i2c-sis630.o: Bus collision! "
"SMBus may be locked until next hard reset (or not...)\n");
/*
printk(KERN_ERR "i2c-sis630.o: Bus collision!\n");
/*
TBD: Datasheet say:
the software should clear this bit and restart SMBUS operation
the software should clear this bit and restart SMBUS operation.
Should we do it or user start request again?
*/
}
return result;
}
static void sis630_transaction_end(u8 oldclock) {
int temp = 0;
/* clear all status "sticky" bits */
sis630_write(SMB_STS, temp);
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
#endif
DBG("SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
/*
* restore old Host Master Clock if high_clock is set
@@ -235,15 +226,98 @@ int sis630_transaction(int size) {
if (high_clock > 0 && !(oldclock & 0x20))
sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
#endif
DBG("SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
}
static int sis630_transaction(int size) {
int result = 0;
u8 oldclock = 0;
if (!(result = sis630_transaction_start(size, &oldclock))) {
result = sis630_transaction_wait(size);
sis630_transaction_end(oldclock);
}
return result;
}
static int sis630_block_data(union i2c_smbus_data * data, int read_write) {
int i, len = 0, rc = 0;
u8 oldclock = 0;
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
else if (len > 32)
len = 32;
sis630_write(SMB_COUNT, len);
for (i=1; i <= len; i++) {
DBG("set data 0x%02x\n", data->block[i]);
/* set data */
sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
if (i==8 || (len<8 && i==len)) {
DBG("start trans len=%d i=%d\n",len ,i);
/* first transaction */
if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock))
return -1;
}
else if ((i-1)%8 == 7 || i==len) {
DBG("trans_wait len=%d i=%d\n",len,i);
if (i>8) {
DBG("clear smbary_sts len=%d i=%d\n",len,i);
/*
If this is not first transaction,
we must clear sticky bit.
clear SMBARY_STS
*/
sis630_write(SMB_STS,0x10);
}
if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
DBG("trans_wait failed\n");
rc = -1;
break;
}
}
}
}
else { /* read request */
data->block[0] = len = 0;
if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) {
return -1;
}
do {
if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
DBG("trans_wait failed\n");
rc = -1;
break;
}
/* if this first transaction then read byte count */
if (len == 0)
data->block[0] = sis630_read(SMB_COUNT);
DBG("block data read len=0x%x\n", data->block[0]);
for (i=0; i < 8 && len < data->block[0]; i++,len++) {
DBG("read i=%d len=%d\n", i, len);
data->block[len+1] = sis630_read(SMB_BYTE+i);
}
DBG("clear smbary_sts len=%d i=%d\n",len,i);
/* clear SMBARY_STS */
sis630_write(SMB_STS,0x10);
} while(len < data->block[0]);
}
sis630_transaction_end(oldclock);
return rc;
}
/* Return -1 on error. */
s32 sis630_access(struct i2c_adapter * adap, u16 addr,
static s32 sis630_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
@@ -277,12 +351,10 @@ s32 sis630_access(struct i2c_adapter * adap, u16 addr,
size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
/* it's not implemented yet, but comming soon */
#if 0
sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
size = SIS630_BLOCK_DATA;
#endif
return sis630_block_data(data, read_write);
default:
printk("Unsupported I2C size\n");
return -1;
@@ -316,12 +388,13 @@ s32 sis630_access(struct i2c_adapter * adap, u16 addr,
}
u32 sis630_func(struct i2c_adapter *adapter) {
static u32 sis630_func(struct i2c_adapter *adapter) {
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL;
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
int sis630_setup(struct pci_dev *dummy) {
static int sis630_setup(struct pci_dev *dummy) {
unsigned char b;
struct pci_dev *sis630_dev = NULL;
@@ -359,9 +432,7 @@ int sis630_setup(struct pci_dev *dummy) {
return -ENODEV;
}
#ifdef DEBUG
printk(KERN_DEBUG "i2c-sis630.o: ACPI base at 0x%04x\n", acpi_base);
#endif
DBG("ACPI base at 0x%04x\n", acpi_base);
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")){
@@ -416,6 +487,7 @@ static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_i
sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
acpi_base + SMB_STS);
return i2c_add_adapter(&sis630_adapter);
}