Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

77
drivers/i2c/Kconfig Normal file
View File

@@ -0,0 +1,77 @@
#
# Character device configuration
#
menu "I2C support"
config I2C
tristate "I2C support"
---help---
I2C (pronounce: I-square-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
or System Management Bus is a subset of the I2C protocol. More
information is contained in the directory <file:Documentation/i2c/>,
especially in the file called "summary" there.
Both I2C and SMBus are supported here. You will need this for
hardware sensors support, and also for Video For Linux support.
If you want I2C support, you should say Y here and also to the
specific driver for your bus adapter(s) below.
This I2C support can also be built as a module. If so, the module
will be called i2c-core.
config I2C_CHARDEV
tristate "I2C device interface"
depends on I2C
help
Say Y here to use i2c-* device files, usually found in the /dev
directory on your system. They make it possible to have user-space
programs use the I2C bus. Information on how to do this is
contained in the file <file:Documentation/i2c/dev-interface>.
This support is also available as a module. If so, the module
will be called i2c-dev.
source drivers/i2c/algos/Kconfig
source drivers/i2c/busses/Kconfig
source drivers/i2c/chips/Kconfig
config I2C_DEBUG_CORE
bool "I2C Core debugging messages"
depends on I2C
help
Say Y here if you want the I2C core to produce a bunch of debug
messages to the system log. Select this if you are having a
problem with I2C support and want to see more of what is going on.
config I2C_DEBUG_ALGO
bool "I2C Algorithm debugging messages"
depends on I2C
help
Say Y here if you want the I2C algorithm drivers to produce a bunch
of debug messages to the system log. Select this if you are having
a problem with I2C support and want to see more of what is going
on.
config I2C_DEBUG_BUS
bool "I2C Bus debugging messages"
depends on I2C
help
Say Y here if you want the I2C bus drivers to produce a bunch of
debug messages to the system log. Select this if you are having
a problem with I2C support and want to see more of what is going
on.
config I2C_DEBUG_CHIP
bool "I2C Chip debugging messages"
depends on I2C
help
Say Y here if you want the I2C chip drivers to produce a bunch of
debug messages to the system log. Select this if you are having
a problem with I2C support and want to see more of what is going
on.
endmenu

15
drivers/i2c/Makefile Normal file
View File

@@ -0,0 +1,15 @@
#
# Makefile for the i2c core.
#
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_SENSOR) += i2c-sensor.o
obj-y += busses/ chips/ algos/
i2c-sensor-objs := i2c-sensor-detect.o i2c-sensor-vid.o
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
endif

70
drivers/i2c/algos/Kconfig Normal file
View File

@@ -0,0 +1,70 @@
#
# Character device configuration
#
menu "I2C Algorithms"
depends on I2C
config I2C_ALGOBIT
tristate "I2C bit-banging interfaces"
depends on I2C
help
This allows you to use a range of I2C adapters called bit-banging
adapters. Say Y if you own an I2C adapter belonging to this class
and then say Y to the specific driver for you adapter below.
This support is also available as a module. If so, the module
will be called i2c-algo-bit.
config I2C_ALGOPCF
tristate "I2C PCF 8584 interfaces"
depends on I2C
help
This allows you to use a range of I2C adapters called PCF adapters.
Say Y if you own an I2C adapter belonging to this class and then say
Y to the specific driver for you adapter below.
This support is also available as a module. If so, the module
will be called i2c-algo-pcf.
config I2C_ALGOPCA
tristate "I2C PCA 9564 interfaces"
depends on I2C
help
This allows you to use a range of I2C adapters called PCA adapters.
Say Y if you own an I2C adapter belonging to this class and then say
Y to the specific driver for you adapter below.
This support is also available as a module. If so, the module
will be called i2c-algo-pca.
config I2C_ALGOITE
tristate "ITE I2C Algorithm"
depends on MIPS_ITE8172 && I2C
help
This supports the use of the ITE8172 I2C interface found on some MIPS
systems. Say Y if you have one of these. You should also say Y for
the ITE I2C peripheral driver support below.
This support is also available as a module. If so, the module
will be called i2c-algo-ite.
config I2C_ALGO8XX
tristate "MPC8xx CPM I2C interface"
depends on 8xx && I2C
config I2C_ALGO_SIBYTE
tristate "SiByte SMBus interface"
depends on SIBYTE_SB1xxx_SOC && I2C
help
Supports the SiByte SOC on-chip I2C interfaces (2 channels).
config I2C_ALGO_SGI
tristate "I2C SGI interfaces"
depends on I2C && (SGI_IP22 || SGI_IP32 || X86_VISWS)
help
Supports the SGI interfaces like the ones found on SGI Indy VINO
or SGI O2 MACE.
endmenu

View File

@@ -0,0 +1,14 @@
#
# Makefile for the i2c algorithms
#
obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o
obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o
obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o
obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o
ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
EXTRA_CFLAGS += -DDEBUG
endif

View File

@@ -0,0 +1,573 @@
/* ------------------------------------------------------------------------- */
/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-2000 Simon G. Vogl
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Ky<4B>sti M<>lkki
<kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
/* ----- global defines ----------------------------------------------- */
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/
#define DEBPROTO(x) if (i2c_debug>=9) { x; }
/* debug the protocol by showing transferred bits */
/* ----- global variables --------------------------------------------- */
/* module parameters:
*/
static int i2c_debug;
static int bit_test; /* see if the line-setting functions work */
/* --- setting states on the bus with the right timing: --------------- */
#define setsda(adap,val) adap->setsda(adap->data, val)
#define setscl(adap,val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)
static inline void sdalo(struct i2c_algo_bit_data *adap)
{
setsda(adap,0);
udelay(adap->udelay);
}
static inline void sdahi(struct i2c_algo_bit_data *adap)
{
setsda(adap,1);
udelay(adap->udelay);
}
static inline void scllo(struct i2c_algo_bit_data *adap)
{
setscl(adap,0);
udelay(adap->udelay);
}
/*
* Raise scl line, and do checking for delays. This is necessary for slower
* devices.
*/
static inline int sclhi(struct i2c_algo_bit_data *adap)
{
unsigned long start;
setscl(adap,1);
/* Not all adapters have scl sense line... */
if (adap->getscl == NULL ) {
udelay(adap->udelay);
return 0;
}
start=jiffies;
while (! getscl(adap) ) {
/* the hw knows how to read the clock line,
* so we wait until it actually gets high.
* This is safer as some chips may hold it low
* while they are processing data internally.
*/
if (time_after_eq(jiffies, start+adap->timeout)) {
return -ETIMEDOUT;
}
cond_resched();
}
DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start));
udelay(adap->udelay);
return 0;
}
/* --- other auxiliary functions -------------------------------------- */
static void i2c_start(struct i2c_algo_bit_data *adap)
{
/* assert: scl, sda are high */
DEBPROTO(printk("S "));
sdalo(adap);
scllo(adap);
}
static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
/* scl, sda may not be high */
DEBPROTO(printk(" Sr "));
setsda(adap,1);
sclhi(adap);
udelay(adap->udelay);
sdalo(adap);
scllo(adap);
}
static void i2c_stop(struct i2c_algo_bit_data *adap)
{
DEBPROTO(printk("P\n"));
/* assert: scl is low */
sdalo(adap);
sclhi(adap);
sdahi(adap);
}
/* send a byte without start cond., look for arbitration,
check ackn. from slave */
/* returns:
* 1 if the device acknowledged
* 0 if the device did not ack
* -ETIMEDOUT if an error occurred (while raising the scl line)
*/
static int i2c_outb(struct i2c_adapter *i2c_adap, char c)
{
int i;
int sb;
int ack;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
for ( i=7 ; i>=0 ; i-- ) {
sb = c & ( 1 << i );
setsda(adap,sb);
udelay(adap->udelay);
DEBPROTO(printk(KERN_DEBUG "%d",sb!=0));
if (sclhi(adap)<0) { /* timed out */
sdahi(adap); /* we don't want to block the net */
DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at bit #%d\n", c&0xff, i));
return -ETIMEDOUT;
};
/* do arbitration here:
* if ( sb && ! getsda(adap) ) -> ouch! Get out of here.
*/
setscl(adap, 0 );
udelay(adap->udelay);
}
sdahi(adap);
if (sclhi(adap)<0){ /* timeout */
DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x, timeout at ack\n", c&0xff));
return -ETIMEDOUT;
};
/* read ack: SDA should be pulled down by slave */
ack=getsda(adap); /* ack: sda is pulled low ->success. */
DEB2(printk(KERN_DEBUG " i2c_outb: 0x%02x , getsda() = %d\n", c & 0xff, ack));
DEBPROTO( printk(KERN_DEBUG "[%2.2x]",c&0xff) );
DEBPROTO(if (0==ack){ printk(KERN_DEBUG " A ");} else printk(KERN_DEBUG " NA ") );
scllo(adap);
return 0==ack; /* return 1 if device acked */
/* assert: scl is low (sda undef) */
}
static int i2c_inb(struct i2c_adapter *i2c_adap)
{
/* read byte via i2c port, without start/stop sequence */
/* acknowledge is sent in i2c_read. */
int i;
unsigned char indata=0;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
sdahi(adap);
for (i=0;i<8;i++) {
if (sclhi(adap)<0) { /* timeout */
DEB2(printk(KERN_DEBUG " i2c_inb: timeout at bit #%d\n", 7-i));
return -ETIMEDOUT;
};
indata *= 2;
if ( getsda(adap) )
indata |= 0x01;
scllo(adap);
}
/* assert: scl is low */
DEB2(printk(KERN_DEBUG "i2c_inb: 0x%02x\n", indata & 0xff));
DEBPROTO(printk(KERN_DEBUG " 0x%02x", indata & 0xff));
return (int) (indata & 0xff);
}
/*
* Sanity check for the adapter hardware - check the reaction of
* the bus lines only if it seems to be idle.
*/
static int test_bus(struct i2c_algo_bit_data *adap, char* name) {
int scl,sda;
if (adap->getscl==NULL)
printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, "
"SCL is not readable.\n");
sda=getsda(adap);
scl=(adap->getscl==NULL?1:getscl(adap));
printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda);
if (!scl || !sda ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name);
goto bailout;
}
sdalo(adap);
sda=getsda(adap);
scl=(adap->getscl==NULL?1:getscl(adap));
printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda);
if ( 0 != sda ) {
printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n");
goto bailout;
}
if ( 0 == scl ) {
printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
"while pulling SDA low!\n");
goto bailout;
}
sdahi(adap);
sda=getsda(adap);
scl=(adap->getscl==NULL?1:getscl(adap));
printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda);
if ( 0 == sda ) {
printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n");
goto bailout;
}
if ( 0 == scl ) {
printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
"while pulling SDA high!\n");
goto bailout;
}
scllo(adap);
sda=getsda(adap);
scl=(adap->getscl==NULL?0:getscl(adap));
printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda);
if ( 0 != scl ) {
printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n");
goto bailout;
}
if ( 0 == sda ) {
printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
"while pulling SCL low!\n");
goto bailout;
}
sclhi(adap);
sda=getsda(adap);
scl=(adap->getscl==NULL?1:getscl(adap));
printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda);
if ( 0 == scl ) {
printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n");
goto bailout;
}
if ( 0 == sda ) {
printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
"while pulling SCL high!\n");
goto bailout;
}
printk(KERN_INFO "i2c-algo-bit.o: %s passed test.\n",name);
return 0;
bailout:
sdahi(adap);
sclhi(adap);
return -ENODEV;
}
/* ----- Utility functions
*/
/* try_address tries to contact a chip for a number of
* times before it gives up.
* return values:
* 1 chip answered
* 0 chip did not answer
* -x transmission error
*/
static inline int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i,ret = -1;
for (i=0;i<=retries;i++) {
ret = i2c_outb(i2c_adap,addr);
if (ret==1)
break; /* success! */
i2c_stop(adap);
udelay(5/*adap->udelay*/);
if (i==retries) /* no success */
break;
i2c_start(adap);
udelay(adap->udelay);
}
DEB2(if (i)
printk(KERN_DEBUG "i2c-algo-bit.o: Used %d tries to %s client at 0x%02x : %s\n",
i+1, addr & 1 ? "read" : "write", addr>>1,
ret==1 ? "success" : ret==0 ? "no ack" : "failed, timeout?" )
);
return ret;
}
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
char c;
const char *temp = msg->buf;
int count = msg->len;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
int retval;
int wrcount=0;
while (count > 0) {
c = *temp;
DEB2(dev_dbg(&i2c_adap->dev, "sendbytes: writing %2.2X\n", c&0xff));
retval = i2c_outb(i2c_adap,c);
if ((retval>0) || (nak_ok && (retval==0))) { /* ok or ignored NAK */
count--;
temp++;
wrcount++;
} else { /* arbitration or no acknowledge */
dev_err(&i2c_adap->dev, "sendbytes: error - bailout.\n");
i2c_stop(adap);
return (retval<0)? retval : -EFAULT;
/* got a better one ?? */
}
#if 0
/* from asm/delay.h */
__delay(adap->mdelay * (loops_per_sec / 1000) );
#endif
}
return wrcount;
}
static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
int inval;
int rdcount=0; /* counts bytes read */
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
char *temp = msg->buf;
int count = msg->len;
while (count > 0) {
inval = i2c_inb(i2c_adap);
/*printk("%#02x ",inval); if ( ! (count % 16) ) printk("\n"); */
if (inval>=0) {
*temp = inval;
rdcount++;
} else { /* read timed out */
printk(KERN_ERR "i2c-algo-bit.o: readbytes: i2c_inb timed out.\n");
break;
}
temp++;
count--;
if (msg->flags & I2C_M_NO_RD_ACK)
continue;
if ( count > 0 ) { /* send ack */
sdalo(adap);
DEBPROTO(printk(" Am "));
} else {
sdahi(adap); /* neg. ack on last byte */
DEBPROTO(printk(" NAm "));
}
if (sclhi(adap)<0) { /* timeout */
sdahi(adap);
printk(KERN_ERR "i2c-algo-bit.o: readbytes: Timeout at ack\n");
return -ETIMEDOUT;
};
scllo(adap);
sdahi(adap);
}
return rdcount;
}
/* doAddress initiates the transfer by generating the start condition (in
* try_address) and transmits the address in the necessary format to handle
* reads, writes as well as 10bit-addresses.
* returns:
* 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
* -x an error occurred (like: -EREMOTEIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
unsigned char addr;
int ret, retries;
retries = nak_ok ? 0 : i2c_adap->retries;
if ( (flags & I2C_M_TEN) ) {
/* a ten bit address */
addr = 0xf0 | (( msg->addr >> 7) & 0x03);
DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
/* try extended address code...*/
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
printk(KERN_ERR "died at extended address code.\n");
return -EREMOTEIO;
}
/* the remaining 8 bit address */
ret = i2c_outb(i2c_adap,msg->addr & 0x7f);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
printk(KERN_ERR "died at 2nd address code.\n");
return -EREMOTEIO;
}
if ( flags & I2C_M_RD ) {
i2c_repstart(adap);
/* okay, now switch into reading mode */
addr |= 0x01;
ret = try_address(i2c_adap, addr, retries);
if ((ret!=1) && !nak_ok) {
printk(KERN_ERR "died at extended address code.\n");
return -EREMOTEIO;
}
}
} else { /* normal 7bit address */
addr = ( msg->addr << 1 );
if (flags & I2C_M_RD )
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR )
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
if ((ret!=1) && !nak_ok)
return -EREMOTEIO;
}
return 0;
}
static int bit_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct i2c_msg *pmsg;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i,ret;
unsigned short nak_ok;
i2c_start(adap);
for (i=0;i<num;i++) {
pmsg = &msgs[i];
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
if (!(pmsg->flags & I2C_M_NOSTART)) {
if (i) {
i2c_repstart(adap);
}
ret = bit_doAddress(i2c_adap, pmsg);
if ((ret != 0) && !nak_ok) {
DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.2x msg #%d\n"
,msgs[i].addr,i));
return (ret<0) ? ret : -EREMOTEIO;
}
}
if (pmsg->flags & I2C_M_RD ) {
/* read bytes into buffer*/
ret = readbytes(i2c_adap, pmsg);
DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: read %d bytes.\n",ret));
if (ret < pmsg->len ) {
return (ret<0)? ret : -EREMOTEIO;
}
} else {
/* write bytes from buffer */
ret = sendbytes(i2c_adap, pmsg);
DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret));
if (ret < pmsg->len ) {
return (ret<0) ? ret : -EREMOTEIO;
}
}
}
i2c_stop(adap);
return num;
}
static u32 bit_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
/* -----exported algorithm data: ------------------------------------- */
static struct i2c_algorithm i2c_bit_algo = {
.name = "Bit-shift algorithm",
.id = I2C_ALGO_BIT,
.master_xfer = bit_xfer,
.functionality = bit_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
struct i2c_algo_bit_data *bit_adap = adap->algo_data;
if (bit_test) {
int ret = test_bus(bit_adap, adap->name);
if (ret<0)
return -ENODEV;
}
DEB2(dev_dbg(&adap->dev, "hw routines registered.\n"));
/* register new adapter to i2c module... */
adap->id |= i2c_bit_algo.id;
adap->algo = &i2c_bit_algo;
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
i2c_add_adapter(adap);
return 0;
}
int i2c_bit_del_bus(struct i2c_adapter *adap)
{
return i2c_del_adapter(adap);
}
EXPORT_SYMBOL(i2c_bit_add_bus);
EXPORT_SYMBOL(i2c_bit_del_bus);
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm");
MODULE_LICENSE("GPL");
module_param(bit_test, bool, 0);
module_param(i2c_debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck");
MODULE_PARM_DESC(i2c_debug,
"debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol");

View File

@@ -0,0 +1,812 @@
/*
-------------------------------------------------------------------------
i2c-algo-ite.c i2c driver algorithms for ITE adapters
Hai-Pao Fan, MontaVista Software, Inc.
hpfan@mvista.com or source@mvista.com
Copyright 2000 MontaVista Software Inc.
---------------------------------------------------------------------------
This file was highly leveraged from i2c-algo-pcf.c, which was created
by Simon G. Vogl and Hans Berglund:
Copyright (C) 1995-1997 Simon G. Vogl
1998-2000 Hans Berglund
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi> and
Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
<mbailey@littlefeet-inc.com> */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-ite.h>
#include "i2c-algo-ite.h"
#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR
#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04
#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR
#define DEB2(x) if (i2c_debug>=2) x
#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/
#define DEF_TIMEOUT 16
/* module parameters:
*/
static int i2c_debug;
static int iic_test; /* see if the line-setting functions work */
/* --- setting states on the bus with the right timing: --------------- */
#define get_clock(adap) adap->getclock(adap->data)
#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val)
#define iic_inw(adap, reg) adap->getiic(adap->data, reg)
/* --- other auxiliary functions -------------------------------------- */
static void iic_start(struct i2c_algo_iic_data *adap)
{
iic_outw(adap,ITE_I2CHCR,ITE_CMD);
}
static void iic_stop(struct i2c_algo_iic_data *adap)
{
iic_outw(adap,ITE_I2CHCR,0);
iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI);
}
static void iic_reset(struct i2c_algo_iic_data *adap)
{
iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80);
}
static int wait_for_bb(struct i2c_algo_iic_data *adap)
{
int timeout = DEF_TIMEOUT;
short status;
status = iic_inw(adap, ITE_I2CHSR);
#ifndef STUB_I2C
while (timeout-- && (status & ITE_I2CHSR_HB)) {
udelay(1000); /* How much is this? */
status = iic_inw(adap, ITE_I2CHSR);
}
#endif
if (timeout<=0) {
printk(KERN_ERR "Timeout, host is busy\n");
iic_reset(adap);
}
return(timeout<=0);
}
/* After we issue a transaction on the IIC bus, this function
* is called. It puts this process to sleep until we get an interrupt from
* from the controller telling us that the transaction we requested in complete.
*/
static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) {
int timeout = DEF_TIMEOUT;
timeout = wait_for_bb(adap);
if (timeout) {
DEB2(printk("Timeout waiting for host not busy\n");)
return -EIO;
}
timeout = DEF_TIMEOUT;
*status = iic_inw(adap, ITE_I2CHSR);
#ifndef STUB_I2C
while (timeout-- && !(*status & ITE_I2CHSR_TDI)) {
adap->waitforpin();
*status = iic_inw(adap, ITE_I2CHSR);
}
#endif
if (timeout <= 0)
return(-1);
else
return(0);
}
static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status)
{
int timeout = DEF_TIMEOUT;
*status = iic_inw(adap, ITE_I2CFSR);
#ifndef STUB_I2C
while (timeout-- && (*status & ITE_I2CFSR_FE)) {
udelay(1000);
iic_inw(adap, ITE_I2CFSR);
}
#endif
if (timeout <= 0)
return(-1);
else
return(0);
}
static int iic_init (struct i2c_algo_iic_data *adap)
{
short i;
/* Clear bit 7 to set I2C to normal operation mode */
i=iic_inw(adap, PM_DSR)& 0xff7f;
iic_outw(adap, PM_DSR, i);
/* set IT_GPCCR port C bit 2&3 as function 2 */
i = iic_inw(adap, GPIO_CCR) & 0xfc0f;
iic_outw(adap,GPIO_CCR,i);
/* Clear slave address/sub-address */
iic_outw(adap,ITE_I2CSAR, 0);
iic_outw(adap,ITE_I2CSSAR, 0);
/* Set clock counter register */
iic_outw(adap,ITE_I2CCKCNT, get_clock(adap));
/* Set START/reSTART/STOP time registers */
iic_outw(adap,ITE_I2CSHDR, 0x0a);
iic_outw(adap,ITE_I2CRSUR, 0x0a);
iic_outw(adap,ITE_I2CPSUR, 0x0a);
/* Enable interrupts on completing the current transaction */
iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE);
/* Clear transfer count */
iic_outw(adap,ITE_I2CFBCR, 0x0);
DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n",
iic_inw(adap, ITE_I2CHSR)));
return 0;
}
/*
* Sanity check for the adapter hardware - check the reaction of
* the bus lines only if it seems to be idle.
*/
static int test_bus(struct i2c_algo_iic_data *adap, char *name) {
#if 0
int scl,sda;
sda=getsda(adap);
if (adap->getscl==NULL) {
printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n");
return 0;
}
scl=getscl(adap);
printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n",
name,getscl(adap),getsda(adap));
if (!scl || !sda ) {
printk("test_bus: %s seems to be busy.\n",adap->name);
goto bailout;
}
sdalo(adap);
printk("test_bus:1 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 != getsda(adap) ) {
printk("test_bus: %s SDA stuck high!\n",name);
sdahi(adap);
goto bailout;
}
if ( 0 == getscl(adap) ) {
printk("test_bus: %s SCL unexpected low while pulling SDA low!\n",
name);
goto bailout;
}
sdahi(adap);
printk("test_bus:2 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 == getsda(adap) ) {
printk("test_bus: %s SDA stuck low!\n",name);
sdahi(adap);
goto bailout;
}
if ( 0 == getscl(adap) ) {
printk("test_bus: %s SCL unexpected low while SDA high!\n",
adap->name);
goto bailout;
}
scllo(adap);
printk("test_bus:3 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 != getscl(adap) ) {
sclhi(adap);
goto bailout;
}
if ( 0 == getsda(adap) ) {
printk("test_bus: %s SDA unexpected low while pulling SCL low!\n",
name);
goto bailout;
}
sclhi(adap);
printk("test_bus:4 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 == getscl(adap) ) {
printk("test_bus: %s SCL stuck low!\n",name);
sclhi(adap);
goto bailout;
}
if ( 0 == getsda(adap) ) {
printk("test_bus: %s SDA unexpected low while SCL high!\n",
name);
goto bailout;
}
printk("test_bus: %s passed test.\n",name);
return 0;
bailout:
sdahi(adap);
sclhi(adap);
return -ENODEV;
#endif
return (0);
}
/* ----- Utility functions
*/
/* Verify the device we want to talk to on the IIC bus really exists. */
static inline int try_address(struct i2c_algo_iic_data *adap,
unsigned int addr, int retries)
{
int i, ret = -1;
short status;
for (i=0;i<retries;i++) {
iic_outw(adap, ITE_I2CSAR, addr);
iic_start(adap);
if (wait_for_pin(adap, &status) == 0) {
if ((status & ITE_I2CHSR_DNE) == 0) {
iic_stop(adap);
iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
ret=1;
break; /* success! */
}
}
iic_stop(adap);
udelay(adap->udelay);
}
DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i,
addr));
return ret;
}
static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf,
int count)
{
struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
int wrcount=0, timeout;
short status;
int loops, remainder, i, j;
union {
char byte[2];
unsigned short word;
} tmp;
iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]);
count--;
if (count == 0)
return -EIO;
loops = count / 32; /* 32-byte FIFO */
remainder = count % 32;
if(loops) {
for(i=0; i<loops; i++) {
iic_outw(adap, ITE_I2CFBCR, 32);
for(j=0; j<32/2; j++) {
tmp.byte[1] = buf[wrcount++];
tmp.byte[0] = buf[wrcount++];
iic_outw(adap, ITE_I2CFDR, tmp.word);
}
/* status FIFO overrun */
iic_inw(adap, ITE_I2CFSR);
iic_inw(adap, ITE_I2CFBCR);
iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
/* Wait for transmission to complete */
timeout = wait_for_pin(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
return -EREMOTEIO; /* got a better one ?? */
}
if (status & ITE_I2CHSR_DB) {
iic_stop(adap);
printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
return -EREMOTEIO; /* got a better one ?? */
}
}
}
if(remainder) {
iic_outw(adap, ITE_I2CFBCR, remainder);
for(i=0; i<remainder/2; i++) {
tmp.byte[1] = buf[wrcount++];
tmp.byte[0] = buf[wrcount++];
iic_outw(adap, ITE_I2CFDR, tmp.word);
}
/* status FIFO overrun */
iic_inw(adap, ITE_I2CFSR);
iic_inw(adap, ITE_I2CFBCR);
iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */
timeout = wait_for_pin(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name);
return -EREMOTEIO; /* got a better one ?? */
}
#ifndef STUB_I2C
if (status & ITE_I2CHSR_DB) {
iic_stop(adap);
printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name);
return -EREMOTEIO; /* got a better one ?? */
}
#endif
}
iic_stop(adap);
return wrcount;
}
static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count,
int sread)
{
int rdcount=0, i, timeout;
short status;
struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
int loops, remainder, j;
union {
char byte[2];
unsigned short word;
} tmp;
loops = count / 32; /* 32-byte FIFO */
remainder = count % 32;
if(loops) {
for(i=0; i<loops; i++) {
iic_outw(adap, ITE_I2CFBCR, 32);
if (sread)
iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
else
iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
timeout = wait_for_pin(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
return (-1);
}
#ifndef STUB_I2C
if (status & ITE_I2CHSR_DB) {
iic_stop(adap);
printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
return (-1);
}
#endif
timeout = wait_for_fe(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
return (-1);
}
for(j=0; j<32/2; j++) {
tmp.word = iic_inw(adap, ITE_I2CFDR);
buf[rdcount++] = tmp.byte[1];
buf[rdcount++] = tmp.byte[0];
}
/* status FIFO underrun */
iic_inw(adap, ITE_I2CFSR);
}
}
if(remainder) {
remainder=(remainder+1)/2 * 2;
iic_outw(adap, ITE_I2CFBCR, remainder);
if (sread)
iic_outw(adap, ITE_I2CHCR, ITE_SREAD);
else
iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */
timeout = wait_for_pin(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_readbytes: %s read timeout.\n", i2c_adap->name);
return (-1);
}
#ifndef STUB_I2C
if (status & ITE_I2CHSR_DB) {
iic_stop(adap);
printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name);
return (-1);
}
#endif
timeout = wait_for_fe(adap, &status);
if(timeout) {
iic_stop(adap);
printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name);
return (-1);
}
for(i=0; i<(remainder+1)/2; i++) {
tmp.word = iic_inw(adap, ITE_I2CFDR);
buf[rdcount++] = tmp.byte[1];
buf[rdcount++] = tmp.byte[0];
}
/* status FIFO underrun */
iic_inw(adap, ITE_I2CFSR);
}
iic_stop(adap);
return rdcount;
}
/* This function implements combined transactions. Combined
* transactions consist of combinations of reading and writing blocks of data.
* Each transfer (i.e. a read or a write) is separated by a repeated start
* condition.
*/
#if 0
static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
int i;
struct i2c_msg *pmsg;
int ret;
DEB2(printk("Beginning combined transaction\n"));
for(i=0; i<(num-1); i++) {
pmsg = &msgs[i];
if(pmsg->flags & I2C_M_RD) {
DEB2(printk(" This one is a read\n"));
ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
}
else if(!(pmsg->flags & I2C_M_RD)) {
DEB2(printk("This one is a write\n"));
ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER);
}
}
/* Last read or write segment needs to be terminated with a stop */
pmsg = &msgs[i];
if(pmsg->flags & I2C_M_RD) {
DEB2(printk("Doing the last read\n"));
ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
}
else if(!(pmsg->flags & I2C_M_RD)) {
DEB2(printk("Doing the last write\n"));
ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER);
}
return ret;
}
#endif
/* Whenever we initiate a transaction, the first byte clocked
* onto the bus after the start condition is the address (7 bit) of the
* device we want to talk to. This function manipulates the address specified
* so that it makes sense to the hardware when written to the IIC peripheral.
*
* Note: 10 bit addresses are not supported in this driver, although they are
* supported by the hardware. This functionality needs to be implemented.
*/
static inline int iic_doAddress(struct i2c_algo_iic_data *adap,
struct i2c_msg *msg, int retries)
{
unsigned short flags = msg->flags;
unsigned int addr;
int ret;
/* Ten bit addresses not supported right now */
if ( (flags & I2C_M_TEN) ) {
#if 0
addr = 0xf0 | (( msg->addr >> 7) & 0x03);
DEB2(printk("addr0: %d\n",addr));
ret = try_address(adap, addr, retries);
if (ret!=1) {
printk("iic_doAddress: died at extended address code.\n");
return -EREMOTEIO;
}
iic_outw(adap,msg->addr & 0x7f);
if (ret != 1) {
printk("iic_doAddress: died at 2nd address code.\n");
return -EREMOTEIO;
}
if ( flags & I2C_M_RD ) {
i2c_repstart(adap);
addr |= 0x01;
ret = try_address(adap, addr, retries);
if (ret!=1) {
printk("iic_doAddress: died at extended address code.\n");
return -EREMOTEIO;
}
}
#endif
} else {
addr = ( msg->addr << 1 );
#if 0
if (flags & I2C_M_RD )
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR )
addr ^= 1;
#endif
if (iic_inw(adap, ITE_I2CSAR) != addr) {
iic_outw(adap, ITE_I2CSAR, addr);
ret = try_address(adap, addr, retries);
if (ret!=1) {
printk("iic_doAddress: died at address code.\n");
return -EREMOTEIO;
}
}
}
return 0;
}
/* Description: Prepares the controller for a transaction (clearing status
* registers, data buffers, etc), and then calls either iic_readbytes or
* iic_sendbytes to do the actual transaction.
*
* still to be done: Before we issue a transaction, we should
* verify that the bus is not busy or in some unknown state.
*/
static int iic_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs,
int num)
{
struct i2c_algo_iic_data *adap = i2c_adap->algo_data;
struct i2c_msg *pmsg;
int i = 0;
int ret, timeout;
pmsg = &msgs[i];
if(!pmsg->len) {
DEB2(printk("iic_xfer: read/write length is 0\n");)
return -EIO;
}
if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) {
DEB2(printk("iic_xfer: write buffer length is not odd\n");)
return -EIO;
}
/* Wait for any pending transfers to complete */
timeout = wait_for_bb(adap);
if (timeout) {
DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");)
return -EIO;
}
/* Flush FIFO */
iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
/* Load address */
ret = iic_doAddress(adap, pmsg, i2c_adap->retries);
if (ret)
return -EIO;
#if 0
/* Combined transaction (read and write) */
if(num > 1) {
DEB2(printk("iic_xfer: Call combined transaction\n"));
ret = iic_combined_transaction(i2c_adap, msgs, num);
}
#endif
DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
if(pmsg->flags & I2C_M_RD) /* Read */
ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0);
else { /* Write */
udelay(1000);
ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len);
}
if (ret != pmsg->len)
DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret));
else
DEB3(printk("iic_xfer: read/write %d bytes.\n",ret));
return ret;
}
/* Implements device specific ioctls. Higher level ioctls can
* be found in i2c-core.c and are typical of any i2c controller (specifying
* slave address, timeouts, etc). These ioctls take advantage of any hardware
* features built into the controller for which this algorithm-adapter set
* was written. These ioctls allow you to take control of the data and clock
* lines and set the either high or low,
* similar to a GPIO pin.
*/
static int algo_control(struct i2c_adapter *adapter,
unsigned int cmd, unsigned long arg)
{
struct i2c_algo_iic_data *adap = adapter->algo_data;
struct i2c_iic_msg s_msg;
char *buf;
int ret;
if (cmd == I2C_SREAD) {
if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg,
sizeof(struct i2c_iic_msg)))
return -EFAULT;
buf = kmalloc(s_msg.len, GFP_KERNEL);
if (buf== NULL)
return -ENOMEM;
/* Flush FIFO */
iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH);
/* Load address */
iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1);
iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff);
ret = iic_readbytes(adapter, buf, s_msg.len, 1);
if (ret>=0) {
if(copy_to_user( s_msg.buf, buf, s_msg.len) )
ret = -EFAULT;
}
kfree(buf);
}
return 0;
}
static u32 iic_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
I2C_FUNC_PROTOCOL_MANGLING;
}
/* -----exported algorithm data: ------------------------------------- */
static struct i2c_algorithm iic_algo = {
.name = "ITE IIC algorithm",
.id = I2C_ALGO_IIC,
.master_xfer = iic_xfer,
.algo_control = algo_control, /* ioctl */
.functionality = iic_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_iic_add_bus(struct i2c_adapter *adap)
{
struct i2c_algo_iic_data *iic_adap = adap->algo_data;
if (iic_test) {
int ret = test_bus(iic_adap, adap->name);
if (ret<0)
return -ENODEV;
}
DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n",
adap->name));
/* register new adapter to i2c module... */
adap->id |= iic_algo.id;
adap->algo = &iic_algo;
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
adap->flags = 0;
i2c_add_adapter(adap);
iic_init(iic_adap);
return 0;
}
int i2c_iic_del_bus(struct i2c_adapter *adap)
{
int res;
if ((res = i2c_del_adapter(adap)) < 0)
return res;
DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name));
return 0;
}
int __init i2c_algo_iic_init (void)
{
printk(KERN_INFO "ITE iic (i2c) algorithm module\n");
return 0;
}
void i2c_algo_iic_exit(void)
{
return;
}
EXPORT_SYMBOL(i2c_iic_add_bus);
EXPORT_SYMBOL(i2c_iic_del_bus);
/* The MODULE_* macros resolve to nothing if MODULES is not defined
* when this file is compiled.
*/
MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
MODULE_DESCRIPTION("ITE iic algorithm");
MODULE_LICENSE("GPL");
module_param(iic_test, bool, 0);
module_param(i2c_debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available");
MODULE_PARM_DESC(i2c_debug,
"debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol");
/* This function resolves to init_module (the function invoked when a module
* is loaded via insmod) when this file is compiled with MODULES defined.
* Otherwise (i.e. if you want this driver statically linked to the kernel),
* a pointer to this function is stored in a table and called
* during the initialization of the kernel (in do_basic_setup in /init/main.c)
*
* All this functionality is complements of the macros defined in linux/init.h
*/
module_init(i2c_algo_iic_init);
/* If MODULES is defined when this file is compiled, then this function will
* resolved to cleanup_module.
*/
module_exit(i2c_algo_iic_exit);

View File

@@ -0,0 +1,117 @@
/*
--------------------------------------------------------------------
i2c-ite.h: Global defines for the I2C controller on board the
ITE MIPS processor.
--------------------------------------------------------------------
Hai-Pao Fan, MontaVista Software, Inc.
hpfan@mvista.com or source@mvista.com
Copyright 2001 MontaVista Software Inc.
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*/
#ifndef I2C_ITE_H
#define I2C_ITE_H 1
#include <asm/it8172/it8172.h>
/* I2C Registers */
#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30
#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34
#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38
#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c
#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48
#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c
#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50
#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54
#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70
#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74
#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78
#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c
/* Host Control Register ITE_I2CHCR */
#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */
#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing
the current transaction */
#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */
#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */
#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */
#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute
the command and send the data programmed in
all required registers to I2C bus */
#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST
#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W
#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R
#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S
/* Host Status Register ITE_I2CHSR */
#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except
in the first and last bytes */
#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */
#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */
#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */
#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */
/* Slave Address Register ITE_I2CSAR */
#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */
#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the
next slave address is addressed */
/* Slave Sub-address Register ITE_I2CSSAR */
#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */
/* Clock Counter Register ITE_I2CCKCNT */
#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */
#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */
#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */
/* START Hold Time Register ITE_I2CSHDR */
/* value is counted based on 16 MHz internal clock */
#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */
#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */
/* (Repeated) START Setup Time Register ITE_I2CRSUR */
/* value is counted based on 16 MHz internal clock */
#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */
#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */
/* STOP setup Time Register ITE_I2CPSUR */
/* FIFO Data Register ITE_I2CFDR */
#define ITE_I2CFDR_MASK 0xff
/* FIFO Byte Count Register ITE_I2CFBCR */
#define ITE_I2CFBCR_MASK 0x3f
/* FIFO Control Register ITE_I2CFCR */
#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point
and I2CFSR */
/* FIFO Status Register ITE_I2CFSR */
#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */
#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */
#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */
#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */
#endif /* I2C_ITE_H */

View File

@@ -0,0 +1,399 @@
/*
* i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters
* Copyright (C) 2004 Arcom Control Systems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-pca.h>
#include "i2c-algo-pca.h"
#define DRIVER "i2c-algo-pca"
#define DEB1(fmt, args...) do { if (i2c_debug>=1) printk(fmt, ## args); } while(0)
#define DEB2(fmt, args...) do { if (i2c_debug>=2) printk(fmt, ## args); } while(0)
#define DEB3(fmt, args...) do { if (i2c_debug>=3) printk(fmt, ## args); } while(0)
static int i2c_debug=0;
#define pca_outw(adap, reg, val) adap->write_byte(adap, reg, val)
#define pca_inw(adap, reg) adap->read_byte(adap, reg)
#define pca_status(adap) pca_inw(adap, I2C_PCA_STA)
#define pca_clock(adap) adap->get_clock(adap)
#define pca_own(adap) adap->get_own(adap)
#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val)
#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON)
#define pca_wait(adap) adap->wait_for_interrupt(adap)
/*
* Generate a start condition on the i2c bus.
*
* returns after the start condition has occured
*/
static void pca_start(struct i2c_algo_pca_data *adap)
{
int sta = pca_get_con(adap);
DEB2("=== START\n");
sta |= I2C_PCA_CON_STA;
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI);
pca_set_con(adap, sta);
pca_wait(adap);
}
/*
* Generate a repeated start condition on the i2c bus
*
* return after the repeated start condition has occured
*/
static void pca_repeated_start(struct i2c_algo_pca_data *adap)
{
int sta = pca_get_con(adap);
DEB2("=== REPEATED START\n");
sta |= I2C_PCA_CON_STA;
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI);
pca_set_con(adap, sta);
pca_wait(adap);
}
/*
* Generate a stop condition on the i2c bus
*
* returns after the stop condition has been generated
*
* STOPs do not generate an interrupt or set the SI flag, since the
* part returns the the idle state (0xf8). Hence we don't need to
* pca_wait here.
*/
static void pca_stop(struct i2c_algo_pca_data *adap)
{
int sta = pca_get_con(adap);
DEB2("=== STOP\n");
sta |= I2C_PCA_CON_STO;
sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI);
pca_set_con(adap, sta);
}
/*
* Send the slave address and R/W bit
*
* returns after the address has been sent
*/
static void pca_address(struct i2c_algo_pca_data *adap,
struct i2c_msg *msg)
{
int sta = pca_get_con(adap);
int addr;
addr = ( (0x7f & msg->addr) << 1 );
if (msg->flags & I2C_M_RD )
addr |= 1;
DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n",
msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr);
pca_outw(adap, I2C_PCA_DAT, addr);
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI);
pca_set_con(adap, sta);
pca_wait(adap);
}
/*
* Transmit a byte.
*
* Returns after the byte has been transmitted
*/
static void pca_tx_byte(struct i2c_algo_pca_data *adap,
__u8 b)
{
int sta = pca_get_con(adap);
DEB2("=== WRITE %#04x\n", b);
pca_outw(adap, I2C_PCA_DAT, b);
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI);
pca_set_con(adap, sta);
pca_wait(adap);
}
/*
* Receive a byte
*
* returns immediately.
*/
static void pca_rx_byte(struct i2c_algo_pca_data *adap,
__u8 *b, int ack)
{
*b = pca_inw(adap, I2C_PCA_DAT);
DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK");
}
/*
* Setup ACK or NACK for next received byte and wait for it to arrive.
*
* Returns after next byte has arrived.
*/
static void pca_rx_ack(struct i2c_algo_pca_data *adap,
int ack)
{
int sta = pca_get_con(adap);
sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA);
if ( ack )
sta |= I2C_PCA_CON_AA;
pca_set_con(adap, sta);
pca_wait(adap);
}
/*
* Reset the i2c bus / SIO
*/
static void pca_reset(struct i2c_algo_pca_data *adap)
{
/* apparently only an external reset will do it. not a lot can be done */
printk(KERN_ERR DRIVER ": Haven't figured out how to do a reset yet\n");
}
static int pca_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs,
int num)
{
struct i2c_algo_pca_data *adap = i2c_adap->algo_data;
struct i2c_msg *msg = NULL;
int curmsg;
int numbytes = 0;
int state;
int ret;
state = pca_status(adap);
if ( state != 0xF8 ) {
dev_dbg(&i2c_adap->dev, "bus is not idle. status is %#04x\n", state );
/* FIXME: what to do. Force stop ? */
return -EREMOTEIO;
}
DEB1("{{{ XFER %d messages\n", num);
if (i2c_debug>=2) {
for (curmsg = 0; curmsg < num; curmsg++) {
int addr, i;
msg = &msgs[curmsg];
addr = (0x7f & msg->addr) ;
if (msg->flags & I2C_M_RD )
printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n",
curmsg, msg->len, addr, (addr<<1) | 1);
else {
printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s",
curmsg, msg->len, addr, addr<<1,
msg->len == 0 ? "" : ", ");
for(i=0; i < msg->len; i++)
printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", ");
printk("]\n");
}
}
}
curmsg = 0;
ret = -EREMOTEIO;
while (curmsg < num) {
state = pca_status(adap);
DEB3("STATE is 0x%02x\n", state);
msg = &msgs[curmsg];
switch (state) {
case 0xf8: /* On reset or stop the bus is idle */
pca_start(adap);
break;
case 0x08: /* A START condition has been transmitted */
case 0x10: /* A repeated start condition has been transmitted */
pca_address(adap, msg);
break;
case 0x18: /* SLA+W has been transmitted; ACK has been received */
case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */
if (numbytes < msg->len) {
pca_tx_byte(adap, msg->buf[numbytes]);
numbytes++;
break;
}
curmsg++; numbytes = 0;
if (curmsg == num)
pca_stop(adap);
else
pca_repeated_start(adap);
break;
case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+W\n");
pca_stop(adap);
goto out;
case 0x40: /* SLA+R has been transmitted; ACK has been received */
pca_rx_ack(adap, msg->len > 1);
break;
case 0x50: /* Data bytes has been received; ACK has been returned */
if (numbytes < msg->len) {
pca_rx_byte(adap, &msg->buf[numbytes], 1);
numbytes++;
pca_rx_ack(adap, numbytes < msg->len - 1);
break;
}
curmsg++; numbytes = 0;
if (curmsg == num)
pca_stop(adap);
else
pca_repeated_start(adap);
break;
case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after SLA+R\n");
pca_stop(adap);
goto out;
case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */
DEB2("NOT ACK received after data byte\n");
goto out;
case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */
DEB2("Arbitration lost\n");
goto out;
case 0x58: /* Data byte has been received; NOT ACK has been returned */
if ( numbytes == msg->len - 1 ) {
pca_rx_byte(adap, &msg->buf[numbytes], 0);
curmsg++; numbytes = 0;
if (curmsg == num)
pca_stop(adap);
else
pca_repeated_start(adap);
} else {
DEB2("NOT ACK sent after data byte received. "
"Not final byte. numbytes %d. len %d\n",
numbytes, msg->len);
pca_stop(adap);
goto out;
}
break;
case 0x70: /* Bus error - SDA stuck low */
DEB2("BUS ERROR - SDA Stuck low\n");
pca_reset(adap);
goto out;
case 0x90: /* Bus error - SCL stuck low */
DEB2("BUS ERROR - SCL Stuck low\n");
pca_reset(adap);
goto out;
case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */
DEB2("BUS ERROR - Illegal START or STOP\n");
pca_reset(adap);
goto out;
default:
printk(KERN_ERR DRIVER ": unhandled SIO state 0x%02x\n", state);
break;
}
}
ret = curmsg;
out:
DEB1(KERN_CRIT "}}} transfered %d/%d messages. "
"status is %#04x. control is %#04x\n",
curmsg, num, pca_status(adap),
pca_get_con(adap));
return ret;
}
static u32 pca_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static int pca_init(struct i2c_algo_pca_data *adap)
{
static int freqs[] = {330,288,217,146,88,59,44,36};
int own, clock;
own = pca_own(adap);
clock = pca_clock(adap);
DEB1(KERN_INFO DRIVER ": own address is %#04x\n", own);
DEB1(KERN_INFO DRIVER ": clock freqeuncy is %dkHz\n", freqs[clock]);
pca_outw(adap, I2C_PCA_ADR, own << 1);
pca_set_con(adap, I2C_PCA_CON_ENSIO | clock);
udelay(500); /* 500 <20>s for oscilator to stabilise */
return 0;
}
static struct i2c_algorithm pca_algo = {
.name = "PCA9564 algorithm",
.id = I2C_ALGO_PCA,
.master_xfer = pca_xfer,
.functionality = pca_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_pca_add_bus(struct i2c_adapter *adap)
{
struct i2c_algo_pca_data *pca_adap = adap->algo_data;
int rval;
/* register new adapter to i2c module... */
adap->id |= pca_algo.id;
adap->algo = &pca_algo;
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
rval = pca_init(pca_adap);
if (!rval)
i2c_add_adapter(adap);
return rval;
}
int i2c_pca_del_bus(struct i2c_adapter *adap)
{
return i2c_del_adapter(adap);
}
EXPORT_SYMBOL(i2c_pca_add_bus);
EXPORT_SYMBOL(i2c_pca_del_bus);
MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm");
MODULE_LICENSE("GPL");
module_param(i2c_debug, int, 0);

View File

@@ -0,0 +1,26 @@
#ifndef I2C_PCA9564_H
#define I2C_PCA9564_H 1
#define I2C_PCA_STA 0x00 /* STATUS Read Only */
#define I2C_PCA_TO 0x00 /* TIMEOUT Write Only */
#define I2C_PCA_DAT 0x01 /* DATA Read/Write */
#define I2C_PCA_ADR 0x02 /* OWN ADR Read/Write */
#define I2C_PCA_CON 0x03 /* CONTROL Read/Write */
#define I2C_PCA_CON_AA 0x80 /* Assert Acknowledge */
#define I2C_PCA_CON_ENSIO 0x40 /* Enable */
#define I2C_PCA_CON_STA 0x20 /* Start */
#define I2C_PCA_CON_STO 0x10 /* Stop */
#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */
#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */
#define I2C_PCA_CON_330kHz 0x00
#define I2C_PCA_CON_288kHz 0x01
#define I2C_PCA_CON_217kHz 0x02
#define I2C_PCA_CON_146kHz 0x03
#define I2C_PCA_CON_88kHz 0x04
#define I2C_PCA_CON_59kHz 0x05
#define I2C_PCA_CON_44kHz 0x06
#define I2C_PCA_CON_36kHz 0x07
#endif /* I2C_PCA9564_H */

View File

@@ -0,0 +1,507 @@
/* ------------------------------------------------------------------------- */
/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-1997 Simon G. Vogl
1998-2000 Hans Berglund
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi> and
Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
<mbailey@littlefeet-inc.com> */
/* Partially rewriten by Oleg I. Vdovikin <vdovikin@jscc.ru> to handle multiple
messages, proper stop/repstart signaling during receive,
added detect code */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-pcf.h>
#include "i2c-algo-pcf.h"
#define DEB2(x) if (i2c_debug>=2) x
#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/
#define DEBPROTO(x) if (i2c_debug>=9) x;
/* debug the protocol by showing transferred bits */
#define DEF_TIMEOUT 16
/* module parameters:
*/
static int i2c_debug;
/* --- setting states on the bus with the right timing: --------------- */
#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val)
#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl)
#define get_own(adap) adap->getown(adap->data)
#define get_clock(adap) adap->getclock(adap->data)
#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val)
#define i2c_inb(adap) adap->getpcf(adap->data, 0)
/* --- other auxiliary functions -------------------------------------- */
static void i2c_start(struct i2c_algo_pcf_data *adap)
{
DEBPROTO(printk("S "));
set_pcf(adap, 1, I2C_PCF_START);
}
static void i2c_repstart(struct i2c_algo_pcf_data *adap)
{
DEBPROTO(printk(" Sr "));
set_pcf(adap, 1, I2C_PCF_REPSTART);
}
static void i2c_stop(struct i2c_algo_pcf_data *adap)
{
DEBPROTO(printk("P\n"));
set_pcf(adap, 1, I2C_PCF_STOP);
}
static int wait_for_bb(struct i2c_algo_pcf_data *adap) {
int timeout = DEF_TIMEOUT;
int status;
status = get_pcf(adap, 1);
#ifndef STUB_I2C
while (timeout-- && !(status & I2C_PCF_BB)) {
udelay(100); /* wait for 100 us */
status = get_pcf(adap, 1);
}
#endif
if (timeout <= 0) {
printk(KERN_ERR "Timeout waiting for Bus Busy\n");
}
return (timeout<=0);
}
static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) {
int timeout = DEF_TIMEOUT;
*status = get_pcf(adap, 1);
#ifndef STUB_I2C
while (timeout-- && (*status & I2C_PCF_PIN)) {
adap->waitforpin();
*status = get_pcf(adap, 1);
}
if (*status & I2C_PCF_LAB) {
DEB2(printk(KERN_INFO
"i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n",
*status));
/* Cleanup from LAB-- reset and enable ESO.
* This resets the PCF8584; since we've lost the bus, no
* further attempts should be made by callers to clean up
* (no i2c_stop() etc.)
*/
set_pcf(adap, 1, I2C_PCF_PIN);
set_pcf(adap, 1, I2C_PCF_ESO);
/* TODO: we should pause for a time period sufficient for any
* running I2C transaction to complete-- the arbitration
* logic won't work properly until the next START is seen.
*/
DEB2(printk(KERN_INFO
"i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n",
get_pcf(adap,1)));
return(-EINTR);
}
#endif
if (timeout <= 0)
return(-1);
else
return(0);
}
/*
* This should perform the 'PCF8584 initialization sequence' as described
* in the Philips IC12 data book (1995, Aug 29).
* There should be a 30 clock cycle wait after reset, I assume this
* has been fulfilled.
* There should be a delay at the end equal to the longest I2C message
* to synchronize the BB-bit (in multimaster systems). How long is
* this? I assume 1 second is always long enough.
*
* vdovikin: added detect code for PCF8584
*/
static int pcf_init_8584 (struct i2c_algo_pcf_data *adap)
{
unsigned char temp;
DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: PCF state 0x%02x\n", get_pcf(adap, 1)));
/* S1=0x80: S0 selected, serial interface off */
set_pcf(adap, 1, I2C_PCF_PIN);
/* check to see S1 now used as R/W ctrl -
PCF8584 does that when ESO is zero */
if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp));
return -ENXIO; /* definetly not PCF8584 */
}
/* load own address in S0, effective address is (own << 1) */
i2c_outb(adap, get_own(adap));
/* check it's really written */
if ((temp = i2c_inb(adap)) != get_own(adap)) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S0 (0x%02x).\n", temp));
return -ENXIO;
}
/* S1=0xA0, next byte in S2 */
set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1);
/* check to see S2 now selected */
if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp));
return -ENXIO;
}
/* load clock register S2 */
i2c_outb(adap, get_clock(adap));
/* check it's really written, the only 5 lowest bits does matter */
if (((temp = i2c_inb(adap)) & 0x1f) != get_clock(adap)) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S2 (0x%02x).\n", temp));
return -ENXIO;
}
/* Enable serial interface, idle, S0 selected */
set_pcf(adap, 1, I2C_PCF_IDLE);
/* check to see PCF is really idled and we can access status register */
if ((temp = get_pcf(adap, 1)) != (I2C_PCF_PIN | I2C_PCF_BB)) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S1` (0x%02x).\n", temp));
return -ENXIO;
}
printk(KERN_DEBUG "i2c-algo-pcf.o: deteted and initialized PCF8584.\n");
return 0;
}
/* ----- Utility functions
*/
static inline int try_address(struct i2c_algo_pcf_data *adap,
unsigned char addr, int retries)
{
int i, status, ret = -1;
int wfp;
for (i=0;i<retries;i++) {
i2c_outb(adap, addr);
i2c_start(adap);
status = get_pcf(adap, 1);
if ((wfp = wait_for_pin(adap, &status)) >= 0) {
if ((status & I2C_PCF_LRB) == 0) {
i2c_stop(adap);
break; /* success! */
}
}
if (wfp == -EINTR) {
/* arbitration lost */
udelay(adap->udelay);
return -EINTR;
}
i2c_stop(adap);
udelay(adap->udelay);
}
DEB2(if (i) printk(KERN_DEBUG "i2c-algo-pcf.o: needed %d retries for %d\n",i,
addr));
return ret;
}
static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf,
int count, int last)
{
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
int wrcount, status, timeout;
for (wrcount=0; wrcount<count; ++wrcount) {
DEB2(dev_dbg(&i2c_adap->dev, "i2c_write: writing %2.2X\n",
buf[wrcount]&0xff));
i2c_outb(adap, buf[wrcount]);
timeout = wait_for_pin(adap, &status);
if (timeout) {
if (timeout == -EINTR) {
/* arbitration lost */
return -EINTR;
}
i2c_stop(adap);
dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n");
return -EREMOTEIO; /* got a better one ?? */
}
#ifndef STUB_I2C
if (status & I2C_PCF_LRB) {
i2c_stop(adap);
dev_err(&i2c_adap->dev, "i2c_write: error - no ack.\n");
return -EREMOTEIO; /* got a better one ?? */
}
#endif
}
if (last) {
i2c_stop(adap);
}
else {
i2c_repstart(adap);
}
return (wrcount);
}
static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf,
int count, int last)
{
int i, status;
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
int wfp;
/* increment number of bytes to read by one -- read dummy byte */
for (i = 0; i <= count; i++) {
if ((wfp = wait_for_pin(adap, &status))) {
if (wfp == -EINTR) {
/* arbitration lost */
return -EINTR;
}
i2c_stop(adap);
dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n");
return (-1);
}
#ifndef STUB_I2C
if ((status & I2C_PCF_LRB) && (i != count)) {
i2c_stop(adap);
dev_err(&i2c_adap->dev, "i2c_read: i2c_inb, No ack.\n");
return (-1);
}
#endif
if (i == count - 1) {
set_pcf(adap, 1, I2C_PCF_ESO);
} else
if (i == count) {
if (last) {
i2c_stop(adap);
} else {
i2c_repstart(adap);
}
};
if (i) {
buf[i - 1] = i2c_inb(adap);
} else {
i2c_inb(adap); /* dummy read */
}
}
return (i - 1);
}
static inline int pcf_doAddress(struct i2c_algo_pcf_data *adap,
struct i2c_msg *msg, int retries)
{
unsigned short flags = msg->flags;
unsigned char addr;
int ret;
if ( (flags & I2C_M_TEN) ) {
/* a ten bit address */
addr = 0xf0 | (( msg->addr >> 7) & 0x03);
DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
/* try extended address code...*/
ret = try_address(adap, addr, retries);
if (ret!=1) {
printk(KERN_ERR "died at extended address code.\n");
return -EREMOTEIO;
}
/* the remaining 8 bit address */
i2c_outb(adap,msg->addr & 0x7f);
/* Status check comes here */
if (ret != 1) {
printk(KERN_ERR "died at 2nd address code.\n");
return -EREMOTEIO;
}
if ( flags & I2C_M_RD ) {
i2c_repstart(adap);
/* okay, now switch into reading mode */
addr |= 0x01;
ret = try_address(adap, addr, retries);
if (ret!=1) {
printk(KERN_ERR "died at extended address code.\n");
return -EREMOTEIO;
}
}
} else { /* normal 7bit address */
addr = ( msg->addr << 1 );
if (flags & I2C_M_RD )
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR )
addr ^= 1;
i2c_outb(adap, addr);
}
return 0;
}
static int pcf_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs,
int num)
{
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
struct i2c_msg *pmsg;
int i;
int ret=0, timeout, status;
/* Check for bus busy */
timeout = wait_for_bb(adap);
if (timeout) {
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: "
"Timeout waiting for BB in pcf_xfer\n");)
return -EIO;
}
for (i = 0;ret >= 0 && i < num; i++) {
pmsg = &msgs[i];
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n",
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, pmsg->addr, i + 1, num);)
ret = pcf_doAddress(adap, pmsg, i2c_adap->retries);
/* Send START */
if (i == 0) {
i2c_start(adap);
}
/* Wait for PIN (pending interrupt NOT) */
timeout = wait_for_pin(adap, &status);
if (timeout) {
if (timeout == -EINTR) {
/* arbitration lost */
return (-EINTR);
}
i2c_stop(adap);
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting "
"for PIN(1) in pcf_xfer\n");)
return (-EREMOTEIO);
}
#ifndef STUB_I2C
/* Check LRB (last rcvd bit - slave ack) */
if (status & I2C_PCF_LRB) {
i2c_stop(adap);
DEB2(printk(KERN_ERR "i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");)
return (-EREMOTEIO);
}
#endif
DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
/* Read */
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer*/
ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len,
(i + 1 == num));
if (ret != pmsg->len) {
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
"only read %d bytes.\n",ret));
} else {
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: read %d bytes.\n",ret));
}
} else { /* Write */
ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len,
(i + 1 == num));
if (ret != pmsg->len) {
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
"only wrote %d bytes.\n",ret));
} else {
DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: wrote %d bytes.\n",ret));
}
}
}
return (i);
}
static u32 pcf_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
/* -----exported algorithm data: ------------------------------------- */
static struct i2c_algorithm pcf_algo = {
.name = "PCF8584 algorithm",
.id = I2C_ALGO_PCF,
.master_xfer = pcf_xfer,
.functionality = pcf_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_pcf_add_bus(struct i2c_adapter *adap)
{
struct i2c_algo_pcf_data *pcf_adap = adap->algo_data;
int rval;
DEB2(dev_dbg(&adap->dev, "hw routines registered.\n"));
/* register new adapter to i2c module... */
adap->id |= pcf_algo.id;
adap->algo = &pcf_algo;
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
rval = pcf_init_8584(pcf_adap);
if (!rval)
i2c_add_adapter(adap);
return rval;
}
int i2c_pcf_del_bus(struct i2c_adapter *adap)
{
return i2c_del_adapter(adap);
}
EXPORT_SYMBOL(i2c_pcf_add_bus);
EXPORT_SYMBOL(i2c_pcf_del_bus);
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm");
MODULE_LICENSE("GPL");
module_param(i2c_debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(i2c_debug,
"debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol");

View File

@@ -0,0 +1,76 @@
/* -------------------------------------------------------------------- */
/* i2c-pcf8584.h: PCF 8584 global defines */
/* -------------------------------------------------------------------- */
/* Copyright (C) 1996 Simon G. Vogl
1999 Hans Berglund
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. */
/* -------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl> */
#ifndef I2C_PCF8584_H
#define I2C_PCF8584_H 1
/* ----- Control register bits ---------------------------------------- */
#define I2C_PCF_PIN 0x80
#define I2C_PCF_ESO 0x40
#define I2C_PCF_ES1 0x20
#define I2C_PCF_ES2 0x10
#define I2C_PCF_ENI 0x08
#define I2C_PCF_STA 0x04
#define I2C_PCF_STO 0x02
#define I2C_PCF_ACK 0x01
#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)
#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK)
/* ----- Status register bits ----------------------------------------- */
/*#define I2C_PCF_PIN 0x80 as above*/
#define I2C_PCF_INI 0x40 /* 1 if not initialized */
#define I2C_PCF_STS 0x20
#define I2C_PCF_BER 0x10
#define I2C_PCF_AD0 0x08
#define I2C_PCF_LRB 0x08
#define I2C_PCF_AAS 0x04
#define I2C_PCF_LAB 0x02
#define I2C_PCF_BB 0x01
/* ----- Chip clock frequencies --------------------------------------- */
#define I2C_PCF_CLK3 0x00
#define I2C_PCF_CLK443 0x10
#define I2C_PCF_CLK6 0x14
#define I2C_PCF_CLK 0x18
#define I2C_PCF_CLK12 0x1c
/* ----- transmission frequencies ------------------------------------- */
#define I2C_PCF_TRNS90 0x00 /* 90 kHz */
#define I2C_PCF_TRNS45 0x01 /* 45 kHz */
#define I2C_PCF_TRNS11 0x02 /* 11 kHz */
#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */
/* ----- Access to internal registers according to ES1,ES2 ------------ */
/* they are mapped to the data port ( a0 = 0 ) */
/* available when ESO == 0 : */
#define I2C_PCF_OWNADR 0
#define I2C_PCF_INTREG I2C_PCF_ES2
#define I2C_PCF_CLKREG I2C_PCF_ES1
#endif /* I2C_PCF8584_H */

View File

@@ -0,0 +1,189 @@
/*
* i2c-algo-sgi.c: i2c driver algorithms for SGI adapters.
*
* This file is subject to the terms and conditions of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-sgi.h>
#define SGI_I2C_FORCE_IDLE (0 << 0)
#define SGI_I2C_NOT_IDLE (1 << 0)
#define SGI_I2C_WRITE (0 << 1)
#define SGI_I2C_READ (1 << 1)
#define SGI_I2C_RELEASE_BUS (0 << 2)
#define SGI_I2C_HOLD_BUS (1 << 2)
#define SGI_I2C_XFER_DONE (0 << 4)
#define SGI_I2C_XFER_BUSY (1 << 4)
#define SGI_I2C_ACK (0 << 5)
#define SGI_I2C_NACK (1 << 5)
#define SGI_I2C_BUS_OK (0 << 7)
#define SGI_I2C_BUS_ERR (1 << 7)
#define get_control() adap->getctrl(adap->data)
#define set_control(val) adap->setctrl(adap->data, val)
#define read_data() adap->rdata(adap->data)
#define write_data(val) adap->wdata(adap->data, val)
static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
{
int i;
for (i = 0; i < adap->xfer_timeout; i++) {
if ((get_control() & SGI_I2C_XFER_BUSY) == 0)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int wait_ack(struct i2c_algo_sgi_data *adap)
{
int i;
if (wait_xfer_done(adap))
return -ETIMEDOUT;
for (i = 0; i < adap->ack_timeout; i++) {
if ((get_control() & SGI_I2C_NACK) == 0)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int force_idle(struct i2c_algo_sgi_data *adap)
{
int i;
set_control(SGI_I2C_FORCE_IDLE);
for (i = 0; i < adap->xfer_timeout; i++) {
if ((get_control() & SGI_I2C_NOT_IDLE) == 0)
goto out;
udelay(1);
}
return -ETIMEDOUT;
out:
if (get_control() & SGI_I2C_BUS_ERR)
return -EIO;
return 0;
}
static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
int rd)
{
if (rd)
set_control(SGI_I2C_NOT_IDLE);
/* Check if bus is idle, eventually force it to do so */
if (get_control() & SGI_I2C_NOT_IDLE)
if (force_idle(adap))
return -EIO;
/* Write out the i2c chip address and specify operation */
set_control(SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
if (rd)
addr |= 1;
write_data(addr);
if (wait_ack(adap))
return -EIO;
return 0;
}
static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
unsigned int len)
{
int i;
set_control(SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
for (i = 0; i < len; i++) {
if (wait_xfer_done(adap))
return -EIO;
buf[i] = read_data();
}
set_control(SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
return 0;
}
static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
unsigned int len)
{
int i;
/* We are already in write state */
for (i = 0; i < len; i++) {
write_data(buf[i]);
if (wait_ack(adap))
return -EIO;
}
return 0;
}
static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
int num)
{
struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
struct i2c_msg *p;
int i, err = 0;
for (i = 0; !err && i < num; i++) {
p = &msgs[i];
err = do_address(adap, p->addr, p->flags & I2C_M_RD);
if (err || !p->len)
continue;
if (p->flags & I2C_M_RD)
err = i2c_read(adap, p->buf, p->len);
else
err = i2c_write(adap, p->buf, p->len);
}
return err;
}
static u32 sgi_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm sgi_algo = {
.name = "SGI algorithm",
.id = I2C_ALGO_SGI,
.master_xfer = sgi_xfer,
.functionality = sgi_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_sgi_add_bus(struct i2c_adapter *adap)
{
adap->id |= sgi_algo.id;
adap->algo = &sgi_algo;
return i2c_add_adapter(adap);
}
int i2c_sgi_del_bus(struct i2c_adapter *adap)
{
return i2c_del_adapter(adap);
}
EXPORT_SYMBOL(i2c_sgi_add_bus);
EXPORT_SYMBOL(i2c_sgi_del_bus);
MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
MODULE_DESCRIPTION("I2C-Bus SGI algorithm");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,222 @@
/* ------------------------------------------------------------------------- */
/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2001,2002,2003 Broadcom Corporation
Copyright (C) 1995-2000 Simon G. Vogl
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl>. */
/* Ported for SiByte SOCs by Broadcom Corporation. */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-sibyte.h>
/* ----- global defines ----------------------------------------------- */
#define SMB_CSR(a,r) ((long)(a->reg_base + r))
/* ----- global variables --------------------------------------------- */
/* module parameters:
*/
static int bit_scan=0; /* have a look at what's hanging 'round */
static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
int data_bytes = 0;
int error;
while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
;
switch (size) {
case I2C_SMBUS_QUICK:
csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
SMB_CSR(adap, R_SMB_START));
data_bytes = 1;
} else {
csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
SMB_CSR(adap, R_SMB_START));
}
break;
case I2C_SMBUS_BYTE_DATA:
csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
if (read_write == I2C_SMBUS_READ) {
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
SMB_CSR(adap, R_SMB_START));
data_bytes = 1;
} else {
csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA));
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
SMB_CSR(adap, R_SMB_START));
}
break;
case I2C_SMBUS_WORD_DATA:
csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
if (read_write == I2C_SMBUS_READ) {
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
SMB_CSR(adap, R_SMB_START));
data_bytes = 2;
} else {
csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA));
csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA));
csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
SMB_CSR(adap, R_SMB_START));
}
break;
default:
return -1; /* XXXKW better error code? */
}
while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
;
error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
if (error & M_SMB_ERROR) {
/* Clear error bit by writing a 1 */
csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
return -1; /* XXXKW better error code? */
}
if (data_bytes == 1)
data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
if (data_bytes == 2)
data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
return 0;
}
static int algo_control(struct i2c_adapter *adapter,
unsigned int cmd, unsigned long arg)
{
return 0;
}
static u32 bit_func(struct i2c_adapter *adap)
{
return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
}
/* -----exported algorithm data: ------------------------------------- */
static struct i2c_algorithm i2c_sibyte_algo = {
.name = "SiByte algorithm",
.id = I2C_ALGO_SIBYTE,
.smbus_xfer = smbus_xfer,
.algo_control = algo_control, /* ioctl */
.functionality = bit_func,
};
/*
* registering functions to load algorithms at runtime
*/
int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
{
int i;
struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
/* register new adapter to i2c module... */
i2c_adap->id |= i2c_sibyte_algo.id;
i2c_adap->algo = &i2c_sibyte_algo;
/* Set the frequency to 100 kHz */
csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
/* scan bus */
if (bit_scan) {
union i2c_smbus_data data;
int rc;
printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
i2c_adap->name);
for (i = 0x00; i < 0x7f; i++) {
/* XXXKW is this a realistic probe? */
rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE_DATA, &data);
if (!rc) {
printk("(%02x)",i);
} else
printk(".");
}
printk("\n");
}
i2c_add_adapter(i2c_adap);
return 0;
}
int i2c_sibyte_del_bus(struct i2c_adapter *adap)
{
int res;
if ((res = i2c_del_adapter(adap)) < 0)
return res;
return 0;
}
int __init i2c_algo_sibyte_init (void)
{
printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n");
return 0;
}
EXPORT_SYMBOL(i2c_sibyte_add_bus);
EXPORT_SYMBOL(i2c_sibyte_del_bus);
#ifdef MODULE
MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
MODULE_DESCRIPTION("SiByte I2C-Bus algorithm");
MODULE_PARM(bit_scan, "i");
MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
MODULE_LICENSE("GPL");
int init_module(void)
{
return i2c_algo_sibyte_init();
}
void cleanup_module(void)
{
}
#endif

499
drivers/i2c/busses/Kconfig Normal file
View File

@@ -0,0 +1,499 @@
#
# Sensor device configuration
#
menu "I2C Hardware Bus support"
depends on I2C
config I2C_ALI1535
tristate "ALI 1535"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the SMB
Host controller on Acer Labs Inc. (ALI) M1535 South Bridges. The SMB
controller is part of the 7101 device, which is an ACPI-compliant
Power Management Unit (PMU).
This driver can also be built as a module. If so, the module
will be called i2c-ali1535.
config I2C_ALI1563
tristate "ALI 1563"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the SMB
Host controller on Acer Labs Inc. (ALI) M1563 South Bridges. The SMB
controller is part of the 7101 device, which is an ACPI-compliant
Power Management Unit (PMU).
This driver can also be built as a module. If so, the module
will be called i2c-ali1563.
config I2C_ALI15X3
tristate "ALI 15x3"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the
Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces.
This driver can also be built as a module. If so, the module
will be called i2c-ali15x3.
config I2C_AMD756
tristate "AMD 756/766/768/8111 and nVidia nForce"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the AMD
756/766/768 mainboard I2C interfaces. The driver also includes
support for the first (SMBus 1.0) I2C interface of the AMD 8111 and
the nVidia nForce I2C interface.
This driver can also be built as a module. If so, the module
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
tristate "AMD 8111"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the
second (SMBus 2.0) AMD 8111 mainboard I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-amd8111.
config I2C_AU1550
tristate "Au1550 SMBus interface"
depends on I2C && SOC_AU1550
help
If you say yes to this option, support will be included for the
Au1550 SMBus interface.
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
config I2C_ELEKTOR
tristate "Elektor ISA card"
depends on I2C && ISA && BROKEN_ON_SMP
select I2C_ALGOPCF
help
This supports the PCF8584 ISA bus I2C adapter. Say Y if you own
such an adapter.
This support is also available as a module. If so, the module
will be called i2c-elektor.
config I2C_HYDRA
tristate "CHRP Apple Hydra Mac I/O I2C interface"
depends on I2C && PCI && PPC_CHRP && EXPERIMENTAL
select I2C_ALGOBIT
help
This supports the use of the I2C interface in the Apple Hydra Mac
I/O chip on some CHRP machines (e.g. the LongTrail). Say Y if you
have such a machine.
This support is also available as a module. If so, the module
will be called i2c-hydra.
config I2C_I801
tristate "Intel 82801 (ICH)"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the Intel
801 family of mainboard I2C interfaces. Specifically, the following
versions of the chipset are supported:
82801AA
82801AB
82801BA
82801CA/CAM
82801DB
82801EB/ER (ICH5/ICH5R)
6300ESB
ICH6
ICH7
This driver can also be built as a module. If so, the module
will be called i2c-i801.
config I2C_I810
tristate "Intel 810/815"
depends on I2C && PCI && EXPERIMENTAL
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the Intel
810/815 family of mainboard I2C interfaces. Specifically, the
following versions of the chipset is supported:
i810AA
i810AB
i810E
i815
This driver can also be built as a module. If so, the module
will be called i2c-i810.
config I2C_PIIX4
tristate "Intel PIIX4"
depends on I2C && PCI
help
If you say yes to this option, support will be included for the Intel
PIIX4 family of mainboard I2C interfaces. Specifically, the following
versions of the chipset are supported:
Intel PIIX4
Intel 440MX
Serverworks OSB4
Serverworks CSB5
Serverworks CSB6
SMSC Victory66
This driver can also be built as a module. If so, the module
will be called i2c-piix4.
config I2C_IBM_IIC
tristate "IBM PPC 4xx on-chip I2C interface"
depends on IBM_OCP && I2C
help
Say Y here if you want to use IIC peripheral found on
embedded IBM PPC 4xx based systems.
This driver can also be built as a module. If so, the module
will be called i2c-ibm_iic.
config I2C_IOP3XX
tristate "Intel IOP3xx and IXP4xx on-chip I2C interface"
depends on (ARCH_IOP3XX || ARCH_IXP4XX) && I2C
help
Say Y here if you want to use the IIC bus controller on
the Intel IOP3xx I/O Processors or IXP4xx Network Processors.
This driver can also be built as a module. If so, the module
will be called i2c-iop3xx.
config I2C_ISA
tristate "ISA Bus support"
depends on I2C && EXPERIMENTAL
help
If you say yes to this option, support will be included for i2c
interfaces that are on the ISA bus.
This driver can also be built as a module. If so, the module
will be called i2c-isa.
config I2C_ITE
tristate "ITE I2C Adapter"
depends on I2C && MIPS_ITE8172
select I2C_ALGOITE
help
This supports the ITE8172 I2C peripheral found on some MIPS
systems. Say Y if you have one of these. You should also say Y for
the ITE I2C driver algorithm support above.
This support is also available as a module. If so, the module
will be called i2c-ite.
config I2C_IXP4XX
tristate "IXP4xx GPIO-Based I2C Interface"
depends on I2C && ARCH_IXP4XX
select I2C_ALGOBIT
help
Say Y here if you have an Intel IXP4xx(420,421,422,425) based
system and are using GPIO lines for an I2C bus.
This support is also available as a module. If so, the module
will be called i2c-ixp4xx.
config I2C_IXP2000
tristate "IXP2000 GPIO-Based I2C Interface"
depends on I2C && ARCH_IXP2000
select I2C_ALGOBIT
help
Say Y here if you have an Intel IXP2000(2400, 2800, 2850) based
system and are using GPIO lines for an I2C bus.
This support is also available as a module. If so, the module
will be called i2c-ixp2000.
config I2C_KEYWEST
tristate "Powermac Keywest I2C interface"
depends on I2C && PPC_PMAC
help
This supports the use of the I2C interface in the combo-I/O
chip on recent Apple machines. Say Y if you have such a machine.
This support is also available as a module. If so, the module
will be called i2c-keywest.
config I2C_MPC
tristate "MPC107/824x/85xx/52xx"
depends on I2C && PPC
help
If you say yes to this option, support will be included for the
built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
MPC85xx family processors. The driver may also work on 52xx
family processors, though interrupts are known not to work.
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
config I2C_NFORCE2
tristate "Nvidia Nforce2"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the Nvidia
Nforce2 family of mainboard I2C interfaces.
This driver also supports the nForce3 Pro 150 MCP.
This driver can also be built as a module. If so, the module
will be called i2c-nforce2.
config I2C_PARPORT
tristate "Parallel port adapter"
depends on I2C && PARPORT
select I2C_ALGOBIT
help
This supports parallel port I2C adapters such as the ones made by
Philips or Velleman, Analog Devices evaluation boards, and more.
Basically any adapter using the parallel port as an I2C bus with
no extra chipset is supported by this driver, or could be.
This driver is a replacement for (and was inspired by) an older
driver named i2c-philips-par. The new driver supports more devices,
and makes it easier to add support for new devices.
Another driver exists, named i2c-parport-light, which doesn't depend
on the parport driver. This is meant for embedded systems. Don't say
Y here if you intend to say Y or M there.
This support is also available as a module. If so, the module
will be called i2c-parport.
config I2C_PARPORT_LIGHT
tristate "Parallel port adapter (light)"
depends on I2C
select I2C_ALGOBIT
help
This supports parallel port I2C adapters such as the ones made by
Philips or Velleman, Analog Devices evaluation boards, and more.
Basically any adapter using the parallel port as an I2C bus with
no extra chipset is supported by this driver, or could be.
This driver is a light version of i2c-parport. It doesn't depend
on the parport driver, and uses direct I/O access instead. This
might be prefered on embedded systems where wasting memory for
the clean but heavy parport handling is not an option. The
drawback is a reduced portability and the impossibility to
dasiy-chain other parallel port devices.
Don't say Y here if you said Y or M to i2c-parport. Saying M to
both is possible but both modules should not be loaded at the same
time.
This support is also available as a module. If so, the module
will be called i2c-parport-light.
config I2C_PROSAVAGE
tristate "S3/VIA (Pro)Savage"
depends on I2C && PCI && EXPERIMENTAL
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the
I2C bus and DDC bus of the S3VIA embedded Savage4 and ProSavage8
graphics processors.
chipsets supported:
S3/VIA KM266/VT8375 aka ProSavage8
S3/VIA KM133/VT8365 aka Savage4
This support is also available as a module. If so, the module
will be called i2c-prosavage.
config I2C_RPXLITE
tristate "Embedded Planet RPX Lite/Classic support"
depends on (RPXLITE || RPXCLASSIC) && I2C
select I2C_ALGO8XX
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
depends on I2C && ARCH_S3C2410
help
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
config I2C_SAVAGE4
tristate "S3 Savage 4"
depends on I2C && PCI && EXPERIMENTAL
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the
S3 Savage 4 I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-savage4.
config I2C_SIBYTE
tristate "SiByte SMBus interface"
depends on SIBYTE_SB1xxx_SOC && I2C
help
Supports the SiByte SOC on-chip I2C interfaces (2 channels).
config SCx200_I2C
tristate "NatSemi SCx200 I2C using GPIO pins"
depends on SCx200_GPIO && I2C
select I2C_ALGOBIT
help
Enable the use of two GPIO pins of a SCx200 processor as an I2C bus.
If you don't know what to do here, say N.
This support is also available as a module. If so, the module
will be called scx200_i2c.
config SCx200_I2C_SCL
int "GPIO pin used for SCL"
depends on SCx200_I2C
default "12"
help
Enter the GPIO pin number used for the SCL signal. This value can
also be specified with a module parameter.
config SCx200_I2C_SDA
int "GPIO pin used for SDA"
depends on SCx200_I2C
default "13"
help
Enter the GPIO pin number used for the SSA signal. This value can
also be specified with a module parameter.
config SCx200_ACB
tristate "NatSemi SCx200 ACCESS.bus"
depends on I2C && PCI
help
Enable the use of the ACCESS.bus controllers of a SCx200 processor.
If you don't know what to do here, say N.
This support is also available as a module. If so, the module
will be called scx200_acb.
config I2C_SIS5595
tristate "SiS 5595"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the
SiS5595 SMBus (a subset of I2C) interface.
This driver can also be built as a module. If so, the module
will be called i2c-sis5595.
config I2C_SIS630
tristate "SiS 630/730"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the
SiS630 and SiS730 SMBus (a subset of I2C) interface.
This driver can also be built as a module. If so, the module
will be called i2c-sis630.
config I2C_SIS96X
tristate "SiS 96x"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the SiS
96x SMBus (a subset of I2C) interfaces. Specifically, the following
chipsets are supported:
645/961
645DX/961
645DX/962
648/961
650/961
735
This driver can also be built as a module. If so, the module
will be called i2c-sis96x.
config I2C_STUB
tristate "I2C/SMBus Test Stub"
depends on I2C && EXPERIMENTAL && 'm'
default 'n'
help
This module may be useful to developers of SMBus client drivers,
especially for certain kinds of sensor chips.
If you do build this module, be sure to read the notes and warnings
in <file:Documentation/i2c/i2c-stub>.
If you don't know what to do here, definitely say N.
config I2C_VIA
tristate "VIA 82C586B"
depends on I2C && PCI && EXPERIMENTAL
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the VIA
82C586B I2C interface
This driver can also be built as a module. If so, the module
will be called i2c-via.
config I2C_VIAPRO
tristate "VIA 82C596/82C686/823x"
depends on I2C && PCI && EXPERIMENTAL
help
If you say yes to this option, support will be included for the VIA
82C596/82C686/823x I2C interfaces. Specifically, the following
chipsets are supported:
82C596A/B
82C686A/B
8231
8233
8233A
8235
8237
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
config I2C_VOODOO3
tristate "Voodoo 3"
depends on I2C && PCI && EXPERIMENTAL
select I2C_ALGOBIT
help
If you say yes to this option, support will be included for the
Voodoo 3 I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-voodoo3.
config I2C_PCA_ISA
tristate "PCA9564 on an ISA bus"
depends on I2C
select I2C_ALGOPCA
help
This driver supports ISA boards using the Philips PCA 9564
Parallel bus to I2C bus controller
This driver can also be built as a module. If so, the module
will be called i2c-pca-isa.
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
depends on I2C && MV64X60 && EXPERIMENTAL
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
This driver can also be built as a module. If so, the module
will be called i2c-mv64xxx.
endmenu

View File

@@ -0,0 +1,47 @@
#
# Makefile for the i2c bus drivers.
#
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.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_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_I810) += i2c-i810.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_ISA) += i2c-isa.o
obj-$(CONFIG_I2C_ITE) += i2c-ite.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o
obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_I2C_VIA) += i2c-via.o
obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
EXTRA_CFLAGS += -DDEBUG
endif

View File

@@ -0,0 +1,543 @@
/*
i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Mark D. Studebaker <mdsxyz123@yahoo.com>,
Dan Eaton <dan.eaton@rocketlogix.com> and
Stephen Rousset<stephen.rousset@rocketlogix.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.
*/
/*
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1535 South Bridge.
The M1535 is a South bridge for portable systems.
It is very similar to the M15x3 South bridges also produced
by Acer Labs Inc. Some of the registers within the part
have moved and some have been redefined slightly. Additionally,
the sequencing of the SMBus transactions has been modified
to be more consistent with the sequencing recommended by
the manufacturer and observed through testing. These
changes are reflected in this driver and can be identified
by comparing this driver to the i2c-ali15x3 driver.
For an overview of these chips see http://www.acerlabs.com
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
This driver controls the SMB Host only.
This driver does not use interrupts.
*/
/* Note: we assume there can only be one ALI1535, with one SMBus interface */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/semaphore.h>
/* ALI1535 SMBus address offsets */
#define SMBHSTSTS (0 + ali1535_smba)
#define SMBHSTTYP (1 + ali1535_smba)
#define SMBHSTPORT (2 + ali1535_smba)
#define SMBHSTCMD (7 + ali1535_smba)
#define SMBHSTADD (3 + ali1535_smba)
#define SMBHSTDAT0 (4 + ali1535_smba)
#define SMBHSTDAT1 (5 + ali1535_smba)
#define SMBBLKDAT (6 + ali1535_smba)
/* PCI Address Constants */
#define SMBCOM 0x004
#define SMBREV 0x008
#define SMBCFG 0x0D1
#define SMBBA 0x0E2
#define SMBHSTCFG 0x0F0
#define SMBCLK 0x0F2
/* Other settings */
#define MAX_TIMEOUT 500 /* times 1/100 sec */
#define ALI1535_SMB_IOSIZE 32
#define ALI1535_SMB_DEFAULTBASE 0x8040
/* ALI1535 address lock bits */
#define ALI1535_LOCK 0x06 /* dwe */
/* ALI1535 command constants */
#define ALI1535_QUICK 0x00
#define ALI1535_BYTE 0x10
#define ALI1535_BYTE_DATA 0x20
#define ALI1535_WORD_DATA 0x30
#define ALI1535_BLOCK_DATA 0x40
#define ALI1535_I2C_READ 0x60
#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */
/* I2C read */
#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */
#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */
/* Alert-Response-Address */
/* (read) */
#define ALI1535_KILL 0x04 /* Kill Command (write) */
#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */
/* Alert-Response-Address */
/* (read) */
#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */
/* of 10-bit address in I2C */
/* Read Command */
/* ALI1535 status register bits */
#define ALI1535_STS_IDLE 0x04
#define ALI1535_STS_BUSY 0x08 /* host busy */
#define ALI1535_STS_DONE 0x10 /* transaction complete */
#define ALI1535_STS_DEV 0x20 /* device error */
#define ALI1535_STS_BUSERR 0x40 /* bus error */
#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */
#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */
#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */
/* ALI1535 device address register bits */
#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */
/* Address field */
/* -> Write = 0 */
/* -> Read = 1 */
#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */
static unsigned short ali1535_smba;
static DECLARE_MUTEX(i2c_ali1535_sem);
/* Detect whether a ALI1535 can be found, and initialize it, where necessary.
Note the differences between kernels with the old PCI BIOS interface and
newer kernels with the real PCI interface. In compat.h some things are
defined to make the transition easier. */
static int ali1535_setup(struct pci_dev *dev)
{
int retval = -ENODEV;
unsigned char temp;
/* Check the following things:
- SMB I/O address is initialized
- Device is enabled
- We can use the addresses
*/
/* Determine the address of the SMBus area */
pci_read_config_word(dev, SMBBA, &ali1535_smba);
ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1));
if (ali1535_smba == 0) {
dev_warn(&dev->dev,
"ALI1535_smb region uninitialized - upgrade BIOS?\n");
goto exit;
}
if (!request_region(ali1535_smba, ALI1535_SMB_IOSIZE, "ali1535-smb")) {
dev_err(&dev->dev, "ALI1535_smb region 0x%x already in use!\n",
ali1535_smba);
goto exit;
}
/* check if whole device is enabled */
pci_read_config_byte(dev, SMBCFG, &temp);
if ((temp & ALI1535_SMBIO_EN) == 0) {
dev_err(&dev->dev, "SMB device not enabled - upgrade BIOS?\n");
goto exit_free;
}
/* Is SMB Host controller enabled? */
pci_read_config_byte(dev, SMBHSTCFG, &temp);
if ((temp & 1) == 0) {
dev_err(&dev->dev, "SMBus controller not enabled - upgrade BIOS?\n");
goto exit_free;
}
/* set SMB clock to 74KHz as recommended in data sheet */
pci_write_config_byte(dev, SMBCLK, 0x20);
/*
The interrupt routing for SMB is set up in register 0x77 in the
1533 ISA Bridge device, NOT in the 7101 device.
Don't bother with finding the 1533 device and reading the register.
if ((....... & 0x0F) == 1)
dev_dbg(&dev->dev, "ALI1535 using Interrupt 9 for SMBus.\n");
*/
pci_read_config_byte(dev, SMBREV, &temp);
dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&dev->dev, "ALI1535_smba = 0x%X\n", ali1535_smba);
retval = 0;
exit:
return retval;
exit_free:
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
return retval;
}
static int ali1535_transaction(struct i2c_adapter *adap)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, TYP=%02x, "
"CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD),
inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
/* get status */
temp = inb_p(SMBHSTSTS);
/* Make sure the SMBus host is ready to start transmitting */
/* Check the busy bit first */
if (temp & ALI1535_STS_BUSY) {
/* If the host controller is still busy, it may have timed out
* in the previous transaction, resulting in a "SMBus Timeout"
* printk. I've tried the following to reset a stuck busy bit.
* 1. Reset the controller with an KILL command. (this
* doesn't seem to clear the controller if an external
* device is hung)
* 2. Reset the controller and the other SMBus devices with a
* T_OUT command. (this clears the host busy bit if an
* external device is hung, but it comes back upon a new
* access to a device)
* 3. Disable and reenable the controller in SMBHSTCFG. Worst
* case, nothing seems to work except power reset.
*/
/* Try resetting entire SMB bus, including other devices - This
* may not work either - it clears the BUSY bit but then the
* BUSY bit may come back on when you try and use the chip
* again. If that's the case you are stuck.
*/
dev_info(&adap->dev,
"Resetting entire SMB Bus to clear busy condition (%02x)\n",
temp);
outb_p(ALI1535_T_OUT, SMBHSTTYP);
temp = inb_p(SMBHSTSTS);
}
/* now check the error bits and the busy bit */
if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
/* do a clear-on-write */
outb_p(0xFF, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) &
(ALI1535_STS_ERR | ALI1535_STS_BUSY)) {
/* This is probably going to be correctable only by a
* power reset as one of the bits now appears to be
* stuck */
/* This may be a bus or device with electrical problems. */
dev_err(&adap->dev,
"SMBus reset failed! (0x%02x) - controller or "
"device on bus is probably hung\n", temp);
return -1;
}
} else {
/* check and clear done bit */
if (temp & ALI1535_STS_DONE) {
outb_p(temp, SMBHSTSTS);
}
}
/* start the transaction by writing anything to the start register */
outb_p(0xFF, SMBHSTPORT);
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
msleep(1);
temp = inb_p(SMBHSTSTS);
} while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE))
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
if (temp & ALI1535_STS_FAIL) {
result = -1;
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
}
/* Unfortunately the ALI SMB controller maps "no response" and "bus
* collision" into a single bit. No reponse is the usual case so don't
* do a printk. This means that bus collisions go unreported.
*/
if (temp & ALI1535_STS_BUSERR) {
result = -1;
dev_dbg(&adap->dev,
"Error: no response or bus collision ADD=%02x\n",
inb_p(SMBHSTADD));
}
/* haven't ever seen this */
if (temp & ALI1535_STS_DEV) {
result = -1;
dev_err(&adap->dev, "Error: device error\n");
}
/* check to see if the "command complete" indication is set */
if (!(temp & ALI1535_STS_DONE)) {
result = -1;
dev_err(&adap->dev, "Error: command never completed\n");
}
dev_dbg(&adap->dev, "Transaction (post): STS=%02x, TYP=%02x, "
"CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD),
inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
/* take consequent actions for error conditions */
if (!(temp & ALI1535_STS_DONE)) {
/* issue "kill" to reset host controller */
outb_p(ALI1535_KILL,SMBHSTTYP);
outb_p(0xFF,SMBHSTSTS);
} else if (temp & ALI1535_STS_ERR) {
/* issue "timeout" to reset all devices on bus */
outb_p(ALI1535_T_OUT,SMBHSTTYP);
outb_p(0xFF,SMBHSTSTS);
}
return result;
}
/* Return -1 on error. */
static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
int i, len;
int temp;
int timeout;
s32 result = 0;
down(&i2c_ali1535_sem);
/* make sure SMBus is idle */
temp = inb_p(SMBHSTSTS);
for (timeout = 0;
(timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE);
timeout++) {
msleep(1);
temp = inb_p(SMBHSTSTS);
}
if (timeout >= MAX_TIMEOUT)
dev_warn(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp);
/* clear status register (clear-on-write) */
outb_p(0xFF, SMBHSTSTS);
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
result = -1;
goto EXIT;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI1535_QUICK;
outb_p(size, SMBHSTTYP); /* output command */
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI1535_BYTE;
outb_p(size, SMBHSTTYP); /* output command */
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI1535_BYTE_DATA;
outb_p(size, SMBHSTTYP); /* output command */
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI1535_WORD_DATA;
outb_p(size, SMBHSTTYP); /* output command */
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI1535_BLOCK_DATA;
outb_p(size, SMBHSTTYP); /* output command */
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0) {
len = 0;
data->block[0] = len;
}
if (len > 32) {
len = 32;
data->block[0] = len;
}
outb_p(len, SMBHSTDAT0);
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
break;
}
if (ali1535_transaction(adap)) {
/* Error in transaction */
result = -1;
goto EXIT;
}
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) {
result = 0;
goto EXIT;
}
switch (size) {
case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI1535_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI1535_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case ALI1535_BLOCK_DATA:
len = inb_p(SMBHSTDAT0);
if (len > 32)
len = 32;
data->block[0] = len;
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP);
for (i = 1; i <= data->block[0]; i++) {
data->block[i] = inb_p(SMBBLKDAT);
dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n",
len, i, data->block[i]);
}
break;
}
EXIT:
up(&i2c_ali1535_sem);
return result;
}
static u32 ali1535_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_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-i2c SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali1535_access,
.functionality = ali1535_func,
};
static struct i2c_adapter ali1535_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static struct pci_device_id ali1535_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ },
};
MODULE_DEVICE_TABLE (pci, ali1535_ids);
static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (ali1535_setup(dev)) {
dev_warn(&dev->dev,
"ALI1535 not detected, module not inserted.\n");
return -ENODEV;
}
/* set up the driverfs linkage to our parent device */
ali1535_adapter.dev.parent = &dev->dev;
snprintf(ali1535_adapter.name, I2C_NAME_SIZE,
"SMBus ALI1535 adapter at %04x", ali1535_smba);
return i2c_add_adapter(&ali1535_adapter);
}
static void __devexit ali1535_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali1535_adapter);
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
}
static struct pci_driver ali1535_driver = {
.name = "ali1535_smbus",
.id_table = ali1535_ids,
.probe = ali1535_probe,
.remove = __devexit_p(ali1535_remove),
};
static int __init i2c_ali1535_init(void)
{
return pci_register_driver(&ali1535_driver);
}
static void __exit i2c_ali1535_exit(void)
{
pci_unregister_driver(&ali1535_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"Mark D. Studebaker <mdsxyz123@yahoo.com> "
"and Dan Eaton <dan.eaton@rocketlogix.com>");
MODULE_DESCRIPTION("ALI1535 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_ali1535_init);
module_exit(i2c_ali1535_exit);

View File

@@ -0,0 +1,415 @@
/**
* i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
*
* Copyright (C) 2004 Patrick Mochel
*
* The 1563 southbridge is deceptively similar to the 1533, with a
* few notable exceptions. One of those happens to be the fact they
* upgraded the i2c core to be 2.0 compliant, and happens to be almost
* identical to the i2c controller found in the Intel 801 south
* bridges.
*
* This driver is based on a mix of the 15x3, 1535, and i801 drivers,
* with a little help from the ALi 1563 spec.
*
* This file is released under the GPLv2
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
#define ALI1563_MAX_TIMEOUT 500
#define ALI1563_SMBBA 0x80
#define ALI1563_SMB_IOEN 1
#define ALI1563_SMB_HOSTEN 2
#define ALI1563_SMB_IOSIZE 16
#define SMB_HST_STS (ali1563_smba + 0)
#define SMB_HST_CNTL1 (ali1563_smba + 1)
#define SMB_HST_CNTL2 (ali1563_smba + 2)
#define SMB_HST_CMD (ali1563_smba + 3)
#define SMB_HST_ADD (ali1563_smba + 4)
#define SMB_HST_DAT0 (ali1563_smba + 5)
#define SMB_HST_DAT1 (ali1563_smba + 6)
#define SMB_BLK_DAT (ali1563_smba + 7)
#define HST_STS_BUSY 0x01
#define HST_STS_INTR 0x02
#define HST_STS_DEVERR 0x04
#define HST_STS_BUSERR 0x08
#define HST_STS_FAIL 0x10
#define HST_STS_DONE 0x80
#define HST_STS_BAD 0x1c
#define HST_CNTL1_TIMEOUT 0x80
#define HST_CNTL1_LAST 0x40
#define HST_CNTL2_KILL 0x04
#define HST_CNTL2_START 0x40
#define HST_CNTL2_QUICK 0x00
#define HST_CNTL2_BYTE 0x01
#define HST_CNTL2_BYTE_DATA 0x02
#define HST_CNTL2_WORD_DATA 0x03
#define HST_CNTL2_BLOCK 0x05
static unsigned short ali1563_smba;
static int ali1563_transaction(struct i2c_adapter * a)
{
u32 data;
int timeout;
dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD) {
dev_warn(&a->dev,"ali1563: Trying to reset busy device\n");
outb_p(data | HST_STS_BAD,SMB_HST_STS);
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD)
return -EBUSY;
}
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
timeout = ALI1563_MAX_TIMEOUT;
do
msleep(1);
while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout);
dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
if (timeout && !(data & HST_STS_BAD))
return 0;
dev_warn(&a->dev, "SMBus Error: %s%s%s%s%s\n",
timeout ? "Timeout " : "",
data & HST_STS_FAIL ? "Transaction Failed " : "",
data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
data & HST_STS_DEVERR ? "Device Error " : "",
!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
if (!(data & HST_STS_DONE))
/* Issue 'kill' to host controller */
outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2);
else
/* Issue timeout to reset all devices on bus */
outb_p(HST_CNTL1_TIMEOUT,SMB_HST_CNTL1);
return -1;
}
static int ali1563_block_start(struct i2c_adapter * a)
{
u32 data;
int timeout;
dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD) {
dev_warn(&a->dev,"ali1563: Trying to reset busy device\n");
outb_p(data | HST_STS_BAD,SMB_HST_STS);
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD)
return -EBUSY;
}
/* Clear byte-ready bit */
outb_p(data | HST_STS_DONE, SMB_HST_STS);
/* Start transaction and wait for byte-ready bit to be set */
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
timeout = ALI1563_MAX_TIMEOUT;
do
msleep(1);
while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout);
dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
if (timeout && !(data & HST_STS_BAD))
return 0;
dev_warn(&a->dev, "SMBus Error: %s%s%s%s%s\n",
timeout ? "Timeout " : "",
data & HST_STS_FAIL ? "Transaction Failed " : "",
data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
data & HST_STS_DEVERR ? "Device Error " : "",
!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
return -1;
}
static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw)
{
int i, len;
int error = 0;
/* Do we need this? */
outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
if (rw == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 1)
len = 1;
else if (len > 32)
len = 32;
outb_p(len,SMB_HST_DAT0);
outb_p(data->block[1],SMB_BLK_DAT);
} else
len = 32;
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2);
for (i = 0; i < len; i++) {
if (rw == I2C_SMBUS_WRITE) {
outb_p(data->block[i + 1], SMB_BLK_DAT);
if ((error = ali1563_block_start(a)))
break;
} else {
if ((error = ali1563_block_start(a)))
break;
if (i == 0) {
len = inb_p(SMB_HST_DAT0);
if (len < 1)
len = 1;
else if (len > 32)
len = 32;
}
data->block[i+1] = inb_p(SMB_BLK_DAT);
}
}
/* Do we need this? */
outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
return error;
}
static s32 ali1563_access(struct i2c_adapter * a, u16 addr,
unsigned short flags, char rw, u8 cmd,
int size, union i2c_smbus_data * data)
{
int error = 0;
int timeout;
u32 reg;
for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) {
if (!(reg = inb_p(SMB_HST_STS) & HST_STS_BUSY))
break;
}
if (!timeout)
dev_warn(&a->dev,"SMBus not idle. HST_STS = %02x\n",reg);
outb_p(0xff,SMB_HST_STS);
/* Map the size to what the chip understands */
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_err(&a->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
error = -EINVAL;
break;
case I2C_SMBUS_QUICK:
size = HST_CNTL2_QUICK;
break;
case I2C_SMBUS_BYTE:
size = HST_CNTL2_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
size = HST_CNTL2_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
size = HST_CNTL2_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
size = HST_CNTL2_BLOCK;
break;
}
outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD);
outb_p(inb_p(SMB_HST_CNTL2) | (size << 3), SMB_HST_CNTL2);
/* Write the command register */
switch(size) {
case HST_CNTL2_BYTE:
if (rw== I2C_SMBUS_WRITE)
outb_p(cmd, SMB_HST_CMD);
break;
case HST_CNTL2_BYTE_DATA:
outb_p(cmd, SMB_HST_CMD);
if (rw == I2C_SMBUS_WRITE)
outb_p(data->byte, SMB_HST_DAT0);
break;
case HST_CNTL2_WORD_DATA:
outb_p(cmd, SMB_HST_CMD);
if (rw == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMB_HST_DAT0);
outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1);
}
break;
case HST_CNTL2_BLOCK:
outb_p(cmd, SMB_HST_CMD);
error = ali1563_block(a,data,rw);
goto Done;
}
if ((error = ali1563_transaction(a)))
goto Done;
if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK))
goto Done;
switch (size) {
case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMB_HST_DAT0);
break;
case HST_CNTL2_BYTE_DATA:
data->byte = inb_p(SMB_HST_DAT0);
break;
case HST_CNTL2_WORD_DATA:
data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8);
break;
}
Done:
return error;
}
static u32 ali1563_func(struct i2c_adapter * a)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static void ali1563_enable(struct pci_dev * dev)
{
u16 ctrl;
pci_read_config_word(dev,ALI1563_SMBBA,&ctrl);
ctrl |= 0x7;
pci_write_config_word(dev,ALI1563_SMBBA,ctrl);
}
static int __devinit ali1563_setup(struct pci_dev * dev)
{
u16 ctrl;
pci_read_config_word(dev,ALI1563_SMBBA,&ctrl);
printk("ali1563: SMBus control = %04x\n",ctrl);
/* Check if device is even enabled first */
if (!(ctrl & ALI1563_SMB_IOEN)) {
dev_warn(&dev->dev,"I/O space not enabled, trying manually\n");
ali1563_enable(dev);
}
if (!(ctrl & ALI1563_SMB_IOEN)) {
dev_warn(&dev->dev,"I/O space still not enabled, giving up\n");
goto Err;
}
if (!(ctrl & ALI1563_SMB_HOSTEN)) {
dev_warn(&dev->dev,"Host Controller not enabled\n");
goto Err;
}
/* SMB I/O Base in high 12 bits and must be aligned with the
* size of the I/O space. */
ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1);
if (!ali1563_smba) {
dev_warn(&dev->dev,"ali1563_smba Uninitialized\n");
goto Err;
}
if (!request_region(ali1563_smba,ALI1563_SMB_IOSIZE,"i2c-ali1563")) {
dev_warn(&dev->dev,"Could not allocate I/O space");
goto Err;
}
return 0;
Err:
return -ENODEV;
}
static void ali1563_shutdown(struct pci_dev *dev)
{
release_region(ali1563_smba,ALI1563_SMB_IOSIZE);
}
static struct i2c_algorithm ali1563_algorithm = {
.name = "Non-i2c SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali1563_access,
.functionality = ali1563_func,
};
static struct i2c_adapter ali1563_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &ali1563_algorithm,
};
static int __devinit ali1563_probe(struct pci_dev * dev,
const struct pci_device_id * id_table)
{
int error;
if ((error = ali1563_setup(dev)))
return error;
ali1563_adapter.dev.parent = &dev->dev;
sprintf(ali1563_adapter.name,"SMBus ALi 1563 Adapter @ %04x",
ali1563_smba);
if ((error = i2c_add_adapter(&ali1563_adapter)))
ali1563_shutdown(dev);
printk("%s: Returning %d\n",__FUNCTION__,error);
return error;
}
static void __devexit ali1563_remove(struct pci_dev * dev)
{
i2c_del_adapter(&ali1563_adapter);
ali1563_shutdown(dev);
}
static struct pci_device_id __devinitdata ali1563_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) },
{},
};
MODULE_DEVICE_TABLE (pci, ali1563_id_table);
static struct pci_driver ali1563_pci_driver = {
.name = "ali1563_i2c",
.id_table = ali1563_id_table,
.probe = ali1563_probe,
.remove = __devexit_p(ali1563_remove),
};
static int __init ali1563_init(void)
{
return pci_register_driver(&ali1563_pci_driver);
}
module_init(ali1563_init);
static void __exit ali1563_exit(void)
{
pci_unregister_driver(&ali1563_pci_driver);
}
module_exit(ali1563_exit);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,532 @@
/*
ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com> and
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.
*/
/*
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1541 and M1543C South Bridges.
The M1543C is a South bridge for desktop systems.
The M1533 is a South bridge for portable systems.
They are part of the following ALI chipsets:
"Aladdin Pro 2": Includes the M1621 Slot 1 North bridge
with AGP and 100MHz CPU Front Side bus
"Aladdin V": Includes the M1541 Socket 7 North bridge
with AGP and 100MHz CPU Front Side bus
"Aladdin IV": Includes the M1541 Socket 7 North bridge
with host bus up to 83.3 MHz.
For an overview of these chips see http://www.acerlabs.com
The M1533/M1543C devices appear as FOUR separate devices
on the PCI bus. An output of lspci will show something similar
to the following:
00:02.0 USB Controller: Acer Laboratories Inc. M5237
00:03.0 Bridge: Acer Laboratories Inc. M7101
00:07.0 ISA bridge: Acer Laboratories Inc. M1533
00:0f.0 IDE interface: Acer Laboratories Inc. M5229
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
This driver controls the SMB Host only.
The SMB Slave controller on the M15X3 is not enabled.
This driver does not use interrupts.
*/
/* Note: we assume there can only be one ALI15X3, with one SMBus interface */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
/* ALI15X3 SMBus address offsets */
#define SMBHSTSTS (0 + ali15x3_smba)
#define SMBHSTCNT (1 + ali15x3_smba)
#define SMBHSTSTART (2 + ali15x3_smba)
#define SMBHSTCMD (7 + ali15x3_smba)
#define SMBHSTADD (3 + ali15x3_smba)
#define SMBHSTDAT0 (4 + ali15x3_smba)
#define SMBHSTDAT1 (5 + ali15x3_smba)
#define SMBBLKDAT (6 + ali15x3_smba)
/* PCI Address Constants */
#define SMBCOM 0x004
#define SMBBA 0x014
#define SMBATPC 0x05B /* used to unlock xxxBA registers */
#define SMBHSTCFG 0x0E0
#define SMBSLVC 0x0E1
#define SMBCLK 0x0E2
#define SMBREV 0x008
/* Other settings */
#define MAX_TIMEOUT 200 /* times 1/100 sec */
#define ALI15X3_SMB_IOSIZE 32
/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB.
We don't use these here. If the bases aren't set to some value we
tell user to upgrade BIOS and we fail.
*/
#define ALI15X3_SMB_DEFAULTBASE 0xE800
/* ALI15X3 address lock bits */
#define ALI15X3_LOCK 0x06
/* ALI15X3 command constants */
#define ALI15X3_ABORT 0x02
#define ALI15X3_T_OUT 0x04
#define ALI15X3_QUICK 0x00
#define ALI15X3_BYTE 0x10
#define ALI15X3_BYTE_DATA 0x20
#define ALI15X3_WORD_DATA 0x30
#define ALI15X3_BLOCK_DATA 0x40
#define ALI15X3_BLOCK_CLR 0x80
/* ALI15X3 status register bits */
#define ALI15X3_STS_IDLE 0x04
#define ALI15X3_STS_BUSY 0x08
#define ALI15X3_STS_DONE 0x10
#define ALI15X3_STS_DEV 0x20 /* device error */
#define ALI15X3_STS_COLL 0x40 /* collision or no response */
#define ALI15X3_STS_TERM 0x80 /* terminated by abort */
#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static u16 force_addr = 0;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr,
"Initialize the base address of the i2c controller");
static unsigned short ali15x3_smba = 0;
static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
{
u16 a;
unsigned char temp;
/* Check the following things:
- SMB I/O address is initialized
- Device is enabled
- We can use the addresses
*/
/* Unlock the register.
The data sheet says that the address registers are read-only
if the lock bits are 1, but in fact the address registers
are zero unless you clear the lock bits.
*/
pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp);
if (temp & ALI15X3_LOCK) {
temp &= ~ALI15X3_LOCK;
pci_write_config_byte(ALI15X3_dev, SMBATPC, temp);
}
/* Determine the address of the SMBus area */
pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba);
ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1));
if (ali15x3_smba == 0 && force_addr == 0) {
dev_err(&ALI15X3_dev->dev, "ALI15X3_smb region uninitialized "
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if(force_addr)
ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1);
if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb")) {
dev_err(&ALI15X3_dev->dev,
"ALI15X3_smb region 0x%x already in use!\n",
ali15x3_smba);
return -ENODEV;
}
if(force_addr) {
dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n",
ali15x3_smba);
if (PCIBIOS_SUCCESSFUL != pci_write_config_word(ALI15X3_dev,
SMBBA,
ali15x3_smba))
goto error;
if (PCIBIOS_SUCCESSFUL != pci_read_config_word(ALI15X3_dev,
SMBBA, &a))
goto error;
if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) {
/* make sure it works */
dev_err(&ALI15X3_dev->dev,
"force address failed - not supported?\n");
goto error;
}
}
/* check if whole device is enabled */
pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp);
if ((temp & 1) == 0) {
dev_info(&ALI15X3_dev->dev, "enabling SMBus device\n");
pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01);
}
/* Is SMB Host controller enabled? */
pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp);
if ((temp & 1) == 0) {
dev_info(&ALI15X3_dev->dev, "enabling SMBus controller\n");
pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01);
}
/* set SMB clock to 74KHz as recommended in data sheet */
pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20);
/*
The interrupt routing for SMB is set up in register 0x77 in the
1533 ISA Bridge device, NOT in the 7101 device.
Don't bother with finding the 1533 device and reading the register.
if ((....... & 0x0F) == 1)
dev_dbg(&ALI15X3_dev->dev, "ALI15X3 using Interrupt 9 for SMBus.\n");
*/
pci_read_config_byte(ALI15X3_dev, SMBREV, &temp);
dev_dbg(&ALI15X3_dev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&ALI15X3_dev->dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba);
return 0;
error:
release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
return -ENODEV;
}
/* Another internally used function */
static int ali15x3_transaction(struct i2c_adapter *adap)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
/* get status */
temp = inb_p(SMBHSTSTS);
/* Make sure the SMBus host is ready to start transmitting */
/* Check the busy bit first */
if (temp & ALI15X3_STS_BUSY) {
/*
If the host controller is still busy, it may have timed out in the
previous transaction, resulting in a "SMBus Timeout" Dev.
I've tried the following to reset a stuck busy bit.
1. Reset the controller with an ABORT command.
(this doesn't seem to clear the controller if an external
device is hung)
2. Reset the controller and the other SMBus devices with a
T_OUT command. (this clears the host busy bit if an
external device is hung, but it comes back upon a new access
to a device)
3. Disable and reenable the controller in SMBHSTCFG
Worst case, nothing seems to work except power reset.
*/
/* Abort - reset the host controller */
/*
Try resetting entire SMB bus, including other devices -
This may not work either - it clears the BUSY bit but
then the BUSY bit may come back on when you try and use the chip again.
If that's the case you are stuck.
*/
dev_info(&adap->dev, "Resetting entire SMB Bus to "
"clear busy condition (%02x)\n", temp);
outb_p(ALI15X3_T_OUT, SMBHSTCNT);
temp = inb_p(SMBHSTSTS);
}
/* now check the error bits and the busy bit */
if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
/* do a clear-on-write */
outb_p(0xFF, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) &
(ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
/* this is probably going to be correctable only by a power reset
as one of the bits now appears to be stuck */
/* This may be a bus or device with electrical problems. */
dev_err(&adap->dev, "SMBus reset failed! (0x%02x) - "
"controller or device on bus is probably hung\n",
temp);
return -1;
}
} else {
/* check and clear done bit */
if (temp & ALI15X3_STS_DONE) {
outb_p(temp, SMBHSTSTS);
}
}
/* start the transaction by writing anything to the start register */
outb_p(0xFF, SMBHSTSTART);
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
msleep(1);
temp = inb_p(SMBHSTSTS);
} while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE)))
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
if (temp & ALI15X3_STS_TERM) {
result = -1;
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
}
/*
Unfortunately the ALI SMB controller maps "no response" and "bus
collision" into a single bit. No reponse is the usual case so don't
do a printk.
This means that bus collisions go unreported.
*/
if (temp & ALI15X3_STS_COLL) {
result = -1;
dev_dbg(&adap->dev,
"Error: no response or bus collision ADD=%02x\n",
inb_p(SMBHSTADD));
}
/* haven't ever seen this */
if (temp & ALI15X3_STS_DEV) {
result = -1;
dev_err(&adap->dev, "Error: device error\n");
}
dev_dbg(&adap->dev, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
return result;
}
/* Return -1 on error. */
static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
{
int i, len;
int temp;
int timeout;
/* clear all the bits (clear-on-write) */
outb_p(0xFF, SMBHSTSTS);
/* make sure SMBus is idle */
temp = inb_p(SMBHSTSTS);
for (timeout = 0;
(timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE);
timeout++) {
msleep(1);
temp = inb_p(SMBHSTSTS);
}
if (timeout >= MAX_TIMEOUT) {
dev_err(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp);
}
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
return -1;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI15X3_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = ALI15X3_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = ALI15X3_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = ALI15X3_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0) {
len = 0;
data->block[0] = len;
}
if (len > 32) {
len = 32;
data->block[0] = len;
}
outb_p(len, SMBHSTDAT0);
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = ALI15X3_BLOCK_DATA;
break;
}
outb_p(size, SMBHSTCNT); /* output command */
if (ali15x3_transaction(adap)) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK))
return 0;
switch (size) {
case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI15X3_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI15X3_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case ALI15X3_BLOCK_DATA:
len = inb_p(SMBHSTDAT0);
if (len > 32)
len = 32;
data->block[0] = len;
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
for (i = 1; i <= data->block[0]; i++) {
data->block[i] = inb_p(SMBBLKDAT);
dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n",
len, i, data->block[i]);
}
break;
}
return 0;
}
static u32 ali15x3_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_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali15x3_access,
.functionality = ali15x3_func,
};
static struct i2c_adapter ali15x3_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static struct pci_device_id ali15x3_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, ali15x3_ids);
static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (ali15x3_setup(dev)) {
dev_err(&dev->dev,
"ALI15X3 not detected, module not inserted.\n");
return -ENODEV;
}
/* set up the driverfs linkage to our parent device */
ali15x3_adapter.dev.parent = &dev->dev;
snprintf(ali15x3_adapter.name, I2C_NAME_SIZE,
"SMBus ALI15X3 adapter at %04x", ali15x3_smba);
return i2c_add_adapter(&ali15x3_adapter);
}
static void __devexit ali15x3_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali15x3_adapter);
release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
}
static struct pci_driver ali15x3_driver = {
.name = "ali15x3_smbus",
.id_table = ali15x3_ids,
.probe = ali15x3_probe,
.remove = __devexit_p(ali15x3_remove),
};
static int __init i2c_ali15x3_init(void)
{
return pci_register_driver(&ali15x3_driver);
}
static void __exit i2c_ali15x3_exit(void)
{
pci_unregister_driver(&ali15x3_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("ALI15X3 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_ali15x3_init);
module_exit(i2c_ali15x3_exit);

View File

@@ -0,0 +1,264 @@
/*
* 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/slab.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)
error = -ENODEV;
else
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_err(&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);

View File

@@ -0,0 +1,431 @@
/*
amd756.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
Shamelessly ripped from i2c-piix4.c:
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.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.
*/
/*
2002-04-08: Added nForce support. (Csaba Halasz)
2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
2002-12-28: Rewritten into something that resembles a Linux driver (hch)
2003-11-29: Added back AMD8111 removed by the previous rewrite.
(Philip Pokorny)
*/
/*
Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
/* AMD756 SMBus address offsets */
#define SMB_ADDR_OFFSET 0xE0
#define SMB_IOSIZE 16
#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport)
#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport)
#define SMB_HOST_ADDRESS (0x4 + amd756_ioport)
#define SMB_HOST_DATA (0x6 + amd756_ioport)
#define SMB_HOST_COMMAND (0x8 + amd756_ioport)
#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport)
#define SMB_HAS_DATA (0xA + amd756_ioport)
#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport)
#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport)
#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport)
/* PCI Address Constants */
/* address of I/O space */
#define SMBBA 0x058 /* mh */
#define SMBBANFORCE 0x014
/* general configuration */
#define SMBGCFG 0x041 /* mh */
/* silicon revision code */
#define SMBREV 0x008
/* Other settings */
#define MAX_TIMEOUT 500
/* AMD756 constants */
#define AMD756_QUICK 0x00
#define AMD756_BYTE 0x01
#define AMD756_BYTE_DATA 0x02
#define AMD756_WORD_DATA 0x03
#define AMD756_PROCESS_CALL 0x04
#define AMD756_BLOCK_DATA 0x05
static unsigned short amd756_ioport = 0;
/*
SMBUS event = I/O 28-29 bit 11
see E0 for the status bits and enabled in E2
*/
#define GS_ABRT_STS (1 << 0)
#define GS_COL_STS (1 << 1)
#define GS_PRERR_STS (1 << 2)
#define GS_HST_STS (1 << 3)
#define GS_HCYC_STS (1 << 4)
#define GS_TO_STS (1 << 5)
#define GS_SMB_STS (1 << 11)
#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
GS_HCYC_STS | GS_TO_STS )
#define GE_CYC_TYPE_MASK (7)
#define GE_HOST_STC (1 << 3)
#define GE_ABORT (1 << 5)
static int amd756_transaction(struct i2c_adapter *adap)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, "
"DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS),
inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS),
inb_p(SMB_HOST_DATA));
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp);
do {
msleep(1);
temp = inw_p(SMB_GLOBAL_STATUS);
} while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
(timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp);
goto abort;
}
timeout = 0;
}
/* start the transaction by setting the start bit */
outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
/* We will always wait for a fraction of a second! */
do {
msleep(1);
temp = inw_p(SMB_GLOBAL_STATUS);
} while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&adap->dev, "Completion timeout!\n");
goto abort;
}
if (temp & GS_PRERR_STS) {
result = -1;
dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n");
}
if (temp & GS_COL_STS) {
result = -1;
dev_warn(&adap->dev, "SMBus collision!\n");
}
if (temp & GS_TO_STS) {
result = -1;
dev_dbg(&adap->dev, "SMBus protocol timeout!\n");
}
if (temp & GS_HCYC_STS)
dev_dbg(&adap->dev, "SMBus protocol success!\n");
outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
#ifdef DEBUG
if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
dev_dbg(&adap->dev,
"Failed reset at end of transaction (%04x)\n", temp);
}
#endif
dev_dbg(&adap->dev,
"Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
return result;
abort:
dev_warn(&adap->dev, "Sending abort\n");
outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
msleep(100);
outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
return -1;
}
/* Return -1 on error. */
static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
/** TODO: Should I supporte the 10-bit transfers? */
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_dbg(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
/* TODO: Well... It is supported, I'm just not sure what to do here... */
return -1;
case I2C_SMBUS_QUICK:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
size = AMD756_QUICK;
break;
case I2C_SMBUS_BYTE:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMB_HOST_DATA);
size = AMD756_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE)
outw_p(data->byte, SMB_HOST_DATA);
size = AMD756_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE)
outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */
size = AMD756_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
outw_p(len, SMB_HOST_DATA);
/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i],
SMB_HOST_BLOCK_DATA);
}
size = AMD756_BLOCK_DATA;
break;
}
/* How about enabling interrupts... */
outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
if (amd756_transaction(adap)) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
return 0;
switch (size) {
case AMD756_BYTE:
data->byte = inw_p(SMB_HOST_DATA);
break;
case AMD756_BYTE_DATA:
data->byte = inw_p(SMB_HOST_DATA);
break;
case AMD756_WORD_DATA:
data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */
break;
case AMD756_BLOCK_DATA:
data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
if(data->block[0] > 32)
data->block[0] = 32;
/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
break;
}
return 0;
}
static u32 amd756_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_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = amd756_access,
.functionality = amd756_func,
};
struct i2c_adapter amd756_smbus = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
static const char* chipname[] = {
"AMD756", "AMD766", "AMD768",
"nVidia nForce", "AMD8111",
};
static struct pci_device_id amd756_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B),
.driver_data = AMD756 },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413),
.driver_data = AMD766 },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443),
.driver_data = AMD768 },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS),
.driver_data = AMD8111 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS),
.driver_data = NFORCE },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, amd756_ids);
static int __devinit amd756_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int nforce = (id->driver_data == NFORCE);
int error;
u8 temp;
if (amd756_ioport) {
dev_err(&pdev->dev, "Only one device supported "
"(you have a strange motherboard, btw)\n");
return -ENODEV;
}
if (nforce) {
if (PCI_FUNC(pdev->devfn) != 1)
return -ENODEV;
pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport);
amd756_ioport &= 0xfffc;
} else { /* amd */
if (PCI_FUNC(pdev->devfn) != 3)
return -ENODEV;
pci_read_config_byte(pdev, SMBGCFG, &temp);
if ((temp & 128) == 0) {
dev_err(&pdev->dev,
"Error: SMBus controller I/O not enabled!\n");
return -ENODEV;
}
/* Determine the address of the SMBus areas */
/* Technically it is a dword but... */
pci_read_config_word(pdev, SMBBA, &amd756_ioport);
amd756_ioport &= 0xff00;
amd756_ioport += SMB_ADDR_OFFSET;
}
if (!request_region(amd756_ioport, SMB_IOSIZE, "amd756-smbus")) {
dev_err(&pdev->dev, "SMB region 0x%x already in use!\n",
amd756_ioport);
return -ENODEV;
}
pci_read_config_byte(pdev, SMBREV, &temp);
dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport);
/* set up the driverfs linkage to our parent device */
amd756_smbus.dev.parent = &pdev->dev;
sprintf(amd756_smbus.name, "SMBus %s adapter at %04x",
chipname[id->driver_data], amd756_ioport);
error = i2c_add_adapter(&amd756_smbus);
if (error) {
dev_err(&pdev->dev,
"Adapter registration failed, module not inserted\n");
goto out_err;
}
return 0;
out_err:
release_region(amd756_ioport, SMB_IOSIZE);
return error;
}
static void __devexit amd756_remove(struct pci_dev *dev)
{
i2c_del_adapter(&amd756_smbus);
release_region(amd756_ioport, SMB_IOSIZE);
}
static struct pci_driver amd756_driver = {
.name = "amd756_smbus",
.id_table = amd756_ids,
.probe = amd756_probe,
.remove = __devexit_p(amd756_remove),
};
static int __init amd756_init(void)
{
return pci_register_driver(&amd756_driver);
}
static void __exit amd756_exit(void)
{
pci_unregister_driver(&amd756_driver);
}
MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(amd756_smbus);
module_init(amd756_init)
module_exit(amd756_exit)

View File

@@ -0,0 +1,415 @@
/*
* SMBus 2.0 driver for AMD-8111 IO-Hub.
*
* Copyright (c) 2002 Vojtech Pavlik
*
* 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 version 2.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <asm/io.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");
struct amd_smbus {
struct pci_dev *dev;
struct i2c_adapter adapter;
int base;
int size;
};
/*
* AMD PCI control registers definitions.
*/
#define AMD_PCI_MISC 0x48
#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */
#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */
#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */
/*
* ACPI 2.0 chapter 13 PCI interface definitions.
*/
#define AMD_EC_DATA 0x00 /* data register */
#define AMD_EC_SC 0x04 /* status of controller */
#define AMD_EC_CMD 0x04 /* command register */
#define AMD_EC_ICR 0x08 /* interrupt control register */
#define AMD_EC_SC_SMI 0x04 /* smi event pending */
#define AMD_EC_SC_SCI 0x02 /* sci event pending */
#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */
#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */
#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */
#define AMD_EC_SC_OBF 0x01 /* data ready for host */
#define AMD_EC_CMD_RD 0x80 /* read EC */
#define AMD_EC_CMD_WR 0x81 /* write EC */
#define AMD_EC_CMD_BE 0x82 /* enable burst mode */
#define AMD_EC_CMD_BD 0x83 /* disable burst mode */
#define AMD_EC_CMD_QR 0x84 /* query EC */
/*
* ACPI 2.0 chapter 13 access of registers of the EC
*/
static unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
{
int timeout = 500;
while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF))
udelay(1);
if (!timeout) {
dev_warn(&smbus->dev->dev, "Timeout while waiting for IBF to clear\n");
return -1;
}
return 0;
}
static unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
{
int timeout = 500;
while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF))
udelay(1);
if (!timeout) {
dev_warn(&smbus->dev->dev, "Timeout while waiting for OBF to set\n");
return -1;
}
return 0;
}
static unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data)
{
if (amd_ec_wait_write(smbus))
return -1;
outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
if (amd_ec_wait_write(smbus))
return -1;
outb(address, smbus->base + AMD_EC_DATA);
if (amd_ec_wait_read(smbus))
return -1;
*data = inb(smbus->base + AMD_EC_DATA);
return 0;
}
static unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data)
{
if (amd_ec_wait_write(smbus))
return -1;
outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
if (amd_ec_wait_write(smbus))
return -1;
outb(address, smbus->base + AMD_EC_DATA);
if (amd_ec_wait_write(smbus))
return -1;
outb(data, smbus->base + AMD_EC_DATA);
return 0;
}
/*
* ACPI 2.0 chapter 13 SMBus 2.0 EC register model
*/
#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */
#define AMD_SMB_STS 0x01 /* status */
#define AMD_SMB_ADDR 0x02 /* address */
#define AMD_SMB_CMD 0x03 /* command */
#define AMD_SMB_DATA 0x04 /* 32 data registers */
#define AMD_SMB_BCNT 0x24 /* number of data bytes */
#define AMD_SMB_ALRM_A 0x25 /* alarm address */
#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */
#define AMD_SMB_STS_DONE 0x80
#define AMD_SMB_STS_ALRM 0x40
#define AMD_SMB_STS_RES 0x20
#define AMD_SMB_STS_STATUS 0x1f
#define AMD_SMB_STATUS_OK 0x00
#define AMD_SMB_STATUS_FAIL 0x07
#define AMD_SMB_STATUS_DNAK 0x10
#define AMD_SMB_STATUS_DERR 0x11
#define AMD_SMB_STATUS_CMD_DENY 0x12
#define AMD_SMB_STATUS_UNKNOWN 0x13
#define AMD_SMB_STATUS_ACC_DENY 0x17
#define AMD_SMB_STATUS_TIMEOUT 0x18
#define AMD_SMB_STATUS_NOTSUP 0x19
#define AMD_SMB_STATUS_BUSY 0x1A
#define AMD_SMB_STATUS_PEC 0x1F
#define AMD_SMB_PRTCL_WRITE 0x00
#define AMD_SMB_PRTCL_READ 0x01
#define AMD_SMB_PRTCL_QUICK 0x02
#define AMD_SMB_PRTCL_BYTE 0x04
#define AMD_SMB_PRTCL_BYTE_DATA 0x06
#define AMD_SMB_PRTCL_WORD_DATA 0x08
#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a
#define AMD_SMB_PRTCL_PROC_CALL 0x0c
#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define AMD_SMB_PRTCL_PEC 0x80
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)
{
struct amd_smbus *smbus = adap->algo_data;
unsigned char protocol, len, pec, temp[2];
int i;
protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE;
pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
switch (size) {
case I2C_SMBUS_QUICK:
protocol |= AMD_SMB_PRTCL_QUICK;
read_write = I2C_SMBUS_WRITE;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE)
amd_ec_write(smbus, AMD_SMB_CMD, command);
protocol |= AMD_SMB_PRTCL_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
amd_ec_write(smbus, AMD_SMB_DATA, data->byte);
protocol |= AMD_SMB_PRTCL_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
amd_ec_write(smbus, AMD_SMB_DATA, data->word);
amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
}
protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
break;
case I2C_SMBUS_BLOCK_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0], 32);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
}
protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
len = min_t(u8, data->block[0], 32);
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
if (read_write == I2C_SMBUS_WRITE)
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
break;
case I2C_SMBUS_PROC_CALL:
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_DATA, data->word);
amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
protocol |= pec;
len = min_t(u8, data->block[0], 31);
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_WORD_DATA_PEC:
case I2C_SMBUS_BLOCK_DATA_PEC:
case I2C_SMBUS_PROC_CALL_PEC:
case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
dev_warn(&adap->dev, "Unexpected software PEC transaction %d\n.", size);
return -1;
default:
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
return -1;
}
amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
if (~temp[0] & AMD_SMB_STS_DONE) {
udelay(500);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
}
if (~temp[0] & AMD_SMB_STS_DONE) {
msleep(1);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
}
if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
return -1;
if (read_write == I2C_SMBUS_WRITE)
return 0;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
data->word = (temp[1] << 8) | temp[0];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
amd_ec_read(smbus, AMD_SMB_BCNT, &len);
len = min_t(u8, len, 32);
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++)
amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1);
data->block[0] = len;
break;
}
return 0;
}
static u32 amd8111_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_BLOCK_DATA |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus 2.0 adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = amd8111_access,
.functionality = amd8111_func,
};
static struct pci_device_id amd8111_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, amd8111_ids);
static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct amd_smbus *smbus;
int error = -ENODEV;
if (~pci_resource_flags(dev, 0) & IORESOURCE_IO)
return -ENODEV;
smbus = kmalloc(sizeof(struct amd_smbus), GFP_KERNEL);
if (!smbus)
return -ENOMEM;
memset(smbus, 0, sizeof(struct amd_smbus));
smbus->dev = dev;
smbus->base = pci_resource_start(dev, 0);
smbus->size = pci_resource_len(dev, 0);
if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0"))
goto out_kfree;
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, I2C_NAME_SIZE,
"SMBus2 AMD8111 adapter at %04x", smbus->base);
smbus->adapter.class = I2C_CLASS_HWMON;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
/* set up the driverfs linkage to our parent device */
smbus->adapter.dev.parent = &dev->dev;
error = i2c_add_adapter(&smbus->adapter);
if (error)
goto out_release_region;
pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);
pci_set_drvdata(dev, smbus);
return 0;
out_release_region:
release_region(smbus->base, smbus->size);
out_kfree:
kfree(smbus);
return -1;
}
static void __devexit amd8111_remove(struct pci_dev *dev)
{
struct amd_smbus *smbus = pci_get_drvdata(dev);
i2c_del_adapter(&smbus->adapter);
release_region(smbus->base, smbus->size);
kfree(smbus);
}
static struct pci_driver amd8111_driver = {
.name = "amd8111_smbus2",
.id_table = amd8111_ids,
.probe = amd8111_probe,
.remove = __devexit_p(amd8111_remove),
};
static int __init i2c_amd8111_init(void)
{
return pci_register_driver(&amd8111_driver);
}
static void __exit i2c_amd8111_exit(void)
{
pci_unregister_driver(&amd8111_driver);
}
module_init(i2c_amd8111_init);
module_exit(i2c_amd8111_exit);

View File

@@ -0,0 +1,435 @@
/*
* i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface
* Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
*
* 2.6 port by Matt Porter <mporter@kernel.crashing.org>
*
* The documentation describes this as an SMBus controller, but it doesn't
* understand any of the SMBus protocol in hardware. It's really an I2C
* controller that could emulate most of the SMBus in software.
*
* This is just a skeleton adapter to use with the Au1550 PSC
* algorithm. It was developed for the Pb1550, but will work with
* any Au1550 board that has a similar PSC configuration.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-pb1x00/pb1550.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include "i2c-au1550.h"
static int
wait_xfer_done(struct i2c_au1550_data *adap)
{
u32 stat;
int i;
volatile psc_smb_t *sp;
sp = (volatile psc_smb_t *)(adap->psc_base);
/* Wait for Tx FIFO Underflow.
*/
for (i = 0; i < adap->xfer_timeout; i++) {
stat = sp->psc_smbevnt;
au_sync();
if ((stat & PSC_SMBEVNT_TU) != 0) {
/* Clear it. */
sp->psc_smbevnt = PSC_SMBEVNT_TU;
au_sync();
return 0;
}
udelay(1);
}
return -ETIMEDOUT;
}
static int
wait_ack(struct i2c_au1550_data *adap)
{
u32 stat;
volatile psc_smb_t *sp;
if (wait_xfer_done(adap))
return -ETIMEDOUT;
sp = (volatile psc_smb_t *)(adap->psc_base);
stat = sp->psc_smbevnt;
au_sync();
if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
return -ETIMEDOUT;
return 0;
}
static int
wait_master_done(struct i2c_au1550_data *adap)
{
u32 stat;
int i;
volatile psc_smb_t *sp;
sp = (volatile psc_smb_t *)(adap->psc_base);
/* Wait for Master Done.
*/
for (i = 0; i < adap->xfer_timeout; i++) {
stat = sp->psc_smbevnt;
au_sync();
if ((stat & PSC_SMBEVNT_MD) != 0)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int
do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd)
{
volatile psc_smb_t *sp;
u32 stat;
sp = (volatile psc_smb_t *)(adap->psc_base);
/* Reset the FIFOs, clear events.
*/
sp->psc_smbpcr = PSC_SMBPCR_DC;
sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
au_sync();
do {
stat = sp->psc_smbpcr;
au_sync();
} while ((stat & PSC_SMBPCR_DC) != 0);
/* Write out the i2c chip address and specify operation
*/
addr <<= 1;
if (rd)
addr |= 1;
/* Put byte into fifo, start up master.
*/
sp->psc_smbtxrx = addr;
au_sync();
sp->psc_smbpcr = PSC_SMBPCR_MS;
au_sync();
if (wait_ack(adap))
return -EIO;
return 0;
}
static u32
wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
{
int j;
u32 data, stat;
volatile psc_smb_t *sp;
if (wait_xfer_done(adap))
return -EIO;
sp = (volatile psc_smb_t *)(adap->psc_base);
j = adap->xfer_timeout * 100;
do {
j--;
if (j <= 0)
return -EIO;
stat = sp->psc_smbstat;
au_sync();
if ((stat & PSC_SMBSTAT_RE) == 0)
j = 0;
else
udelay(1);
} while (j > 0);
data = sp->psc_smbtxrx;
au_sync();
*ret_data = data;
return 0;
}
static int
i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
int i;
u32 data;
volatile psc_smb_t *sp;
if (len == 0)
return 0;
/* A read is performed by stuffing the transmit fifo with
* zero bytes for timing, waiting for bytes to appear in the
* receive fifo, then reading the bytes.
*/
sp = (volatile psc_smb_t *)(adap->psc_base);
i = 0;
while (i < (len-1)) {
sp->psc_smbtxrx = 0;
au_sync();
if (wait_for_rx_byte(adap, &data))
return -EIO;
buf[i] = data;
i++;
}
/* The last byte has to indicate transfer done.
*/
sp->psc_smbtxrx = PSC_SMBTXRX_STP;
au_sync();
if (wait_master_done(adap))
return -EIO;
data = sp->psc_smbtxrx;
au_sync();
buf[i] = data;
return 0;
}
static int
i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
unsigned int len)
{
int i;
u32 data;
volatile psc_smb_t *sp;
if (len == 0)
return 0;
sp = (volatile psc_smb_t *)(adap->psc_base);
i = 0;
while (i < (len-1)) {
data = buf[i];
sp->psc_smbtxrx = data;
au_sync();
if (wait_ack(adap))
return -EIO;
i++;
}
/* The last byte has to indicate transfer done.
*/
data = buf[i];
data |= PSC_SMBTXRX_STP;
sp->psc_smbtxrx = data;
au_sync();
if (wait_master_done(adap))
return -EIO;
return 0;
}
static int
au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct i2c_au1550_data *adap = i2c_adap->algo_data;
struct i2c_msg *p;
int i, err = 0;
for (i = 0; !err && i < num; i++) {
p = &msgs[i];
err = do_address(adap, p->addr, p->flags & I2C_M_RD);
if (err || !p->len)
continue;
if (p->flags & I2C_M_RD)
err = i2c_read(adap, p->buf, p->len);
else
err = i2c_write(adap, p->buf, p->len);
}
/* Return the number of messages processed, or the error code.
*/
if (err == 0)
err = num;
return err;
}
static u32
au1550_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm au1550_algo = {
.name = "Au1550 algorithm",
.id = I2C_ALGO_AU1550,
.master_xfer = au1550_xfer,
.functionality = au1550_func,
};
/*
* registering functions to load algorithms at runtime
* Prior to calling us, the 50MHz clock frequency and routing
* must have been set up for the PSC indicated by the adapter.
*/
int
i2c_au1550_add_bus(struct i2c_adapter *i2c_adap)
{
struct i2c_au1550_data *adap = i2c_adap->algo_data;
volatile psc_smb_t *sp;
u32 stat;
i2c_adap->algo = &au1550_algo;
/* Now, set up the PSC for SMBus PIO mode.
*/
sp = (volatile psc_smb_t *)(adap->psc_base);
sp->psc_ctrl = PSC_CTRL_DISABLE;
au_sync();
sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
sp->psc_smbcfg = 0;
au_sync();
sp->psc_ctrl = PSC_CTRL_ENABLE;
au_sync();
do {
stat = sp->psc_smbstat;
au_sync();
} while ((stat & PSC_SMBSTAT_SR) == 0);
sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
PSC_SMBCFG_DD_DISABLE);
/* Divide by 8 to get a 6.25 MHz clock. The later protocol
* timings are based on this clock.
*/
sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
au_sync();
/* Set the protocol timer values. See Table 71 in the
* Au1550 Data Book for standard timing values.
*/
sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
PSC_SMBTMR_SET_CH(15);
au_sync();
sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
do {
stat = sp->psc_smbstat;
au_sync();
} while ((stat & PSC_SMBSTAT_DR) == 0);
return i2c_add_adapter(i2c_adap);
}
int
i2c_au1550_del_bus(struct i2c_adapter *adap)
{
return i2c_del_adapter(adap);
}
static int
pb1550_reg(struct i2c_client *client)
{
return 0;
}
static int
pb1550_unreg(struct i2c_client *client)
{
return 0;
}
static struct i2c_au1550_data pb1550_i2c_info = {
SMBUS_PSC_BASE, 200, 200
};
static struct i2c_adapter pb1550_board_adapter = {
name: "pb1550 adapter",
id: I2C_HW_AU1550_PSC,
algo: NULL,
algo_data: &pb1550_i2c_info,
client_register: pb1550_reg,
client_unregister: pb1550_unreg,
};
/* BIG hack to support the control interface on the Wolfson WM8731
* audio codec on the Pb1550 board. We get an address and two data
* bytes to write, create an i2c message, and send it across the
* i2c transfer function. We do this here because we have access to
* the i2c adapter structure.
*/
static struct i2c_msg wm_i2c_msg; /* We don't want this stuff on the stack */
static u8 i2cbuf[2];
int
pb1550_wm_codec_write(u8 addr, u8 reg, u8 val)
{
wm_i2c_msg.addr = addr;
wm_i2c_msg.flags = 0;
wm_i2c_msg.buf = i2cbuf;
wm_i2c_msg.len = 2;
i2cbuf[0] = reg;
i2cbuf[1] = val;
return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, &wm_i2c_msg, 1);
}
static int __init
i2c_au1550_init(void)
{
printk(KERN_INFO "Au1550 I2C: ");
/* This is where we would set up a 50MHz clock source
* and routing. On the Pb1550, the SMBus is PSC2, which
* uses a shared clock with USB. This has been already
* configured by Yamon as a 48MHz clock, close enough
* for our work.
*/
if (i2c_au1550_add_bus(&pb1550_board_adapter) < 0) {
printk("failed to initialize.\n");
return -ENODEV;
}
printk("initialized.\n");
return 0;
}
static void __exit
i2c_au1550_exit(void)
{
i2c_au1550_del_bus(&pb1550_board_adapter);
}
MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC.");
MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550");
MODULE_LICENSE("GPL");
module_init (i2c_au1550_init);
module_exit (i2c_au1550_exit);

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
* 2.6 port by Matt Porter <mporter@kernel.crashing.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.
*/
#ifndef I2C_AU1550_H
#define I2C_AU1550_H
struct i2c_au1550_data {
u32 psc_base;
int xfer_timeout;
int ack_timeout;
};
int i2c_au1550_add_bus(struct i2c_adapter *);
int i2c_au1550_del_bus(struct i2c_adapter *);
#endif /* I2C_AU1550_H */

View File

@@ -0,0 +1,295 @@
/* ------------------------------------------------------------------------- */
/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-97 Simon G. Vogl
1998-99 Hans Berglund
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
/* Partialy rewriten by Oleg I. Vdovikin for mmapped support of
for Alpha Processor Inc. UP-2000(+) boards */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-pcf.h>
#include <asm/io.h>
#include <asm/irq.h>
#include "../algos/i2c-algo-pcf.h"
#define DEFAULT_BASE 0x330
static int base;
static int irq;
static int clock = 0x1c;
static int own = 0x55;
static int mmapped;
/* vdovikin: removed static struct i2c_pcf_isa gpi; code -
this module in real supports only one device, due to missing arguments
in some functions, called from the algo-pcf module. Sometimes it's
need to be rewriten - but for now just remove this for simpler reading */
static wait_queue_head_t pcf_wait;
static int pcf_pending;
static spinlock_t lock;
/* ----- local functions ---------------------------------------------- */
static void pcf_isa_setbyte(void *data, int ctl, int val)
{
int address = ctl ? (base + 1) : base;
/* enable irq if any specified for serial operation */
if (ctl && irq && (val & I2C_PCF_ESO)) {
val |= I2C_PCF_ENI;
}
pr_debug("i2c-elektor: Write 0x%X 0x%02X\n", address, val & 255);
switch (mmapped) {
case 0: /* regular I/O */
outb(val, address);
break;
case 2: /* double mapped I/O needed for UP2000 board,
I don't know why this... */
writeb(val, (void *)address);
/* fall */
case 1: /* memory mapped I/O */
writeb(val, (void *)address);
break;
}
}
static int pcf_isa_getbyte(void *data, int ctl)
{
int address = ctl ? (base + 1) : base;
int val = mmapped ? readb((void *)address) : inb(address);
pr_debug("i2c-elektor: Read 0x%X 0x%02X\n", address, val);
return (val);
}
static int pcf_isa_getown(void *data)
{
return (own);
}
static int pcf_isa_getclock(void *data)
{
return (clock);
}
static void pcf_isa_waitforpin(void) {
DEFINE_WAIT(wait);
int timeout = 2;
unsigned long flags;
if (irq > 0) {
spin_lock_irqsave(&lock, flags);
if (pcf_pending == 0) {
spin_unlock_irqrestore(&lock, flags);
prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE);
if (schedule_timeout(timeout*HZ)) {
spin_lock_irqsave(&lock, flags);
if (pcf_pending == 1) {
pcf_pending = 0;
}
spin_unlock_irqrestore(&lock, flags);
}
finish_wait(&pcf_wait, &wait);
} else {
pcf_pending = 0;
spin_unlock_irqrestore(&lock, flags);
}
} else {
udelay(100);
}
}
static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
spin_lock(&lock);
pcf_pending = 1;
spin_unlock(&lock);
wake_up_interruptible(&pcf_wait);
return IRQ_HANDLED;
}
static int pcf_isa_init(void)
{
spin_lock_init(&lock);
if (!mmapped) {
if (!request_region(base, 2, "i2c (isa bus adapter)")) {
printk(KERN_ERR
"i2c-elektor: requested I/O region (0x%X:2) "
"is in use.\n", base);
return -ENODEV;
}
}
if (irq > 0) {
if (request_irq(irq, pcf_isa_handler, 0, "PCF8584", NULL) < 0) {
printk(KERN_ERR "i2c-elektor: Request irq%d failed\n", irq);
irq = 0;
} else
enable_irq(irq);
}
return 0;
}
/* ------------------------------------------------------------------------
* Encapsulate the above functions in the correct operations structure.
* This is only done when more than one hardware adapter is supported.
*/
static struct i2c_algo_pcf_data pcf_isa_data = {
.setpcf = pcf_isa_setbyte,
.getpcf = pcf_isa_getbyte,
.getown = pcf_isa_getown,
.getclock = pcf_isa_getclock,
.waitforpin = pcf_isa_waitforpin,
.udelay = 10,
.mdelay = 10,
.timeout = 100,
};
static struct i2c_adapter pcf_isa_ops = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.id = I2C_HW_P_ELEK,
.algo_data = &pcf_isa_data,
.name = "PCF8584 ISA adapter",
};
static int __init i2c_pcfisa_init(void)
{
#ifdef __alpha__
/* check to see we have memory mapped PCF8584 connected to the
Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
if (base == 0) {
struct pci_dev *cy693_dev;
cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
PCI_DEVICE_ID_CONTAQ_82C693, NULL);
if (cy693_dev) {
char config;
/* yeap, we've found cypress, let's check config */
if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
pr_debug("i2c-elektor: found cy82c693, config register 0x47 = 0x%02x.\n", config);
/* UP2000 board has this register set to 0xe1,
but the most significant bit as seems can be
reset during the proper initialisation
sequence if guys from API decides to do that
(so, we can even enable Tsunami Pchip
window for the upper 1 Gb) */
/* so just check for ROMCS at 0xe0000,
ROMCS enabled for writes
and external XD Bus buffer in use. */
if ((config & 0x7f) == 0x61) {
/* seems to be UP2000 like board */
base = 0xe0000;
/* I don't know why we need to
write twice */
mmapped = 2;
/* UP2000 drives ISA with
8.25 MHz (PCI/4) clock
(this can be read from cypress) */
clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
printk(KERN_INFO "i2c-elektor: found API UP2000 like board, will probe PCF8584 later.\n");
}
}
pci_dev_put(cy693_dev);
}
}
#endif
/* sanity checks for mmapped I/O */
if (mmapped && base < 0xc8000) {
printk(KERN_ERR "i2c-elektor: incorrect base address (0x%0X) specified for mmapped I/O.\n", base);
return -ENODEV;
}
printk(KERN_INFO "i2c-elektor: i2c pcf8584-isa adapter driver\n");
if (base == 0) {
base = DEFAULT_BASE;
}
init_waitqueue_head(&pcf_wait);
if (pcf_isa_init())
return -ENODEV;
if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
goto fail;
printk(KERN_ERR "i2c-elektor: found device at %#x.\n", base);
return 0;
fail:
if (irq > 0) {
disable_irq(irq);
free_irq(irq, NULL);
}
if (!mmapped)
release_region(base , 2);
return -ENODEV;
}
static void i2c_pcfisa_exit(void)
{
i2c_pcf_del_bus(&pcf_isa_ops);
if (irq > 0) {
disable_irq(irq);
free_irq(irq, NULL);
}
if (!mmapped)
release_region(base , 2);
}
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
MODULE_LICENSE("GPL");
module_param(base, int, 0);
module_param(irq, int, 0);
module_param(clock, int, 0);
module_param(own, int, 0);
module_param(mmapped, int, 0);
module_init(i2c_pcfisa_init);
module_exit(i2c_pcfisa_exit);

View File

@@ -0,0 +1,86 @@
/*
* linux/drivers/i2c/i2c-frodo.c
*
* Author: Abraham van der Merwe <abraham@2d3d.co.za>
*
* An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110
* Development board (Frodo).
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/hardware.h>
static void frodo_setsda (void *data,int state)
{
if (state)
FRODO_CPLD_I2C |= FRODO_I2C_SDA_OUT;
else
FRODO_CPLD_I2C &= ~FRODO_I2C_SDA_OUT;
}
static void frodo_setscl (void *data,int state)
{
if (state)
FRODO_CPLD_I2C |= FRODO_I2C_SCL_OUT;
else
FRODO_CPLD_I2C &= ~FRODO_I2C_SCL_OUT;
}
static int frodo_getsda (void *data)
{
return ((FRODO_CPLD_I2C & FRODO_I2C_SDA_IN) != 0);
}
static int frodo_getscl (void *data)
{
return ((FRODO_CPLD_I2C & FRODO_I2C_SCL_IN) != 0);
}
static struct i2c_algo_bit_data bit_frodo_data = {
.setsda = frodo_setsda,
.setscl = frodo_setscl,
.getsda = frodo_getsda,
.getscl = frodo_getscl,
.udelay = 80,
.mdelay = 80,
.timeout = HZ
};
static struct i2c_adapter frodo_ops = {
.owner = THIS_MODULE,
.id = I2C_HW_B_FRODO,
.algo_data = &bit_frodo_data,
.dev = {
.name = "Frodo adapter driver",
},
};
static int __init i2c_frodo_init (void)
{
return i2c_bit_add_bus(&frodo_ops);
}
static void __exit i2c_frodo_exit (void)
{
i2c_bit_del_bus(&frodo_ops);
}
MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo");
MODULE_LICENSE ("GPL");
module_init (i2c_frodo_init);
module_exit (i2c_frodo_exit);

View File

@@ -0,0 +1,183 @@
/*
i2c-hydra.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
i2c Support for the Apple `Hydra' Mac I/O
Copyright (c) 1999-2004 Geert Uytterhoeven <geert@linux-m68k.org>
Based on i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/hydra.h>
#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */
#define HYDRA_CPD_PD1 0x00000002
#define HYDRA_CPD_PD2 0x00000004
#define HYDRA_CPD_PD3 0x00000008
#define HYDRA_SCLK HYDRA_CPD_PD0
#define HYDRA_SDAT HYDRA_CPD_PD1
#define HYDRA_SCLK_OE 0x00000010
#define HYDRA_SDAT_OE 0x00000020
static inline void pdregw(void *data, u32 val)
{
struct Hydra *hydra = (struct Hydra *)data;
writel(val, &hydra->CachePD);
}
static inline u32 pdregr(void *data)
{
struct Hydra *hydra = (struct Hydra *)data;
return readl(&hydra->CachePD);
}
static void hydra_bit_setscl(void *data, int state)
{
u32 val = pdregr(data);
if (state)
val &= ~HYDRA_SCLK_OE;
else {
val &= ~HYDRA_SCLK;
val |= HYDRA_SCLK_OE;
}
pdregw(data, val);
}
static void hydra_bit_setsda(void *data, int state)
{
u32 val = pdregr(data);
if (state)
val &= ~HYDRA_SDAT_OE;
else {
val &= ~HYDRA_SDAT;
val |= HYDRA_SDAT_OE;
}
pdregw(data, val);
}
static int hydra_bit_getscl(void *data)
{
return (pdregr(data) & HYDRA_SCLK) != 0;
}
static int hydra_bit_getsda(void *data)
{
return (pdregr(data) & HYDRA_SDAT) != 0;
}
/* ------------------------------------------------------------------------ */
static struct i2c_algo_bit_data hydra_bit_data = {
.setsda = hydra_bit_setsda,
.setscl = hydra_bit_setscl,
.getsda = hydra_bit_getsda,
.getscl = hydra_bit_getscl,
.udelay = 5,
.mdelay = 5,
.timeout = HZ
};
static struct i2c_adapter hydra_adap = {
.owner = THIS_MODULE,
.name = "Hydra i2c",
.id = I2C_HW_B_HYDRA,
.algo_data = &hydra_bit_data,
};
static struct pci_device_id hydra_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_HYDRA) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, hydra_ids);
static int __devinit hydra_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned long base = pci_resource_start(dev, 0);
int res;
if (!request_mem_region(base+offsetof(struct Hydra, CachePD), 4,
hydra_adap.name))
return -EBUSY;
hydra_bit_data.data = ioremap(base, pci_resource_len(dev, 0));
if (hydra_bit_data.data == NULL) {
release_mem_region(base+offsetof(struct Hydra, CachePD), 4);
return -ENODEV;
}
pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */
hydra_adap.dev.parent = &dev->dev;
res = i2c_bit_add_bus(&hydra_adap);
if (res < 0) {
iounmap(hydra_bit_data.data);
release_mem_region(base+offsetof(struct Hydra, CachePD), 4);
return res;
}
return 0;
}
static void __devexit hydra_remove(struct pci_dev *dev)
{
pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */
i2c_bit_del_bus(&hydra_adap);
iounmap(hydra_bit_data.data);
release_mem_region(pci_resource_start(dev, 0)+
offsetof(struct Hydra, CachePD), 4);
}
static struct pci_driver hydra_driver = {
.name = "hydra_smbus",
.id_table = hydra_ids,
.probe = hydra_probe,
.remove = __devexit_p(hydra_remove),
};
static int __init i2c_hydra_init(void)
{
return pci_register_driver(&hydra_driver);
}
static void __exit i2c_hydra_exit(void)
{
pci_unregister_driver(&hydra_driver);
}
MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
MODULE_LICENSE("GPL");
module_init(i2c_hydra_init);
module_exit(i2c_hydra_exit);

View File

@@ -0,0 +1,613 @@
/*
i801.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and 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.
*/
/*
SUPPORTED DEVICES PCI ID
82801AA 2413
82801AB 2423
82801BA 2443
82801CA/CAM 2483
82801DB 24C3 (HW PEC supported, 32 byte buffer not supported)
82801EB 24D3 (HW PEC supported, 32 byte buffer not supported)
6300ESB 25A4
ICH6 266A
ICH7 27DA
This driver supports several versions of Intel's I/O Controller Hubs (ICH).
For SMBus support, they are similar to the PIIX4 and are part
of Intel's '810' and other chipsets.
See the doc/busses/i2c-i801 file for details.
I2C Block Read and Process Call are not supported.
*/
/* Note: we assume there can only be one I801, with one SMBus interface */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/io.h>
#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC
#define HAVE_PEC
#endif
/* I801 SMBus address offsets */
#define SMBHSTSTS (0 + i801_smba)
#define SMBHSTCNT (2 + i801_smba)
#define SMBHSTCMD (3 + i801_smba)
#define SMBHSTADD (4 + i801_smba)
#define SMBHSTDAT0 (5 + i801_smba)
#define SMBHSTDAT1 (6 + i801_smba)
#define SMBBLKDAT (7 + i801_smba)
#define SMBPEC (8 + i801_smba) /* ICH4 only */
#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */
#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */
/* PCI Address Constants */
#define SMBBA 0x020
#define SMBHSTCFG 0x040
#define SMBREV 0x008
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN 1
#define SMBHSTCFG_SMB_SMI_EN 2
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
#define MAX_TIMEOUT 100
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
#define I801_QUICK 0x00
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */
#define I801_BLOCK_LAST 0x34
#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */
#define I801_START 0x40
#define I801_PEC_EN 0x80 /* ICH4 only */
/* insmod parameters */
/* If force_addr is set to anything different from 0, we forcibly enable
the I801 at the given address. VERY DANGEROUS! */
static u16 force_addr;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr,
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
static int i801_transaction(void);
static int i801_block_transaction(union i2c_smbus_data *data,
char read_write, int command);
static unsigned short i801_smba;
static struct pci_dev *I801_dev;
static int isich4;
static int i801_setup(struct pci_dev *dev)
{
int error_return = 0;
unsigned char temp;
/* Note: we keep on searching until we have found 'function 3' */
if(PCI_FUNC(dev->devfn) != 3)
return -ENODEV;
I801_dev = dev;
if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) ||
(dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) ||
(dev->device == PCI_DEVICE_ID_INTEL_ESB_4))
isich4 = 1;
else
isich4 = 0;
/* Determine the address of the SMBus areas */
if (force_addr) {
i801_smba = force_addr & 0xfff0;
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
if(i801_smba == 0) {
dev_err(&dev->dev, "SMB base address uninitialized"
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
}
if (!request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus")) {
dev_err(&dev->dev, "I801_smb region 0x%x already in use!\n",
i801_smba);
error_return = -EBUSY;
goto END;
}
pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the device first. */
if (force_addr) {
pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(I801_dev, SMBBA, i801_smba);
pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01);
dev_warn(&dev->dev, "WARNING: I801 SMBus interface set to "
"new address %04x!\n", i801_smba);
} else if ((temp & 1) == 0) {
pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1);
dev_warn(&dev->dev, "enabling SMBus device\n");
}
if (temp & 0x02)
dev_dbg(&dev->dev, "I801 using Interrupt SMI# for SMBus.\n");
else
dev_dbg(&dev->dev, "I801 using PCI Interrupt for SMBus.\n");
pci_read_config_byte(I801_dev, SMBREV, &temp);
dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&dev->dev, "I801_smba = 0x%X\n", i801_smba);
END:
return error_return;
}
static int i801_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n",
temp);
outb_p(temp, SMBHSTSTS);
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
return -1;
} else {
dev_dbg(&I801_dev->dev, "Successfull!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
/* We will always wait for a fraction of a second! */
do {
msleep(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
result = -1;
}
if (temp & 0x10) {
result = -1;
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
}
if (temp & 0x08) {
result = -1;
dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
"until next hard reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
result = -1;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
}
if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
"(%02x)\n", temp);
}
dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
return result;
}
/* All-inclusive block transaction function */
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
int command)
{
int i, len;
int smbcmd;
int temp;
int result = 0;
int timeout;
unsigned char hostc, errmask;
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
if (read_write == I2C_SMBUS_WRITE) {
/* set I2C_EN bit in configuration register */
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
pci_write_config_byte(I801_dev, SMBHSTCFG,
hostc | SMBHSTCFG_I2C_EN);
} else {
dev_err(&I801_dev->dev,
"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
return -1;
}
}
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 1)
len = 1;
if (len > 32)
len = 32;
outb_p(len, SMBHSTDAT0);
outb_p(data->block[1], SMBBLKDAT);
} else {
len = 32; /* max for reads */
}
if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
for (i = 1; i <= len; i++) {
if (i == len && read_write == I2C_SMBUS_READ)
smbcmd = I801_BLOCK_LAST;
else
smbcmd = I801_BLOCK_DATA;
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
/* Make sure the SMBus host is ready to start transmitting */
temp = inb_p(SMBHSTSTS);
if (i == 1) {
/* Erronenous conditions before transaction:
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
errmask=0x9f;
} else {
/* Erronenous conditions during transaction:
* Failed, Bus_Err, Dev_Err, Intr */
errmask=0x1e;
}
if (temp & errmask) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
"Resetting... \n", temp);
outb_p(temp, SMBHSTSTS);
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
dev_err(&I801_dev->dev,
"Reset failed! (%02x)\n", temp);
result = -1;
goto END;
}
if (i != 1) {
/* if die in middle of block transaction, fail */
result = -1;
goto END;
}
}
if (i == 1)
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
temp = inb_p(SMBHSTSTS);
msleep(1);
}
while ((!(temp & 0x80))
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
}
if (temp & 0x10) {
result = -1;
dev_dbg(&I801_dev->dev,
"Error: Failed bus transaction\n");
} else if (temp & 0x08) {
result = -1;
dev_err(&I801_dev->dev, "Bus collision!\n");
} else if (temp & 0x04) {
result = -1;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
}
if (i == 1 && read_write == I2C_SMBUS_READ) {
len = inb_p(SMBHSTDAT0);
if (len < 1)
len = 1;
if (len > 32)
len = 32;
data->block[0] = len;
}
/* Retrieve/store value in SMBBLKDAT */
if (read_write == I2C_SMBUS_READ)
data->block[i] = inb_p(SMBBLKDAT);
if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
outb_p(data->block[i+1], SMBBLKDAT);
if ((temp & 0x9e) != 0x00)
outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev,
"Bad status (%02x) at end of transaction\n",
temp);
}
dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
if (result < 0)
goto END;
}
#ifdef HAVE_PEC
if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
/* wait for INTR bit as advised by Intel */
timeout = 0;
do {
temp = inb_p(SMBHSTSTS);
msleep(1);
} while ((!(temp & 0x02))
&& (timeout++ < MAX_TIMEOUT));
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
}
outb_p(temp, SMBHSTSTS);
}
#endif
result = 0;
END:
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
/* restore saved configuration register value */
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
return result;
}
/* Return -1 on error. */
static s32 i801_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
{
int hwpec = 0;
int block = 0;
int ret, xact = 0;
#ifdef HAVE_PEC
if(isich4)
hwpec = (flags & I2C_CLIENT_PEC) != 0;
#endif
switch (size) {
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
#ifdef HAVE_PEC
case I2C_SMBUS_BLOCK_DATA_PEC:
if(hwpec && size == I2C_SMBUS_BLOCK_DATA)
size = I2C_SMBUS_BLOCK_DATA_PEC;
#endif
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
return -1;
}
#ifdef HAVE_PEC
if(isich4 && hwpec) {
if(size != I2C_SMBUS_QUICK &&
size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(1, SMBAUXCTL); /* enable HW PEC */
}
#endif
if(block)
ret = i801_block_transaction(data, read_write, size);
else {
outb_p(xact | ENABLE_INT9, SMBHSTCNT);
ret = i801_transaction();
}
#ifdef HAVE_PEC
if(isich4 && hwpec) {
if(size != I2C_SMBUS_QUICK &&
size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(0, SMBAUXCTL);
}
#endif
if(block)
return ret;
if(ret)
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
return 0;
switch (xact & 0x7f) {
case I801_BYTE: /* Result put in SMBHSTDAT0 */
case I801_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case I801_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
}
return 0;
}
static u32 i801_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_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
#ifdef HAVE_PEC
| (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC |
I2C_FUNC_SMBUS_HWPEC_CALC
: 0)
#endif
;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = i801_access,
.functionality = i801_func,
};
static struct i2c_adapter i801_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_4) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, i801_ids);
static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (i801_setup(dev)) {
dev_warn(&dev->dev,
"I801 not detected, module not inserted.\n");
return -ENODEV;
}
/* set up the driverfs linkage to our parent device */
i801_adapter.dev.parent = &dev->dev;
snprintf(i801_adapter.name, I2C_NAME_SIZE,
"SMBus I801 adapter at %04x", i801_smba);
return i2c_add_adapter(&i801_adapter);
}
static void __devexit i801_remove(struct pci_dev *dev)
{
i2c_del_adapter(&i801_adapter);
release_region(i801_smba, (isich4 ? 16 : 8));
}
static struct pci_driver i801_driver = {
.name = "i801_smbus",
.id_table = i801_ids,
.probe = i801_probe,
.remove = __devexit_p(i801_remove),
};
static int __init i2c_i801_init(void)
{
return pci_register_driver(&i801_driver);
}
static void __exit i2c_i801_exit(void)
{
pci_unregister_driver(&i801_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_i801_init);
module_exit(i2c_i801_exit);

View File

@@ -0,0 +1,260 @@
/*
i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Ralph Metzler <rjkm@thp.uni-koeln.de>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
Simon Vogl
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.
*/
/*
This interfaces to the I810/I815 to provide access to
the DDC Bus and the I2C Bus.
SUPPORTED DEVICES PCI ID
i810AA 7121
i810AB 7123
i810E 7125
i815 1132
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
/* GPIO register locations */
#define I810_IOCONTROL_OFFSET 0x5000
#define I810_HVSYNC 0x00 /* not used */
#define I810_GPIOA 0x10
#define I810_GPIOB 0x14
/* bit locations in the registers */
#define SCL_DIR_MASK 0x0001
#define SCL_DIR 0x0002
#define SCL_VAL_MASK 0x0004
#define SCL_VAL_OUT 0x0008
#define SCL_VAL_IN 0x0010
#define SDA_DIR_MASK 0x0100
#define SDA_DIR 0x0200
#define SDA_VAL_MASK 0x0400
#define SDA_VAL_OUT 0x0800
#define SDA_VAL_IN 0x1000
/* initialization states */
#define INIT1 0x1
#define INIT2 0x2
#define INIT3 0x4
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void __iomem *ioaddr;
/* The i810 GPIO registers have individual masks for each bit
so we never have to read before writing. Nice. */
static void bit_i810i2c_setscl(void *data, int val)
{
writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
ioaddr + I810_GPIOB);
readl(ioaddr + I810_GPIOB); /* flush posted write */
}
static void bit_i810i2c_setsda(void *data, int val)
{
writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
ioaddr + I810_GPIOB);
readl(ioaddr + I810_GPIOB); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins could always remain outputs.
However, some chip versions don't latch the inputs unless they
are set as inputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. Following guidance in the 815
prog. ref. guide, we do a "dummy write" of 0 to the register before
reading which forces the input value to be latched. We presume this
applies to the 810 as well; shouldn't hurt anyway. This is necessary to get
i2c_algo_bit bit_test=1 to pass. */
static int bit_i810i2c_getscl(void *data)
{
writel(SCL_DIR_MASK, ioaddr + I810_GPIOB);
writel(0, ioaddr + I810_GPIOB);
return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN));
}
static int bit_i810i2c_getsda(void *data)
{
writel(SDA_DIR_MASK, ioaddr + I810_GPIOB);
writel(0, ioaddr + I810_GPIOB);
return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN));
}
static void bit_i810ddc_setscl(void *data, int val)
{
writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
ioaddr + I810_GPIOA);
readl(ioaddr + I810_GPIOA); /* flush posted write */
}
static void bit_i810ddc_setsda(void *data, int val)
{
writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
ioaddr + I810_GPIOA);
readl(ioaddr + I810_GPIOA); /* flush posted write */
}
static int bit_i810ddc_getscl(void *data)
{
writel(SCL_DIR_MASK, ioaddr + I810_GPIOA);
writel(0, ioaddr + I810_GPIOA);
return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN));
}
static int bit_i810ddc_getsda(void *data)
{
writel(SDA_DIR_MASK, ioaddr + I810_GPIOA);
writel(0, ioaddr + I810_GPIOA);
return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN));
}
static int config_i810(struct pci_dev *dev)
{
unsigned long cadr;
/* map I810 memory */
cadr = dev->resource[1].start;
cadr += I810_IOCONTROL_OFFSET;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = ioremap_nocache(cadr, 0x1000);
if (ioaddr) {
bit_i810i2c_setscl(NULL, 1);
bit_i810i2c_setsda(NULL, 1);
bit_i810ddc_setscl(NULL, 1);
bit_i810ddc_setsda(NULL, 1);
return 0;
}
return -ENODEV;
}
static struct i2c_algo_bit_data i810_i2c_bit_data = {
.setsda = bit_i810i2c_setsda,
.setscl = bit_i810i2c_setscl,
.getsda = bit_i810i2c_getsda,
.getscl = bit_i810i2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
static struct i2c_adapter i810_i2c_adapter = {
.owner = THIS_MODULE,
.name = "I810/I815 I2C Adapter",
.algo_data = &i810_i2c_bit_data,
};
static struct i2c_algo_bit_data i810_ddc_bit_data = {
.setsda = bit_i810ddc_setsda,
.setscl = bit_i810ddc_setscl,
.getsda = bit_i810ddc_getsda,
.getscl = bit_i810ddc_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
static struct i2c_adapter i810_ddc_adapter = {
.owner = THIS_MODULE,
.name = "I810/I815 DDC Adapter",
.algo_data = &i810_ddc_bit_data,
};
static struct pci_device_id i810_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) },
{ 0, },
};
MODULE_DEVICE_TABLE (pci, i810_ids);
static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
retval = config_i810(dev);
if (retval)
return retval;
dev_info(&dev->dev, "i810/i815 i2c device found.\n");
/* set up the sysfs linkage to our parent device */
i810_i2c_adapter.dev.parent = &dev->dev;
i810_ddc_adapter.dev.parent = &dev->dev;
retval = i2c_bit_add_bus(&i810_i2c_adapter);
if (retval)
return retval;
retval = i2c_bit_add_bus(&i810_ddc_adapter);
if (retval)
i2c_bit_del_bus(&i810_i2c_adapter);
return retval;
}
static void __devexit i810_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&i810_ddc_adapter);
i2c_bit_del_bus(&i810_i2c_adapter);
iounmap(ioaddr);
}
static struct pci_driver i810_driver = {
.name = "i810_smbus",
.id_table = i810_ids,
.probe = i810_probe,
.remove = __devexit_p(i810_remove),
};
static int __init i2c_i810_init(void)
{
return pci_register_driver(&i810_driver);
}
static void __exit i2c_i810_exit(void)
{
pci_unregister_driver(&i810_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"Ralph Metzler <rjkm@thp.uni-koeln.de>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("I810/I815 I2C/DDC driver");
MODULE_LICENSE("GPL");
module_init(i2c_i810_init);
module_exit(i2c_i810_exit);

View File

@@ -0,0 +1,819 @@
/*
* drivers/i2c/i2c-ibm_iic.c
*
* Support for the IIC peripheral on IBM PPC 4xx
*
* Copyright (c) 2003, 2004 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Ian DaSilva <idasilva@mvista.com>
* Armin Kuster <akuster@mvista.com>
* Matt Porter <mporter@mvista.com>
*
* Copyright 2000-2003 MontaVista Software Inc.
*
* Original driver version was highly leveraged from i2c-elektor.c
*
* Copyright 1995-97 Simon G. Vogl
* 1998-99 Hans Berglund
*
* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
* and even Frodo Looijaard <frodol@dds.nl>
*
* 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.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <asm/ocp.h>
#include <asm/ibm4xx.h>
#include "i2c-ibm_iic.h"
#define DRIVER_VERSION "2.1"
MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION);
MODULE_LICENSE("GPL");
static int iic_force_poll;
module_param(iic_force_poll, bool, 0);
MODULE_PARM_DESC(iic_force_poll, "Force polling mode");
static int iic_force_fast;
module_param(iic_force_fast, bool, 0);
MODULE_PARM_DESC(iic_fast_poll, "Force fast mode (400 kHz)");
#define DBG_LEVEL 0
#ifdef DBG
#undef DBG
#endif
#ifdef DBG2
#undef DBG2
#endif
#if DBG_LEVEL > 0
# define DBG(f,x...) printk(KERN_DEBUG "ibm-iic" f, ##x)
#else
# define DBG(f,x...) ((void)0)
#endif
#if DBG_LEVEL > 1
# define DBG2(f,x...) DBG(f, ##x)
#else
# define DBG2(f,x...) ((void)0)
#endif
#if DBG_LEVEL > 2
static void dump_iic_regs(const char* header, struct ibm_iic_private* dev)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
printk(KERN_DEBUG "ibm-iic%d: %s\n", dev->idx, header);
printk(KERN_DEBUG " cntl = 0x%02x, mdcntl = 0x%02x\n"
KERN_DEBUG " sts = 0x%02x, extsts = 0x%02x\n"
KERN_DEBUG " clkdiv = 0x%02x, xfrcnt = 0x%02x\n"
KERN_DEBUG " xtcntlss = 0x%02x, directcntl = 0x%02x\n",
in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts),
in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt),
in_8(&iic->xtcntlss), in_8(&iic->directcntl));
}
# define DUMP_REGS(h,dev) dump_iic_regs((h),(dev))
#else
# define DUMP_REGS(h,dev) ((void)0)
#endif
/* Bus timings (in ns) for bit-banging */
static struct i2c_timings {
unsigned int hd_sta;
unsigned int su_sto;
unsigned int low;
unsigned int high;
unsigned int buf;
} timings [] = {
/* Standard mode (100 KHz) */
{
.hd_sta = 4000,
.su_sto = 4000,
.low = 4700,
.high = 4000,
.buf = 4700,
},
/* Fast mode (400 KHz) */
{
.hd_sta = 600,
.su_sto = 600,
.low = 1300,
.high = 600,
.buf = 1300,
}};
/* Enable/disable interrupt generation */
static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable)
{
out_8(&dev->vaddr->intmsk, enable ? INTRMSK_EIMTC : 0);
}
/*
* Initialize IIC interface.
*/
static void iic_dev_init(struct ibm_iic_private* dev)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
DBG("%d: init\n", dev->idx);
/* Clear master address */
out_8(&iic->lmadr, 0);
out_8(&iic->hmadr, 0);
/* Clear slave address */
out_8(&iic->lsadr, 0);
out_8(&iic->hsadr, 0);
/* Clear status & extended status */
out_8(&iic->sts, STS_SCMP | STS_IRQA);
out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | EXTSTS_LA
| EXTSTS_ICT | EXTSTS_XFRA);
/* Set clock divider */
out_8(&iic->clkdiv, dev->clckdiv);
/* Clear transfer count */
out_8(&iic->xfrcnt, 0);
/* Clear extended control and status */
out_8(&iic->xtcntlss, XTCNTLSS_SRC | XTCNTLSS_SRS | XTCNTLSS_SWC
| XTCNTLSS_SWS);
/* Clear control register */
out_8(&iic->cntl, 0);
/* Enable interrupts if possible */
iic_interrupt_mode(dev, dev->irq >= 0);
/* Set mode control */
out_8(&iic->mdcntl, MDCNTL_FMDB | MDCNTL_EINT | MDCNTL_EUBS
| (dev->fast_mode ? MDCNTL_FSM : 0));
DUMP_REGS("iic_init", dev);
}
/*
* Reset IIC interface
*/
static void iic_dev_reset(struct ibm_iic_private* dev)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
int i;
u8 dc;
DBG("%d: soft reset\n", dev->idx);
DUMP_REGS("reset", dev);
/* Place chip in the reset state */
out_8(&iic->xtcntlss, XTCNTLSS_SRST);
/* Check if bus is free */
dc = in_8(&iic->directcntl);
if (!DIRCTNL_FREE(dc)){
DBG("%d: trying to regain bus control\n", dev->idx);
/* Try to set bus free state */
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
/* Wait until we regain bus control */
for (i = 0; i < 100; ++i){
dc = in_8(&iic->directcntl);
if (DIRCTNL_FREE(dc))
break;
/* Toggle SCL line */
dc ^= DIRCNTL_SCC;
out_8(&iic->directcntl, dc);
udelay(10);
dc ^= DIRCNTL_SCC;
out_8(&iic->directcntl, dc);
/* be nice */
cond_resched();
}
}
/* Remove reset */
out_8(&iic->xtcntlss, 0);
/* Reinitialize interface */
iic_dev_init(dev);
}
/*
* Do 0-length transaction using bit-banging through IIC_DIRECTCNTL register.
*/
/* Wait for SCL and/or SDA to be high */
static int iic_dc_wait(volatile struct iic_regs __iomem *iic, u8 mask)
{
unsigned long x = jiffies + HZ / 28 + 2;
while ((in_8(&iic->directcntl) & mask) != mask){
if (unlikely(time_after(jiffies, x)))
return -1;
cond_resched();
}
return 0;
}
static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0];
u8 mask, v, sda;
int i, res;
/* Only 7-bit addresses are supported */
if (unlikely(p->flags & I2C_M_TEN)){
DBG("%d: smbus_quick - 10 bit addresses are not supported\n",
dev->idx);
return -EINVAL;
}
DBG("%d: smbus_quick(0x%02x)\n", dev->idx, p->addr);
/* Reset IIC interface */
out_8(&iic->xtcntlss, XTCNTLSS_SRST);
/* Wait for bus to become free */
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSDA | DIRCNTL_MSC)))
goto err;
ndelay(t->buf);
/* START */
out_8(&iic->directcntl, DIRCNTL_SCC);
sda = 0;
ndelay(t->hd_sta);
/* Send address */
v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0));
for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
sda = (v & mask) ? DIRCNTL_SDAC : 0;
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SCC | sda);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
ndelay(t->high);
}
/* ACK */
out_8(&iic->directcntl, sda);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SDAC);
ndelay(t->low / 2);
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
res = (in_8(&iic->directcntl) & DIRCNTL_MSDA) ? -EREMOTEIO : 1;
ndelay(t->high);
/* STOP */
out_8(&iic->directcntl, 0);
ndelay(t->low);
out_8(&iic->directcntl, DIRCNTL_SCC);
if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC)))
goto err;
ndelay(t->su_sto);
out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC);
ndelay(t->buf);
DBG("%d: smbus_quick -> %s\n", dev->idx, res ? "NACK" : "ACK");
out:
/* Remove reset */
out_8(&iic->xtcntlss, 0);
/* Reinitialize interface */
iic_dev_init(dev);
return res;
err:
DBG("%d: smbus_quick - bus is stuck\n", dev->idx);
res = -EREMOTEIO;
goto out;
}
/*
* IIC interrupt handler
*/
static irqreturn_t iic_handler(int irq, void *dev_id, struct pt_regs *regs)
{
struct ibm_iic_private* dev = (struct ibm_iic_private*)dev_id;
volatile struct iic_regs __iomem *iic = dev->vaddr;
DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n",
dev->idx, in_8(&iic->sts), in_8(&iic->extsts));
/* Acknowledge IRQ and wakeup iic_wait_for_tc */
out_8(&iic->sts, STS_IRQA | STS_SCMP);
wake_up_interruptible(&dev->wq);
return IRQ_HANDLED;
}
/*
* Get master transfer result and clear errors if any.
* Returns the number of actually transferred bytes or error (<0)
*/
static int iic_xfer_result(struct ibm_iic_private* dev)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
if (unlikely(in_8(&iic->sts) & STS_ERR)){
DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx,
in_8(&iic->extsts));
/* Clear errors and possible pending IRQs */
out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD |
EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA);
/* Flush master data buffer */
out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB);
/* Is bus free?
* If error happened during combined xfer
* IIC interface is usually stuck in some strange
* state, the only way out - soft reset.
*/
if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){
DBG("%d: bus is stuck, resetting\n", dev->idx);
iic_dev_reset(dev);
}
return -EREMOTEIO;
}
else
return in_8(&iic->xfrcnt) & XFRCNT_MTC_MASK;
}
/*
* Try to abort active transfer.
*/
static void iic_abort_xfer(struct ibm_iic_private* dev)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
unsigned long x;
DBG("%d: iic_abort_xfer\n", dev->idx);
out_8(&iic->cntl, CNTL_HMT);
/*
* Wait for the abort command to complete.
* It's not worth to be optimized, just poll (timeout >= 1 tick)
*/
x = jiffies + 2;
while ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){
if (time_after(jiffies, x)){
DBG("%d: abort timeout, resetting...\n", dev->idx);
iic_dev_reset(dev);
return;
}
schedule();
}
/* Just to clear errors */
iic_xfer_result(dev);
}
/*
* Wait for master transfer to complete.
* It puts current process to sleep until we get interrupt or timeout expires.
* Returns the number of transferred bytes or error (<0)
*/
static int iic_wait_for_tc(struct ibm_iic_private* dev){
volatile struct iic_regs __iomem *iic = dev->vaddr;
int ret = 0;
if (dev->irq >= 0){
/* Interrupt mode */
ret = wait_event_interruptible_timeout(dev->wq,
!(in_8(&iic->sts) & STS_PT), dev->adap.timeout * HZ);
if (unlikely(ret < 0))
DBG("%d: wait interrupted\n", dev->idx);
else if (unlikely(in_8(&iic->sts) & STS_PT)){
DBG("%d: wait timeout\n", dev->idx);
ret = -ETIMEDOUT;
}
}
else {
/* Polling mode */
unsigned long x = jiffies + dev->adap.timeout * HZ;
while (in_8(&iic->sts) & STS_PT){
if (unlikely(time_after(jiffies, x))){
DBG("%d: poll timeout\n", dev->idx);
ret = -ETIMEDOUT;
break;
}
if (unlikely(signal_pending(current))){
DBG("%d: poll interrupted\n", dev->idx);
ret = -ERESTARTSYS;
break;
}
schedule();
}
}
if (unlikely(ret < 0))
iic_abort_xfer(dev);
else
ret = iic_xfer_result(dev);
DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret);
return ret;
}
/*
* Low level master transfer routine
*/
static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm,
int combined_xfer)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
char* buf = pm->buf;
int i, j, loops, ret = 0;
int len = pm->len;
u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT;
if (pm->flags & I2C_M_RD)
cntl |= CNTL_RW;
loops = (len + 3) / 4;
for (i = 0; i < loops; ++i, len -= 4){
int count = len > 4 ? 4 : len;
u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT);
if (!(cntl & CNTL_RW))
for (j = 0; j < count; ++j)
out_8((void __iomem *)&iic->mdbuf, *buf++);
if (i < loops - 1)
cmd |= CNTL_CHT;
else if (combined_xfer)
cmd |= CNTL_RPST;
DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd);
/* Start transfer */
out_8(&iic->cntl, cmd);
/* Wait for completion */
ret = iic_wait_for_tc(dev);
if (unlikely(ret < 0))
break;
else if (unlikely(ret != count)){
DBG("%d: xfer_bytes, requested %d, transfered %d\n",
dev->idx, count, ret);
/* If it's not a last part of xfer, abort it */
if (combined_xfer || (i < loops - 1))
iic_abort_xfer(dev);
ret = -EREMOTEIO;
break;
}
if (cntl & CNTL_RW)
for (j = 0; j < count; ++j)
*buf++ = in_8((void __iomem *)&iic->mdbuf);
}
return ret > 0 ? 0 : ret;
}
/*
* Set target slave address for master transfer
*/
static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
u16 addr = msg->addr;
DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx,
addr, msg->flags & I2C_M_TEN ? 10 : 7);
if (msg->flags & I2C_M_TEN){
out_8(&iic->cntl, CNTL_AMD);
out_8(&iic->lmadr, addr);
out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06));
}
else {
out_8(&iic->cntl, 0);
out_8(&iic->lmadr, addr << 1);
}
}
static inline int iic_invalid_address(const struct i2c_msg* p)
{
return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f));
}
static inline int iic_address_neq(const struct i2c_msg* p1,
const struct i2c_msg* p2)
{
return (p1->addr != p2->addr)
|| ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN));
}
/*
* Generic master transfer entrypoint.
* Returns the number of processed messages or error (<0)
*/
static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct ibm_iic_private* dev = (struct ibm_iic_private*)(i2c_get_adapdata(adap));
volatile struct iic_regs __iomem *iic = dev->vaddr;
int i, ret = 0;
DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num);
if (!num)
return 0;
/* Check the sanity of the passed messages.
* Uhh, generic i2c layer is more suitable place for such code...
*/
if (unlikely(iic_invalid_address(&msgs[0]))){
DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx,
msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7);
return -EINVAL;
}
for (i = 0; i < num; ++i){
if (unlikely(msgs[i].len <= 0)){
if (num == 1 && !msgs[0].len){
/* Special case for I2C_SMBUS_QUICK emulation.
* IBM IIC doesn't support 0-length transactions
* so we have to emulate them using bit-banging.
*/
return iic_smbus_quick(dev, &msgs[0]);
}
DBG("%d: invalid len %d in msg[%d]\n", dev->idx,
msgs[i].len, i);
return -EINVAL;
}
if (unlikely(iic_address_neq(&msgs[0], &msgs[i]))){
DBG("%d: invalid addr in msg[%d]\n", dev->idx, i);
return -EINVAL;
}
}
/* Check bus state */
if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){
DBG("%d: iic_xfer, bus is not free\n", dev->idx);
/* Usually it means something serious has happend.
* We *cannot* have unfinished previous transfer
* so it doesn't make any sense to try to stop it.
* Probably we were not able to recover from the
* previous error.
* The only *reasonable* thing I can think of here
* is soft reset. --ebs
*/
iic_dev_reset(dev);
if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){
DBG("%d: iic_xfer, bus is still not free\n", dev->idx);
return -EREMOTEIO;
}
}
else {
/* Flush master data buffer (just in case) */
out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB);
}
/* Load slave address */
iic_address(dev, &msgs[0]);
/* Do real transfer */
for (i = 0; i < num && !ret; ++i)
ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1);
return ret < 0 ? ret : num;
}
static u32 iic_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
}
static struct i2c_algorithm iic_algo = {
.name = "IBM IIC algorithm",
.id = I2C_ALGO_OCP,
.master_xfer = iic_xfer,
.functionality = iic_func
};
/*
* Calculates IICx_CLCKDIV value for a specific OPB clock frequency
*/
static inline u8 iic_clckdiv(unsigned int opb)
{
/* Compatibility kludge, should go away after all cards
* are fixed to fill correct value for opbfreq.
* Previous driver version used hardcoded divider value 4,
* it corresponds to OPB frequency from the range (40, 50] MHz
*/
if (!opb){
printk(KERN_WARNING "ibm-iic: using compatibility value for OPB freq,"
" fix your board specific setup\n");
opb = 50000000;
}
/* Convert to MHz */
opb /= 1000000;
if (opb < 20 || opb > 150){
printk(KERN_CRIT "ibm-iic: invalid OPB clock frequency %u MHz\n",
opb);
opb = opb < 20 ? 20 : 150;
}
return (u8)((opb + 9) / 10 - 1);
}
/*
* Register single IIC interface
*/
static int __devinit iic_probe(struct ocp_device *ocp){
struct ibm_iic_private* dev;
struct i2c_adapter* adap;
struct ocp_func_iic_data* iic_data = ocp->def->additions;
int ret;
if (!iic_data)
printk(KERN_WARNING"ibm-iic%d: missing additional data!\n",
ocp->def->index);
if (!(dev = kmalloc(sizeof(*dev), GFP_KERNEL))){
printk(KERN_CRIT "ibm-iic%d: failed to allocate device data\n",
ocp->def->index);
return -ENOMEM;
}
memset(dev, 0, sizeof(*dev));
dev->idx = ocp->def->index;
ocp_set_drvdata(ocp, dev);
if (!(dev->vaddr = ioremap(ocp->def->paddr, sizeof(struct iic_regs)))){
printk(KERN_CRIT "ibm-iic%d: failed to ioremap device registers\n",
dev->idx);
ret = -ENXIO;
goto fail2;
}
init_waitqueue_head(&dev->wq);
dev->irq = iic_force_poll ? -1 : ocp->def->irq;
if (dev->irq >= 0){
/* Disable interrupts until we finish intialization,
assumes level-sensitive IRQ setup...
*/
iic_interrupt_mode(dev, 0);
if (request_irq(dev->irq, iic_handler, 0, "IBM IIC", dev)){
printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n",
dev->idx, dev->irq);
/* Fallback to the polling mode */
dev->irq = -1;
}
}
if (dev->irq < 0)
printk(KERN_WARNING "ibm-iic%d: using polling mode\n",
dev->idx);
/* Board specific settings */
dev->fast_mode = iic_force_fast ? 1 : (iic_data ? iic_data->fast_mode : 0);
/* clckdiv is the same for *all* IIC interfaces,
* but I'd rather make a copy than introduce another global. --ebs
*/
dev->clckdiv = iic_clckdiv(ocp_sys_info.opb_bus_freq);
DBG("%d: clckdiv = %d\n", dev->idx, dev->clckdiv);
/* Initialize IIC interface */
iic_dev_init(dev);
/* Register it with i2c layer */
adap = &dev->adap;
strcpy(adap->name, "IBM IIC");
i2c_set_adapdata(adap, dev);
adap->id = I2C_HW_OCP | iic_algo.id;
adap->algo = &iic_algo;
adap->client_register = NULL;
adap->client_unregister = NULL;
adap->timeout = 1;
adap->retries = 1;
if ((ret = i2c_add_adapter(adap)) != 0){
printk(KERN_CRIT "ibm-iic%d: failed to register i2c adapter\n",
dev->idx);
goto fail;
}
printk(KERN_INFO "ibm-iic%d: using %s mode\n", dev->idx,
dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)");
return 0;
fail:
if (dev->irq >= 0){
iic_interrupt_mode(dev, 0);
free_irq(dev->irq, dev);
}
iounmap(dev->vaddr);
fail2:
ocp_set_drvdata(ocp, NULL);
kfree(dev);
return ret;
}
/*
* Cleanup initialized IIC interface
*/
static void __devexit iic_remove(struct ocp_device *ocp)
{
struct ibm_iic_private* dev = (struct ibm_iic_private*)ocp_get_drvdata(ocp);
BUG_ON(dev == NULL);
if (i2c_del_adapter(&dev->adap)){
printk(KERN_CRIT "ibm-iic%d: failed to delete i2c adapter :(\n",
dev->idx);
/* That's *very* bad, just shutdown IRQ ... */
if (dev->irq >= 0){
iic_interrupt_mode(dev, 0);
free_irq(dev->irq, dev);
dev->irq = -1;
}
} else {
if (dev->irq >= 0){
iic_interrupt_mode(dev, 0);
free_irq(dev->irq, dev);
}
iounmap(dev->vaddr);
kfree(dev);
}
}
static struct ocp_device_id ibm_iic_ids[] __devinitdata =
{
{ .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_IIC },
{ .vendor = OCP_VENDOR_INVALID }
};
MODULE_DEVICE_TABLE(ocp, ibm_iic_ids);
static struct ocp_driver ibm_iic_driver =
{
.name = "iic",
.id_table = ibm_iic_ids,
.probe = iic_probe,
.remove = __devexit_p(iic_remove),
#if defined(CONFIG_PM)
.suspend = NULL,
.resume = NULL,
#endif
};
static int __init iic_init(void)
{
printk(KERN_INFO "IBM IIC driver v" DRIVER_VERSION "\n");
return ocp_register_driver(&ibm_iic_driver);
}
static void __exit iic_exit(void)
{
ocp_unregister_driver(&ibm_iic_driver);
}
module_init(iic_init);
module_exit(iic_exit);

View File

@@ -0,0 +1,124 @@
/*
* drivers/i2c/i2c-ibm_iic.h
*
* Support for the IIC peripheral on IBM PPC 4xx
*
* Copyright (c) 2003 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Ian DaSilva <idasilva@mvista.com>
* Armin Kuster <akuster@mvista.com>
* Matt Porter <mporter@mvista.com>
*
* Copyright 2000-2003 MontaVista Software Inc.
*
* 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.
*
*/
#ifndef __I2C_IBM_IIC_H_
#define __I2C_IBM_IIC_H_
#include <linux/config.h>
#include <linux/i2c.h>
struct iic_regs {
u16 mdbuf;
u16 sbbuf;
u8 lmadr;
u8 hmadr;
u8 cntl;
u8 mdcntl;
u8 sts;
u8 extsts;
u8 lsadr;
u8 hsadr;
u8 clkdiv;
u8 intmsk;
u8 xfrcnt;
u8 xtcntlss;
u8 directcntl;
};
struct ibm_iic_private {
struct i2c_adapter adap;
volatile struct iic_regs __iomem *vaddr;
wait_queue_head_t wq;
int idx;
int irq;
int fast_mode;
u8 clckdiv;
};
/* IICx_CNTL register */
#define CNTL_HMT 0x80
#define CNTL_AMD 0x40
#define CNTL_TCT_MASK 0x30
#define CNTL_TCT_SHIFT 4
#define CNTL_RPST 0x08
#define CNTL_CHT 0x04
#define CNTL_RW 0x02
#define CNTL_PT 0x01
/* IICx_MDCNTL register */
#define MDCNTL_FSDB 0x80
#define MDCNTL_FMDB 0x40
#define MDCNTL_EGC 0x20
#define MDCNTL_FSM 0x10
#define MDCNTL_ESM 0x08
#define MDCNTL_EINT 0x04
#define MDCNTL_EUBS 0x02
#define MDCNTL_HSCL 0x01
/* IICx_STS register */
#define STS_SSS 0x80
#define STS_SLPR 0x40
#define STS_MDBS 0x20
#define STS_MDBF 0x10
#define STS_SCMP 0x08
#define STS_ERR 0x04
#define STS_IRQA 0x02
#define STS_PT 0x01
/* IICx_EXTSTS register */
#define EXTSTS_IRQP 0x80
#define EXTSTS_BCS_MASK 0x70
#define EXTSTS_BCS_FREE 0x40
#define EXTSTS_IRQD 0x08
#define EXTSTS_LA 0x04
#define EXTSTS_ICT 0x02
#define EXTSTS_XFRA 0x01
/* IICx_INTRMSK register */
#define INTRMSK_EIRC 0x80
#define INTRMSK_EIRS 0x40
#define INTRMSK_EIWC 0x20
#define INTRMSK_EIWS 0x10
#define INTRMSK_EIHE 0x08
#define INTRMSK_EIIC 0x04
#define INTRMSK_EITA 0x02
#define INTRMSK_EIMTC 0x01
/* IICx_XFRCNT register */
#define XFRCNT_MTC_MASK 0x07
/* IICx_XTCNTLSS register */
#define XTCNTLSS_SRC 0x80
#define XTCNTLSS_SRS 0x40
#define XTCNTLSS_SWC 0x20
#define XTCNTLSS_SWS 0x10
#define XTCNTLSS_SRST 0x01
/* IICx_DIRECTCNTL register */
#define DIRCNTL_SDAC 0x08
#define DIRCNTL_SCC 0x04
#define DIRCNTL_MSDA 0x02
#define DIRCNTL_MSC 0x01
/* Check if we really control the I2C bus and bus is free */
#define DIRCTNL_FREE(v) (((v) & 0x0f) == 0x0f)
#endif /* __I2C_IBM_IIC_H_ */

View File

@@ -0,0 +1,554 @@
/* ------------------------------------------------------------------------- */
/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd
* <Peter dot Milne at D hyphen TACQ dot com>
*
* With acknowledgements to i2c-algo-ibm_ocp.c by
* Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com
*
* And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund:
*
* Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund
*
* And which acknowledged Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>,
* Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com>
*
* Major cleanup by Deepak Saxena <dsaxena@plexity.net>, 01/2005:
*
* - Use driver model to pass per-chip info instead of hardcoding and #ifdefs
* - Use ioremap/__raw_readl/__raw_writel instead of direct dereference
* - Make it work with IXP46x chips
* - Cleanup function names, coding style, etc
*
* 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, version 2.
*/
#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include "i2c-iop3xx.h"
/* global unit counter */
static int i2c_id = 0;
static inline unsigned char
iic_cook_addr(struct i2c_msg *msg)
{
unsigned char addr;
addr = (msg->addr << 1);
if (msg->flags & I2C_M_RD)
addr |= 1;
/*
* Read or Write?
*/
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
return addr;
}
static void
iop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
/* Follows devman 9.3 */
__raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET);
__raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET);
__raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET);
}
static void
iop3xx_i2c_set_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
__raw_writel(MYSAR, iop3xx_adap->ioaddr + SAR_OFFSET);
}
static void
iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE;
/*
* Everytime unit enable is asserted, GPOD needs to be cleared
* on IOP321 to avoid data corruption on the bus.
*/
#ifdef CONFIG_ARCH_IOP321
#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */
#define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */
*IOP321_GPOD &= (iop3xx_adap->id == 0) ? ~IOP321_GPOD_I2C0 :
~IOP321_GPOD_I2C1;
#endif
/* NB SR bits not same position as CR IE bits :-( */
iop3xx_adap->SR_enabled =
IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD |
IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY;
cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE;
__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
}
static void
iop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE |
IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN);
__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
}
/*
* NB: the handler has to clear the source of the interrupt!
* Then it passes the SR flags of interest to BH via adap data
*/
static irqreturn_t
iop3xx_i2c_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id;
u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET);
if ((sr &= iop3xx_adap->SR_enabled)) {
__raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET);
iop3xx_adap->SR_received |= sr;
wake_up_interruptible(&iop3xx_adap->waitq);
}
return IRQ_HANDLED;
}
/* check all error conditions, clear them , report most important */
static int
iop3xx_i2c_error(u32 sr)
{
int rc = 0;
if ((sr & IOP3XX_ISR_BERRD)) {
if ( !rc ) rc = -I2C_ERR_BERR;
}
if ((sr & IOP3XX_ISR_ALD)) {
if ( !rc ) rc = -I2C_ERR_ALD;
}
return rc;
}
static inline u32
iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
unsigned long flags;
u32 sr;
spin_lock_irqsave(&iop3xx_adap->lock, flags);
sr = iop3xx_adap->SR_received;
iop3xx_adap->SR_received = 0;
spin_unlock_irqrestore(&iop3xx_adap->lock, flags);
return sr;
}
/*
* sleep until interrupted, then recover and analyse the SR
* saved by handler
*/
typedef int (* compare_func)(unsigned test, unsigned mask);
/* returns 1 on correct comparison */
static int
iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
unsigned flags, unsigned* status,
compare_func compare)
{
unsigned sr = 0;
int interrupted;
int done;
int rc = 0;
do {
interrupted = wait_event_interruptible_timeout (
iop3xx_adap->waitq,
(done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
1 * HZ;
);
if ((rc = iop3xx_i2c_error(sr)) < 0) {
*status = sr;
return rc;
} else if (!interrupted) {
*status = sr;
return -ETIMEDOUT;
}
} while(!done);
*status = sr;
return 0;
}
/*
* Concrete compare_funcs
*/
static int
all_bits_clear(unsigned test, unsigned mask)
{
return (test & mask) == 0;
}
static int
any_bits_set(unsigned test, unsigned mask)
{
return (test & mask) != 0;
}
static int
iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_i2c_wait_event(
iop3xx_adap,
IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
status, any_bits_set);
}
static int
iop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_i2c_wait_event(
iop3xx_adap,
IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
status, any_bits_set);
}
static int
iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_i2c_wait_event(
iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear);
}
static int
iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
struct i2c_msg* msg)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
int status;
int rc;
__raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET);
cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE;
__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
return rc;
}
static int
iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte,
int stop)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
int status;
int rc = 0;
__raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET);
cr &= ~IOP3XX_ICR_MSTART;
if (stop) {
cr |= IOP3XX_ICR_MSTOP;
} else {
cr &= ~IOP3XX_ICR_MSTOP;
}
cr |= IOP3XX_ICR_TBYTE;
__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
return rc;
}
static int
iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte,
int stop)
{
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
int status;
int rc = 0;
cr &= ~IOP3XX_ICR_MSTART;
if (stop) {
cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK;
} else {
cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
}
cr |= IOP3XX_ICR_TBYTE;
__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status);
*byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET);
return rc;
}
static int
iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int ii;
int rc = 0;
for (ii = 0; rc == 0 && ii != count; ++ii)
rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
return rc;
}
static int
iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int ii;
int rc = 0;
for (ii = 0; rc == 0 && ii != count; ++ii)
rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
return rc;
}
/*
* Description: This function implements combined transactions. Combined
* transactions consist of combinations of reading and writing blocks of data.
* FROM THE SAME ADDRESS
* Each transfer (i.e. a read or a write) is separated by a repeated start
* condition.
*/
static int
iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int rc;
rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg);
if (rc < 0) {
return rc;
}
if ((pmsg->flags&I2C_M_RD)) {
return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len);
} else {
return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len);
}
}
/*
* master_xfer() - main read/write entry
*/
static int
iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
int num)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
int im = 0;
int ret = 0;
int status;
iop3xx_i2c_wait_idle(iop3xx_adap, &status);
iop3xx_i2c_reset(iop3xx_adap);
iop3xx_i2c_enable(iop3xx_adap);
for (im = 0; ret == 0 && im != num; im++) {
ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]);
}
iop3xx_i2c_transaction_cleanup(iop3xx_adap);
if(ret)
return ret;
return im;
}
static int
iop3xx_i2c_algo_control(struct i2c_adapter *adapter, unsigned int cmd,
unsigned long arg)
{
return 0;
}
static u32
iop3xx_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm iop3xx_i2c_algo = {
.name = "IOP3xx I2C algorithm",
.id = I2C_ALGO_IOP3XX,
.master_xfer = iop3xx_i2c_master_xfer,
.algo_control = iop3xx_i2c_algo_control,
.functionality = iop3xx_i2c_func,
};
static int
iop3xx_i2c_remove(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct i2c_adapter *padapter = dev_get_drvdata(&pdev->dev);
struct i2c_algo_iop3xx_data *adapter_data =
(struct i2c_algo_iop3xx_data *)padapter->algo_data;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET);
/*
* Disable the actual HW unit
*/
cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE);
__raw_writel(cr, adapter_data->ioaddr + CR_OFFSET);
iounmap((void __iomem*)adapter_data->ioaddr);
release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
kfree(adapter_data);
kfree(padapter);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static int
iop3xx_i2c_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
int ret;
struct i2c_adapter *new_adapter;
struct i2c_algo_iop3xx_data *adapter_data;
new_adapter = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
if (!new_adapter) {
ret = -ENOMEM;
goto out;
}
memset((void*)new_adapter, 0, sizeof(*new_adapter));
adapter_data = kmalloc(sizeof(struct i2c_algo_iop3xx_data), GFP_KERNEL);
if (!adapter_data) {
ret = -ENOMEM;
goto free_adapter;
}
memset((void*)adapter_data, 0, sizeof(*adapter_data));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto free_both;
}
if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) {
ret = -EBUSY;
goto free_both;
}
/* set the adapter enumeration # */
adapter_data->id = i2c_id++;
adapter_data->ioaddr = (u32)ioremap(res->start, IOP3XX_I2C_IO_SIZE);
if (!adapter_data->ioaddr) {
ret = -ENOMEM;
goto release_region;
}
res = request_irq(platform_get_irq(pdev, 0), iop3xx_i2c_irq_handler, 0,
pdev->name, adapter_data);
if (res) {
ret = -EIO;
goto unmap;
}
memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
new_adapter->id = I2C_HW_IOP3XX;
new_adapter->owner = THIS_MODULE;
new_adapter->dev.parent = &pdev->dev;
/*
* Default values...should these come in from board code?
*/
new_adapter->timeout = 100;
new_adapter->retries = 3;
new_adapter->algo = &iop3xx_i2c_algo;
init_waitqueue_head(&adapter_data->waitq);
spin_lock_init(&adapter_data->lock);
iop3xx_i2c_reset(adapter_data);
iop3xx_i2c_set_slave_addr(adapter_data);
iop3xx_i2c_enable(adapter_data);
dev_set_drvdata(&pdev->dev, new_adapter);
new_adapter->algo_data = adapter_data;
i2c_add_adapter(new_adapter);
return 0;
unmap:
iounmap((void __iomem*)adapter_data->ioaddr);
release_region:
release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
free_both:
kfree(adapter_data);
free_adapter:
kfree(new_adapter);
out:
return ret;
}
static struct device_driver iop3xx_i2c_driver = {
.name = "IOP3xx-I2C",
.bus = &platform_bus_type,
.probe = iop3xx_i2c_probe,
.remove = iop3xx_i2c_remove
};
static int __init
i2c_iop3xx_init (void)
{
return driver_register(&iop3xx_i2c_driver);
}
static void __exit
i2c_iop3xx_exit (void)
{
driver_unregister(&iop3xx_i2c_driver);
return;
}
module_init (i2c_iop3xx_init);
module_exit (i2c_iop3xx_exit);
MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>");
MODULE_DESCRIPTION("IOP3xx iic algorithm and driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,107 @@
/* ------------------------------------------------------------------------- */
/* i2c-iop3xx.h algorithm driver definitions private to i2c-iop3xx.c */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd
* <Peter dot Milne at D hyphen TACQ dot 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, version 2.
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. */
/* ------------------------------------------------------------------------- */
#ifndef I2C_IOP3XX_H
#define I2C_IOP3XX_H 1
/*
* iop321 hardware bit definitions
*/
#define IOP3XX_ICR_FAST_MODE 0x8000 /* 1=400kBps, 0=100kBps */
#define IOP3XX_ICR_UNIT_RESET 0x4000 /* 1=RESET */
#define IOP3XX_ICR_SAD_IE 0x2000 /* 1=Slave Detect Interrupt Enable */
#define IOP3XX_ICR_ALD_IE 0x1000 /* 1=Arb Loss Detect Interrupt Enable */
#define IOP3XX_ICR_SSD_IE 0x0800 /* 1=Slave STOP Detect Interrupt Enable */
#define IOP3XX_ICR_BERR_IE 0x0400 /* 1=Bus Error Interrupt Enable */
#define IOP3XX_ICR_RXFULL_IE 0x0200 /* 1=Receive Full Interrupt Enable */
#define IOP3XX_ICR_TXEMPTY_IE 0x0100 /* 1=Transmit Empty Interrupt Enable */
#define IOP3XX_ICR_GCD 0x0080 /* 1=General Call Disable */
/*
* IOP3XX_ICR_GCD: 1 disables response as slave. "This bit must be set
* when sending a master mode general call message from the I2C unit"
*/
#define IOP3XX_ICR_UE 0x0040 /* 1=Unit Enable */
/*
* "NOTE: To avoid I2C bus integrity problems,
* the user needs to ensure that the GPIO Output Data Register -
* GPOD bits associated with an I2C port are cleared prior to setting
* the enable bit for that I2C serial port.
* The user prepares to enable I2C port 0 and
* I2C port 1 by clearing GPOD bits 7:6 and GPOD bits 5:4, respectively.
*/
#define IOP3XX_ICR_SCLEN 0x0020 /* 1=SCL enable for master mode */
#define IOP3XX_ICR_MABORT 0x0010 /* 1=Send a STOP with no data
* NB TBYTE must be clear */
#define IOP3XX_ICR_TBYTE 0x0008 /* 1=Send/Receive a byte. i2c clears */
#define IOP3XX_ICR_NACK 0x0004 /* 1=reply with NACK */
#define IOP3XX_ICR_MSTOP 0x0002 /* 1=send a STOP after next data byte */
#define IOP3XX_ICR_MSTART 0x0001 /* 1=initiate a START */
#define IOP3XX_ISR_BERRD 0x0400 /* 1=BUS ERROR Detected */
#define IOP3XX_ISR_SAD 0x0200 /* 1=Slave ADdress Detected */
#define IOP3XX_ISR_GCAD 0x0100 /* 1=General Call Address Detected */
#define IOP3XX_ISR_RXFULL 0x0080 /* 1=Receive Full */
#define IOP3XX_ISR_TXEMPTY 0x0040 /* 1=Transmit Empty */
#define IOP3XX_ISR_ALD 0x0020 /* 1=Arbitration Loss Detected */
#define IOP3XX_ISR_SSD 0x0010 /* 1=Slave STOP Detected */
#define IOP3XX_ISR_BBUSY 0x0008 /* 1=Bus BUSY */
#define IOP3XX_ISR_UNITBUSY 0x0004 /* 1=Unit Busy */
#define IOP3XX_ISR_NACK 0x0002 /* 1=Unit Rx or Tx a NACK */
#define IOP3XX_ISR_RXREAD 0x0001 /* 1=READ 0=WRITE (R/W bit of slave addr */
#define IOP3XX_ISR_CLEARBITS 0x07f0
#define IOP3XX_ISAR_SAMASK 0x007f
#define IOP3XX_IDBR_MASK 0x00ff
#define IOP3XX_IBMR_SCL 0x0002
#define IOP3XX_IBMR_SDA 0x0001
#define IOP3XX_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */
#define IOP3XX_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */
#define MYSAR 0x02 /* SWAG a suitable slave address */
#define I2C_ERR 321
#define I2C_ERR_BERR (I2C_ERR+0)
#define I2C_ERR_ALD (I2C_ERR+1)
#define CR_OFFSET 0
#define SR_OFFSET 0x4
#define SAR_OFFSET 0x8
#define DBR_OFFSET 0xc
#define CCR_OFFSET 0x10
#define BMR_OFFSET 0x14
#define IOP3XX_I2C_IO_SIZE 0x18
struct i2c_algo_iop3xx_data {
u32 ioaddr;
wait_queue_head_t waitq;
spinlock_t lock;
u32 SR_enabled, SR_received;
int id;
};
#endif /* I2C_IOP3XX_H */

View File

@@ -0,0 +1,72 @@
/*
i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
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.
*/
/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
on first sight very useful; almost no functionality is preserved.
Except that it makes writing drivers for chips which can be on both
the SMBus and the ISA bus very much easier. See lm78.c for an example
of this. */
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/i2c.h>
static u32 isa_func(struct i2c_adapter *adapter);
/* This is the actual algorithm we define */
static struct i2c_algorithm isa_algorithm = {
.name = "ISA bus algorithm",
.id = I2C_ALGO_ISA,
.functionality = isa_func,
};
/* There can only be one... */
static struct i2c_adapter isa_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &isa_algorithm,
.name = "ISA main adapter",
};
/* We can't do a thing... */
static u32 isa_func(struct i2c_adapter *adapter)
{
return 0;
}
static int __init i2c_isa_init(void)
{
return i2c_add_adapter(&isa_adapter);
}
static void __exit i2c_isa_exit(void)
{
i2c_del_adapter(&isa_adapter);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("ISA bus access through i2c");
MODULE_LICENSE("GPL");
module_init(i2c_isa_init);
module_exit(i2c_isa_exit);

View File

@@ -0,0 +1,282 @@
/*
-------------------------------------------------------------------------
i2c-adap-ite.c i2c-hw access for the IIC peripheral on the ITE MIPS system
-------------------------------------------------------------------------
Hai-Pao Fan, MontaVista Software, Inc.
hpfan@mvista.com or source@mvista.com
Copyright 2001 MontaVista Software Inc.
----------------------------------------------------------------------------
This file was highly leveraged from i2c-elektor.c, which was created
by Simon G. Vogl and Hans Berglund:
Copyright (C) 1995-97 Simon G. Vogl
1998-99 Hans Berglund
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. */
/* ------------------------------------------------------------------------- */
/* With some changes from Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-ite.h>
#include <linux/i2c-adap-ite.h>
#include "../i2c-ite.h"
#define DEFAULT_BASE 0x14014030
#define ITE_IIC_IO_SIZE 0x40
#define DEFAULT_IRQ 0
#define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */
#define DEFAULT_OWN 0x55
static int base;
static int irq;
static int clock;
static int own;
static struct iic_ite gpi;
static wait_queue_head_t iic_wait;
static int iic_pending;
static spinlock_t lock;
/* ----- local functions ---------------------------------------------- */
static void iic_ite_setiic(void *data, int ctl, short val)
{
unsigned long j = jiffies + 10;
pr_debug(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff);
#ifdef DEBUG
while (time_before(jiffies, j))
schedule();
#endif
outw(val,ctl);
}
static short iic_ite_getiic(void *data, int ctl)
{
short val;
val = inw(ctl);
pr_debug("Read 0x%02x from 0x%x\n",(unsigned short)val, ctl&0xff);
return (val);
}
/* Return our slave address. This is the address
* put on the I2C bus when another master on the bus wants to address us
* as a slave
*/
static int iic_ite_getown(void *data)
{
return (gpi.iic_own);
}
static int iic_ite_getclock(void *data)
{
return (gpi.iic_clock);
}
/* Put this process to sleep. We will wake up when the
* IIC controller interrupts.
*/
static void iic_ite_waitforpin(void) {
DEFINE_WAIT(wait);
int timeout = 2;
long flags;
/* If interrupts are enabled (which they are), then put the process to
* sleep. This process will be awakened by two events -- either the
* the IIC peripheral interrupts or the timeout expires.
* If interrupts are not enabled then delay for a reasonable amount
* of time and return.
*/
if (gpi.iic_irq > 0) {
spin_lock_irqsave(&lock, flags);
if (iic_pending == 0) {
spin_unlock_irqrestore(&lock, flags);
prepare_to_wait(&iic_wait, &wait, TASK_INTERRUPTIBLE);
if (schedule_timeout(timeout*HZ)) {
spin_lock_irqsave(&lock, flags);
if (iic_pending == 1) {
iic_pending = 0;
}
spin_unlock_irqrestore(&lock, flags);
}
finish_wait(&iic_wait, &wait);
} else {
iic_pending = 0;
spin_unlock_irqrestore(&lock, flags);
}
} else {
udelay(100);
}
}
static irqreturn_t iic_ite_handler(int this_irq, void *dev_id,
struct pt_regs *regs)
{
spin_lock(&lock);
iic_pending = 1;
spin_unlock(&lock);
wake_up_interruptible(&iic_wait);
return IRQ_HANDLED;
}
/* Lock the region of memory where I/O registers exist. Request our
* interrupt line and register its associated handler.
*/
static int iic_hw_resrc_init(void)
{
if (!request_region(gpi.iic_base, ITE_IIC_IO_SIZE, "i2c"))
return -ENODEV;
if (gpi.iic_irq <= 0)
return 0;
if (request_irq(gpi.iic_irq, iic_ite_handler, 0, "ITE IIC", 0) < 0)
gpi.iic_irq = 0;
else
enable_irq(gpi.iic_irq);
return 0;
}
static void iic_ite_release(void)
{
if (gpi.iic_irq > 0) {
disable_irq(gpi.iic_irq);
free_irq(gpi.iic_irq, 0);
}
release_region(gpi.iic_base , 2);
}
/* ------------------------------------------------------------------------
* Encapsulate the above functions in the correct operations structure.
* This is only done when more than one hardware adapter is supported.
*/
static struct i2c_algo_iic_data iic_ite_data = {
NULL,
iic_ite_setiic,
iic_ite_getiic,
iic_ite_getown,
iic_ite_getclock,
iic_ite_waitforpin,
80, 80, 100, /* waits, timeout */
};
static struct i2c_adapter iic_ite_ops = {
.owner = THIS_MODULE,
.id = I2C_HW_I_IIC,
.algo_data = &iic_ite_data,
.dev = {
.name = "ITE IIC adapter",
},
};
/* Called when the module is loaded. This function starts the
* cascade of calls up through the hierarchy of i2c modules (i.e. up to the
* algorithm layer and into to the core layer)
*/
static int __init iic_ite_init(void)
{
struct iic_ite *piic = &gpi;
printk(KERN_INFO "Initialize ITE IIC adapter module\n");
if (base == 0)
piic->iic_base = DEFAULT_BASE;
else
piic->iic_base = base;
if (irq == 0)
piic->iic_irq = DEFAULT_IRQ;
else
piic->iic_irq = irq;
if (clock == 0)
piic->iic_clock = DEFAULT_CLOCK;
else
piic->iic_clock = clock;
if (own == 0)
piic->iic_own = DEFAULT_OWN;
else
piic->iic_own = own;
iic_ite_data.data = (void *)piic;
init_waitqueue_head(&iic_wait);
spin_lock_init(&lock);
if (iic_hw_resrc_init() == 0) {
if (i2c_iic_add_bus(&iic_ite_ops) < 0)
return -ENODEV;
} else {
return -ENODEV;
}
printk(KERN_INFO " found device at %#x irq %d.\n",
piic->iic_base, piic->iic_irq);
return 0;
}
static void iic_ite_exit(void)
{
i2c_iic_del_bus(&iic_ite_ops);
iic_ite_release();
}
/* If modules is NOT defined when this file is compiled, then the MODULE_*
* macros will resolve to nothing
*/
MODULE_AUTHOR("MontaVista Software <www.mvista.com>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter");
MODULE_LICENSE("GPL");
module_param(base, int, 0);
module_param(irq, int, 0);
module_param(clock, int, 0);
module_param(own, int, 0);
/* Called when module is loaded or when kernel is initialized.
* If MODULES is defined when this file is compiled, then this function will
* resolve to init_module (the function called when insmod is invoked for a
* module). Otherwise, this function is called early in the boot, when the
* kernel is intialized. Check out /include/init.h to see how this works.
*/
module_init(iic_ite_init);
/* Resolves to module_cleanup when MODULES is defined. */
module_exit(iic_ite_exit);

View File

@@ -0,0 +1,171 @@
/*
* drivers/i2c/busses/i2c-ixp2000.c
*
* I2C adapter for IXP2000 systems using GPIOs for I2C bus
*
* Author: Deepak Saxena <dsaxena@plexity.net>
* Based on IXDP2400 code by: Naeem M. Afzal <naeem.m.afzal@intel.com>
* Made generic by: Jeff Daly <jeffrey.daly@intel.com>
*
* Copyright (c) 2003-2004 MontaVista Software Inc.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* From Jeff Daly:
*
* I2C adapter driver for Intel IXDP2xxx platforms. This should work for any
* IXP2000 platform if it uses the HW GPIO in the same manner. Basically,
* SDA and SCL GPIOs have external pullups. Setting the respective GPIO to
* an input will make the signal a '1' via the pullup. Setting them to
* outputs will pull them down.
*
* The GPIOs are open drain signals and are used as configuration strap inputs
* during power-up so there's generally a buffer on the board that needs to be
* 'enabled' to drive the GPIOs.
*/
#include <linux/config.h>
#ifdef CONFIG_I2C_DEBUG_BUS
#define DEBUG 1
#endif
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/hardware.h> /* Pick up IXP42000-specific bits */
static inline int ixp2000_scl_pin(void *data)
{
return ((struct ixp2000_i2c_pins*)data)->scl_pin;
}
static inline int ixp2000_sda_pin(void *data)
{
return ((struct ixp2000_i2c_pins*)data)->sda_pin;
}
static void ixp2000_bit_setscl(void *data, int val)
{
int i = 5000;
if (val) {
gpio_line_config(ixp2000_scl_pin(data), GPIO_IN);
while(!gpio_line_get(ixp2000_scl_pin(data)) && i--);
} else {
gpio_line_config(ixp2000_scl_pin(data), GPIO_OUT);
}
}
static void ixp2000_bit_setsda(void *data, int val)
{
if (val) {
gpio_line_config(ixp2000_sda_pin(data), GPIO_IN);
} else {
gpio_line_config(ixp2000_sda_pin(data), GPIO_OUT);
}
}
static int ixp2000_bit_getscl(void *data)
{
return gpio_line_get(ixp2000_scl_pin(data));
}
static int ixp2000_bit_getsda(void *data)
{
return gpio_line_get(ixp2000_sda_pin(data));
}
struct ixp2000_i2c_data {
struct ixp2000_i2c_pins *gpio_pins;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo_data;
};
static int ixp2000_i2c_remove(struct device *dev)
{
struct platform_device *plat_dev = to_platform_device(dev);
struct ixp2000_i2c_data *drv_data = dev_get_drvdata(&plat_dev->dev);
dev_set_drvdata(&plat_dev->dev, NULL);
i2c_bit_del_bus(&drv_data->adapter);
kfree(drv_data);
return 0;
}
static int ixp2000_i2c_probe(struct device *dev)
{
int err;
struct platform_device *plat_dev = to_platform_device(dev);
struct ixp2000_i2c_pins *gpio = plat_dev->dev.platform_data;
struct ixp2000_i2c_data *drv_data =
kmalloc(sizeof(struct ixp2000_i2c_data), GFP_KERNEL);
if (!drv_data)
return -ENOMEM;
memzero(drv_data, sizeof(*drv_data));
drv_data->gpio_pins = gpio;
drv_data->algo_data.data = gpio;
drv_data->algo_data.setsda = ixp2000_bit_setsda;
drv_data->algo_data.setscl = ixp2000_bit_setscl;
drv_data->algo_data.getsda = ixp2000_bit_getsda;
drv_data->algo_data.getscl = ixp2000_bit_getscl;
drv_data->algo_data.udelay = 6;
drv_data->algo_data.mdelay = 6;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP2000,
drv_data->adapter.algo_data = &drv_data->algo_data,
drv_data->adapter.dev.parent = &plat_dev->dev;
gpio_line_config(gpio->sda_pin, GPIO_IN);
gpio_line_config(gpio->scl_pin, GPIO_IN);
gpio_line_set(gpio->scl_pin, 0);
gpio_line_set(gpio->sda_pin, 0);
if ((err = i2c_bit_add_bus(&drv_data->adapter)) != 0) {
dev_err(dev, "Could not install, error %d\n", err);
kfree(drv_data);
return err;
}
dev_set_drvdata(&plat_dev->dev, drv_data);
return 0;
}
static struct device_driver ixp2000_i2c_driver = {
.name = "IXP2000-I2C",
.bus = &platform_bus_type,
.probe = ixp2000_i2c_probe,
.remove = ixp2000_i2c_remove,
};
static int __init ixp2000_i2c_init(void)
{
return driver_register(&ixp2000_i2c_driver);
}
static void __exit ixp2000_i2c_exit(void)
{
driver_unregister(&ixp2000_i2c_driver);
}
module_init(ixp2000_i2c_init);
module_exit(ixp2000_i2c_exit);
MODULE_AUTHOR ("Deepak Saxena <dsaxena@plexity.net>");
MODULE_DESCRIPTION("IXP2000 GPIO-based I2C bus driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,181 @@
/*
* drivers/i2c/i2c-adap-ixp4xx.c
*
* Intel's IXP4xx XScale NPU chipsets (IXP420, 421, 422, 425) do not have
* an on board I2C controller but provide 16 GPIO pins that are often
* used to create an I2C bus. This driver provides an i2c_adapter
* interface that plugs in under algo_bit and drives the GPIO pins
* as instructed by the alogorithm driver.
*
* Author: Deepak Saxena <dsaxena@plexity.net>
*
* Copyright (c) 2003-2004 MontaVista Software Inc.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* NOTE: Since different platforms will use different GPIO pins for
* I2C, this driver uses an IXP4xx-specific platform_data
* pointer to pass the GPIO numbers to the driver. This
* allows us to support all the different IXP4xx platforms
* w/o having to put #ifdefs in this driver.
*
* See arch/arm/mach-ixp4xx/ixdp425.c for an example of building a
* device list and filling in the ixp4xx_i2c_pins data structure
* that is passed as the platform_data to this driver.
*/
#include <linux/config.h>
#ifdef CONFIG_I2C_DEBUG_BUS
#define DEBUG 1
#endif
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/hardware.h> /* Pick up IXP4xx-specific bits */
static inline int ixp4xx_scl_pin(void *data)
{
return ((struct ixp4xx_i2c_pins*)data)->scl_pin;
}
static inline int ixp4xx_sda_pin(void *data)
{
return ((struct ixp4xx_i2c_pins*)data)->sda_pin;
}
static void ixp4xx_bit_setscl(void *data, int val)
{
gpio_line_set(ixp4xx_scl_pin(data), 0);
gpio_line_config(ixp4xx_scl_pin(data),
val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT );
}
static void ixp4xx_bit_setsda(void *data, int val)
{
gpio_line_set(ixp4xx_sda_pin(data), 0);
gpio_line_config(ixp4xx_sda_pin(data),
val ? IXP4XX_GPIO_IN : IXP4XX_GPIO_OUT );
}
static int ixp4xx_bit_getscl(void *data)
{
int scl;
gpio_line_config(ixp4xx_scl_pin(data), IXP4XX_GPIO_IN );
gpio_line_get(ixp4xx_scl_pin(data), &scl);
return scl;
}
static int ixp4xx_bit_getsda(void *data)
{
int sda;
gpio_line_config(ixp4xx_sda_pin(data), IXP4XX_GPIO_IN );
gpio_line_get(ixp4xx_sda_pin(data), &sda);
return sda;
}
struct ixp4xx_i2c_data {
struct ixp4xx_i2c_pins *gpio_pins;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo_data;
};
static int ixp4xx_i2c_remove(struct device *dev)
{
struct platform_device *plat_dev = to_platform_device(dev);
struct ixp4xx_i2c_data *drv_data = dev_get_drvdata(&plat_dev->dev);
dev_set_drvdata(&plat_dev->dev, NULL);
i2c_bit_del_bus(&drv_data->adapter);
kfree(drv_data);
return 0;
}
static int ixp4xx_i2c_probe(struct device *dev)
{
int err;
struct platform_device *plat_dev = to_platform_device(dev);
struct ixp4xx_i2c_pins *gpio = plat_dev->dev.platform_data;
struct ixp4xx_i2c_data *drv_data =
kmalloc(sizeof(struct ixp4xx_i2c_data), GFP_KERNEL);
if(!drv_data)
return -ENOMEM;
memzero(drv_data, sizeof(struct ixp4xx_i2c_data));
drv_data->gpio_pins = gpio;
/*
* We could make a lot of these structures static, but
* certain platforms may have multiple GPIO-based I2C
* buses for various device domains, so we need per-device
* algo_data->data.
*/
drv_data->algo_data.data = gpio;
drv_data->algo_data.setsda = ixp4xx_bit_setsda;
drv_data->algo_data.setscl = ixp4xx_bit_setscl;
drv_data->algo_data.getsda = ixp4xx_bit_getsda;
drv_data->algo_data.getscl = ixp4xx_bit_getscl;
drv_data->algo_data.udelay = 10;
drv_data->algo_data.mdelay = 10;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP4XX;
drv_data->adapter.algo_data = &drv_data->algo_data;
drv_data->adapter.dev.parent = &plat_dev->dev;
gpio_line_config(gpio->scl_pin, IXP4XX_GPIO_IN);
gpio_line_config(gpio->sda_pin, IXP4XX_GPIO_IN);
gpio_line_set(gpio->scl_pin, 0);
gpio_line_set(gpio->sda_pin, 0);
if ((err = i2c_bit_add_bus(&drv_data->adapter) != 0)) {
printk(KERN_ERR "ERROR: Could not install %s\n", dev->bus_id);
kfree(drv_data);
return err;
}
dev_set_drvdata(&plat_dev->dev, drv_data);
return 0;
}
static struct device_driver ixp4xx_i2c_driver = {
.name = "IXP4XX-I2C",
.bus = &platform_bus_type,
.probe = ixp4xx_i2c_probe,
.remove = ixp4xx_i2c_remove,
};
static int __init ixp4xx_i2c_init(void)
{
return driver_register(&ixp4xx_i2c_driver);
}
static void __exit ixp4xx_i2c_exit(void)
{
driver_unregister(&ixp4xx_i2c_driver);
}
module_init(ixp4xx_i2c_init);
module_exit(ixp4xx_i2c_exit);
MODULE_DESCRIPTION("GPIO-based I2C adapter for IXP4xx systems");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");

View File

@@ -0,0 +1,763 @@
/*
i2c Support for Apple Keywest I2C Bus Controller
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Original work by
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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.
Changes:
2001/12/13 BenH New implementation
2001/12/15 BenH Add support for "byte" and "quick"
transfers. Add i2c_xfer routine.
2003/09/21 BenH Rework state machine with Paulus help
2004/01/21 BenH Merge in Greg KH changes, polled mode is back
2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree
My understanding of the various modes supported by keywest are:
- Dumb mode : not implemented, probably direct tweaking of lines
- Standard mode : simple i2c transaction of type
S Addr R/W A Data A Data ... T
- Standard sub mode : combined 8 bit subaddr write with data read
S Addr R/W A SubAddr A Data A Data ... T
- Combined mode : Subaddress and Data sequences appended with no stop
S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
Currently, this driver uses only Standard mode for i2c xfer, and
smbus byte & quick transfers ; and uses StandardSub mode for
other smbus transfers instead of combined as we need that for the
sound driver to be happy
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pmac_low_i2c.h>
#include "i2c-keywest.h"
#undef POLLED_MODE
/* Some debug macros */
#define WRONG_STATE(name) do {\
pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \
name, __kw_state_names[iface->state], isr); \
} while(0)
#ifdef DEBUG
static const char *__kw_state_names[] = {
"state_idle",
"state_addr",
"state_read",
"state_write",
"state_stop",
"state_dead"
};
#endif /* DEBUG */
static int probe;
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
MODULE_LICENSE("GPL");
module_param(probe, bool, 0);
#ifdef POLLED_MODE
/* Don't schedule, the g5 fan controller is too
* timing sensitive
*/
static u8
wait_interrupt(struct keywest_iface* iface)
{
int i;
u8 isr;
for (i = 0; i < 200000; i++) {
isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
if (isr != 0)
return isr;
udelay(10);
}
return isr;
}
#endif /* POLLED_MODE */
static void
do_stop(struct keywest_iface* iface, int result)
{
write_reg(reg_control, KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
/* Main state machine for standard & standard sub mode */
static void
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
if (isr == 0) {
if (iface->state != state_stop) {
pr_debug("KW: Timeout !\n");
do_stop(iface, -EIO);
}
if (iface->state == state_stop) {
ack = read_reg(reg_status);
if (!(ack & KW_I2C_STAT_BUSY)) {
iface->state = state_idle;
write_reg(reg_ier, 0x00);
#ifndef POLLED_MODE
complete(&iface->complete);
#endif /* POLLED_MODE */
}
}
return;
}
if (isr & KW_I2C_IRQ_ADDR) {
ack = read_reg(reg_status);
if (iface->state != state_addr) {
write_reg(reg_isr, KW_I2C_IRQ_ADDR);
WRONG_STATE("KW_I2C_IRQ_ADDR");
do_stop(iface, -EIO);
return;
}
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
iface->state = state_stop;
iface->result = -ENODEV;
pr_debug("KW: NAK on address\n");
} else {
/* Handle rw "quick" mode */
if (iface->datalen == 0) {
do_stop(iface, 0);
} else if (iface->read_write == I2C_SMBUS_READ) {
iface->state = state_read;
if (iface->datalen > 1)
write_reg(reg_control, KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
}
write_reg(reg_isr, KW_I2C_IRQ_ADDR);
}
if (isr & KW_I2C_IRQ_DATA) {
if (iface->state == state_read) {
*(iface->data++) = read_reg(reg_data);
write_reg(reg_isr, KW_I2C_IRQ_DATA);
iface->datalen--;
if (iface->datalen == 0)
iface->state = state_stop;
else if (iface->datalen == 1)
write_reg(reg_control, 0);
} else if (iface->state == state_write) {
/* Check ack status */
ack = read_reg(reg_status);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
pr_debug("KW: nack on data write (%x): %x\n",
iface->data[-1], ack);
do_stop(iface, -EIO);
} else if (iface->datalen) {
write_reg(reg_data, *(iface->data++));
iface->datalen--;
} else {
write_reg(reg_control, KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = 0;
}
write_reg(reg_isr, KW_I2C_IRQ_DATA);
} else {
write_reg(reg_isr, KW_I2C_IRQ_DATA);
WRONG_STATE("KW_I2C_IRQ_DATA");
if (iface->state != state_stop)
do_stop(iface, -EIO);
}
}
if (isr & KW_I2C_IRQ_STOP) {
write_reg(reg_isr, KW_I2C_IRQ_STOP);
if (iface->state != state_stop) {
WRONG_STATE("KW_I2C_IRQ_STOP");
iface->result = -EIO;
}
iface->state = state_idle;
write_reg(reg_ier, 0x00);
#ifndef POLLED_MODE
complete(&iface->complete);
#endif /* POLLED_MODE */
}
if (isr & KW_I2C_IRQ_START)
write_reg(reg_isr, KW_I2C_IRQ_START);
}
#ifndef POLLED_MODE
/* Interrupt handler */
static irqreturn_t
keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct keywest_iface *iface = (struct keywest_iface *)dev_id;
unsigned long flags;
spin_lock_irqsave(&iface->lock, flags);
del_timer(&iface->timeout_timer);
handle_interrupt(iface, read_reg(reg_isr));
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock_irqrestore(&iface->lock, flags);
return IRQ_HANDLED;
}
static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
unsigned long flags;
pr_debug("timeout !\n");
spin_lock_irqsave(&iface->lock, flags);
handle_interrupt(iface, read_reg(reg_isr));
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock_irqrestore(&iface->lock, flags);
}
#endif /* POLLED_MODE */
/*
* SMBUS-type transfer entrypoint
*/
static s32
keywest_smbus_xfer( struct i2c_adapter* adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data* data)
{
struct keywest_chan* chan = i2c_get_adapdata(adap);
struct keywest_iface* iface = chan->iface;
int len;
u8* buffer;
u16 cur_word;
int rc = 0;
if (iface->state == state_dead)
return -ENXIO;
/* Prepare datas & select mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
switch (size) {
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
default:
return -1;
}
/* Turn a standardsub read into a combined mode access */
if (read_write == I2C_SMBUS_READ
&& (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) {
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
iface->cur_mode |= KW_I2C_MODE_COMBINED;
}
/* Original driver had this limitation */
if (len > 32)
len = 32;
if (pmac_low_i2c_lock(iface->node))
return -ENXIO;
pr_debug("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
iface->data = buffer;
iface->datalen = len;
iface->state = state_addr;
iface->result = 0;
iface->read_write = read_write;
/* Setup channel & clear pending irqs */
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_status, 0);
/* Set up address and r/w bit */
write_reg(reg_addr,
(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
/* Set up the sub address */
if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
|| (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
write_reg(reg_subaddr, command);
#ifndef POLLED_MODE
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
#endif
/* Start sending address & enable interrupt*/
write_reg(reg_control, KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
#ifdef POLLED_MODE
pr_debug("using polled mode...\n");
/* State machine, to turn into an interrupt handler */
while(iface->state != state_idle) {
unsigned long flags;
u8 isr = wait_interrupt(iface);
spin_lock_irqsave(&iface->lock, flags);
handle_interrupt(iface, isr);
spin_unlock_irqrestore(&iface->lock, flags);
}
#else /* POLLED_MODE */
pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
#endif /* POLLED_MODE */
rc = iface->result;
pr_debug("transfer done, result: %d\n", rc);
if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
data->word = le16_to_cpu(cur_word);
/* Release sem */
pmac_low_i2c_unlock(iface->node);
return rc;
}
/*
* Generic i2c master transfer entrypoint
*/
static int
keywest_xfer( struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num)
{
struct keywest_chan* chan = i2c_get_adapdata(adap);
struct keywest_iface* iface = chan->iface;
struct i2c_msg *pmsg;
int i, completed;
int rc = 0;
if (iface->state == state_dead)
return -ENXIO;
if (pmac_low_i2c_lock(iface->node))
return -ENXIO;
/* Set adapter to standard mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
completed = 0;
for (i = 0; rc >= 0 && i < num;) {
u8 addr;
pmsg = &msgs[i++];
addr = pmsg->addr;
if (pmsg->flags & I2C_M_TEN) {
printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
rc = -EINVAL;
break;
}
pr_debug("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
chan->chan_no,
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, addr, i, num);
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_status, 0);
iface->data = pmsg->buf;
iface->datalen = pmsg->len;
iface->state = state_addr;
iface->result = 0;
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else
iface->read_write = I2C_SMBUS_WRITE;
/* Set up address and r/w bit */
if (pmsg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
write_reg(reg_addr,
(addr << 1) |
((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
#ifndef POLLED_MODE
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
#endif
/* Start sending address & enable interrupt*/
write_reg(reg_ier, KW_I2C_IRQ_MASK);
write_reg(reg_control, KW_I2C_CTL_XADDR);
#ifdef POLLED_MODE
pr_debug("using polled mode...\n");
/* State machine, to turn into an interrupt handler */
while(iface->state != state_idle) {
u8 isr = wait_interrupt(iface);
handle_interrupt(iface, isr);
}
#else /* POLLED_MODE */
pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
#endif /* POLLED_MODE */
rc = iface->result;
if (rc == 0)
completed++;
pr_debug("transfer done, result: %d\n", rc);
}
/* Release sem */
pmac_low_i2c_unlock(iface->node);
return completed;
}
static u32
keywest_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_BLOCK_DATA;
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm keywest_algorithm = {
.name = "Keywest i2c",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = keywest_smbus_xfer,
.master_xfer = keywest_xfer,
.functionality = keywest_func,
};
static int
create_iface(struct device_node *np, struct device *dev)
{
unsigned long steps;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
u32 *psteps, *prate;
int rc;
if (pmac_low_i2c_lock(np))
return -ENODEV;
psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
/* Hrm... maybe we can be smarter here */
for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
steps >>= 1;
if (np->parent->name[0] == 'u') {
nchan = 2;
addroffset = 3;
} else {
addroffset = 0;
nchan = 1;
}
tsize = sizeof(struct keywest_iface) +
(sizeof(struct keywest_chan) + 4) * nchan;
iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
pmac_low_i2c_unlock(np);
return -ENOMEM;
}
memset(iface, 0, tsize);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
iface->node = of_node_get(np);
iface->bsteps = bsteps;
iface->chan_count = nchan;
iface->state = state_idle;
iface->irq = np->intrs[0].line;
iface->channels = (struct keywest_chan *)
(((unsigned long)(iface + 1) + 3UL) & ~3UL);
iface->base = ioremap(np->addrs[0].address + addroffset,
np->addrs[0].size);
if (!iface->base) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
pmac_low_i2c_unlock(np);
return -ENOMEM;
}
#ifndef POLLED_MODE
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
#endif
/* Select interface rate */
iface->cur_mode = KW_I2C_MODE_100KHZ;
prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
if (prate) switch(*prate) {
case 100:
iface->cur_mode = KW_I2C_MODE_100KHZ;
break;
case 50:
iface->cur_mode = KW_I2C_MODE_50KHZ;
break;
case 25:
iface->cur_mode = KW_I2C_MODE_25KHZ;
break;
default:
printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
(long)*prate);
}
/* Select standard mode by default */
iface->cur_mode |= KW_I2C_MODE_STANDARD;
/* Write mode */
write_reg(reg_mode, iface->cur_mode);
/* Switch interrupts off & clear them*/
write_reg(reg_ier, 0x00);
write_reg(reg_isr, KW_I2C_IRQ_MASK);
#ifndef POLLED_MODE
/* Request chip interrupt */
rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface);
if (rc) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap(iface->base);
kfree(iface);
pmac_low_i2c_unlock(np);
return -ENODEV;
}
#endif /* POLLED_MODE */
pmac_low_i2c_unlock(np);
dev_set_drvdata(dev, iface);
for (i=0; i<nchan; i++) {
struct keywest_chan* chan = &iface->channels[i];
u8 addr;
sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
chan->iface = iface;
chan->chan_no = i;
chan->adapter.id = I2C_ALGO_SMBUS;
chan->adapter.algo = &keywest_algorithm;
chan->adapter.algo_data = NULL;
chan->adapter.client_register = NULL;
chan->adapter.client_unregister = NULL;
i2c_set_adapdata(&chan->adapter, chan);
chan->adapter.dev.parent = dev;
rc = i2c_add_adapter(&chan->adapter);
if (rc) {
printk("i2c-keywest.c: Adapter %s registration failed\n",
chan->adapter.name);
i2c_set_adapdata(&chan->adapter, NULL);
}
if (probe) {
printk("Probe: ");
for (addr = 0x00; addr <= 0x7f; addr++) {
if (i2c_smbus_xfer(&chan->adapter,addr,
0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
printk("%02x ", addr);
}
printk("\n");
}
}
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
return 0;
}
static int
dispose_iface(struct device *dev)
{
struct keywest_iface *iface = dev_get_drvdata(dev);
int i, rc;
/* Make sure we stop all activity */
if (pmac_low_i2c_lock(iface->node))
return -ENODEV;
#ifndef POLLED_MODE
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
msleep(100);
spin_lock_irq(&iface->lock);
}
#endif /* POLLED_MODE */
iface->state = state_dead;
#ifndef POLLED_MODE
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
#endif /* POLLED_MODE */
pmac_low_i2c_unlock(iface->node);
/* Release all channels */
for (i=0; i<iface->chan_count; i++) {
struct keywest_chan* chan = &iface->channels[i];
if (i2c_get_adapdata(&chan->adapter) == NULL)
continue;
rc = i2c_del_adapter(&chan->adapter);
i2c_set_adapdata(&chan->adapter, NULL);
/* We aren't that prepared to deal with this... */
if (rc)
printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
}
iounmap(iface->base);
dev_set_drvdata(dev, NULL);
of_node_put(iface->node);
kfree(iface);
return 0;
}
static int
create_iface_macio(struct macio_dev* dev, const struct of_match *match)
{
return create_iface(dev->ofdev.node, &dev->ofdev.dev);
}
static int
dispose_iface_macio(struct macio_dev* dev)
{
return dispose_iface(&dev->ofdev.dev);
}
static int
create_iface_of_platform(struct of_device* dev, const struct of_match *match)
{
return create_iface(dev->node, &dev->dev);
}
static int
dispose_iface_of_platform(struct of_device* dev)
{
return dispose_iface(&dev->dev);
}
static struct of_match i2c_keywest_match[] =
{
{
.name = OF_ANY_MATCH,
.type = "i2c",
.compatible = "keywest"
},
{},
};
static struct macio_driver i2c_keywest_macio_driver =
{
.name = "i2c-keywest",
.match_table = i2c_keywest_match,
.probe = create_iface_macio,
.remove = dispose_iface_macio
};
static struct of_platform_driver i2c_keywest_of_platform_driver =
{
.name = "i2c-keywest",
.match_table = i2c_keywest_match,
.probe = create_iface_of_platform,
.remove = dispose_iface_of_platform
};
static int __init
i2c_keywest_init(void)
{
of_register_driver(&i2c_keywest_of_platform_driver);
macio_register_driver(&i2c_keywest_macio_driver);
return 0;
}
static void __exit
i2c_keywest_cleanup(void)
{
of_unregister_driver(&i2c_keywest_of_platform_driver);
macio_unregister_driver(&i2c_keywest_macio_driver);
}
module_init(i2c_keywest_init);
module_exit(i2c_keywest_cleanup);

View File

@@ -0,0 +1,108 @@
#ifndef __I2C_KEYWEST_H__
#define __I2C_KEYWEST_H__
/* The Tumbler audio equalizer can be really slow sometimes */
#define POLL_TIMEOUT (2*HZ)
/* Register indices */
typedef enum {
reg_mode = 0,
reg_control,
reg_status,
reg_isr,
reg_ier,
reg_addr,
reg_subaddr,
reg_data
} reg_t;
/* Mode register */
#define KW_I2C_MODE_100KHZ 0x00
#define KW_I2C_MODE_50KHZ 0x01
#define KW_I2C_MODE_25KHZ 0x02
#define KW_I2C_MODE_DUMB 0x00
#define KW_I2C_MODE_STANDARD 0x04
#define KW_I2C_MODE_STANDARDSUB 0x08
#define KW_I2C_MODE_COMBINED 0x0C
#define KW_I2C_MODE_MODE_MASK 0x0C
#define KW_I2C_MODE_CHAN_MASK 0xF0
/* Control register */
#define KW_I2C_CTL_AAK 0x01
#define KW_I2C_CTL_XADDR 0x02
#define KW_I2C_CTL_STOP 0x04
#define KW_I2C_CTL_START 0x08
/* Status register */
#define KW_I2C_STAT_BUSY 0x01
#define KW_I2C_STAT_LAST_AAK 0x02
#define KW_I2C_STAT_LAST_RW 0x04
#define KW_I2C_STAT_SDA 0x08
#define KW_I2C_STAT_SCL 0x10
/* IER & ISR registers */
#define KW_I2C_IRQ_DATA 0x01
#define KW_I2C_IRQ_ADDR 0x02
#define KW_I2C_IRQ_STOP 0x04
#define KW_I2C_IRQ_START 0x08
#define KW_I2C_IRQ_MASK 0x0F
/* Physical interface */
struct keywest_iface
{
struct device_node *node;
void __iomem * base;
unsigned bsteps;
int irq;
spinlock_t lock;
struct keywest_chan *channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
u8 *data;
unsigned datalen;
int state;
int result;
struct timer_list timeout_timer;
struct completion complete;
};
enum {
state_idle,
state_addr,
state_read,
state_write,
state_stop,
state_dead
};
/* Channel on an interface */
struct keywest_chan
{
struct i2c_adapter adapter;
struct keywest_iface* iface;
unsigned chan_no;
};
/* Register access */
static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
{
return in_8(iface->base
+ (((unsigned)reg) << iface->bsteps));
}
static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(iface->base
+ (((unsigned)reg) << iface->bsteps), val);
(void)__read_reg(iface, reg_subaddr);
}
#define write_reg(reg, val) __write_reg(iface, reg, val)
#define read_reg(reg) __read_reg(iface, reg)
#endif /* __I2C_KEYWEST_H__ */

View File

@@ -0,0 +1,496 @@
/*
* (C) Copyright 2003-2004
* Humboldt Solutions Ltd, adrian@humboldt.co.uk.
* This is a combined i2c adapter and algorithm driver for the
* MPC107/Tsi107 PowerPC northbridge and processors that include
* the same I2C unit (8240, 8245, 85xx).
*
* Release 0.8
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/io.h>
#ifdef CONFIG_FSL_OCP
#include <asm/ocp.h>
#define FSL_I2C_DEV_SEPARATE_DFSRR FS_I2C_SEPARATE_DFSRR
#define FSL_I2C_DEV_CLOCK_5200 FS_I2C_CLOCK_5200
#else
#include <linux/fsl_devices.h>
#endif
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#define MPC_I2C_ADDR 0x00
#define MPC_I2C_FDR 0x04
#define MPC_I2C_CR 0x08
#define MPC_I2C_SR 0x0c
#define MPC_I2C_DR 0x10
#define MPC_I2C_DFSRR 0x14
#define MPC_I2C_REGION 0x20
#define CCR_MEN 0x80
#define CCR_MIEN 0x40
#define CCR_MSTA 0x20
#define CCR_MTX 0x10
#define CCR_TXAK 0x08
#define CCR_RSTA 0x04
#define CSR_MCF 0x80
#define CSR_MAAS 0x40
#define CSR_MBB 0x20
#define CSR_MAL 0x10
#define CSR_SRW 0x04
#define CSR_MIF 0x02
#define CSR_RXAK 0x01
struct mpc_i2c {
char *base;
u32 interrupt;
wait_queue_head_t queue;
struct i2c_adapter adap;
int irq;
u32 flags;
};
static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x)
{
writeb(x, i2c->base + MPC_I2C_CR);
}
static irqreturn_t mpc_i2c_isr(int irq, void *dev_id, struct pt_regs *regs)
{
struct mpc_i2c *i2c = dev_id;
if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
/* Read again to allow register to stabilise */
i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
writeb(0, i2c->base + MPC_I2C_SR);
wake_up_interruptible(&i2c->queue);
}
return IRQ_HANDLED;
}
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
{
unsigned long orig_jiffies = jiffies;
u32 x;
int result = 0;
if (i2c->irq == 0)
{
while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
schedule();
if (time_after(jiffies, orig_jiffies + timeout)) {
pr_debug("I2C: timeout\n");
result = -EIO;
break;
}
}
x = readb(i2c->base + MPC_I2C_SR);
writeb(0, i2c->base + MPC_I2C_SR);
} else {
/* Interrupt mode */
result = wait_event_interruptible_timeout(i2c->queue,
(i2c->interrupt & CSR_MIF), timeout * HZ);
if (unlikely(result < 0))
pr_debug("I2C: wait interrupted\n");
else if (unlikely(!(i2c->interrupt & CSR_MIF))) {
pr_debug("I2C: wait timeout\n");
result = -ETIMEDOUT;
}
x = i2c->interrupt;
i2c->interrupt = 0;
}
if (result < 0)
return result;
if (!(x & CSR_MCF)) {
pr_debug("I2C: unfinished\n");
return -EIO;
}
if (x & CSR_MAL) {
pr_debug("I2C: MAL\n");
return -EIO;
}
if (writing && (x & CSR_RXAK)) {
pr_debug("I2C: No RXAK\n");
/* generate stop */
writeccr(i2c, CCR_MEN);
return -EIO;
}
return 0;
}
static void mpc_i2c_setclock(struct mpc_i2c *i2c)
{
/* Set clock and filters */
if (i2c->flags & FSL_I2C_DEV_SEPARATE_DFSRR) {
writeb(0x31, i2c->base + MPC_I2C_FDR);
writeb(0x10, i2c->base + MPC_I2C_DFSRR);
} else if (i2c->flags & FSL_I2C_DEV_CLOCK_5200)
writeb(0x3f, i2c->base + MPC_I2C_FDR);
else
writel(0x1031, i2c->base + MPC_I2C_FDR);
}
static void mpc_i2c_start(struct mpc_i2c *i2c)
{
/* Clear arbitration */
writeb(0, i2c->base + MPC_I2C_SR);
/* Start with MEN */
writeccr(i2c, CCR_MEN);
}
static void mpc_i2c_stop(struct mpc_i2c *i2c)
{
writeccr(i2c, CCR_MEN);
}
static int mpc_write(struct mpc_i2c *i2c, int target,
const u8 * data, int length, int restart)
{
int i;
unsigned timeout = i2c->adap.timeout;
u32 flags = restart ? CCR_RSTA : 0;
/* Start with MEN */
if (!restart)
writeccr(i2c, CCR_MEN);
/* Start as master */
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
/* Write target byte */
writeb((target << 1), i2c->base + MPC_I2C_DR);
if (i2c_wait(i2c, timeout, 1) < 0)
return -1;
for (i = 0; i < length; i++) {
/* Write data byte */
writeb(data[i], i2c->base + MPC_I2C_DR);
if (i2c_wait(i2c, timeout, 1) < 0)
return -1;
}
return 0;
}
static int mpc_read(struct mpc_i2c *i2c, int target,
u8 * data, int length, int restart)
{
unsigned timeout = i2c->adap.timeout;
int i;
u32 flags = restart ? CCR_RSTA : 0;
/* Start with MEN */
if (!restart)
writeccr(i2c, CCR_MEN);
/* Switch to read - restart */
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
/* Write target address byte - this time with the read flag set */
writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
if (i2c_wait(i2c, timeout, 1) < 0)
return -1;
if (length) {
if (length == 1)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
else
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
/* Dummy read */
readb(i2c->base + MPC_I2C_DR);
}
for (i = 0; i < length; i++) {
if (i2c_wait(i2c, timeout, 0) < 0)
return -1;
/* Generate txack on next to last byte */
if (i == length - 2)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
/* Generate stop on last byte */
if (i == length - 1)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
data[i] = readb(i2c->base + MPC_I2C_DR);
}
return length;
}
static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct i2c_msg *pmsg;
int i;
int ret = 0;
unsigned long orig_jiffies = jiffies;
struct mpc_i2c *i2c = i2c_get_adapdata(adap);
mpc_i2c_start(i2c);
/* Allow bus up to 1s to become not busy */
while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
if (signal_pending(current)) {
pr_debug("I2C: Interrupted\n");
return -EINTR;
}
if (time_after(jiffies, orig_jiffies + HZ)) {
pr_debug("I2C: timeout\n");
return -EIO;
}
schedule();
}
for (i = 0; ret >= 0 && i < num; i++) {
pmsg = &msgs[i];
pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n",
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, pmsg->addr, i + 1, num);
if (pmsg->flags & I2C_M_RD)
ret =
mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
else
ret =
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
}
mpc_i2c_stop(i2c);
return (ret < 0) ? ret : num;
}
static u32 mpc_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm mpc_algo = {
.name = "MPC algorithm",
.id = I2C_ALGO_MPC107,
.master_xfer = mpc_xfer,
.functionality = mpc_functionality,
};
static struct i2c_adapter mpc_ops = {
.owner = THIS_MODULE,
.name = "MPC adapter",
.id = I2C_ALGO_MPC107 | I2C_HW_MPC107,
.algo = &mpc_algo,
.class = I2C_CLASS_HWMON,
.timeout = 1,
.retries = 1
};
#ifdef CONFIG_FSL_OCP
static int __devinit mpc_i2c_probe(struct ocp_device *ocp)
{
int result = 0;
struct mpc_i2c *i2c;
if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
return -ENOMEM;
}
memset(i2c, 0, sizeof(*i2c));
i2c->irq = ocp->def->irq;
i2c->flags = ((struct ocp_fs_i2c_data *)ocp->def->additions)->flags;
init_waitqueue_head(&i2c->queue);
if (!request_mem_region(ocp->def->paddr, MPC_I2C_REGION, "i2c-mpc")) {
printk(KERN_ERR "i2c-mpc - resource unavailable\n");
return -ENODEV;
}
i2c->base = ioremap(ocp->def->paddr, MPC_I2C_REGION);
if (!i2c->base) {
printk(KERN_ERR "i2c-mpc - failed to map controller\n");
result = -ENOMEM;
goto fail_map;
}
if (i2c->irq != OCP_IRQ_NA)
{
if ((result = request_irq(ocp->def->irq, mpc_i2c_isr,
0, "i2c-mpc", i2c)) < 0) {
printk(KERN_ERR
"i2c-mpc - failed to attach interrupt\n");
goto fail_irq;
}
} else
i2c->irq = 0;
i2c->adap = mpc_ops;
i2c_set_adapdata(&i2c->adap, i2c);
if ((result = i2c_add_adapter(&i2c->adap)) < 0) {
printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
goto fail_add;
}
mpc_i2c_setclock(i2c);
ocp_set_drvdata(ocp, i2c);
return result;
fail_add:
if (ocp->def->irq != OCP_IRQ_NA)
free_irq(ocp->def->irq, 0);
fail_irq:
iounmap(i2c->base);
fail_map:
release_mem_region(ocp->def->paddr, MPC_I2C_REGION);
kfree(i2c);
return result;
}
static void __devexit mpc_i2c_remove(struct ocp_device *ocp)
{
struct mpc_i2c *i2c = ocp_get_drvdata(ocp);
ocp_set_drvdata(ocp, NULL);
i2c_del_adapter(&i2c->adap);
if (ocp->def->irq != OCP_IRQ_NA)
free_irq(i2c->irq, i2c);
iounmap(i2c->base);
release_mem_region(ocp->def->paddr, MPC_I2C_REGION);
kfree(i2c);
}
static struct ocp_device_id mpc_iic_ids[] __devinitdata = {
{.vendor = OCP_VENDOR_FREESCALE,.function = OCP_FUNC_IIC},
{.vendor = OCP_VENDOR_INVALID}
};
MODULE_DEVICE_TABLE(ocp, mpc_iic_ids);
static struct ocp_driver mpc_iic_driver = {
.name = "iic",
.id_table = mpc_iic_ids,
.probe = mpc_i2c_probe,
.remove = __devexit_p(mpc_i2c_remove)
};
static int __init iic_init(void)
{
return ocp_register_driver(&mpc_iic_driver);
}
static void __exit iic_exit(void)
{
ocp_unregister_driver(&mpc_iic_driver);
}
module_init(iic_init);
module_exit(iic_exit);
#else
static int fsl_i2c_probe(struct device *device)
{
int result = 0;
struct mpc_i2c *i2c;
struct platform_device *pdev = to_platform_device(device);
struct fsl_i2c_platform_data *pdata;
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
return -ENOMEM;
}
memset(i2c, 0, sizeof(*i2c));
i2c->irq = platform_get_irq(pdev, 0);
i2c->flags = pdata->device_flags;
init_waitqueue_head(&i2c->queue);
i2c->base = ioremap((phys_addr_t)r->start, MPC_I2C_REGION);
if (!i2c->base) {
printk(KERN_ERR "i2c-mpc - failed to map controller\n");
result = -ENOMEM;
goto fail_map;
}
if (i2c->irq != 0)
if ((result = request_irq(i2c->irq, mpc_i2c_isr,
0, "fsl-i2c", i2c)) < 0) {
printk(KERN_ERR
"i2c-mpc - failed to attach interrupt\n");
goto fail_irq;
}
i2c->adap = mpc_ops;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
if ((result = i2c_add_adapter(&i2c->adap)) < 0) {
printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
goto fail_add;
}
mpc_i2c_setclock(i2c);
dev_set_drvdata(device, i2c);
return result;
fail_add:
if (i2c->irq != 0)
free_irq(i2c->irq, 0);
fail_irq:
iounmap(i2c->base);
fail_map:
kfree(i2c);
return result;
};
static int fsl_i2c_remove(struct device *device)
{
struct mpc_i2c *i2c = dev_get_drvdata(device);
dev_set_drvdata(device, NULL);
i2c_del_adapter(&i2c->adap);
if (i2c->irq != 0)
free_irq(i2c->irq, i2c);
iounmap(i2c->base);
kfree(i2c);
return 0;
};
/* Structure for a device driver */
static struct device_driver fsl_i2c_driver = {
.name = "fsl-i2c",
.bus = &platform_bus_type,
.probe = fsl_i2c_probe,
.remove = fsl_i2c_remove,
};
static int __init fsl_i2c_init(void)
{
return driver_register(&fsl_i2c_driver);
}
static void __exit fsl_i2c_exit(void)
{
driver_unregister(&fsl_i2c_driver);
}
module_init(fsl_i2c_init);
module_exit(fsl_i2c_exit);
#endif /* CONFIG_FSL_OCP */
MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
MODULE_DESCRIPTION
("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,598 @@
/*
* drivers/i2c/busses/i2c-mv64xxx.c
*
* Driver for the i2c controller on the Marvell line of host bridges for MIPS
* and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
*
* Author: Mark A. Greer <mgreer@mvista.com>
*
* 2005 (c) MontaVista, Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mv643xx.h>
#include <asm/io.h>
/* Register defines */
#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00
#define MV64XXX_I2C_REG_DATA 0x04
#define MV64XXX_I2C_REG_CONTROL 0x08
#define MV64XXX_I2C_REG_STATUS 0x0c
#define MV64XXX_I2C_REG_BAUD 0x0c
#define MV64XXX_I2C_REG_EXT_SLAVE_ADDR 0x10
#define MV64XXX_I2C_REG_SOFT_RESET 0x1c
#define MV64XXX_I2C_REG_CONTROL_ACK 0x00000004
#define MV64XXX_I2C_REG_CONTROL_IFLG 0x00000008
#define MV64XXX_I2C_REG_CONTROL_STOP 0x00000010
#define MV64XXX_I2C_REG_CONTROL_START 0x00000020
#define MV64XXX_I2C_REG_CONTROL_TWSIEN 0x00000040
#define MV64XXX_I2C_REG_CONTROL_INTEN 0x00000080
/* Ctlr status values */
#define MV64XXX_I2C_STATUS_BUS_ERR 0x00
#define MV64XXX_I2C_STATUS_MAST_START 0x08
#define MV64XXX_I2C_STATUS_MAST_REPEAT_START 0x10
#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK 0x18
#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK 0x20
#define MV64XXX_I2C_STATUS_MAST_WR_ACK 0x28
#define MV64XXX_I2C_STATUS_MAST_WR_NO_ACK 0x30
#define MV64XXX_I2C_STATUS_MAST_LOST_ARB 0x38
#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK 0x40
#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK 0x48
#define MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK 0x50
#define MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK 0x58
#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK 0xd0
#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK 0xd8
#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK 0xe0
#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK 0xe8
#define MV64XXX_I2C_STATUS_NO_STATUS 0xf8
/* Driver states */
enum {
MV64XXX_I2C_STATE_INVALID,
MV64XXX_I2C_STATE_IDLE,
MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
MV64XXX_I2C_STATE_ABORTING,
};
/* Driver actions */
enum {
MV64XXX_I2C_ACTION_INVALID,
MV64XXX_I2C_ACTION_CONTINUE,
MV64XXX_I2C_ACTION_SEND_START,
MV64XXX_I2C_ACTION_SEND_ADDR_1,
MV64XXX_I2C_ACTION_SEND_ADDR_2,
MV64XXX_I2C_ACTION_SEND_DATA,
MV64XXX_I2C_ACTION_RCV_DATA,
MV64XXX_I2C_ACTION_RCV_DATA_STOP,
MV64XXX_I2C_ACTION_SEND_STOP,
};
struct mv64xxx_i2c_data {
int irq;
u32 state;
u32 action;
u32 cntl_bits;
void __iomem *reg_base;
u32 reg_base_p;
u32 addr1;
u32 addr2;
u32 bytes_left;
u32 byte_posn;
u32 block;
int rc;
u32 freq_m;
u32 freq_n;
wait_queue_head_t waitq;
spinlock_t lock;
struct i2c_msg *msg;
struct i2c_adapter adapter;
};
/*
*****************************************************************************
*
* Finite State Machine & Interrupt Routines
*
*****************************************************************************
*/
static void
mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
{
/*
* If state is idle, then this is likely the remnants of an old
* operation that driver has given up on or the user has killed.
* If so, issue the stop condition and go to idle.
*/
if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
return;
}
if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
return;
}
/* The status from the ctlr [mostly] tells us what to do next */
switch (status) {
/* Start condition interrupt */
case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
break;
/* Performing a write */
case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
if (drv_data->msg->flags & I2C_M_TEN) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
break;
}
/* FALLTHRU */
case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
if (drv_data->bytes_left > 0) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
drv_data->bytes_left--;
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
}
break;
/* Performing a read */
case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
if (drv_data->msg->flags & I2C_M_TEN) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
break;
}
/* FALLTHRU */
case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
if (drv_data->bytes_left == 0) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
break;
}
/* FALLTHRU */
case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
else {
drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
drv_data->bytes_left--;
}
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
if (drv_data->bytes_left == 1)
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
break;
case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
break;
case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
/* Doesn't seem to be a device at other end */
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
drv_data->rc = -ENODEV;
break;
default:
dev_err(&drv_data->adapter.dev,
"mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
"status: 0x%x, addr: 0x%x, flags: 0x%x\n",
drv_data->state, status, drv_data->msg->addr,
drv_data->msg->flags);
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
drv_data->rc = -EIO;
}
}
static void
mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
{
switch(drv_data->action) {
case MV64XXX_I2C_ACTION_CONTINUE:
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_SEND_START:
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_SEND_ADDR_1:
writel(drv_data->addr1,
drv_data->reg_base + MV64XXX_I2C_REG_DATA);
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_SEND_ADDR_2:
writel(drv_data->addr2,
drv_data->reg_base + MV64XXX_I2C_REG_DATA);
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_SEND_DATA:
writel(drv_data->msg->buf[drv_data->byte_posn++],
drv_data->reg_base + MV64XXX_I2C_REG_DATA);
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_RCV_DATA:
drv_data->msg->buf[drv_data->byte_posn++] =
readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
writel(drv_data->cntl_bits,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
break;
case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
drv_data->msg->buf[drv_data->byte_posn++] =
readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
drv_data->block = 0;
wake_up_interruptible(&drv_data->waitq);
break;
case MV64XXX_I2C_ACTION_INVALID:
default:
dev_err(&drv_data->adapter.dev,
"mv64xxx_i2c_do_action: Invalid action: %d\n",
drv_data->action);
drv_data->rc = -EIO;
/* FALLTHRU */
case MV64XXX_I2C_ACTION_SEND_STOP:
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
drv_data->block = 0;
wake_up_interruptible(&drv_data->waitq);
break;
}
}
static int
mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct mv64xxx_i2c_data *drv_data = dev_id;
unsigned long flags;
u32 status;
int rc = IRQ_NONE;
spin_lock_irqsave(&drv_data->lock, flags);
while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
MV64XXX_I2C_REG_CONTROL_IFLG) {
status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
mv64xxx_i2c_fsm(drv_data, status);
mv64xxx_i2c_do_action(drv_data);
rc = IRQ_HANDLED;
}
spin_unlock_irqrestore(&drv_data->lock, flags);
return rc;
}
/*
*****************************************************************************
*
* I2C Msg Execution Routines
*
*****************************************************************************
*/
static void
mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
struct i2c_msg *msg)
{
u32 dir = 0;
drv_data->msg = msg;
drv_data->byte_posn = 0;
drv_data->bytes_left = msg->len;
drv_data->rc = 0;
drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
if (msg->flags & I2C_M_RD)
dir = 1;
if (msg->flags & I2C_M_REV_DIR_ADDR)
dir ^= 1;
if (msg->flags & I2C_M_TEN) {
drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
drv_data->addr2 = (u32)msg->addr & 0xff;
} else {
drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
drv_data->addr2 = 0;
}
}
static void
mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
{
long time_left;
unsigned long flags;
char abort = 0;
time_left = wait_event_interruptible_timeout(drv_data->waitq,
!drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
spin_lock_irqsave(&drv_data->lock, flags);
if (!time_left) { /* Timed out */
drv_data->rc = -ETIMEDOUT;
abort = 1;
} else if (time_left < 0) { /* Interrupted/Error */
drv_data->rc = time_left; /* errno value */
abort = 1;
}
if (abort && drv_data->block) {
drv_data->state = MV64XXX_I2C_STATE_ABORTING;
spin_unlock_irqrestore(&drv_data->lock, flags);
time_left = wait_event_timeout(drv_data->waitq,
!drv_data->block,
msecs_to_jiffies(drv_data->adapter.timeout));
if (time_left <= 0) {
drv_data->state = MV64XXX_I2C_STATE_IDLE;
dev_err(&drv_data->adapter.dev,
"mv64xxx: I2C bus locked\n");
}
} else
spin_unlock_irqrestore(&drv_data->lock, flags);
}
static int
mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
{
unsigned long flags;
spin_lock_irqsave(&drv_data->lock, flags);
mv64xxx_i2c_prepare_for_io(drv_data, msg);
if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
if (drv_data->msg->flags & I2C_M_RD) {
/* No action to do, wait for slave to send a byte */
drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
drv_data->bytes_left--;
}
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
}
drv_data->block = 1;
mv64xxx_i2c_do_action(drv_data);
spin_unlock_irqrestore(&drv_data->lock, flags);
mv64xxx_i2c_wait_for_completion(drv_data);
return drv_data->rc;
}
/*
*****************************************************************************
*
* I2C Core Support Routines (Interface to higher level I2C code)
*
*****************************************************************************
*/
static u32
mv64xxx_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
}
static int
mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
int i, rc = 0;
for (i=0; i<num; i++)
if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
break;
return rc;
}
static struct i2c_algorithm mv64xxx_i2c_algo = {
.name = MV64XXX_I2C_CTLR_NAME " algorithm",
.id = I2C_ALGO_MV64XXX,
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
/*
*****************************************************************************
*
* Driver Interface & Early Init Routines
*
*****************************************************************************
*/
static void __devinit
mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
{
writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
drv_data->state = MV64XXX_I2C_STATE_IDLE;
}
static int __devinit
mv64xxx_i2c_map_regs(struct platform_device *pd,
struct mv64xxx_i2c_data *drv_data)
{
struct resource *r;
if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
drv_data->adapter.name)) {
drv_data->reg_base = ioremap(r->start,
MV64XXX_I2C_REG_BLOCK_SIZE);
drv_data->reg_base_p = r->start;
} else
return -ENOMEM;
return 0;
}
static void __devexit
mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
{
if (drv_data->reg_base) {
iounmap(drv_data->reg_base);
release_mem_region(drv_data->reg_base_p,
MV64XXX_I2C_REG_BLOCK_SIZE);
}
drv_data->reg_base = NULL;
drv_data->reg_base_p = 0;
}
static int __devinit
mv64xxx_i2c_probe(struct device *dev)
{
struct platform_device *pd = to_platform_device(dev);
struct mv64xxx_i2c_data *drv_data;
struct mv64xxx_i2c_pdata *pdata = dev->platform_data;
int rc;
if ((pd->id != 0) || !pdata)
return -ENODEV;
drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
if (!drv_data)
return -ENOMEM;
memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
if (mv64xxx_i2c_map_regs(pd, drv_data)) {
rc = -ENODEV;
goto exit_kfree;
}
strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
I2C_NAME_SIZE);
init_waitqueue_head(&drv_data->waitq);
spin_lock_init(&drv_data->lock);
drv_data->freq_m = pdata->freq_m;
drv_data->freq_n = pdata->freq_n;
drv_data->irq = platform_get_irq(pd, 0);
drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
drv_data->adapter.class = I2C_CLASS_HWMON;
drv_data->adapter.timeout = pdata->timeout;
drv_data->adapter.retries = pdata->retries;
dev_set_drvdata(dev, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
MV64XXX_I2C_CTLR_NAME, drv_data)) {
dev_err(dev, "mv64xxx: Can't register intr handler "
"irq: %d\n", drv_data->irq);
rc = -EINVAL;
goto exit_unmap_regs;
} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
goto exit_free_irq;
}
mv64xxx_i2c_hw_init(drv_data);
return 0;
exit_free_irq:
free_irq(drv_data->irq, drv_data);
exit_unmap_regs:
mv64xxx_i2c_unmap_regs(drv_data);
exit_kfree:
kfree(drv_data);
return rc;
}
static int __devexit
mv64xxx_i2c_remove(struct device *dev)
{
struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
int rc;
rc = i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data);
mv64xxx_i2c_unmap_regs(drv_data);
kfree(drv_data);
return rc;
}
static struct device_driver mv64xxx_i2c_driver = {
.name = MV64XXX_I2C_CTLR_NAME,
.bus = &platform_bus_type,
.probe = mv64xxx_i2c_probe,
.remove = mv64xxx_i2c_remove,
};
static int __init
mv64xxx_i2c_init(void)
{
return driver_register(&mv64xxx_i2c_driver);
}
static void __exit
mv64xxx_i2c_exit(void)
{
driver_unregister(&mv64xxx_i2c_driver);
}
module_init(mv64xxx_i2c_init);
module_exit(mv64xxx_i2c_exit);
MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,410 @@
/*
SMBus driver for nVidia nForce2 MCP
Added nForce3 Pro 150 Thomas Leibold <thomas@plx.com>,
Ported to 2.5 Patrick Dreker <patrick@dreker.de>,
Copyright (c) 2003 Hans-Frieder Vogt <hfvogt@arcor.de>,
Based on
SMBus 2.0 driver for AMD-8111 IO-Hub
Copyright (c) 2002 Vojtech Pavlik
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.
*/
/*
SUPPORTED DEVICES PCI ID
nForce2 MCP 0064
nForce2 Ultra 400 MCP 0084
nForce3 Pro150 MCP 00D4
nForce3 250Gb MCP 00E4
nForce4 MCP 0052
This driver supports the 2 SMBuses that are included in the MCP of the
nForce2/3/4 chipsets.
*/
/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <asm/io.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>");
MODULE_DESCRIPTION("nForce2 SMBus driver");
struct nforce2_smbus {
struct pci_dev *dev;
struct i2c_adapter adapter;
int base;
int size;
};
/*
* nVidia nForce2 SMBus control register definitions
*/
#define NFORCE_PCI_SMB1 0x50
#define NFORCE_PCI_SMB2 0x54
/*
* ACPI 2.0 chapter 13 SMBus 2.0 EC register model
*/
#define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */
#define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */
#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */
#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */
#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */
#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data bytes */
#define NVIDIA_SMB_ALRM_A (smbus->base + 0x25) /* alarm address */
#define NVIDIA_SMB_ALRM_D (smbus->base + 0x26) /* 2 bytes alarm data */
#define NVIDIA_SMB_STS_DONE 0x80
#define NVIDIA_SMB_STS_ALRM 0x40
#define NVIDIA_SMB_STS_RES 0x20
#define NVIDIA_SMB_STS_STATUS 0x1f
#define NVIDIA_SMB_PRTCL_WRITE 0x00
#define NVIDIA_SMB_PRTCL_READ 0x01
#define NVIDIA_SMB_PRTCL_QUICK 0x02
#define NVIDIA_SMB_PRTCL_BYTE 0x04
#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06
#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08
#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a
#define NVIDIA_SMB_PRTCL_PROC_CALL 0x0c
#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define NVIDIA_SMB_PRTCL_PEC 0x80
/* Other settings */
#define MAX_TIMEOUT 256
static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
static u32 nforce2_func(struct i2c_adapter *adapter);
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = nforce2_access,
.functionality = nforce2_func,
};
static struct i2c_adapter nforce2_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
/* Return -1 on error. See smbus.h for more information */
static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct nforce2_smbus *smbus = adap->algo_data;
unsigned char protocol, pec, temp;
unsigned char len = 0; /* to keep the compiler quiet */
int timeout = 0;
int i;
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
NVIDIA_SMB_PRTCL_WRITE;
pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0;
switch (size) {
case I2C_SMBUS_QUICK:
protocol |= NVIDIA_SMB_PRTCL_QUICK;
read_write = I2C_SMBUS_WRITE;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, NVIDIA_SMB_CMD);
protocol |= NVIDIA_SMB_PRTCL_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, NVIDIA_SMB_DATA);
protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word, NVIDIA_SMB_DATA);
outb_p(data->word >> 8, NVIDIA_SMB_DATA+1);
}
protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0], 32);
outb_p(len, NVIDIA_SMB_BCNT);
for (i = 0; i < len; i++)
outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
}
protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
len = min_t(u8, data->block[0], 32);
outb_p(command, NVIDIA_SMB_CMD);
outb_p(len, NVIDIA_SMB_BCNT);
if (read_write == I2C_SMBUS_WRITE)
for (i = 0; i < len; i++)
outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA;
break;
case I2C_SMBUS_PROC_CALL:
dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
return -1;
/*
outb_p(command, NVIDIA_SMB_CMD);
outb_p(data->word, NVIDIA_SMB_DATA);
outb_p(data->word >> 8, NVIDIA_SMB_DATA + 1);
protocol = NVIDIA_SMB_PRTCL_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
*/
case I2C_SMBUS_BLOCK_PROC_CALL:
dev_err(&adap->dev, "I2C_SMBUS_BLOCK_PROC_CALL not supported!\n");
return -1;
/*
protocol |= pec;
len = min_t(u8, data->block[0], 31);
outb_p(command, NVIDIA_SMB_CMD);
outb_p(len, NVIDIA_SMB_BCNT);
for (i = 0; i < len; i++)
outb_p(data->block[i + 1], NVIDIA_SMB_DATA + i);
protocol = NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
*/
case I2C_SMBUS_WORD_DATA_PEC:
case I2C_SMBUS_BLOCK_DATA_PEC:
case I2C_SMBUS_PROC_CALL_PEC:
case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
dev_err(&adap->dev, "Unexpected software PEC transaction %d\n.", size);
return -1;
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
return -1;
}
outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR);
outb_p(protocol, NVIDIA_SMB_PRTCL);
temp = inb_p(NVIDIA_SMB_STS);
#if 0
do {
i2c_do_pause(1);
temp = inb_p(NVIDIA_SMB_STS);
} while (((temp & NVIDIA_SMB_STS_DONE) == 0) && (timeout++ < MAX_TIMEOUT));
#endif
if (~temp & NVIDIA_SMB_STS_DONE) {
udelay(500);
temp = inb_p(NVIDIA_SMB_STS);
}
if (~temp & NVIDIA_SMB_STS_DONE) {
msleep(10);
temp = inb_p(NVIDIA_SMB_STS);
}
if ((timeout >= MAX_TIMEOUT) || (~temp & NVIDIA_SMB_STS_DONE)
|| (temp & NVIDIA_SMB_STS_STATUS))
return -1;
if (read_write == I2C_SMBUS_WRITE)
return 0;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
data->byte = inb_p(NVIDIA_SMB_DATA);
break;
case I2C_SMBUS_WORD_DATA:
/* case I2C_SMBUS_PROC_CALL: not supported */
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
break;
case I2C_SMBUS_BLOCK_DATA:
/* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */
len = inb_p(NVIDIA_SMB_BCNT);
len = min_t(u8, len, 32);
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++)
data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
data->block[0] = len;
break;
}
return 0;
}
static u32 nforce2_func(struct i2c_adapter *adapter)
{
/* other functionality might be possible, but is not tested */
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* |
I2C_FUNC_SMBUS_BLOCK_DATA */;
}
static struct pci_device_id nforce2_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) },
{ 0 }
};
MODULE_DEVICE_TABLE (pci, nforce2_ids);
static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg,
struct nforce2_smbus *smbus, char *name)
{
u16 iobase;
int error;
if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) {
dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name);
return -1;
}
smbus->dev = dev;
smbus->base = iobase & 0xfffc;
smbus->size = 8;
if (!request_region(smbus->base, smbus->size, "nForce2 SMBus")) {
dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n",
smbus->base, smbus->base+smbus->size-1, name);
return -1;
}
smbus->adapter = nforce2_adapter;
smbus->adapter.algo_data = smbus;
smbus->adapter.dev.parent = &dev->dev;
snprintf(smbus->adapter.name, I2C_NAME_SIZE,
"SMBus nForce2 adapter at %04x", smbus->base);
error = i2c_add_adapter(&smbus->adapter);
if (error) {
dev_err(&smbus->adapter.dev, "Failed to register adapter.\n");
release_region(smbus->base, smbus->size);
return -1;
}
dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", smbus->base);
return 0;
}
static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct nforce2_smbus *smbuses;
int res1, res2;
/* we support 2 SMBus adapters */
if (!(smbuses = (void *)kmalloc(2*sizeof(struct nforce2_smbus),
GFP_KERNEL)))
return -ENOMEM;
memset (smbuses, 0, 2*sizeof(struct nforce2_smbus));
pci_set_drvdata(dev, smbuses);
/* SMBus adapter 1 */
res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
if (res1 < 0) {
dev_err(&dev->dev, "Error probing SMB1.\n");
smbuses[0].base = 0; /* to have a check value */
}
res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
if (res2 < 0) {
dev_err(&dev->dev, "Error probing SMB2.\n");
smbuses[1].base = 0; /* to have a check value */
}
if ((res1 < 0) && (res2 < 0)) {
/* we did not find even one of the SMBuses, so we give up */
kfree(smbuses);
return -ENODEV;
}
return 0;
}
static void __devexit nforce2_remove(struct pci_dev *dev)
{
struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev);
if (smbuses[0].base) {
i2c_del_adapter(&smbuses[0].adapter);
release_region(smbuses[0].base, smbuses[0].size);
}
if (smbuses[1].base) {
i2c_del_adapter(&smbuses[1].adapter);
release_region(smbuses[1].base, smbuses[1].size);
}
kfree(smbuses);
}
static struct pci_driver nforce2_driver = {
.name = "nForce2_smbus",
.id_table = nforce2_ids,
.probe = nforce2_probe,
.remove = __devexit_p(nforce2_remove),
};
static int __init nforce2_init(void)
{
return pci_register_driver(&nforce2_driver);
}
static void __exit nforce2_exit(void)
{
pci_unregister_driver(&nforce2_driver);
}
module_init(nforce2_init);
module_exit(nforce2_exit);

View File

@@ -0,0 +1,175 @@
/* ------------------------------------------------------------------------ *
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
Based on older i2c-velleman.c driver
Copyright (C) 1995-2000 Simon G. Vogl
With some changes from:
Frodo Looijaard <frodol@dds.nl>
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
#include "i2c-parport.h"
#define DEFAULT_BASE 0x378
static u16 base;
module_param(base, ushort, 0);
MODULE_PARM_DESC(base, "Base I/O address");
/* ----- Low-level parallel port access ----------------------------------- */
static inline void port_write(unsigned char p, unsigned char d)
{
outb(d, base+p);
}
static inline unsigned char port_read(unsigned char p)
{
return inb(base+p);
}
/* ----- Unified line operation functions --------------------------------- */
static inline void line_set(int state, const struct lineop *op)
{
u8 oldval = port_read(op->port);
/* Touch only the bit(s) needed */
if ((op->inverted && !state) || (!op->inverted && state))
port_write(op->port, oldval | op->val);
else
port_write(op->port, oldval & ~op->val);
}
static inline int line_get(const struct lineop *op)
{
u8 oldval = port_read(op->port);
return ((op->inverted && (oldval & op->val) != op->val)
|| (!op->inverted && (oldval & op->val) == op->val));
}
/* ----- I2C algorithm call-back functions and structures ----------------- */
static void parport_setscl(void *data, int state)
{
line_set(state, &adapter_parm[type].setscl);
}
static void parport_setsda(void *data, int state)
{
line_set(state, &adapter_parm[type].setsda);
}
static int parport_getscl(void *data)
{
return line_get(&adapter_parm[type].getscl);
}
static int parport_getsda(void *data)
{
return line_get(&adapter_parm[type].getsda);
}
/* Encapsulate the functions above in the correct structure
Note that getscl will be set to NULL by the attaching code for adapters
that cannot read SCL back */
static struct i2c_algo_bit_data parport_algo_data = {
.setsda = parport_setsda,
.setscl = parport_setscl,
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 50,
.mdelay = 50,
.timeout = HZ,
};
/* ----- I2c structure ---------------------------------------------------- */
static struct i2c_adapter parport_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.id = I2C_HW_B_LP,
.algo_data = &parport_algo_data,
.name = "Parallel port adapter (light)",
};
/* ----- Module loading, unloading and information ------------------------ */
static int __init i2c_parport_init(void)
{
int type_count;
type_count = sizeof(adapter_parm)/sizeof(struct adapter_parm);
if (type < 0 || type >= type_count) {
printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type);
type = 0;
}
if (base == 0) {
printk(KERN_INFO "i2c-parport: using default base 0x%x\n", DEFAULT_BASE);
base = DEFAULT_BASE;
}
if (!request_region(base, 3, "i2c-parport"))
return -ENODEV;
if (!adapter_parm[type].getscl.val)
parport_algo_data.getscl = NULL;
/* Reset hardware to a sane state (SCL and SDA high) */
parport_setsda(NULL, 1);
parport_setscl(NULL, 1);
/* Other init if needed (power on...) */
if (adapter_parm[type].init.val)
line_set(1, &adapter_parm[type].init);
if (i2c_bit_add_bus(&parport_adapter) < 0) {
printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
release_region(base, 3);
return -ENODEV;
}
return 0;
}
static void __exit i2c_parport_exit(void)
{
/* Un-init if needed (power off...) */
if (adapter_parm[type].init.val)
line_set(0, &adapter_parm[type].init);
i2c_bit_del_bus(&parport_adapter);
release_region(base, 3);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("I2C bus over parallel port (light)");
MODULE_LICENSE("GPL");
module_init(i2c_parport_init);
module_exit(i2c_parport_exit);

View File

@@ -0,0 +1,267 @@
/* ------------------------------------------------------------------------ *
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
Based on older i2c-philips-par.c driver
Copyright (C) 1995-2000 Simon G. Vogl
With some changes from:
Frodo Looijaard <frodol@dds.nl>
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include "i2c-parport.h"
/* ----- Device list ------------------------------------------------------ */
struct i2c_par {
struct pardevice *pdev;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo_data;
struct i2c_par *next;
};
static struct i2c_par *adapter_list;
/* ----- Low-level parallel port access ----------------------------------- */
static void port_write_data(struct parport *p, unsigned char d)
{
parport_write_data(p, d);
}
static void port_write_control(struct parport *p, unsigned char d)
{
parport_write_control(p, d);
}
static unsigned char port_read_data(struct parport *p)
{
return parport_read_data(p);
}
static unsigned char port_read_status(struct parport *p)
{
return parport_read_status(p);
}
static unsigned char port_read_control(struct parport *p)
{
return parport_read_control(p);
}
static void (*port_write[])(struct parport *, unsigned char) = {
port_write_data,
NULL,
port_write_control,
};
static unsigned char (*port_read[])(struct parport *) = {
port_read_data,
port_read_status,
port_read_control,
};
/* ----- Unified line operation functions --------------------------------- */
static inline void line_set(struct parport *data, int state,
const struct lineop *op)
{
u8 oldval = port_read[op->port](data);
/* Touch only the bit(s) needed */
if ((op->inverted && !state) || (!op->inverted && state))
port_write[op->port](data, oldval | op->val);
else
port_write[op->port](data, oldval & ~op->val);
}
static inline int line_get(struct parport *data,
const struct lineop *op)
{
u8 oldval = port_read[op->port](data);
return ((op->inverted && (oldval & op->val) != op->val)
|| (!op->inverted && (oldval & op->val) == op->val));
}
/* ----- I2C algorithm call-back functions and structures ----------------- */
static void parport_setscl(void *data, int state)
{
line_set((struct parport *) data, state, &adapter_parm[type].setscl);
}
static void parport_setsda(void *data, int state)
{
line_set((struct parport *) data, state, &adapter_parm[type].setsda);
}
static int parport_getscl(void *data)
{
return line_get((struct parport *) data, &adapter_parm[type].getscl);
}
static int parport_getsda(void *data)
{
return line_get((struct parport *) data, &adapter_parm[type].getsda);
}
/* Encapsulate the functions above in the correct structure.
Note that this is only a template, from which the real structures are
copied. The attaching code will set getscl to NULL for adapters that
cannot read SCL back, and will also make the the data field point to
the parallel port structure. */
static struct i2c_algo_bit_data parport_algo_data = {
.setsda = parport_setsda,
.setscl = parport_setscl,
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 60,
.mdelay = 60,
.timeout = HZ,
};
/* ----- I2c and parallel port call-back functions and structures --------- */
static struct i2c_adapter parport_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.id = I2C_HW_B_LP,
.name = "Parallel port adapter",
};
static void i2c_parport_attach (struct parport *port)
{
struct i2c_par *adapter;
adapter = kmalloc(sizeof(struct i2c_par), GFP_KERNEL);
if (adapter == NULL) {
printk(KERN_ERR "i2c-parport: Failed to kmalloc\n");
return;
}
memset(adapter, 0x00, sizeof(struct i2c_par));
pr_debug("i2c-parport: attaching to %s\n", port->name);
adapter->pdev = parport_register_device(port, "i2c-parport",
NULL, NULL, NULL, PARPORT_FLAG_EXCL, NULL);
if (!adapter->pdev) {
printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
goto ERROR0;
}
/* Fill the rest of the structure */
adapter->adapter = parport_adapter;
adapter->algo_data = parport_algo_data;
if (!adapter_parm[type].getscl.val)
adapter->algo_data.getscl = NULL;
adapter->algo_data.data = port;
adapter->adapter.algo_data = &adapter->algo_data;
if (parport_claim_or_block(adapter->pdev) < 0) {
printk(KERN_ERR "i2c-parport: Could not claim parallel port\n");
goto ERROR1;
}
/* Reset hardware to a sane state (SCL and SDA high) */
parport_setsda(port, 1);
parport_setscl(port, 1);
/* Other init if needed (power on...) */
if (adapter_parm[type].init.val)
line_set(port, 1, &adapter_parm[type].init);
parport_release(adapter->pdev);
if (i2c_bit_add_bus(&adapter->adapter) < 0) {
printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
goto ERROR1;
}
/* Add the new adapter to the list */
adapter->next = adapter_list;
adapter_list = adapter;
return;
ERROR1:
parport_unregister_device(adapter->pdev);
ERROR0:
kfree(adapter);
}
static void i2c_parport_detach (struct parport *port)
{
struct i2c_par *adapter, *prev;
/* Walk the list */
for (prev = NULL, adapter = adapter_list; adapter;
prev = adapter, adapter = adapter->next) {
if (adapter->pdev->port == port) {
/* Un-init if needed (power off...) */
if (adapter_parm[type].init.val)
line_set(port, 0, &adapter_parm[type].init);
i2c_bit_del_bus(&adapter->adapter);
parport_unregister_device(adapter->pdev);
if (prev)
prev->next = adapter->next;
else
adapter_list = adapter->next;
kfree(adapter);
return;
}
}
}
static struct parport_driver i2c_driver = {
.name = "i2c-parport",
.attach = i2c_parport_attach,
.detach = i2c_parport_detach,
};
/* ----- Module loading, unloading and information ------------------------ */
static int __init i2c_parport_init(void)
{
int type_count;
type_count = sizeof(adapter_parm)/sizeof(struct adapter_parm);
if (type < 0 || type >= type_count) {
printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type);
type = 0;
}
return parport_register_driver(&i2c_driver);
}
static void __exit i2c_parport_exit(void)
{
parport_unregister_driver(&i2c_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("I2C bus over parallel port");
MODULE_LICENSE("GPL");
module_init(i2c_parport_init);
module_exit(i2c_parport_exit);

View File

@@ -0,0 +1,94 @@
/* ------------------------------------------------------------------------ *
* i2c-parport.h I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-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.
* ------------------------------------------------------------------------ */
#ifdef DATA
#undef DATA
#endif
#define DATA 0
#define STAT 1
#define CTRL 2
struct lineop {
u8 val;
u8 port;
u8 inverted;
};
struct adapter_parm {
struct lineop setsda;
struct lineop setscl;
struct lineop getsda;
struct lineop getscl;
struct lineop init;
};
static struct adapter_parm adapter_parm[] = {
/* type 0: Philips adapter */
{
.setsda = { 0x80, DATA, 1 },
.setscl = { 0x08, CTRL, 0 },
.getsda = { 0x80, STAT, 0 },
.getscl = { 0x08, STAT, 0 },
},
/* type 1: home brew teletext adapter */
{
.setsda = { 0x02, DATA, 0 },
.setscl = { 0x01, DATA, 0 },
.getsda = { 0x80, STAT, 1 },
},
/* type 2: Velleman K8000 adapter */
{
.setsda = { 0x02, CTRL, 1 },
.setscl = { 0x08, CTRL, 1 },
.getsda = { 0x10, STAT, 0 },
},
/* type 3: ELV adapter */
{
.setsda = { 0x02, DATA, 1 },
.setscl = { 0x01, DATA, 1 },
.getsda = { 0x40, STAT, 1 },
.getscl = { 0x08, STAT, 1 },
},
/* type 4: ADM1032 evaluation board */
{
.setsda = { 0x02, DATA, 1 },
.setscl = { 0x01, DATA, 1 },
.getsda = { 0x10, STAT, 1 },
.init = { 0xf0, DATA, 0 },
},
/* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */
{
.setsda = { 0x02, DATA, 1 },
.setscl = { 0x01, DATA, 1 },
.getsda = { 0x10, STAT, 1 },
},
};
static int type;
module_param(type, int, 0);
MODULE_PARM_DESC(type,
"Type of adapter:\n"
" 0 = Philips adapter\n"
" 1 = home brew teletext adapter\n"
" 2 = Velleman K8000 adapter\n"
" 3 = ELV adapter\n"
" 4 = ADM1032 evaluation board\n"
" 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n");

View File

@@ -0,0 +1,184 @@
/*
* i2c-pca-isa.c driver for PCA9564 on ISA boards
* Copyright (C) 2004 Arcom Control Systems
*
* 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/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-pca.h>
#include <asm/io.h>
#include <asm/irq.h>
#include "../algos/i2c-algo-pca.h"
#define IO_SIZE 4
#undef DEBUG_IO
//#define DEBUG_IO
static unsigned long base = 0x330;
static int irq = 10;
/* Data sheet recommends 59kHz for 100kHz operation due to variation
* in the actual clock rate */
static int clock = I2C_PCA_CON_59kHz;
static int own = 0x55;
static wait_queue_head_t pca_wait;
static int pca_isa_getown(struct i2c_algo_pca_data *adap)
{
return (own);
}
static int pca_isa_getclock(struct i2c_algo_pca_data *adap)
{
return (clock);
}
static void
pca_isa_writebyte(struct i2c_algo_pca_data *adap, int reg, int val)
{
#ifdef DEBUG_IO
static char *names[] = { "T/O", "DAT", "ADR", "CON" };
printk("*** write %s at %#lx <= %#04x\n", names[reg], base+reg, val);
#endif
outb(val, base+reg);
}
static int
pca_isa_readbyte(struct i2c_algo_pca_data *adap, int reg)
{
int res = inb(base+reg);
#ifdef DEBUG_IO
{
static char *names[] = { "STA", "DAT", "ADR", "CON" };
printk("*** read %s => %#04x\n", names[reg], res);
}
#endif
return res;
}
static int pca_isa_waitforinterrupt(struct i2c_algo_pca_data *adap)
{
int ret = 0;
if (irq > -1) {
ret = wait_event_interruptible(pca_wait,
pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI);
} else {
while ((pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0)
udelay(100);
}
return ret;
}
static irqreturn_t pca_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
wake_up_interruptible(&pca_wait);
return IRQ_HANDLED;
}
static struct i2c_algo_pca_data pca_isa_data = {
.get_own = pca_isa_getown,
.get_clock = pca_isa_getclock,
.write_byte = pca_isa_writebyte,
.read_byte = pca_isa_readbyte,
.wait_for_interrupt = pca_isa_waitforinterrupt,
};
static struct i2c_adapter pca_isa_ops = {
.owner = THIS_MODULE,
.id = I2C_HW_A_ISA,
.algo_data = &pca_isa_data,
.name = "PCA9564 ISA Adapter",
};
static int __init pca_isa_init(void)
{
init_waitqueue_head(&pca_wait);
printk(KERN_INFO "i2c-pca-isa: i/o base %#08lx. irq %d\n", base, irq);
if (!request_region(base, IO_SIZE, "i2c-pca-isa")) {
printk(KERN_ERR "i2c-pca-isa: I/O address %#08lx is in use.\n", base);
goto out;
}
if (irq > -1) {
if (request_irq(irq, pca_handler, 0, "i2c-pca-isa", &pca_isa_ops) < 0) {
printk(KERN_ERR "i2c-pca-isa: Request irq%d failed\n", irq);
goto out_region;
}
}
if (i2c_pca_add_bus(&pca_isa_ops) < 0) {
printk(KERN_ERR "i2c-pca-isa: Failed to add i2c bus\n");
goto out_irq;
}
return 0;
out_irq:
if (irq > -1)
free_irq(irq, &pca_isa_ops);
out_region:
release_region(base, IO_SIZE);
out:
return -ENODEV;
}
static void pca_isa_exit(void)
{
i2c_pca_del_bus(&pca_isa_ops);
if (irq > 0) {
disable_irq(irq);
free_irq(irq, &pca_isa_ops);
}
release_region(base, IO_SIZE);
}
MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
MODULE_DESCRIPTION("ISA base PCA9564 driver");
MODULE_LICENSE("GPL");
module_param(base, ulong, 0);
MODULE_PARM_DESC(base, "I/O base address");
module_param(irq, int, 0);
MODULE_PARM_DESC(irq, "IRQ");
module_param(clock, int, 0);
MODULE_PARM_DESC(clock, "Clock rate as described in table 1 of PCA9564 datasheet");
module_param(own, int, 0); /* the driver can't do slave mode, so there's no real point in this */
module_init(pca_isa_init);
module_exit(pca_isa_exit);

View File

@@ -0,0 +1,490 @@
/*
piix4.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.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:
Intel PIIX4, 440MX
Serverworks OSB4, CSB5, CSB6
SMSC Victory66
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/apm_bios.h>
#include <linux/dmi.h>
#include <asm/io.h>
struct sd {
const unsigned short mfr;
const unsigned short dev;
const unsigned char fn;
const char *name;
};
/* PIIX4 SMBus address offsets */
#define SMBHSTSTS (0 + piix4_smba)
#define SMBHSLVSTS (1 + piix4_smba)
#define SMBHSTCNT (2 + piix4_smba)
#define SMBHSTCMD (3 + piix4_smba)
#define SMBHSTADD (4 + piix4_smba)
#define SMBHSTDAT0 (5 + piix4_smba)
#define SMBHSTDAT1 (6 + piix4_smba)
#define SMBBLKDAT (7 + piix4_smba)
#define SMBSLVCNT (8 + piix4_smba)
#define SMBSHDWCMD (9 + piix4_smba)
#define SMBSLVEVT (0xA + piix4_smba)
#define SMBSLVDAT (0xC + piix4_smba)
/* count for request_region */
#define SMBIOSIZE 8
/* PCI Address Constants */
#define SMBBA 0x090
#define SMBHSTCFG 0x0D2
#define SMBSLVC 0x0D3
#define SMBSHDW1 0x0D4
#define SMBSHDW2 0x0D5
#define SMBREV 0x0D6
/* Other settings */
#define MAX_TIMEOUT 500
#define ENABLE_INT9 0
/* PIIX4 constants */
#define PIIX4_QUICK 0x00
#define PIIX4_BYTE 0x04
#define PIIX4_BYTE_DATA 0x08
#define PIIX4_WORD_DATA 0x0C
#define PIIX4_BLOCK_DATA 0x14
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
PIIX4. DANGEROUS! */
static int force = 0;
module_param (force, int, 0);
MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
/* If force_addr is set to anything different from 0, we forcibly enable
the PIIX4 at the given address. VERY DANGEROUS! */
static int force_addr = 0;
module_param (force_addr, int, 0);
MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!");
/* If fix_hstcfg is set to anything different from 0, we reset one of the
registers to be a valid value. */
static int fix_hstcfg = 0;
module_param (fix_hstcfg, int, 0);
MODULE_PARM_DESC(fix_hstcfg,
"Fix config register. Needed on some boards (Force CPCI735).");
static int piix4_transaction(void);
static unsigned short piix4_smba = 0;
static struct i2c_adapter piix4_adapter;
static struct dmi_system_id __devinitdata piix4_dmi_table[] = {
{
.ident = "IBM",
.matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
},
{ },
};
static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id)
{
unsigned char temp;
/* match up the function */
if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data)
return -ENODEV;
dev_info(&PIIX4_dev->dev, "Found %s device\n", pci_name(PIIX4_dev));
/* Don't access SMBus on IBM systems which get corrupted eeproms */
if (dmi_check_system(piix4_dmi_table) &&
PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
dev_err(&PIIX4_dev->dev, "IBM Laptop detected; this module "
"may corrupt your serial eeprom! Refusing to load "
"module!\n");
return -EPERM;
}
/* Determine the address of the SMBus areas */
if (force_addr) {
piix4_smba = force_addr & 0xfff0;
force = 0;
} else {
pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
piix4_smba &= 0xfff0;
if(piix4_smba == 0) {
dev_err(&PIIX4_dev->dev, "SMB base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
return -ENODEV;
}
}
if (!request_region(piix4_smba, SMBIOSIZE, "piix4-smbus")) {
dev_err(&PIIX4_dev->dev, "SMB region 0x%x already in use!\n",
piix4_smba);
return -ENODEV;
}
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
/* Some BIOS will set up the chipset incorrectly and leave a register
in an undefined state (causing I2C to act very strangely). */
if (temp & 0x02) {
if (fix_hstcfg) {
dev_info(&PIIX4_dev->dev, "Working around buggy BIOS "
"(I2C)\n");
temp &= 0xfd;
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp);
} else {
dev_info(&PIIX4_dev->dev, "Unusual config register "
"value\n");
dev_info(&PIIX4_dev->dev, "Try using fix_hstcfg=1 if "
"you experience problems\n");
}
}
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the PIIX4 first. */
if (force_addr) {
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01);
dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to "
"new address %04x!\n", piix4_smba);
} else if ((temp & 1) == 0) {
if (force) {
/* This should never need to be done, but has been
* noted that many Dell machines have the SMBus
* interface on the PIIX4 disabled!? NOTE: This assumes
* I/O space and other allocations WERE done by the
* Bios! Don't complain if your hardware does weird
* things after enabling this. :') Check for Bios
* updates before resorting to this.
*/
pci_write_config_byte(PIIX4_dev, SMBHSTCFG,
temp | 1);
dev_printk(KERN_NOTICE, &PIIX4_dev->dev,
"WARNING: SMBus interface has been "
"FORCEFULLY ENABLED!\n");
} else {
dev_err(&PIIX4_dev->dev,
"Host SMBus controller not enabled!\n");
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
return -ENODEV;
}
}
if ((temp & 0x0E) == 8)
dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n");
else if ((temp & 0x0E) == 0)
dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n");
else
dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration "
"(or code out of date)!\n");
pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
dev_dbg(&PIIX4_dev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&PIIX4_dev->dev, "SMBA = 0x%X\n", piix4_smba);
return 0;
}
/* Another internally used function */
static int piix4_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&piix4_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
dev_dbg(&piix4_adapter.dev, "SMBus busy (%02x). "
"Resetting... \n", temp);
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp);
return -1;
} else {
dev_dbg(&piix4_adapter.dev, "Successfull!\n");
}
}
/* start the transaction by setting bit 6 */
outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
do {
msleep(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_err(&piix4_adapter.dev, "SMBus Timeout!\n");
result = -1;
}
if (temp & 0x10) {
result = -1;
dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n");
}
if (temp & 0x08) {
result = -1;
dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be "
"locked until next hard reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
result = -1;
dev_dbg(&piix4_adapter.dev, "Error: no response!\n");
}
if (inb_p(SMBHSTSTS) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
dev_err(&piix4_adapter.dev, "Failed reset at end of "
"transaction (%02x)\n", temp);
}
dev_dbg(&piix4_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
return result;
}
/* Return -1 on error. */
static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
return -1;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = PIIX4_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = PIIX4_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = PIIX4_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = PIIX4_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
outb_p(len, SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = PIIX4_BLOCK_DATA;
break;
}
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
if (piix4_transaction()) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
return 0;
switch (size) {
case PIIX4_BYTE: /* Where is the result put? I assume here it is in
SMBHSTDAT0 but it might just as well be in the
SMBHSTCMD. No clue in the docs */
data->byte = inb_p(SMBHSTDAT0);
break;
case PIIX4_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case PIIX4_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case PIIX4_BLOCK_DATA:
data->block[0] = inb_p(SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
break;
}
return 0;
}
static u32 piix4_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_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = piix4_access,
.functionality = piix4_func,
};
static struct i2c_adapter piix4_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3),
.driver_data = 3 },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4),
.driver_data = 0 },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
.driver_data = 0 },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6),
.driver_data = 0 },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3),
.driver_data = 3 },
{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3),
.driver_data = 0 },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, piix4_ids);
static int __devinit piix4_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int retval;
retval = piix4_setup(dev, id);
if (retval)
return retval;
/* set up the driverfs linkage to our parent device */
piix4_adapter.dev.parent = &dev->dev;
snprintf(piix4_adapter.name, I2C_NAME_SIZE,
"SMBus PIIX4 adapter at %04x", piix4_smba);
if ((retval = i2c_add_adapter(&piix4_adapter))) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
}
return retval;
}
static void __devexit piix4_remove(struct pci_dev *dev)
{
if (piix4_smba) {
i2c_del_adapter(&piix4_adapter);
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
}
}
static struct pci_driver piix4_driver = {
.name = "piix4_smbus",
.id_table = piix4_ids,
.probe = piix4_probe,
.remove = __devexit_p(piix4_remove),
};
static int __init i2c_piix4_init(void)
{
return pci_register_driver(&piix4_driver);
}
static void __exit i2c_piix4_exit(void)
{
pci_unregister_driver(&piix4_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("PIIX4 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_piix4_init);
module_exit(i2c_piix4_exit);

View File

@@ -0,0 +1,334 @@
/*
* kernel/busses/i2c-prosavage.c
*
* i2c bus driver for S3/VIA 8365/8375 graphics processor.
* Copyright (c) 2003 Henk Vergonet <henk@god.dyndns.org>
* Based on code written by:
* Frodo Looijaard <frodol@dds.nl>,
* Philip Edelbrock <phil@netroedge.com>,
* Ralph Metzler <rjkm@thp.uni-koeln.de>, and
* Mark D. Studebaker <mdsxyz123@yahoo.com>
* Simon Vogl
* and others
*
* Please read the lm_sensors documentation for details on use.
*
* 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.
*
*/
/* 18-05-2003 HVE - created
* 14-06-2003 HVE - adapted for lm_sensors2
* 17-06-2003 HVE - linux 2.5.xx compatible
* 18-06-2003 HVE - codingstyle
* 21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx
* codingstyle, mmio enabled
*
* This driver interfaces to the I2C bus of the VIA north bridge embedded
* ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips.
*
* Graphics cores:
* S3/VIA KM266/VT8375 aka ProSavage8
* S3/VIA KM133/VT8365 aka Savage4
*
* Two serial busses are implemented:
* SERIAL1 - I2C serial communications interface
* SERIAL2 - DDC2 monitor communications interface
*
* Tested on a FX41 mainboard, see http://www.shuttle.com
*
*
* TODO:
* - integration with prosavage framebuffer device
* (Additional documentation needed :(
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
/*
* driver configuration
*/
#define MAX_BUSSES 2
struct s_i2c_bus {
void __iomem *mmvga;
int i2c_reg;
int adap_ok;
struct i2c_adapter adap;
struct i2c_algo_bit_data algo;
};
struct s_i2c_chip {
void __iomem *mmio;
struct s_i2c_bus i2c_bus[MAX_BUSSES];
};
/*
* i2c configuration
*/
#ifndef I2C_HW_B_S3VIA
#define I2C_HW_B_S3VIA 0x18 /* S3VIA ProSavage adapter */
#endif
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
/*
* S3/VIA 8365/8375 registers
*/
#define VGA_CR_IX 0x3d4
#define VGA_CR_DATA 0x3d5
#define CR_SERIAL1 0xa0 /* I2C serial communications interface */
#define MM_SERIAL1 0xff20
#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */
/* based on vt8365 documentation */
#define I2C_ENAB 0x10
#define I2C_SCL_OUT 0x01
#define I2C_SDA_OUT 0x02
#define I2C_SCL_IN 0x04
#define I2C_SDA_IN 0x08
#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX)
#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA)
#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA)
/*
* Serial bus line handling
*
* serial communications register as parameter in private data
*
* TODO: locks with other code sections accessing video registers?
*/
static void bit_s3via_setscl(void *bus, int val)
{
struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
unsigned int r;
SET_CR_IX(p, p->i2c_reg);
r = GET_CR_DATA(p);
r |= I2C_ENAB;
if (val) {
r |= I2C_SCL_OUT;
} else {
r &= ~I2C_SCL_OUT;
}
SET_CR_DATA(p, r);
}
static void bit_s3via_setsda(void *bus, int val)
{
struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
unsigned int r;
SET_CR_IX(p, p->i2c_reg);
r = GET_CR_DATA(p);
r |= I2C_ENAB;
if (val) {
r |= I2C_SDA_OUT;
} else {
r &= ~I2C_SDA_OUT;
}
SET_CR_DATA(p, r);
}
static int bit_s3via_getscl(void *bus)
{
struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
SET_CR_IX(p, p->i2c_reg);
return (0 != (GET_CR_DATA(p) & I2C_SCL_IN));
}
static int bit_s3via_getsda(void *bus)
{
struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
SET_CR_IX(p, p->i2c_reg);
return (0 != (GET_CR_DATA(p) & I2C_SDA_IN));
}
/*
* adapter initialisation
*/
static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg)
{
int ret;
p->adap.owner = THIS_MODULE;
p->adap.id = I2C_HW_B_S3VIA;
p->adap.algo_data = &p->algo;
p->adap.dev.parent = &dev->dev;
p->algo.setsda = bit_s3via_setsda;
p->algo.setscl = bit_s3via_setscl;
p->algo.getsda = bit_s3via_getsda;
p->algo.getscl = bit_s3via_getscl;
p->algo.udelay = CYCLE_DELAY;
p->algo.mdelay = CYCLE_DELAY;
p->algo.timeout = TIMEOUT;
p->algo.data = p;
p->mmvga = mmvga;
p->i2c_reg = i2c_reg;
ret = i2c_bit_add_bus(&p->adap);
if (ret) {
return ret;
}
p->adap_ok = 1;
return 0;
}
/*
* Cleanup stuff
*/
static void prosavage_remove(struct pci_dev *dev)
{
struct s_i2c_chip *chip;
int i, ret;
chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
if (!chip) {
return;
}
for (i = MAX_BUSSES - 1; i >= 0; i--) {
if (chip->i2c_bus[i].adap_ok == 0)
continue;
ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap);
if (ret) {
dev_err(&dev->dev, "%s not removed\n",
chip->i2c_bus[i].adap.name);
}
}
if (chip->mmio) {
iounmap(chip->mmio);
}
kfree(chip);
}
/*
* Detect chip and initialize it
*/
static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int ret;
unsigned long base, len;
struct s_i2c_chip *chip;
struct s_i2c_bus *bus;
pci_set_drvdata(dev, kmalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
if (chip == NULL) {
return -ENOMEM;
}
memset(chip, 0, sizeof(struct s_i2c_chip));
base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK;
len = dev->resource[0].end - base + 1;
chip->mmio = ioremap_nocache(base, len);
if (chip->mmio == NULL) {
dev_err(&dev->dev, "ioremap failed\n");
prosavage_remove(dev);
return -ENODEV;
}
/*
* Chip initialisation
*/
/* Unlock Extended IO Space ??? */
/*
* i2c bus registration
*/
bus = &chip->i2c_bus[0];
snprintf(bus->adap.name, sizeof(bus->adap.name),
"ProSavage I2C bus at %02x:%02x.%x",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1);
if (ret) {
goto err_adap;
}
/*
* ddc bus registration
*/
bus = &chip->i2c_bus[1];
snprintf(bus->adap.name, sizeof(bus->adap.name),
"ProSavage DDC bus at %02x:%02x.%x",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2);
if (ret) {
goto err_adap;
}
return 0;
err_adap:
dev_err(&dev->dev, "%s failed\n", bus->adap.name);
prosavage_remove(dev);
return ret;
}
/*
* Data for PCI driver interface
*/
static struct pci_device_id prosavage_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) },
{ PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) },
{ 0, },
};
MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl);
static struct pci_driver prosavage_driver = {
.name = "prosavage_smbus",
.id_table = prosavage_pci_tbl,
.probe = prosavage_probe,
.remove = prosavage_remove,
};
static int __init i2c_prosavage_init(void)
{
return pci_register_driver(&prosavage_driver);
}
static void __exit i2c_prosavage_exit(void)
{
pci_unregister_driver(&prosavage_driver);
}
MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl);
MODULE_AUTHOR("Henk Vergonet");
MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver");
MODULE_LICENSE("GPL");
module_init (i2c_prosavage_init);
module_exit (i2c_prosavage_exit);

View File

@@ -0,0 +1,102 @@
/*
* Embedded Planet RPX Lite MPC8xx CPM I2C interface.
* Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
*
* moved into proper i2c interface;
* Brad Parker (brad@heeltoe.com)
*
* RPX lite specific parts of the i2c interface
* Update: There actually isn't anything RPXLite-specific about this module.
* This should work for most any 8xx board. The console messages have been
* changed to eliminate RPXLite references.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/stddef.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-8xx.h>
#include <asm/mpc8xx.h>
#include <asm/commproc.h>
static void
rpx_iic_init(struct i2c_algo_8xx_data *data)
{
volatile cpm8xx_t *cp;
volatile immap_t *immap;
cp = cpmp; /* Get pointer to Communication Processor */
immap = (immap_t *)IMAP_ADDR; /* and to internal registers */
data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
/* Check for and use a microcode relocation patch.
*/
if ((data->reloc = data->iip->iic_rpbase))
data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
data->i2c = (i2c8xx_t *)&(immap->im_i2c);
data->cp = cp;
/* Initialize Port B IIC pins.
*/
cp->cp_pbpar |= 0x00000030;
cp->cp_pbdir |= 0x00000030;
cp->cp_pbodr |= 0x00000030;
/* Allocate space for two transmit and two receive buffer
* descriptors in the DP ram.
*/
data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 4, 8);
/* ptr to i2c area */
data->i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
}
static int rpx_install_isr(int irq, void (*func)(void *, void *), void *data)
{
/* install interrupt handler */
cpm_install_handler(irq, (void (*)(void *, struct pt_regs *)) func, data);
return 0;
}
static struct i2c_algo_8xx_data rpx_data = {
.setisr = rpx_install_isr
};
static struct i2c_adapter rpx_ops = {
.owner = THIS_MODULE,
.name = "m8xx",
.id = I2C_HW_MPC8XX_EPON,
.algo_data = &rpx_data,
};
int __init i2c_rpx_init(void)
{
printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
/* reset hardware to sane state */
rpx_iic_init(&rpx_data);
if (i2c_8xx_add_bus(&rpx_ops) < 0) {
printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
return -ENODEV;
}
return 0;
}
void __exit i2c_rpx_exit(void)
{
i2c_8xx_del_bus(&rpx_ops);
}
MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
module_init(i2c_rpx_init);
module_exit(i2c_rpx_exit);

View File

@@ -0,0 +1,938 @@
/* linux/drivers/i2c/busses/i2c-s3c2410.c
*
* Copyright (C) 2004,2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2410 I2C Controller
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/device.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-iic.h>
#include <asm/arch/iic.h>
/* i2c controller state */
enum s3c24xx_i2c_state {
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
};
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
enum s3c24xx_i2c_state state;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *irq;
struct resource *ioarea;
struct i2c_adapter adap;
};
/* default platform data to use if not supplied in the platform_device
*/
static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
.flags = 0,
.slave_addr = 0x10,
.bus_freq = 100*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
*
* get the platform data associated with the given device, or return
* the default if there is none
*/
static inline struct s3c2410_platform_i2c *s3c24xx_i2c_get_platformdata(struct device *dev)
{
if (dev->platform_data != NULL)
return (struct s3c2410_platform_i2c *)dev->platform_data;
return &s3c24xx_i2c_default_platform;
}
/* s3c24xx_i2c_master_complete
*
* complete the message and wake up the caller, using the given return code,
* or zero to mean ok.
*/
static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
{
dev_dbg(i2c->dev, "master_complete %d\n", ret);
i2c->msg_ptr = 0;
i2c->msg = NULL;
i2c->msg_idx ++;
i2c->msg_num = 0;
if (ret)
i2c->msg_idx = ret;
wake_up(&i2c->wait);
}
static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}
static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}
/* irq enable/disable functions */
static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}
static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}
/* s3c24xx_i2c_message_start
*
* put the start of a message onto the bus
*/
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat;
unsigned long iiccon;
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) {
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
} else
stat |= S3C2410_IICSTAT_MASTER_TX;
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
// todo - check for wether ack wanted or not
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);
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);
}
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "STOP\n");
/* stop the transfer */
iicstat &= ~ S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
i2c->state = STATE_STOP;
s3c24xx_i2c_master_complete(i2c, ret);
s3c24xx_i2c_disable_irq(i2c);
}
/* helper functions to determine the current state in the set of
* messages we are sending */
/* is_lastmsg()
*
* returns TRUE if the current message is the last in the set
*/
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
{
return i2c->msg_idx >= (i2c->msg_num - 1);
}
/* is_msglast
*
* returns TRUE if we this is the last byte in the current message
*/
static inline int is_msglast(struct s3c24xx_i2c *i2c)
{
return i2c->msg_ptr == i2c->msg->len-1;
}
/* is_msgend
*
* returns TRUE if we reached the end of the current message
*/
static inline int is_msgend(struct s3c24xx_i2c *i2c)
{
return i2c->msg_ptr >= i2c->msg->len;
}
/* i2s_s3c_irq_nextbyte
*
* process an interrupt and work out what to do
*/
static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
unsigned long tmp;
unsigned char byte;
int ret = 0;
switch (i2c->state) {
case STATE_IDLE:
dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __FUNCTION__);
goto out;
break;
case STATE_STOP:
dev_err(i2c->dev, "%s: called in STATE_STOP\n", __FUNCTION__);
s3c24xx_i2c_disable_irq(i2c);
goto out_ack;
case STATE_START:
/* last thing we did was send a start condition on the
* bus, or started a new i2c message
*/
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not received\n");
s3c24xx_i2c_stop(i2c, -EREMOTEIO);
goto out_ack;
}
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
/* terminate the transfer if there is nothing to do
* (used by the i2c probe to find devices */
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
s3c24xx_i2c_stop(i2c, 0);
goto out_ack;
}
if (i2c->state == STATE_READ)
goto prepare_read;
/* fall through to the write state, as we will need to
* send a byte as well */
case STATE_WRITE:
/* we are writing data to the device... check for the
* end of the message, and if so, work out what to do
*/
retry_write:
if (!is_msgend(i2c)) {
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
} else if (!is_lastmsg(i2c)) {
/* we need to go to the next i2c message */
dev_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0;
i2c->msg_idx ++;
i2c->msg++;
/* check to see if we need to do another message */
if (i2c->msg->flags & I2C_M_NOSTART) {
if (i2c->msg->flags & I2C_M_RD) {
/* cannot do this, the controller
* forces us to send a new START
* when we change direction */
s3c24xx_i2c_stop(i2c, -EINVAL);
}
goto retry_write;
} else {
/* send the new start */
s3c24xx_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
} else {
/* send stop */
s3c24xx_i2c_stop(i2c, 0);
}
break;
case STATE_READ:
/* we have a byte of data in the data register, do
* something with it, and then work out wether we are
* going to do any more read/write
*/
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
!(is_msglast(i2c) && is_lastmsg(i2c))) {
if (iicstat & S3C2410_IICSTAT_LASTBIT) {
dev_dbg(i2c->dev, "READ: No Ack\n");
s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
goto out_ack;
}
}
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
prepare_read:
if (is_msglast(i2c)) {
/* last byte of buffer */
if (is_lastmsg(i2c))
s3c24xx_i2c_disable_ack(i2c);
} else if (is_msgend(i2c)) {
/* ok, we've read the entire buffer, see if there
* is anything else we need to do */
if (is_lastmsg(i2c)) {
/* last message, send stop and complete */
dev_dbg(i2c->dev, "READ: Send Stop\n");
s3c24xx_i2c_stop(i2c, 0);
} else {
/* go to the next transfer */
dev_dbg(i2c->dev, "READ: Next Transfer\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
}
}
break;
}
/* acknowlegde the IRQ and get back on with the work */
out_ack:
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
out:
return ret;
}
/* s3c24xx_i2c_irq
*
* top level IRQ servicing routine
*/
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
struct pt_regs *regs)
{
struct s3c24xx_i2c *i2c = dev_id;
unsigned long status;
unsigned long tmp;
status = readl(i2c->regs + S3C2410_IICSTAT);
if (status & S3C2410_IICSTAT_ARBITR) {
// deal with arbitration loss
dev_err(i2c->dev, "deal with arbitration loss\n");
}
if (i2c->state == STATE_IDLE) {
dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
goto out;
}
/* pretty much this leaves us with the fact that we've
* transmitted or received whatever byte we last sent */
i2s_s3c_irq_nextbyte(i2c, status);
out:
return IRQ_HANDLED;
}
/* s3c24xx_i2c_set_master
*
* get the i2c bus for a master transaction
*/
static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
{
unsigned long iicstat;
int timeout = 400;
while (timeout-- > 0) {
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
return 0;
msleep(1);
}
dev_dbg(i2c->dev, "timeout: GPEDAT is %08x\n",
__raw_readl(S3C2410_GPEDAT));
return -ETIMEDOUT;
}
/* s3c24xx_i2c_doxfer
*
* this starts an i2c transfer
*/
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{
unsigned long timeout;
int ret;
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
ret = -EAGAIN;
goto out;
}
spin_lock_irq(&i2c->lock);
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START;
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs);
spin_unlock_irq(&i2c->lock);
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
ret = i2c->msg_idx;
/* having these next two as dev_err() makes life very
* noisy when doing an i2cdetect */
if (timeout == 0)
dev_dbg(i2c->dev, "timeout\n");
else if (ret != num)
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
/* ensure the stop has been through the bus */
msleep(1);
out:
return ret;
}
/* s3c24xx_i2c_xfer
*
* first port of call from the i2c bus code when an message needs
* transfering across the i2c bus.
*/
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret;
for (retry = 0; retry < adap->retries; retry++) {
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return ret;
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
/* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
/* i2c bus registration info */
static struct i2c_algorithm s3c24xx_i2c_algorithm = {
.name = "S3C2410-I2C-Algorithm",
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static struct s3c24xx_i2c s3c24xx_i2c = {
.lock = SPIN_LOCK_UNLOCKED,
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
.adap = {
.name = "s3c2410-i2c",
.owner = THIS_MODULE,
.algo = &s3c24xx_i2c_algorithm,
.retries = 2,
.class = I2C_CLASS_HWMON,
},
};
/* s3c24xx_i2c_calcdivisor
*
* return the divisor settings for a given frequency
*/
static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
unsigned int *div1, unsigned int *divs)
{
unsigned int calc_divs = clkin / wanted;
unsigned int calc_div1;
if (calc_divs > (16*16))
calc_div1 = 512;
else
calc_div1 = 16;
calc_divs += calc_div1-1;
calc_divs /= calc_div1;
if (calc_divs == 0)
calc_divs = 1;
if (calc_divs > 17)
calc_divs = 17;
*divs = calc_divs;
*div1 = calc_div1;
return clkin / (calc_divs * calc_div1);
}
/* freq_acceptable
*
* test wether a frequency is within the acceptable range of error
*/
static inline int freq_acceptable(unsigned int freq, unsigned int wanted)
{
int diff = freq - wanted;
return (diff >= -2 && diff <= 2);
}
/* s3c24xx_i2c_getdivisor
*
* work out a divisor for the user requested frequency setting,
* either by the requested frequency, or scanning the acceptable
* range of frequencies until something is found
*/
static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c,
struct s3c2410_platform_i2c *pdata,
unsigned long *iicon,
unsigned int *got)
{
unsigned long clkin = clk_get_rate(i2c->clk);
unsigned int divs, div1;
int freq;
int start, end;
clkin /= 1000; /* clkin now in KHz */
dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n",
pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
if (pdata->bus_freq != 0) {
freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000,
&div1, &divs);
if (freq_acceptable(freq, pdata->bus_freq/1000))
goto found;
}
/* ok, we may have to search for something suitable... */
start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;
end = pdata->min_freq;
start /= 1000;
end /= 1000;
/* search loop... */
for (; start > end; start--) {
freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
if (freq_acceptable(freq, start))
goto found;
}
/* cannot find frequency spec */
return -EINVAL;
found:
*got = freq;
*iicon |= (divs-1);
*iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0;
return 0;
}
/* s3c24xx_i2c_init
*
* initialise the controller, set the IO lines and frequency
*/
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
struct s3c2410_platform_i2c *pdata;
unsigned int freq;
/* get the plafrom data */
pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);
/* inititalise the gpio */
s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
/* write slave address */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
/* we need to work out the divisors for the clock... */
if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) {
dev_err(i2c->dev, "cannot meet bus frequency required\n");
return -EINVAL;
}
/* todo - check that the i2c lines aren't being dragged anywhere */
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;
}
static void s3c24xx_i2c_free(struct s3c24xx_i2c *i2c)
{
if (i2c->clk != NULL && !IS_ERR(i2c->clk)) {
clk_disable(i2c->clk);
clk_unuse(i2c->clk);
clk_put(i2c->clk);
i2c->clk = NULL;
}
if (i2c->regs != NULL) {
iounmap(i2c->regs);
i2c->regs = NULL;
}
if (i2c->ioarea != NULL) {
release_resource(i2c->ioarea);
kfree(i2c->ioarea);
i2c->ioarea = NULL;
}
}
/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
*/
static int s3c24xx_i2c_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
struct resource *res;
int ret;
/* find the clock and enable it */
i2c->dev = dev;
i2c->clk = clk_get(dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(dev, "cannot get clock\n");
ret = -ENOENT;
goto out;
}
dev_dbg(dev, "clock source %p\n", i2c->clk);
clk_use(i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "cannot find IO resource\n");
ret = -ENOENT;
goto out;
}
i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(dev, "cannot request IO\n");
ret = -ENXIO;
goto out;
}
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
if (i2c->regs == NULL) {
dev_err(dev, "cannot map IO\n");
ret = -ENXIO;
goto out;
}
dev_dbg(dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = dev;
/* initialise the i2c controller */
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto out;
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(dev, "cannot find IRQ\n");
ret = -ENOENT;
goto out;
}
ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,
pdev->name, i2c);
if (ret != 0) {
dev_err(dev, "cannot claim IRQ\n");
goto out;
}
i2c->irq = res;
dev_dbg(dev, "irq resource %p (%ld)\n", res, res->start);
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
dev_err(dev, "failed to add bus to i2c core\n");
goto out;
}
dev_set_drvdata(dev, i2c);
dev_info(dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
out:
if (ret < 0)
s3c24xx_i2c_free(i2c);
return ret;
}
/* s3c24xx_i2c_remove
*
* called when device is removed from the bus
*/
static int s3c24xx_i2c_remove(struct device *dev)
{
struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
if (i2c != NULL) {
s3c24xx_i2c_free(i2c);
dev_set_drvdata(dev, NULL);
}
return 0;
}
#ifdef CONFIG_PM
static int s3c24xx_i2c_resume(struct device *dev, u32 level)
{
struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
if (i2c != NULL && level == RESUME_ENABLE) {
dev_dbg(dev, "resume: level %d\n", level);
s3c24xx_i2c_init(i2c);
}
return 0;
}
#else
#define s3c24xx_i2c_resume NULL
#endif
/* device driver for platform bus bits */
static struct device_driver s3c2410_i2c_driver = {
.name = "s3c2410-i2c",
.bus = &platform_bus_type,
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.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)
{
int ret;
ret = driver_register(&s3c2410_i2c_driver);
if (ret == 0)
ret = driver_register(&s3c2440_i2c_driver);
return ret;
}
static void __exit i2c_adap_s3c_exit(void)
{
driver_unregister(&s3c2410_i2c_driver);
driver_unregister(&s3c2440_i2c_driver);
}
module_init(i2c_adap_s3c_init);
module_exit(i2c_adap_s3c_exit);
MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,205 @@
/*
i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 1998-2003 The LM Sensors Team
Alexander Wold <awold@bigfoot.com>
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on i2c-voodoo3.c.
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.
*/
/* This interfaces to the I2C bus of the Savage4 to gain access to
the BT869 and possibly other I2C devices. The DDC bus is not
yet supported because its register is not memory-mapped.
However we leave the DDC code here, commented out, to make
it easier to add later.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
/* 3DFX defines */
#define PCI_CHIP_SAVAGE3D 0x8A20
#define PCI_CHIP_SAVAGE3D_MV 0x8A21
#define PCI_CHIP_SAVAGE4 0x8A22
#define PCI_CHIP_SAVAGE2000 0x9102
#define PCI_CHIP_PROSAVAGE_PM 0x8A25
#define PCI_CHIP_PROSAVAGE_KM 0x8A26
#define PCI_CHIP_SAVAGE_MX_MV 0x8c10
#define PCI_CHIP_SAVAGE_MX 0x8c11
#define PCI_CHIP_SAVAGE_IX_MV 0x8c12
#define PCI_CHIP_SAVAGE_IX 0x8c13
#define REG 0xff20 /* Serial Port 1 Register */
/* bit locations in the register */
#define DDC_ENAB 0x00040000
#define DDC_SCL_OUT 0x00080000
#define DDC_SDA_OUT 0x00100000
#define DDC_SCL_IN 0x00200000
#define DDC_SDA_IN 0x00400000
#define I2C_ENAB 0x00000020
#define I2C_SCL_OUT 0x00000001
#define I2C_SDA_OUT 0x00000002
#define I2C_SCL_IN 0x00000008
#define I2C_SDA_IN 0x00000010
/* initialization states */
#define INIT2 0x20
#define INIT3 0x04
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void __iomem *ioaddr;
/* The sav GPIO registers don't have individual masks for each bit
so we always have to read before writing. */
static void bit_savi2c_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SCL_OUT;
else
r &= ~I2C_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_savi2c_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SDA_OUT;
else
r &= ~I2C_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins always remain outputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. */
static int bit_savi2c_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
}
static int bit_savi2c_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
}
/* Configures the chip */
static int config_s4(struct pci_dev *dev)
{
unsigned long cadr;
/* map memory */
cadr = dev->resource[0].start;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = ioremap_nocache(cadr, 0x0080000);
if (ioaddr) {
/* writel(0x8160, ioaddr + REG2); */
writel(0x00000020, ioaddr + REG);
dev_info(&dev->dev, "Using Savage4 at %p\n", ioaddr);
return 0;
}
return -ENODEV;
}
static struct i2c_algo_bit_data sav_i2c_bit_data = {
.setsda = bit_savi2c_setsda,
.setscl = bit_savi2c_setscl,
.getsda = bit_savi2c_getsda,
.getscl = bit_savi2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter savage4_i2c_adapter = {
.owner = THIS_MODULE,
.name = "I2C Savage4 adapter",
.algo_data = &sav_i2c_bit_data,
};
static struct pci_device_id savage4_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4) },
{ PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, savage4_ids);
static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
retval = config_s4(dev);
if (retval)
return retval;
/* set up the sysfs linkage to our parent device */
savage4_i2c_adapter.dev.parent = &dev->dev;
return i2c_bit_add_bus(&savage4_i2c_adapter);
}
static void __devexit savage4_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&savage4_i2c_adapter);
iounmap(ioaddr);
}
static struct pci_driver savage4_driver = {
.name = "savage4_smbus",
.id_table = savage4_ids,
.probe = savage4_probe,
.remove = __devexit_p(savage4_remove),
};
static int __init i2c_savage4_init(void)
{
return pci_register_driver(&savage4_driver);
}
static void __exit i2c_savage4_exit(void)
{
pci_unregister_driver(&savage4_driver);
}
MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_savage4_init);
module_exit(i2c_savage4_exit);

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2004 Steven J. Hill
* Copyright (C) 2001,2002,2003 Broadcom Corporation
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/i2c-algo-sibyte.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
{ NULL, 0, (void *) (KSEG1+A_SMB_BASE(0)) },
{ NULL, 1, (void *) (KSEG1+A_SMB_BASE(1)) }
};
static struct i2c_adapter sibyte_board_adapter[2] = {
{
.owner = THIS_MODULE,
.id = I2C_HW_SIBYTE,
.class = I2C_CLASS_HWMON,
.algo = NULL,
.algo_data = &sibyte_board_data[0],
.name = "SiByte SMBus 0",
},
{
.owner = THIS_MODULE,
.id = I2C_HW_SIBYTE,
.class = I2C_CLASS_HWMON,
.algo = NULL,
.algo_data = &sibyte_board_data[1],
.name = "SiByte SMBus 1",
},
};
static int __init i2c_sibyte_init(void)
{
printk("i2c-swarm.o: i2c SMBus adapter module for SiByte board\n");
if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
return -ENODEV;
if (i2c_sibyte_add_bus(&sibyte_board_adapter[1], K_SMB_FREQ_400KHZ) < 0)
return -ENODEV;
return 0;
}
static void __exit i2c_sibyte_exit(void)
{
i2c_sibyte_del_bus(&sibyte_board_adapter[0]);
i2c_sibyte_del_bus(&sibyte_board_adapter[1]);
}
module_init(i2c_sibyte_init);
module_exit(i2c_sibyte_exit);
MODULE_AUTHOR("Kip Walker <kwalker@broadcom.com>, Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,424 @@
/*
sis5595.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.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.
*/
/* Note: we assume there can only be one SIS5595 with one SMBus interface */
/*
Note: all have mfr. ID 0x1039.
SUPPORTED PCI ID
5595 0008
Note: these chips contain a 0008 device which is incompatible with the
5595. We recognize these by the presence of the listed
"blacklist" PCI ID and refuse to load.
NOT SUPPORTED PCI ID BLACKLIST PCI ID
540 0008 0540
550 0008 0550
5513 0008 5511
5581 0008 5597
5582 0008 5597
5597 0008 5597
5598 0008 5597/5598
630 0008 0630
645 0008 0645
646 0008 0646
648 0008 0648
650 0008 0650
651 0008 0651
730 0008 0730
735 0008 0735
745 0008 0745
746 0008 0746
*/
/* TO DO:
* Add Block Transfers (ugly, but supported by the adapter)
* Add adapter resets
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/io.h>
static int blacklist[] = {
PCI_DEVICE_ID_SI_540,
PCI_DEVICE_ID_SI_550,
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_645,
PCI_DEVICE_ID_SI_646,
PCI_DEVICE_ID_SI_648,
PCI_DEVICE_ID_SI_650,
PCI_DEVICE_ID_SI_651,
PCI_DEVICE_ID_SI_730,
PCI_DEVICE_ID_SI_735,
PCI_DEVICE_ID_SI_745,
PCI_DEVICE_ID_SI_746,
PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but that ID
shows up in other chips so we use the 5511
ID for recognition */
PCI_DEVICE_ID_SI_5597,
PCI_DEVICE_ID_SI_5598,
0, /* terminates the list */
};
/* Length of ISA address segment */
#define SIS5595_EXTENT 8
/* SIS5595 SMBus registers */
#define SMB_STS_LO 0x00
#define SMB_STS_HI 0x01
#define SMB_CTL_LO 0x02
#define SMB_CTL_HI 0x03
#define SMB_ADDR 0x04
#define SMB_CMD 0x05
#define SMB_PCNT 0x06
#define SMB_CNT 0x07
#define SMB_BYTE 0x08
#define SMB_DEV 0x10
#define SMB_DB0 0x11
#define SMB_DB1 0x12
#define SMB_HAA 0x13
/* PCI Address Constants */
#define SMB_INDEX 0x38
#define SMB_DAT 0x39
#define SIS5595_ENABLE_REG 0x40
#define ACPI_BASE 0x90
/* Other settings */
#define MAX_TIMEOUT 500
/* SIS5595 constants */
#define SIS5595_QUICK 0x00
#define SIS5595_BYTE 0x02
#define SIS5595_BYTE_DATA 0x04
#define SIS5595_WORD_DATA 0x06
#define SIS5595_PROC_CALL 0x08
#define SIS5595_BLOCK_DATA 0x0A
/* insmod parameters */
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static u16 force_addr = 0;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller");
static unsigned short sis5595_base = 0;
static u8 sis5595_read(u8 reg)
{
outb(reg, sis5595_base + SMB_INDEX);
return inb(sis5595_base + SMB_DAT);
}
static void sis5595_write(u8 reg, u8 data)
{
outb(reg, sis5595_base + SMB_INDEX);
outb(data, sis5595_base + SMB_DAT);
}
static int sis5595_setup(struct pci_dev *SIS5595_dev)
{
u16 a;
u8 val;
int *i;
int retval = -ENODEV;
/* Look for imposters */
for (i = blacklist; *i != 0; i++) {
struct pci_dev *dev;
dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
if (dev) {
dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
pci_dev_put(dev);
return -ENODEV;
}
}
/* Determine the address of the SMBus areas */
pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
if (sis5595_base == 0 && force_addr == 0) {
dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if (force_addr)
sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base);
/* NB: We grab just the two SMBus registers here, but this may still
* interfere with ACPI :-( */
if (!request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus")) {
dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n",
sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1);
return -ENODEV;
}
if (force_addr) {
dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base);
if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)
!= PCIBIOS_SUCCESSFUL)
goto error;
if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)
!= PCIBIOS_SUCCESSFUL)
goto error;
if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
/* doesn't work for some chips! */
dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n");
goto error;
}
}
if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
!= PCIBIOS_SUCCESSFUL)
goto error;
if ((val & 0x80) == 0) {
dev_info(&SIS5595_dev->dev, "enabling ACPI\n");
if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80)
!= PCIBIOS_SUCCESSFUL)
goto error;
if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)
!= PCIBIOS_SUCCESSFUL)
goto error;
if ((val & 0x80) == 0) {
/* doesn't work for some chips? */
dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n");
goto error;
}
}
/* Everything is happy */
return 0;
error:
release_region(sis5595_base + SMB_INDEX, 2);
return retval;
}
static int sis5595_transaction(struct i2c_adapter *adap)
{
int temp;
int result = 0;
int timeout = 0;
/* Make sure the SMBus host is ready to start transmitting */
temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
if (temp != 0x00) {
dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting... \n", temp);
sis5595_write(SMB_STS_LO, temp & 0xff);
sis5595_write(SMB_STS_HI, temp >> 8);
if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
return -1;
} else {
dev_dbg(&adap->dev, "Successfull!\n");
}
}
/* start the transaction by setting bit 4 */
sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
/* We will always wait for a fraction of a second! */
do {
msleep(1);
temp = sis5595_read(SMB_STS_LO);
} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
result = -1;
}
if (temp & 0x10) {
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
result = -1;
}
if (temp & 0x20) {
dev_err(&adap->dev, "Bus collision! SMBus may be locked until "
"next hard reset (or not...)\n");
/* Clock stops and slave is stuck in mid-transmission */
result = -1;
}
temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
if (temp != 0x00) {
sis5595_write(SMB_STS_LO, temp & 0xff);
sis5595_write(SMB_STS_HI, temp >> 8);
}
temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
if (temp != 0x00)
dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp);
return result;
}
/* Return -1 on error. */
static s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS5595_QUICK;
break;
case I2C_SMBUS_BYTE:
sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis5595_write(SMB_CMD, command);
size = SIS5595_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis5595_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis5595_write(SMB_BYTE, data->byte);
size = SIS5595_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis5595_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis5595_write(SMB_BYTE, data->word & 0xff);
sis5595_write(SMB_BYTE + 1,
(data->word & 0xff00) >> 8);
}
size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA;
break;
/*
case I2C_SMBUS_BLOCK_DATA:
printk(KERN_WARNING "sis5595.o: Block data not yet implemented!\n");
return -1;
break;
*/
default:
printk(KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size);
return -1;
}
sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
if (sis5595_transaction(adap))
return -1;
if ((size != SIS5595_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
return 0;
switch (size) {
case SIS5595_BYTE: /* Where is the result put? I assume here it is in
SMB_DATA but it might just as well be in the
SMB_CMD. No clue in the docs */
case SIS5595_BYTE_DATA:
data->byte = sis5595_read(SMB_BYTE);
break;
case SIS5595_WORD_DATA:
case SIS5595_PROC_CALL:
data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8);
break;
}
return 0;
}
static u32 sis5595_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;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis5595_access,
.functionality = sis5595_func,
};
static struct i2c_adapter sis5595_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.name = "unset",
.algo = &smbus_algorithm,
};
static struct pci_device_id sis5595_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, sis5595_ids);
static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (sis5595_setup(dev)) {
dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
return -ENODEV;
}
/* set up the driverfs linkage to our parent device */
sis5595_adapter.dev.parent = &dev->dev;
sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
sis5595_base + SMB_INDEX);
return i2c_add_adapter(&sis5595_adapter);
}
static void __devexit sis5595_remove(struct pci_dev *dev)
{
i2c_del_adapter(&sis5595_adapter);
release_region(sis5595_base + SMB_INDEX, 2);
}
static struct pci_driver sis5595_driver = {
.name = "sis5595_smbus",
.id_table = sis5595_ids,
.probe = sis5595_probe,
.remove = __devexit_p(sis5595_remove),
};
static int __init i2c_sis5595_init(void)
{
return pci_register_driver(&sis5595_driver);
}
static void __exit i2c_sis5595_exit(void)
{
pci_unregister_driver(&sis5595_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("SIS5595 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_sis5595_init);
module_exit(i2c_sis5595_exit);

View File

@@ -0,0 +1,523 @@
/*
i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
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
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.
*/
/*
Changes:
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 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
31.07.2003
Added block data read/write support.
*/
/*
Status: beta
Supports:
SIS 630
SIS 730
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/io.h>
/* SIS630 SMBus registers */
#define SMB_STS 0x80 /* status */
#define SMB_EN 0x81 /* status enable */
#define SMB_CNT 0x82
#define SMBHOST_CNT 0x83
#define SMB_ADDR 0x84
#define SMB_CMD 0x85
#define SMB_PCOUNT 0x86 /* processed count */
#define SMB_COUNT 0x87
#define SMB_BYTE 0x88 /* ~0x8F data byte field */
#define SMBDEV_ADDR 0x90
#define SMB_DB0 0x91
#define SMB_DB1 0x92
#define SMB_SAA 0x93
/* register count for request_region */
#define SIS630_SMB_IOREGION 20
/* PCI address constants */
/* acpi base address register */
#define SIS630_ACPI_BASE_REG 0x74
/* bios control register */
#define SIS630_BIOS_CTL_REG 0x40
/* Other settings */
#define MAX_TIMEOUT 500
/* SIS630 constants */
#define SIS630_QUICK 0x00
#define SIS630_BYTE 0x01
#define SIS630_BYTE_DATA 0x02
#define SIS630_WORD_DATA 0x03
#define SIS630_PCALL 0x04
#define SIS630_BLOCK_DATA 0x05
/* insmod parameters */
static int high_clock;
static int force;
module_param(high_clock, bool, 0);
MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
/* acpi base address */
static unsigned short acpi_base = 0;
/* supported chips */
static int supported[] = {
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_730,
0 /* terminates the list */
};
static inline u8 sis630_read(u8 reg)
{
return inb(acpi_base + reg);
}
static inline void sis630_write(u8 reg, u8 data)
{
outb(data, acpi_base + reg);
}
static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
{
int temp;
/* Make sure the SMBus host is ready to start transmitting. */
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp);
/* kill smbus transaction */
sis630_write(SMBHOST_CNT, 0x20);
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
return -1;
} else {
dev_dbg(&adap->dev, "Successfull!\n");
}
}
/* save old clock, so we can prevent machine for hung */
*oldclock = sis630_read(SMB_CNT);
dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock);
/* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
if (high_clock)
sis630_write(SMB_CNT, 0x20);
else
sis630_write(SMB_CNT, (*oldclock & ~0x40));
/* clear all sticky bits */
temp = sis630_read(SMB_STS);
sis630_write(SMB_STS, temp & 0x1e);
/* start the transaction by setting bit 4 and size */
sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
return 0;
}
static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
{
int temp, result = 0, timeout = 0;
/* We will always wait for a fraction of a second! */
do {
msleep(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) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
result = -1;
}
if (temp & 0x02) {
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
result = -1;
}
if (temp & 0x04) {
dev_err(&adap->dev, "Bus collision!\n");
result = -1;
/*
TBD: Datasheet say:
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(struct i2c_adapter *adap, u8 oldclock)
{
int temp = 0;
/* clear all status "sticky" bits */
sis630_write(SMB_STS, temp);
dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
/*
* restore old Host Master Clock if high_clock is set
* and oldclock was not 56KHz
*/
if (high_clock && !(oldclock & 0x20))
sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
}
static int sis630_transaction(struct i2c_adapter *adap, int size)
{
int result = 0;
u8 oldclock = 0;
result = sis630_transaction_start(adap, size, &oldclock);
if (!result) {
result = sis630_transaction_wait(adap, size);
sis630_transaction_end(adap, oldclock);
}
return result;
}
static int sis630_block_data(struct i2c_adapter *adap, 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++) {
dev_dbg(&adap->dev, "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)) {
dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
/* first transaction */
if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock))
return -1;
}
else if ((i-1)%8 == 7 || i==len) {
dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
if (i>8) {
dev_dbg(&adap->dev, "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(adap, SIS630_BLOCK_DATA)) {
dev_dbg(&adap->dev, "trans_wait failed\n");
rc = -1;
break;
}
}
}
}
else {
/* read request */
data->block[0] = len = 0;
if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock)) {
return -1;
}
do {
if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
dev_dbg(&adap->dev, "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);
/* just to be sure */
if (data->block[0] > 32)
data->block[0] = 32;
dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]);
for (i=0; i < 8 && len < data->block[0]; i++,len++) {
dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len);
data->block[len+1] = sis630_read(SMB_BYTE+i);
}
dev_dbg(&adap->dev, "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(adap, oldclock);
return rc;
}
/* Return -1 on error. */
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)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS630_QUICK;
break;
case I2C_SMBUS_BYTE:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis630_write(SMB_CMD, command);
size = SIS630_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis630_write(SMB_BYTE, data->byte);
size = SIS630_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis630_write(SMB_BYTE, data->word & 0xff);
sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
}
size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
size = SIS630_BLOCK_DATA;
return sis630_block_data(adap, data, read_write);
default:
printk("Unsupported I2C size\n");
return -1;
break;
}
if (sis630_transaction(adap, size))
return -1;
if ((size != SIS630_PCALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
return 0;
}
switch(size) {
case SIS630_BYTE:
case SIS630_BYTE_DATA:
data->byte = sis630_read(SMB_BYTE);
break;
case SIS630_PCALL:
case SIS630_WORD_DATA:
data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
break;
default:
return -1;
break;
}
return 0;
}
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_BLOCK_DATA;
}
static int sis630_setup(struct pci_dev *sis630_dev)
{
unsigned char b;
struct pci_dev *dummy = NULL;
int retval = -ENODEV, i;
/* check for supported SiS devices */
for (i=0; supported[i] > 0 ; i++) {
if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
break; /* found */
}
if (dummy) {
pci_dev_put(dummy);
}
else if (force) {
dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but "
"loading because of force option enabled\n");
}
else {
return -ENODEV;
}
/*
Enable ACPI first , so we can accsess reg 74-75
in acpi io space and read acpi base addr
*/
if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n");
goto exit;
}
/* if ACPI already enabled , do nothing */
if (!(b & 0x80) &&
pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) {
dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n");
goto exit;
}
/* Determine the ACPI base address */
if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n");
goto exit;
}
dev_dbg(&sis630_dev->dev, "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")) {
dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
"in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
goto exit;
}
retval = 0;
exit:
if (retval)
acpi_base = 0;
return retval;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis630_access,
.functionality = sis630_func,
};
static struct i2c_adapter sis630_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.name = "unset",
.algo = &smbus_algorithm,
};
static struct pci_device_id sis630_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, sis630_ids);
static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (sis630_setup(dev)) {
dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n");
return -ENODEV;
}
/* set up the driverfs linkage to our parent device */
sis630_adapter.dev.parent = &dev->dev;
sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
acpi_base + SMB_STS);
return i2c_add_adapter(&sis630_adapter);
}
static void __devexit sis630_remove(struct pci_dev *dev)
{
if (acpi_base) {
i2c_del_adapter(&sis630_adapter);
release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
acpi_base = 0;
}
}
static struct pci_driver sis630_driver = {
.name = "sis630_smbus",
.id_table = sis630_ids,
.probe = sis630_probe,
.remove = __devexit_p(sis630_remove),
};
static int __init i2c_sis630_init(void)
{
return pci_register_driver(&sis630_driver);
}
static void __exit i2c_sis630_exit(void)
{
pci_unregister_driver(&sis630_driver);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
MODULE_DESCRIPTION("SIS630 SMBus driver");
module_init(i2c_sis630_init);
module_exit(i2c_sis630_exit);

View File

@@ -0,0 +1,358 @@
/*
sis96x.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.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.
*/
/*
This module must be considered BETA unless and until
the chipset manufacturer releases a datasheet.
The register definitions are based on the SiS630.
This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c)
for just about every machine for which users have reported.
If this module isn't detecting your 96x south bridge, have a
look there.
We assume there can only be one SiS96x with one SMBus interface.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
/*
HISTORY:
2003-05-11 1.0.0 Updated from lm_sensors project for kernel 2.5
(was i2c-sis645.c from lm_sensors 2.7.0)
*/
#define SIS96x_VERSION "1.0.0"
/* base address register in PCI config space */
#define SIS96x_BAR 0x04
/* SiS96x SMBus registers */
#define SMB_STS 0x00
#define SMB_EN 0x01
#define SMB_CNT 0x02
#define SMB_HOST_CNT 0x03
#define SMB_ADDR 0x04
#define SMB_CMD 0x05
#define SMB_PCOUNT 0x06
#define SMB_COUNT 0x07
#define SMB_BYTE 0x08
#define SMB_DEV_ADDR 0x10
#define SMB_DB0 0x11
#define SMB_DB1 0x12
#define SMB_SAA 0x13
/* register count for request_region */
#define SMB_IOSIZE 0x20
/* Other settings */
#define MAX_TIMEOUT 500
/* SiS96x SMBus constants */
#define SIS96x_QUICK 0x00
#define SIS96x_BYTE 0x01
#define SIS96x_BYTE_DATA 0x02
#define SIS96x_WORD_DATA 0x03
#define SIS96x_PROC_CALL 0x04
#define SIS96x_BLOCK_DATA 0x05
static struct i2c_adapter sis96x_adapter;
static u16 sis96x_smbus_base = 0;
static inline u8 sis96x_read(u8 reg)
{
return inb(sis96x_smbus_base + reg) ;
}
static inline void sis96x_write(u8 reg, u8 data)
{
outb(data, sis96x_smbus_base + reg) ;
}
/* Execute a SMBus transaction.
int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA
*/
static int sis96x_transaction(int size)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size);
/* Make sure the SMBus host is ready to start transmitting */
if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). "
"Resetting...\n", temp);
/* kill the transaction */
sis96x_write(SMB_HOST_CNT, 0x20);
/* check it again */
if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp);
return -1;
} else {
dev_dbg(&sis96x_adapter.dev, "Successful\n");
}
}
/* Turn off timeout interrupts, set fast host clock */
sis96x_write(SMB_CNT, 0x20);
/* clear all (sticky) status flags */
temp = sis96x_read(SMB_STS);
sis96x_write(SMB_STS, temp & 0x1e);
/* start the transaction by setting bit 4 and size bits */
sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
/* We will always wait for a fraction of a second! */
do {
msleep(1);
temp = sis96x_read(SMB_STS);
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp);
result = -1;
}
/* device error - probably missing ACK */
if (temp & 0x02) {
dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n");
result = -1;
}
/* bus collision */
if (temp & 0x04) {
dev_dbg(&sis96x_adapter.dev, "Bus collision!\n");
result = -1;
}
/* Finish up by resetting the bus */
sis96x_write(SMB_STS, temp);
if ((temp = sis96x_read(SMB_STS))) {
dev_dbg(&sis96x_adapter.dev, "Failed reset at "
"end of transaction! (0x%02x)\n", temp);
}
return result;
}
/* Return -1 on error. */
static s32 sis96x_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS96x_QUICK;
break;
case I2C_SMBUS_BYTE:
sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis96x_write(SMB_CMD, command);
size = SIS96x_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis96x_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis96x_write(SMB_BYTE, data->byte);
size = SIS96x_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis96x_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis96x_write(SMB_BYTE, data->word & 0xff);
sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
}
size = (size == I2C_SMBUS_PROC_CALL ?
SIS96x_PROC_CALL : SIS96x_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
/* TO DO: */
dev_info(&adap->dev, "SMBus block not implemented!\n");
return -1;
break;
default:
dev_info(&adap->dev, "Unsupported I2C size\n");
return -1;
break;
}
if (sis96x_transaction(size))
return -1;
if ((size != SIS96x_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK)))
return 0;
switch (size) {
case SIS96x_BYTE:
case SIS96x_BYTE_DATA:
data->byte = sis96x_read(SMB_BYTE);
break;
case SIS96x_WORD_DATA:
case SIS96x_PROC_CALL:
data->word = sis96x_read(SMB_BYTE) +
(sis96x_read(SMB_BYTE + 1) << 8);
break;
}
return 0;
}
static u32 sis96x_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;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis96x_access,
.functionality = sis96x_func,
};
static struct i2c_adapter sis96x_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static struct pci_device_id sis96x_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, sis96x_ids);
static int __devinit sis96x_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 ww = 0;
int retval;
if (sis96x_smbus_base) {
dev_err(&dev->dev, "Only one device supported.\n");
return -EBUSY;
}
pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww);
if (PCI_CLASS_SERIAL_SMBUS != ww) {
dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww);
return -ENODEV;
}
sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR);
if (!sis96x_smbus_base) {
dev_err(&dev->dev, "SiS96x SMBus base address "
"not initialized!\n");
return -EINVAL;
}
dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n",
sis96x_smbus_base);
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(sis96x_smbus_base, SMB_IOSIZE, "sis96x-smbus")) {
dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x "
"already in use!\n", sis96x_smbus_base,
sis96x_smbus_base + SMB_IOSIZE - 1);
sis96x_smbus_base = 0;
return -EINVAL;
}
/* set up the driverfs linkage to our parent device */
sis96x_adapter.dev.parent = &dev->dev;
snprintf(sis96x_adapter.name, I2C_NAME_SIZE,
"SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base);
if ((retval = i2c_add_adapter(&sis96x_adapter))) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
release_region(sis96x_smbus_base, SMB_IOSIZE);
sis96x_smbus_base = 0;
}
return retval;
}
static void __devexit sis96x_remove(struct pci_dev *dev)
{
if (sis96x_smbus_base) {
i2c_del_adapter(&sis96x_adapter);
release_region(sis96x_smbus_base, SMB_IOSIZE);
sis96x_smbus_base = 0;
}
}
static struct pci_driver sis96x_driver = {
.name = "sis96x_smbus",
.id_table = sis96x_ids,
.probe = sis96x_probe,
.remove = __devexit_p(sis96x_remove),
};
static int __init i2c_sis96x_init(void)
{
printk(KERN_INFO "i2c-sis96x version %s\n", SIS96x_VERSION);
return pci_register_driver(&sis96x_driver);
}
static void __exit i2c_sis96x_exit(void)
{
pci_unregister_driver(&sis96x_driver);
}
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
MODULE_DESCRIPTION("SiS96x SMBus driver");
MODULE_LICENSE("GPL");
/* Register initialization functions using helper macros */
module_init(i2c_sis96x_init);
module_exit(i2c_sis96x_exit);

View File

@@ -0,0 +1,143 @@
/*
i2c-stub.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.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.
*/
#define DEBUG 1
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/i2c.h>
static u8 stub_pointer;
static u8 stub_bytes[256];
static u16 stub_words[256];
/* Return -1 on error. */
static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data * data)
{
s32 ret;
switch (size) {
case I2C_SMBUS_QUICK:
dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr);
ret = 0;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE) {
stub_pointer = command;
dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, "
"wrote 0x%02x.\n",
addr, command);
} else {
data->byte = stub_bytes[stub_pointer++];
dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, "
"read 0x%02x.\n",
addr, data->byte);
}
ret = 0;
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_WRITE) {
stub_bytes[command] = data->byte;
dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
"wrote 0x%02x at 0x%02x.\n",
addr, data->byte, command);
} else {
data->byte = stub_bytes[command];
dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
"read 0x%02x at 0x%02x.\n",
addr, data->byte, command);
}
stub_pointer = command + 1;
ret = 0;
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_WRITE) {
stub_words[command] = data->word;
dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
"wrote 0x%04x at 0x%02x.\n",
addr, data->word, command);
} else {
data->word = stub_words[command];
dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
"read 0x%04x at 0x%02x.\n",
addr, data->word, command);
}
ret = 0;
break;
default:
dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
ret = -1;
break;
} /* switch (size) */
return ret;
}
static u32 stub_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.functionality = stub_func,
.smbus_xfer = stub_xfer,
};
static struct i2c_adapter stub_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "SMBus stub driver",
};
static int __init i2c_stub_init(void)
{
printk(KERN_INFO "i2c-stub loaded\n");
return i2c_add_adapter(&stub_adapter);
}
static void __exit i2c_stub_exit(void)
{
i2c_del_adapter(&stub_adapter);
}
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
MODULE_DESCRIPTION("I2C stub driver");
MODULE_LICENSE("GPL");
module_init(i2c_stub_init);
module_exit(i2c_stub_exit);

View File

@@ -0,0 +1,185 @@
/*
i2c-via.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
/* Power management registers */
#define PM_CFG_REVID 0x08 /* silicon revision code */
#define PM_CFG_IOBASE0 0x20
#define PM_CFG_IOBASE1 0x48
#define I2C_DIR (pm_io_base+0x40)
#define I2C_OUT (pm_io_base+0x42)
#define I2C_IN (pm_io_base+0x44)
#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */
#define I2C_SDA 0x04
/* io-region reservation */
#define IOSPACE 0x06
#define IOTEXT "via-i2c"
static u16 pm_io_base = 0;
/*
It does not appear from the datasheet that the GPIO pins are
open drain. So a we set a low value by setting the direction to
output and a high value by setting the direction to input and
relying on the required I2C pullup. The data value is initialized
to 0 in via_init() and never changed.
*/
static void bit_via_setscl(void *data, int state)
{
outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, I2C_DIR);
}
static void bit_via_setsda(void *data, int state)
{
outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, I2C_DIR);
}
static int bit_via_getscl(void *data)
{
return (0 != (inb(I2C_IN) & I2C_SCL));
}
static int bit_via_getsda(void *data)
{
return (0 != (inb(I2C_IN) & I2C_SDA));
}
static struct i2c_algo_bit_data bit_data = {
.setsda = bit_via_setsda,
.setscl = bit_via_setscl,
.getsda = bit_via_getsda,
.getscl = bit_via_getscl,
.udelay = 5,
.mdelay = 5,
.timeout = HZ
};
static struct i2c_adapter vt586b_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.name = "VIA i2c",
.algo_data = &bit_data,
};
static struct pci_device_id vt586b_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, vt586b_ids);
static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
u16 base;
u8 rev;
int res;
if (pm_io_base) {
dev_err(&dev->dev, "i2c-via: Will only support one host\n");
return -ENODEV;
}
pci_read_config_byte(dev, PM_CFG_REVID, &rev);
switch (rev) {
case 0x00:
base = PM_CFG_IOBASE0;
break;
case 0x01:
case 0x10:
base = PM_CFG_IOBASE1;
break;
default:
base = PM_CFG_IOBASE1;
/* later revision */
}
pci_read_config_word(dev, base, &pm_io_base);
pm_io_base &= (0xff << 8);
if (!request_region(I2C_DIR, IOSPACE, IOTEXT)) {
dev_err(&dev->dev, "IO 0x%x-0x%x already in use\n", I2C_DIR, I2C_DIR + IOSPACE);
return -ENODEV;
}
outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
/* set up the driverfs linkage to our parent device */
vt586b_adapter.dev.parent = &dev->dev;
res = i2c_bit_add_bus(&vt586b_adapter);
if ( res < 0 ) {
release_region(I2C_DIR, IOSPACE);
pm_io_base = 0;
return res;
}
return 0;
}
static void __devexit vt586b_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&vt586b_adapter);
release_region(I2C_DIR, IOSPACE);
pm_io_base = 0;
}
static struct pci_driver vt586b_driver = {
.name = "vt586b_smbus",
.id_table = vt586b_ids,
.probe = vt586b_probe,
.remove = __devexit_p(vt586b_remove),
};
static int __init i2c_vt586b_init(void)
{
return pci_register_driver(&vt586b_driver);
}
static void __exit i2c_vt586b_exit(void)
{
pci_unregister_driver(&vt586b_driver);
}
MODULE_AUTHOR("Ky<EFBFBD>sti M<>lkki <kmalkki@cc.hut.fi>");
MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge");
MODULE_LICENSE("GPL");
module_init(i2c_vt586b_init);
module_exit(i2c_vt586b_exit);

View File

@@ -0,0 +1,458 @@
/*
i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>,
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 Via devices:
82C596A/B (0x3050)
82C596B (0x3051)
82C686A/B
8231
8233
8233A (0x3147 and 0x3177)
8235
8237
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
static struct pci_dev *vt596_pdev;
#define SMBBA1 0x90
#define SMBBA2 0x80
#define SMBBA3 0xD0
/* SMBus address offsets */
static unsigned short vt596_smba;
#define SMBHSTSTS (vt596_smba + 0)
#define SMBHSLVSTS (vt596_smba + 1)
#define SMBHSTCNT (vt596_smba + 2)
#define SMBHSTCMD (vt596_smba + 3)
#define SMBHSTADD (vt596_smba + 4)
#define SMBHSTDAT0 (vt596_smba + 5)
#define SMBHSTDAT1 (vt596_smba + 6)
#define SMBBLKDAT (vt596_smba + 7)
#define SMBSLVCNT (vt596_smba + 8)
#define SMBSHDWCMD (vt596_smba + 9)
#define SMBSLVEVT (vt596_smba + 0xA)
#define SMBSLVDAT (vt596_smba + 0xC)
/* PCI Address Constants */
/* SMBus data in configuration space can be found in two places,
We try to select the better one*/
static unsigned short smb_cf_hstcfg = 0xD2;
#define SMBHSTCFG (smb_cf_hstcfg)
#define SMBSLVC (smb_cf_hstcfg + 1)
#define SMBSHDW1 (smb_cf_hstcfg + 2)
#define SMBSHDW2 (smb_cf_hstcfg + 3)
#define SMBREV (smb_cf_hstcfg + 4)
/* Other settings */
#define MAX_TIMEOUT 500
#define ENABLE_INT9 0
/* VT82C596 constants */
#define VT596_QUICK 0x00
#define VT596_BYTE 0x04
#define VT596_BYTE_DATA 0x08
#define VT596_WORD_DATA 0x0C
#define VT596_BLOCK_DATA 0x14
/* If force is set to anything different from 0, we forcibly enable the
VT596. DANGEROUS! */
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!");
/* If force_addr is set to anything different from 0, we forcibly enable
the VT596 at the given address. VERY DANGEROUS! */
static u16 force_addr;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr,
"Forcibly enable the SMBus at the given address. "
"EXTREMELY DANGEROUS!");
static struct i2c_adapter vt596_adapter;
/* Another internally used function */
static int vt596_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(&vt596_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). "
"Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_dbg(&vt596_adapter.dev, "Failed! (0x%02x)\n", temp);
return -1;
} else {
dev_dbg(&vt596_adapter.dev, "Successfull!\n");
}
}
/* start the transaction by setting bit 6 */
outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
/* We will always wait for a fraction of a second!
I don't know if VIA needs this, Intel did */
do {
msleep(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_dbg(&vt596_adapter.dev, "SMBus Timeout!\n");
}
if (temp & 0x10) {
result = -1;
dev_dbg(&vt596_adapter.dev, "Error: Failed bus transaction\n");
}
if (temp & 0x08) {
result = -1;
dev_info(&vt596_adapter.dev, "Bus collision! SMBus may be "
"locked until next hard\nreset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
result = -1;
dev_dbg(&vt596_adapter.dev, "Error: no response!\n");
}
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_warn(&vt596_adapter.dev, "Failed reset at end "
"of transaction (%02x)\n", temp);
}
}
dev_dbg(&vt596_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
return result;
}
/* Return -1 on error. */
static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
int i, len;
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_info(&vt596_adapter.dev,
"I2C_SMBUS_PROC_CALL not supported!\n");
return -1;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = VT596_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = VT596_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = VT596_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = VT596_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
if (len > I2C_SMBUS_BLOCK_MAX)
len = I2C_SMBUS_BLOCK_MAX;
outb_p(len, SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = VT596_BLOCK_DATA;
break;
}
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
if (vt596_transaction()) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
return 0;
switch (size) {
case VT596_BYTE:
/* Where is the result put? I assume here it is in
* SMBHSTDAT0 but it might just as well be in the
* SMBHSTCMD. No clue in the docs
*/
data->byte = inb_p(SMBHSTDAT0);
break;
case VT596_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case VT596_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case VT596_BLOCK_DATA:
data->block[0] = inb_p(SMBHSTDAT0);
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
data->block[0] = I2C_SMBUS_BLOCK_MAX;
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
break;
}
return 0;
}
static u32 vt596_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_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = vt596_access,
.functionality = vt596_func,
};
static struct i2c_adapter vt596_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
.name = "unset",
};
static int __devinit vt596_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
unsigned char temp;
int error = -ENODEV;
/* Determine the address of the SMBus areas */
if (force_addr) {
vt596_smba = force_addr & 0xfff0;
force = 0;
goto found;
}
if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) ||
!(vt596_smba & 0x1)) {
/* try 2nd address and config reg. for 596 */
if (id->device == PCI_DEVICE_ID_VIA_82C596_3 &&
!pci_read_config_word(pdev, SMBBA2, &vt596_smba) &&
(vt596_smba & 0x1)) {
smb_cf_hstcfg = 0x84;
} else {
/* no matches at all */
dev_err(&pdev->dev, "Cannot configure "
"SMBus I/O Base address\n");
return -ENODEV;
}
}
vt596_smba &= 0xfff0;
if (vt596_smba == 0) {
dev_err(&pdev->dev, "SMBus base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
return -ENODEV;
}
found:
if (!request_region(vt596_smba, 8, "viapro-smbus")) {
dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n",
vt596_smba);
return -ENODEV;
}
pci_read_config_byte(pdev, SMBHSTCFG, &temp);
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the VT596 first. */
if (force_addr) {
pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(pdev, id->driver_data, vt596_smba);
pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
dev_warn(&pdev->dev, "WARNING: SMBus interface set to new "
"address 0x%04x!\n", vt596_smba);
} else if ((temp & 1) == 0) {
if (force) {
/* NOTE: This assumes I/O space and other allocations
* WERE done by the Bios! Don't complain if your
* hardware does weird things after enabling this.
* :') Check for Bios updates before resorting to
* this.
*/
pci_write_config_byte(pdev, SMBHSTCFG, temp | 1);
dev_info(&pdev->dev, "Enabling SMBus device\n");
} else {
dev_err(&pdev->dev, "SMBUS: Error: Host SMBus "
"controller not enabled! - upgrade BIOS or "
"use force=1\n");
goto release_region;
}
}
if ((temp & 0x0E) == 8)
dev_dbg(&pdev->dev, "using Interrupt 9 for SMBus.\n");
else if ((temp & 0x0E) == 0)
dev_dbg(&pdev->dev, "using Interrupt SMI# for SMBus.\n");
else
dev_dbg(&pdev->dev, "Illegal Interrupt configuration "
"(or code out of date)!\n");
pci_read_config_byte(pdev, SMBREV, &temp);
dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp);
dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba);
vt596_adapter.dev.parent = &pdev->dev;
snprintf(vt596_adapter.name, I2C_NAME_SIZE,
"SMBus Via Pro adapter at %04x", vt596_smba);
vt596_pdev = pci_dev_get(pdev);
if (i2c_add_adapter(&vt596_adapter)) {
pci_dev_put(vt596_pdev);
vt596_pdev = NULL;
}
/* Always return failure here. This is to allow other drivers to bind
* to this pci device. We don't really want to have control over the
* pci device, we only wanted to read as few register values from it.
*/
return -ENODEV;
release_region:
release_region(vt596_smba, 8);
return error;
}
static struct pci_device_id vt596_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3),
.driver_data = SMBBA1 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3),
.driver_data = SMBBA1 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4),
.driver_data = SMBBA1 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_0),
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233A),
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235),
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237),
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4),
.driver_data = SMBBA1 },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, vt596_ids);
static struct pci_driver vt596_driver = {
.name = "vt596_smbus",
.id_table = vt596_ids,
.probe = vt596_probe,
};
static int __init i2c_vt596_init(void)
{
return pci_register_driver(&vt596_driver);
}
static void __exit i2c_vt596_exit(void)
{
pci_unregister_driver(&vt596_driver);
if (vt596_pdev != NULL) {
i2c_del_adapter(&vt596_adapter);
release_region(vt596_smba, 8);
pci_dev_put(vt596_pdev);
vt596_pdev = NULL;
}
}
MODULE_AUTHOR(
"Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("vt82c596 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_vt596_init);
module_exit(i2c_vt596_exit);

View File

@@ -0,0 +1,254 @@
/*
voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Ralph Metzler <rjkm@thp.uni-koeln.de>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
Simon Vogl
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.
*/
/* This interfaces to the I2C bus of the Voodoo3 to gain access to
the BT869 and possibly other I2C devices. */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
/* the only registers we use */
#define REG 0x78
#define REG2 0x70
/* bit locations in the register */
#define DDC_ENAB 0x00040000
#define DDC_SCL_OUT 0x00080000
#define DDC_SDA_OUT 0x00100000
#define DDC_SCL_IN 0x00200000
#define DDC_SDA_IN 0x00400000
#define I2C_ENAB 0x00800000
#define I2C_SCL_OUT 0x01000000
#define I2C_SDA_OUT 0x02000000
#define I2C_SCL_IN 0x04000000
#define I2C_SDA_IN 0x08000000
/* initialization states */
#define INIT2 0x2
#define INIT3 0x4
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void __iomem *ioaddr;
/* The voo GPIO registers don't have individual masks for each bit
so we always have to read before writing. */
static void bit_vooi2c_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if (val)
r |= I2C_SCL_OUT;
else
r &= ~I2C_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_vooi2c_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if (val)
r |= I2C_SDA_OUT;
else
r &= ~I2C_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins always remain outputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. */
static int bit_vooi2c_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
}
static int bit_vooi2c_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
}
static void bit_vooddc_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if (val)
r |= DDC_SCL_OUT;
else
r &= ~DDC_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_vooddc_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if (val)
r |= DDC_SDA_OUT;
else
r &= ~DDC_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static int bit_vooddc_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
}
static int bit_vooddc_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
}
static int config_v3(struct pci_dev *dev)
{
unsigned long cadr;
/* map Voodoo3 memory */
cadr = dev->resource[0].start;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = ioremap_nocache(cadr, 0x1000);
if (ioaddr) {
writel(0x8160, ioaddr + REG2);
writel(0xcffc0020, ioaddr + REG);
dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr);
return 0;
}
return -ENODEV;
}
static struct i2c_algo_bit_data voo_i2c_bit_data = {
.setsda = bit_vooi2c_setsda,
.setscl = bit_vooi2c_setscl,
.getsda = bit_vooi2c_getsda,
.getscl = bit_vooi2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter voodoo3_i2c_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_TV_ANALOG,
.name = "I2C Voodoo3/Banshee adapter",
.algo_data = &voo_i2c_bit_data,
};
static struct i2c_algo_bit_data voo_ddc_bit_data = {
.setsda = bit_vooddc_setsda,
.setscl = bit_vooddc_setscl,
.getsda = bit_vooddc_getsda,
.getscl = bit_vooddc_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter voodoo3_ddc_adapter = {
.owner = THIS_MODULE,
.class = I2C_CLASS_DDC,
.name = "DDC Voodoo3/Banshee adapter",
.algo_data = &voo_ddc_bit_data,
};
static struct pci_device_id voodoo3_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) },
{ PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) },
{ 0, }
};
MODULE_DEVICE_TABLE (pci, voodoo3_ids);
static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
retval = config_v3(dev);
if (retval)
return retval;
/* set up the sysfs linkage to our parent device */
voodoo3_i2c_adapter.dev.parent = &dev->dev;
voodoo3_ddc_adapter.dev.parent = &dev->dev;
retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
if (retval)
return retval;
retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
if (retval)
i2c_bit_del_bus(&voodoo3_i2c_adapter);
return retval;
}
static void __devexit voodoo3_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&voodoo3_i2c_adapter);
i2c_bit_del_bus(&voodoo3_ddc_adapter);
iounmap(ioaddr);
}
static struct pci_driver voodoo3_driver = {
.name = "voodoo3_smbus",
.id_table = voodoo3_ids,
.probe = voodoo3_probe,
.remove = __devexit_p(voodoo3_remove),
};
static int __init i2c_voodoo3_init(void)
{
return pci_register_driver(&voodoo3_driver);
}
static void __exit i2c_voodoo3_exit(void)
{
pci_unregister_driver(&voodoo3_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"Ralph Metzler <rjkm@thp.uni-koeln.de>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_voodoo3_init);
module_exit(i2c_voodoo3_exit);

View File

@@ -0,0 +1,557 @@
/* linux/drivers/i2c/scx200_acb.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 ACCESS.bus support
Based on i2c-keywest.c which is:
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/scx200.h>
#define NAME "scx200_acb"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
MODULE_LICENSE("GPL");
#define MAX_DEVICES 4
static int base[MAX_DEVICES] = { 0x820, 0x840 };
module_param_array(base, int, NULL, 0);
MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers");
#ifdef DEBUG
#define DBG(x...) printk(KERN_DEBUG NAME ": " x)
#else
#define DBG(x...)
#endif
/* The hardware supports interrupt driven mode too, but I haven't
implemented that. */
#define POLLED_MODE 1
#define POLL_TIMEOUT (HZ)
enum scx200_acb_state {
state_idle,
state_address,
state_command,
state_repeat_start,
state_quick,
state_read,
state_write,
};
static const char *scx200_acb_state_name[] = {
"idle",
"address",
"command",
"repeat_start",
"quick",
"read",
"write",
};
/* Physical interface */
struct scx200_acb_iface
{
struct scx200_acb_iface *next;
struct i2c_adapter adapter;
unsigned base;
struct semaphore sem;
/* State machine data */
enum scx200_acb_state state;
int result;
u8 address_byte;
u8 command;
u8 *ptr;
char needs_reset;
unsigned len;
};
/* Register Definitions */
#define ACBSDA (iface->base + 0)
#define ACBST (iface->base + 1)
#define ACBST_SDAST 0x40 /* SDA Status */
#define ACBST_BER 0x20
#define ACBST_NEGACK 0x10 /* Negative Acknowledge */
#define ACBST_STASTR 0x08 /* Stall After Start */
#define ACBST_MASTER 0x02
#define ACBCST (iface->base + 2)
#define ACBCST_BB 0x02
#define ACBCTL1 (iface->base + 3)
#define ACBCTL1_STASTRE 0x80
#define ACBCTL1_NMINTE 0x40
#define ACBCTL1_ACK 0x10
#define ACBCTL1_STOP 0x02
#define ACBCTL1_START 0x01
#define ACBADDR (iface->base + 4)
#define ACBCTL2 (iface->base + 5)
#define ACBCTL2_ENABLE 0x01
/************************************************************************/
static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
{
const char *errmsg;
DBG("state %s, status = 0x%02x\n",
scx200_acb_state_name[iface->state], status);
if (status & ACBST_BER) {
errmsg = "bus error";
goto error;
}
if (!(status & ACBST_MASTER)) {
errmsg = "not master";
goto error;
}
if (status & ACBST_NEGACK)
goto negack;
switch (iface->state) {
case state_idle:
dev_warn(&iface->adapter.dev, "interrupt in idle state\n");
break;
case state_address:
/* Do a pointer write first */
outb(iface->address_byte & ~1, ACBSDA);
iface->state = state_command;
break;
case state_command:
outb(iface->command, ACBSDA);
if (iface->address_byte & 1)
iface->state = state_repeat_start;
else
iface->state = state_write;
break;
case state_repeat_start:
outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
/* fallthrough */
case state_quick:
if (iface->address_byte & 1) {
if (iface->len == 1)
outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
else
outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
outb(iface->address_byte, ACBSDA);
iface->state = state_read;
} else {
outb(iface->address_byte, ACBSDA);
iface->state = state_write;
}
break;
case state_read:
/* Set ACK if receiving the last byte */
if (iface->len == 1)
outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
else
outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
*iface->ptr++ = inb(ACBSDA);
--iface->len;
if (iface->len == 0) {
iface->result = 0;
iface->state = state_idle;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
}
break;
case state_write:
if (iface->len == 0) {
iface->result = 0;
iface->state = state_idle;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
break;
}
outb(*iface->ptr++, ACBSDA);
--iface->len;
break;
}
return;
negack:
DBG("negative acknowledge in state %s\n",
scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -ENXIO;
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
outb(ACBST_STASTR | ACBST_NEGACK, ACBST);
return;
error:
dev_err(&iface->adapter.dev, "%s in state %s\n", errmsg,
scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -EIO;
iface->needs_reset = 1;
}
static void scx200_acb_timeout(struct scx200_acb_iface *iface)
{
dev_err(&iface->adapter.dev, "timeout in state %s\n",
scx200_acb_state_name[iface->state]);
iface->state = state_idle;
iface->result = -EIO;
iface->needs_reset = 1;
}
#ifdef POLLED_MODE
static void scx200_acb_poll(struct scx200_acb_iface *iface)
{
u8 status = 0;
unsigned long timeout;
timeout = jiffies + POLL_TIMEOUT;
while (time_before(jiffies, timeout)) {
status = inb(ACBST);
if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) {
scx200_acb_machine(iface, status);
return;
}
msleep(10);
}
scx200_acb_timeout(iface);
}
#endif /* POLLED_MODE */
static void scx200_acb_reset(struct scx200_acb_iface *iface)
{
/* Disable the ACCESS.bus device and Configure the SCL
frequency: 16 clock cycles */
outb(0x70, ACBCTL2);
/* Polling mode */
outb(0, ACBCTL1);
/* Disable slave address */
outb(0, ACBADDR);
/* Enable the ACCESS.bus device */
outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
/* Free STALL after START */
outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1);
/* Send a STOP */
outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
/* Clear BER, NEGACK and STASTR bits */
outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST);
/* Clear BB bit */
outb(inb(ACBCST) | ACBCST_BB, ACBCST);
}
static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
u16 address, unsigned short flags,
char rw, u8 command, int size,
union i2c_smbus_data *data)
{
struct scx200_acb_iface *iface = i2c_get_adapdata(adapter);
int len;
u8 *buffer;
u16 cur_word;
int rc;
switch (size) {
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
break;
case I2C_SMBUS_BYTE:
if (rw == I2C_SMBUS_READ) {
len = 1;
buffer = &data->byte;
} else {
len = 1;
buffer = &command;
}
break;
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
break;
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
break;
default:
return -EINVAL;
}
DBG("size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n",
size, address, command, len, rw == I2C_SMBUS_READ);
if (!len && rw == I2C_SMBUS_READ) {
dev_warn(&adapter->dev, "zero length read\n");
return -EINVAL;
}
if (len && !buffer) {
dev_warn(&adapter->dev, "nonzero length but no buffer\n");
return -EFAULT;
}
down(&iface->sem);
iface->address_byte = address<<1;
if (rw == I2C_SMBUS_READ)
iface->address_byte |= 1;
iface->command = command;
iface->ptr = buffer;
iface->len = len;
iface->result = -EINVAL;
iface->needs_reset = 0;
outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)
iface->state = state_quick;
else
iface->state = state_address;
#ifdef POLLED_MODE
while (iface->state != state_idle)
scx200_acb_poll(iface);
#else /* POLLED_MODE */
#error Interrupt driven mode not implemented
#endif /* POLLED_MODE */
if (iface->needs_reset)
scx200_acb_reset(iface);
rc = iface->result;
up(&iface->sem);
if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ)
data->word = le16_to_cpu(cur_word);
#ifdef DEBUG
DBG(": transfer done, result: %d", rc);
if (buffer) {
int i;
printk(" data:");
for (i = 0; i < len; ++i)
printk(" %02x", buffer[i]);
}
printk("\n");
#endif
return rc;
}
static u32 scx200_acb_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_BLOCK_DATA;
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm scx200_acb_algorithm = {
.name = "NatSemi SCx200 ACCESS.bus",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = scx200_acb_smbus_xfer,
.functionality = scx200_acb_func,
};
static struct scx200_acb_iface *scx200_acb_list;
static int scx200_acb_probe(struct scx200_acb_iface *iface)
{
u8 val;
/* Disable the ACCESS.bus device and Configure the SCL
frequency: 16 clock cycles */
outb(0x70, ACBCTL2);
if (inb(ACBCTL2) != 0x70) {
DBG("ACBCTL2 readback failed\n");
return -ENXIO;
}
outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
val = inb(ACBCTL1);
if (val) {
DBG("disabled, but ACBCTL1=0x%02x\n", val);
return -ENXIO;
}
outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
val = inb(ACBCTL1);
if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) {
DBG("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", val);
return -ENXIO;
}
return 0;
}
static int __init scx200_acb_create(int base, int index)
{
struct scx200_acb_iface *iface;
struct i2c_adapter *adapter;
int rc = 0;
char description[64];
iface = kmalloc(sizeof(*iface), GFP_KERNEL);
if (!iface) {
printk(KERN_ERR NAME ": can't allocate memory\n");
rc = -ENOMEM;
goto errout;
}
memset(iface, 0, sizeof(*iface));
adapter = &iface->adapter;
i2c_set_adapdata(adapter, iface);
snprintf(adapter->name, I2C_NAME_SIZE, "SCx200 ACB%d", index);
adapter->owner = THIS_MODULE;
adapter->id = I2C_ALGO_SMBUS;
adapter->algo = &scx200_acb_algorithm;
adapter->class = I2C_CLASS_HWMON;
init_MUTEX(&iface->sem);
snprintf(description, sizeof(description), "NatSemi SCx200 ACCESS.bus [%s]", adapter->name);
if (request_region(base, 8, description) == 0) {
dev_err(&adapter->dev, "can't allocate io 0x%x-0x%x\n",
base, base + 8-1);
rc = -EBUSY;
goto errout;
}
iface->base = base;
rc = scx200_acb_probe(iface);
if (rc) {
dev_warn(&adapter->dev, "probe failed\n");
goto errout;
}
scx200_acb_reset(iface);
if (i2c_add_adapter(adapter) < 0) {
dev_err(&adapter->dev, "failed to register\n");
rc = -ENODEV;
goto errout;
}
lock_kernel();
iface->next = scx200_acb_list;
scx200_acb_list = iface;
unlock_kernel();
return 0;
errout:
if (iface) {
if (iface->base)
release_region(iface->base, 8);
kfree(iface);
}
return rc;
}
static struct pci_device_id scx200[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
{ },
};
static int __init scx200_acb_init(void)
{
int i;
int rc;
pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
/* Verify that this really is a SCx200 processor */
if (pci_dev_present(scx200) == 0)
return -ENODEV;
rc = -ENXIO;
for (i = 0; i < MAX_DEVICES; ++i) {
if (base[i] > 0)
rc = scx200_acb_create(base[i], i);
}
if (scx200_acb_list)
return 0;
return rc;
}
static void __exit scx200_acb_cleanup(void)
{
struct scx200_acb_iface *iface;
lock_kernel();
while ((iface = scx200_acb_list) != NULL) {
scx200_acb_list = iface->next;
unlock_kernel();
i2c_del_adapter(&iface->adapter);
release_region(iface->base, 8);
kfree(iface);
lock_kernel();
}
unlock_kernel();
}
module_init(scx200_acb_init);
module_exit(scx200_acb_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
c-basic-offset: 8
End:
*/

View File

@@ -0,0 +1,131 @@
/* linux/drivers/i2c/scx200_i2c.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 I2C bus on GPIO pins
Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl
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/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define NAME "scx200_i2c"
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver");
MODULE_LICENSE("GPL");
static int scl = CONFIG_SCx200_I2C_SCL;
static int sda = CONFIG_SCx200_I2C_SDA;
module_param(scl, int, 0);
MODULE_PARM_DESC(scl, "GPIO line for SCL");
module_param(sda, int, 0);
MODULE_PARM_DESC(sda, "GPIO line for SDA");
static void scx200_i2c_setscl(void *data, int state)
{
scx200_gpio_set(scl, state);
}
static void scx200_i2c_setsda(void *data, int state)
{
scx200_gpio_set(sda, state);
}
static int scx200_i2c_getscl(void *data)
{
return scx200_gpio_get(scl);
}
static int scx200_i2c_getsda(void *data)
{
return scx200_gpio_get(sda);
}
/* ------------------------------------------------------------------------
* Encapsulate the above functions in the correct operations structure.
* This is only done when more than one hardware adapter is supported.
*/
static struct i2c_algo_bit_data scx200_i2c_data = {
NULL,
scx200_i2c_setsda,
scx200_i2c_setscl,
scx200_i2c_getsda,
scx200_i2c_getscl,
10, 10, 100, /* waits, timeout */
};
static struct i2c_adapter scx200_i2c_ops = {
.owner = THIS_MODULE,
.algo_data = &scx200_i2c_data,
.name = "NatSemi SCx200 I2C",
};
static int scx200_i2c_init(void)
{
pr_debug(NAME ": NatSemi SCx200 I2C Driver\n");
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
pr_debug(NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n", scl, sda);
if (scl == -1 || sda == -1 || scl == sda) {
printk(KERN_ERR NAME ": scl and sda must be specified\n");
return -EINVAL;
}
/* Configure GPIOs as open collector outputs */
scx200_gpio_configure(scl, ~2, 5);
scx200_gpio_configure(sda, ~2, 5);
if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) {
printk(KERN_ERR NAME ": adapter %s registration failed\n",
scx200_i2c_ops.name);
return -ENODEV;
}
return 0;
}
static void scx200_i2c_cleanup(void)
{
i2c_bit_del_bus(&scx200_i2c_ops);
}
module_init(scx200_i2c_init);
module_exit(scx200_i2c_cleanup);
/*
Local variables:
compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
c-basic-offset: 8
End:
*/

443
drivers/i2c/chips/Kconfig Normal file
View File

@@ -0,0 +1,443 @@
#
# I2C Sensor device configuration
#
menu "Hardware Sensors Chip support"
depends on I2C
config I2C_SENSOR
tristate
default n
config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10,
and the XEON processor built-in sensor.
This driver can also be built as a module. If so, the module
will be called adm1021.
config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1025
and Philips NE1619 sensor chips.
This driver can also be built as a module. If so, the module
will be called adm1025.
config SENSORS_ADM1026
tristate "Analog Devices ADM1026 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1026
This driver can also be built as a module. If so, the module
will be called adm1026.
config SENSORS_ADM1031
tristate "Analog Devices ADM1031 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Analog Devices ADM1031
and ADM1030 sensor chips.
This driver can also be built as a module. If so, the module
will be called adm1031.
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for the ASB100 Bach sensor
chip found on some Asus mainboards.
This driver can also be built as a module. If so, the module
will be called asb100.
config SENSORS_DS1621
tristate "Dallas Semiconductor DS1621 and DS1625"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Dallas Semiconductor
DS1621 and DS1625 sensor chips.
This driver can also be built as a module. If so, the module
will be called ds1621.
config SENSORS_FSCHER
tristate "FSC Hermes"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Fujitsu Siemens
Computers Hermes sensor chips.
This driver can also be built as a module. If so, the module
will be called fscher.
config SENSORS_FSCPOS
tristate "FSC Poseidon"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Fujitsu Siemens
Computers Poseidon sensor chips.
This driver can also be built as a module. If so, the module
will be called fscpos.
config SENSORS_GL518SM
tristate "Genesys Logic GL518SM"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Genesys Logic GL518SM
sensor chips.
This driver can also be built as a module. If so, the module
will be called gl518sm.
config SENSORS_GL520SM
tristate "Genesys Logic GL520SM"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Genesys Logic GL520SM
sensor chips.
This driver can also be built as a module. If so, the module
will be called gl520sm.
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for ITE IT87xx sensor chips
and clones: SiS960.
This driver can also be built as a module. If so, the module
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
tristate "National Semiconductor LM75 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM75
sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in
9-bit precision mode), and TelCom (now Microchip) TCN75.
The DS75 and DS1775 in 10- to 12-bit precision modes will require
a force module parameter. The driver will not handle the extra
precision anyhow.
This driver can also be built as a module. If so, the module
will be called lm75.
config SENSORS_LM77
tristate "National Semiconductor LM77"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM77
sensor chips.
This driver can also be built as a module. If so, the module
will be called lm77.
config SENSORS_LM78
tristate "National Semiconductor LM78 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM78,
LM78-J and LM79. This can also be built as a module which can be
inserted and removed while the kernel is running.
This driver can also be built as a module. If so, the module
will be called lm78.
config SENSORS_LM80
tristate "National Semiconductor LM80"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor
LM80 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm80.
config SENSORS_LM83
tristate "National Semiconductor LM83"
depends on I2C
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor
LM83 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm83.
config SENSORS_LM85
tristate "National Semiconductor LM85 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM85
sensor chips and clones: ADT7463 and ADM1027.
This driver can also be built as a module. If so, the module
will be called lm85.
config SENSORS_LM87
tristate "National Semiconductor LM87"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM87
sensor chips.
This driver can also be built as a module. If so, the module
will be called lm87.
config SENSORS_LM90
tristate "National Semiconductor LM90 and compatibles"
depends on I2C
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM90,
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and
MAX6658 sensor chips.
The Analog Devices ADT7461 sensor chip is also supported, but only
if found in ADM1032 compatibility mode.
This driver can also be built as a module. If so, the module
will be called lm90.
config SENSORS_LM92
tristate "National Semiconductor LM92 and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for National Semiconductor LM92
and Maxim MAX6635 sensor chips.
This driver can also be built as a module. If so, the module
will be called lm92.
config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for MAX1619 sensor chip.
This driver can also be built as a module. If so, the module
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_SMSC47B397
tristate "SMSC LPC47B397-NC"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get support for the SMSC LPC47B397-NC
sensor chip.
This driver can also be built as a module. If so, the module
will be called smsc47b397.
config SENSORS_SIS5595
tristate "Silicon Integrated Systems Corp. SiS5595"
depends on I2C && PCI && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get support for the integrated sensors in
SiS5595 South Bridges.
This driver can also be built as a module. If so, the module
will be called sis5595.
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get support for the integrated fan
monitoring and control capabilities of the SMSC LPC47B27x,
LPC47M10x, LPC47M13x and LPC47M14x chips.
This driver can also be built as a module. If so, the module
will be called smsc47m1.
config SENSORS_VIA686A
tristate "VIA686A"
depends on I2C && PCI && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get support for the integrated sensors in
Via 686A/B South Bridges.
This driver can also be built as a module. If so, the module
will be called via686a.
config SENSORS_W83781D
tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for the Winbond W8378x series
of sensor chips: the W83781D, W83782D, W83783S and W83627HF,
and the similar Asus AS99127F.
This driver can also be built as a module. If so, the module
will be called w83781d.
config SENSORS_W83L785TS
tristate "Winbond W83L785TS-S"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for the Winbond W83L785TS-S
sensor chip, which is used on the Asus A7N8X, among other
motherboards.
This driver can also be built as a module. If so, the module
will be called w83l785ts.
config SENSORS_W83627HF
tristate "Winbond W83627HF, W83627THF, W83637HF, W83697HF"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
select I2C_ISA
help
If you say yes here you get support for the Winbond W836X7 series
of sensor chips: the W83627HF, W83627THF, W83637HF, and the W83697HF
This driver can also be built as a module. If so, the module
will be called w83627hf.
endmenu
menu "Other I2C Chip support"
depends on I2C
config SENSORS_DS1337
tristate "Dallas Semiconductor DS1337 Real Time Clock"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Dallas Semiconductor
DS1337 real-time clock chips.
This driver can also be built as a module. If so, the module
will be called ds1337.
config SENSORS_EEPROM
tristate "EEPROM reader"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get read-only access to the EEPROM data
available on modern memory DIMMs and Sony Vaio laptops. Such
EEPROMs could theoretically be available on other devices as well.
This driver can also be built as a module. If so, the module
will be called eeprom.
config SENSORS_PCF8574
tristate "Philips PCF8574 and PCF8574A"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Philips PCF8574 and
PCF8574A chips.
This driver can also be built as a module. If so, the module
will be called pcf8574.
config SENSORS_PCF8591
tristate "Philips PCF8591"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for Philips PCF8591 chips.
This driver can also be built as a module. If so, the module
will be called pcf8591.
config SENSORS_RTC8564
tristate "Epson 8564 RTC chip"
depends on I2C && EXPERIMENTAL
select I2C_SENSOR
help
If you say yes here you get support for the Epson 8564 RTC chip.
This driver can also be built as a module. If so, the module
will be called i2c-rtc8564.
config ISP1301_OMAP
tristate "Philips ISP1301 with OMAP OTG"
depends on I2C && ARCH_OMAP_OTG
help
If you say yes here you get support for the Philips ISP1301
USB-On-The-Go transceiver working with the OMAP OTG controller.
The ISP1301 is used in products including H2 and H3 development
boards for Texas Instruments OMAP processors.
This driver can also be built as a module. If so, the module
will be called isp1301_omap.
config SENSORS_M41T00
tristate "ST M41T00 RTC chip"
depends on I2C && PPC32
help
If you say yes here you get support for the ST M41T00 RTC chip.
This driver can also be built as a module. If so, the module
will be called m41t00.
endmenu

View File

@@ -0,0 +1,48 @@
#
# Makefile for the kernel hardware sensors chip drivers.
#
# asb100, then w83781d go first, as they can override other drivers' addresses.
obj-$(CONFIG_SENSORS_ASB100) += asb100.o
obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_DS1337) += ds1337.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM77) += lm77.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o
obj-$(CONFIG_SENSORS_LM80) += lm80.o
obj-$(CONFIG_SENSORS_LM83) += lm83.o
obj-$(CONFIG_SENSORS_LM85) += lm85.o
obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif

411
drivers/i2c/chips/adm1021.c Normal file
View File

@@ -0,0 +1,411 @@
/*
adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066);
/* adm1021 constants specified below */
/* The adm1021 registers */
/* Read-only */
#define ADM1021_REG_TEMP 0x00
#define ADM1021_REG_REMOTE_TEMP 0x01
#define ADM1021_REG_STATUS 0x02
#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */
#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */
/* These use different addresses for reading/writing */
#define ADM1021_REG_CONFIG_R 0x03
#define ADM1021_REG_CONFIG_W 0x09
#define ADM1021_REG_CONV_RATE_R 0x04
#define ADM1021_REG_CONV_RATE_W 0x0A
/* These are for the ADM1023's additional precision on the remote temp sensor */
#define ADM1021_REG_REM_TEMP_PREC 0x010
#define ADM1021_REG_REM_OFFSET 0x011
#define ADM1021_REG_REM_OFFSET_PREC 0x012
#define ADM1021_REG_REM_TOS_PREC 0x013
#define ADM1021_REG_REM_THYST_PREC 0x014
/* limits */
#define ADM1021_REG_TOS_R 0x05
#define ADM1021_REG_TOS_W 0x0B
#define ADM1021_REG_REMOTE_TOS_R 0x07
#define ADM1021_REG_REMOTE_TOS_W 0x0D
#define ADM1021_REG_THYST_R 0x06
#define ADM1021_REG_THYST_W 0x0C
#define ADM1021_REG_REMOTE_THYST_R 0x08
#define ADM1021_REG_REMOTE_THYST_W 0x0E
/* write-only */
#define ADM1021_REG_ONESHOT 0x0F
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. Note that you should be a bit careful with which arguments
these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */
/* Conversions note: 1021 uses normal integer signed-byte format*/
#define TEMP_FROM_REG(val) (val > 127 ? (val-256)*1000 : val*1000)
#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255))
/* Initial values */
/* Note: Even though I left the low and high limits named os and hyst,
they don't quite work like a thermostat the way the LM75 does. I.e.,
a lower temp than THYST actually triggers an alarm instead of
clearing it. Weird, ey? --Phil */
/* Each client has this additional data */
struct adm1021_data {
struct i2c_client client;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 temp_max; /* Register values */
u8 temp_hyst;
u8 temp_input;
u8 remote_temp_max;
u8 remote_temp_hyst;
u8 remote_temp_input;
u8 alarms;
/* special values for ADM1021 only */
u8 die_code;
/* Special values for ADM1023 only */
u8 remote_temp_prec;
u8 remote_temp_os_prec;
u8 remote_temp_hyst_prec;
u8 remote_temp_offset;
u8 remote_temp_offset_prec;
};
static int adm1021_attach_adapter(struct i2c_adapter *adapter);
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1021_init_client(struct i2c_client *client);
static int adm1021_detach_client(struct i2c_client *client);
static int adm1021_read_value(struct i2c_client *client, u8 reg);
static int adm1021_write_value(struct i2c_client *client, u8 reg,
u16 value);
static struct adm1021_data *adm1021_update_device(struct device *dev);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
static int read_only = 0;
/* This is the driver that will be inserted */
static struct i2c_driver adm1021_driver = {
.owner = THIS_MODULE,
.name = "adm1021",
.id = I2C_DRIVERID_ADM1021,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1021_attach_adapter,
.detach_client = adm1021_detach_client,
};
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct adm1021_data *data = adm1021_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
show(remote_temp_max);
show(remote_temp_hyst);
show(remote_temp_input);
#define show2(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct adm1021_data *data = adm1021_update_device(dev); \
return sprintf(buf, "%d\n", data->value); \
}
show2(alarms);
show2(die_code);
#define set(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 adm1021_data *data = i2c_get_clientdata(client); \
int temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(temp); \
adm1021_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set(temp_max, ADM1021_REG_TOS_W);
set(temp_hyst, ADM1021_REG_THYST_W);
set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W);
set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote_temp_input, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(die_code, S_IRUGO, show_die_code, NULL);
static int adm1021_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1021_detect);
}
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct adm1021_data *data;
int err = 0;
const char *type_name = "";
/* Make sure we aren't probing the ISA bus!! This is just a safety check
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n");
return 0;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto error0;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access adm1021_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error0;
}
memset(data, 0, sizeof(struct adm1021_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1021_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00
|| (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00
|| (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) {
err = -ENODEV;
goto error1;
}
}
/* Determine the chip type. */
if (kind <= 0) {
i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
if (i == 0x41)
if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
kind = adm1023;
else
kind = adm1021;
else if (i == 0x49)
kind = thmc10;
else if (i == 0x23)
kind = gl523sm;
else if ((i == 0x4d) &&
(adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01))
kind = max1617a;
else if (i == 0x54)
kind = mc1066;
/* LM84 Mfr ID in a different place, and it has more unused bits */
else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00
&& (kind == 0 /* skip extra detection */
|| ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00
&& (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00)))
kind = lm84;
else
kind = max1617;
}
if (kind == max1617) {
type_name = "max1617";
} else if (kind == max1617a) {
type_name = "max1617a";
} else if (kind == adm1021) {
type_name = "adm1021";
} else if (kind == adm1023) {
type_name = "adm1023";
} else if (kind == thmc10) {
type_name = "thmc10";
} else if (kind == lm84) {
type_name = "lm84";
} else if (kind == gl523sm) {
type_name = "gl523sm";
} else if (kind == mc1066) {
type_name = "mc1066";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, type_name, I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto error1;
/* Initialize the ADM1021 chip */
if (kind != lm84)
adm1021_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_alarms);
if (data->type == adm1021)
device_create_file(&new_client->dev, &dev_attr_die_code);
return 0;
error1:
kfree(data);
error0:
return err;
}
static void adm1021_init_client(struct i2c_client *client)
{
/* Enable ADC and disable suspend mode */
adm1021_write_value(client, ADM1021_REG_CONFIG_W,
adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF);
/* Set Conversion rate to 1/sec (this can be tinkered with) */
adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04);
}
static int adm1021_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;
}
/* All registers are byte-sized */
static int adm1021_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (!read_only)
return i2c_smbus_write_byte_data(client, reg, value);
return 0;
}
static struct adm1021_data *adm1021_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1021_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting adm1021 update\n");
data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP);
data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R);
data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R);
data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c;
if (data->type == adm1021)
data->die_code = adm1021_read_value(client, ADM1021_REG_DIE_CODE);
if (data->type == adm1023) {
data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1021_init(void)
{
return i2c_add_driver(&adm1021_driver);
}
static void __exit sensors_adm1021_exit(void)
{
i2c_del_driver(&adm1021_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("adm1021 driver");
MODULE_LICENSE("GPL");
module_param(read_only, bool, 0);
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
module_init(sensors_adm1021_init)
module_exit(sensors_adm1021_exit)

574
drivers/i2c/chips/adm1025.c Normal file
View File

@@ -0,0 +1,574 @@
/*
* adm1025.c
*
* Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com>
* Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
*
* The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
* voltages (including its own power source) and up to two temperatures
* (its own plus up to one external one). Voltages are scaled internally
* (which is not the common way) with ratios such that the nominal value
* of each voltage correspond to a register value of 192 (which means a
* resolution of about 0.5% of the nominal value). Temperature values are
* reported with a 1 deg resolution and a 3 deg accuracy. Complete
* datasheet can be obtained from Analog's website at:
* http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html
*
* This driver also supports the ADM1025A, which differs from the ADM1025
* only in that it has "open-drain VID inputs while the ADM1025 has
* on-chip 100k pull-ups on the VID inputs". It doesn't make any
* difference for us.
*
* This driver also supports the NE1619, a sensor chip made by Philips.
* That chip is similar to the ADM1025A, with a few differences. The only
* difference that matters to us is that the NE1619 has only two possible
* addresses while the ADM1025A has a third one. Complete datasheet can be
* obtained from Philips's website at:
* http://www.semiconductors.philips.com/pip/NE1619DS.html
*
* Since the ADM1025 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* 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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/*
* Addresses to scan
* ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e.
* NE1619 has two possible addresses: 0x2c and 0x2d.
*/
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_2(adm1025, ne1619);
/*
* The ADM1025 registers
*/
#define ADM1025_REG_MAN_ID 0x3E
#define ADM1025_REG_CHIP_ID 0x3F
#define ADM1025_REG_CONFIG 0x40
#define ADM1025_REG_STATUS1 0x41
#define ADM1025_REG_STATUS2 0x42
#define ADM1025_REG_IN(nr) (0x20 + (nr))
#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2)
#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2)
#define ADM1025_REG_TEMP(nr) (0x26 + (nr))
#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2)
#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2)
#define ADM1025_REG_VID 0x47
#define ADM1025_REG_VID4 0x49
/*
* Conversions and various macros
* The ADM1025 uses signed 8-bit values for temperatures.
*/
static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \
((val) * 192 + (scale)/2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000)
#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
(val) >= 126500 ? 127 : \
(((val) < 0 ? (val)-500 : (val)+500) / 1000))
/*
* Functions declaration
*/
static int adm1025_attach_adapter(struct i2c_adapter *adapter);
static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1025_init_client(struct i2c_client *client);
static int adm1025_detach_client(struct i2c_client *client);
static struct adm1025_data *adm1025_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver adm1025_driver = {
.owner = THIS_MODULE,
.name = "adm1025",
.id = I2C_DRIVERID_ADM1025,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1025_attach_adapter,
.detach_client = adm1025_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct adm1025_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
u8 in[6]; /* register value */
u8 in_max[6]; /* register value */
u8 in_min[6]; /* register value */
s8 temp[2]; /* register value */
s8 temp_min[2]; /* register value */
s8 temp_max[2]; /* register value */
u16 alarms; /* register values, combined */
u8 vid; /* register values, combined */
u8 vrm;
};
/*
* Sysfs stuff
*/
#define show_in(offset) \
static ssize_t show_in##offset(struct device *dev, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
in_scale[offset])); \
} \
static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
in_scale[offset])); \
} \
static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
in_scale[offset])); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);
show_in(0);
show_in(1);
show_in(2);
show_in(3);
show_in(4);
show_in(5);
#define show_temp(offset) \
static ssize_t show_temp##offset(struct device *dev, char *buf) \
{ \
struct adm1025_data *data = adm1025_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 adm1025_data *data = adm1025_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 adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
}\
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL);
show_temp(1);
show_temp(2);
#define set_in(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 adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->in_min[offset] = IN_TO_REG(val, in_scale[offset]); \
i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \
data->in_min[offset]); \
up(&data->update_lock); \
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 adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->in_max[offset] = IN_TO_REG(val, in_scale[offset]); \
i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \
data->in_max[offset]); \
up(&data->update_lock); \
return count; \
} \
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);
set_in(0);
set_in(1);
set_in(2);
set_in(3);
set_in(4);
set_in(5);
#define set_temp(offset) \
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 adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->temp_min[offset-1] = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \
data->temp_min[offset-1]); \
up(&data->update_lock); \
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 adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->temp_max[offset-1] = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \
data->temp_max[offset-1]); \
up(&data->update_lock); \
return count; \
} \
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);
set_temp(1);
set_temp(2);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static ssize_t show_vid(struct device *dev, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL);
static ssize_t show_vrm(struct device *dev, char *buf)
{
struct adm1025_data *data = adm1025_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 adm1025_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);
/*
* Real code
*/
static int adm1025_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1025_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct adm1025_data *data;
int err = 0;
const char *name = "";
u8 config;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm1025_data));
/* The common I2C client data is placed right before the
ADM1025-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1025_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
config = i2c_smbus_read_byte_data(new_client, ADM1025_REG_CONFIG);
if (kind < 0) { /* detection */
if ((config & 0x80) != 0x00
|| (i2c_smbus_read_byte_data(new_client,
ADM1025_REG_STATUS1) & 0xC0) != 0x00
|| (i2c_smbus_read_byte_data(new_client,
ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
dev_dbg(&adapter->dev,
"ADM1025 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
u8 man_id, chip_id;
man_id = i2c_smbus_read_byte_data(new_client,
ADM1025_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
ADM1025_REG_CHIP_ID);
if (man_id == 0x41) { /* Analog Devices */
if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
kind = adm1025;
}
} else
if (man_id == 0xA1) { /* Philips */
if (address != 0x2E
&& (chip_id & 0xF0) == 0x20) { /* NE1619 */
kind = ne1619;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == adm1025) {
name = "adm1025";
} else if (kind == ne1619) {
name = "ne1619";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, 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 ADM1025 chip */
adm1025_init_client(new_client);
/* Register sysfs hooks */
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_in5_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_in5_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_in5_max);
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_alarms);
device_create_file(&new_client->dev, &dev_attr_in1_ref);
device_create_file(&new_client->dev, &dev_attr_vrm);
/* Pin 11 is either in4 (+12V) or VID4 */
if (!(config & 0x20)) {
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
}
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void adm1025_init_client(struct i2c_client *client)
{
u8 reg;
struct adm1025_data *data = i2c_get_clientdata(client);
int i;
data->vrm = i2c_which_vrm();
/*
* Set high limits
* Usually we avoid setting limits on driver init, but it happens
* that the ADM1025 comes with stupid default limits (all registers
* set to 0). In case the chip has not gone through any limit
* setting yet, we better set the high limits to the max so that
* no alarm triggers.
*/
for (i=0; i<6; i++) {
reg = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MAX(i));
if (reg == 0)
i2c_smbus_write_byte_data(client,
ADM1025_REG_IN_MAX(i),
0xFF);
}
for (i=0; i<2; i++) {
reg = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_HIGH(i));
if (reg == 0)
i2c_smbus_write_byte_data(client,
ADM1025_REG_TEMP_HIGH(i),
0x7F);
}
/*
* Start the conversions
*/
reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
if (!(reg & 0x01))
i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG,
(reg&0x7E)|0x01);
}
static int adm1025_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 adm1025_data *adm1025_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1025_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
int i;
dev_dbg(&client->dev, "Updating data.\n");
for (i=0; i<6; i++) {
data->in[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN(i));
data->in_min[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MIN(i));
data->in_max[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MAX(i));
}
for (i=0; i<2; i++) {
data->temp[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP(i));
data->temp_min[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_LOW(i));
data->temp_max[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_HIGH(i));
}
data->alarms = i2c_smbus_read_byte_data(client,
ADM1025_REG_STATUS1)
| (i2c_smbus_read_byte_data(client,
ADM1025_REG_STATUS2) << 8);
data->vid = (i2c_smbus_read_byte_data(client,
ADM1025_REG_VID) & 0x0f)
| ((i2c_smbus_read_byte_data(client,
ADM1025_REG_VID4) & 0x01) << 4);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1025_init(void)
{
return i2c_add_driver(&adm1025_driver);
}
static void __exit sensors_adm1025_exit(void)
{
i2c_del_driver(&adm1025_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("ADM1025 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm1025_init);
module_exit(sensors_adm1025_exit);

1754
drivers/i2c/chips/adm1026.c Normal file

File diff suppressed because it is too large Load Diff

977
drivers/i2c/chips/adm1031.c Normal file
View File

@@ -0,0 +1,977 @@
/*
adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Based on lm75.c and lm85.c
Supports adm1030 / adm1031
Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
Reworked by 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Following macros takes channel parameter starting from 0 to 2 */
#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
#define ADM1031_REG_PWM (0x22)
#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4*(nr))
#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4*(nr))
#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4*(nr))
#define ADM1031_REG_TEMP(nr) (0xa + (nr))
#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
#define ADM1031_REG_STATUS(nr) (0x2 + (nr))
#define ADM1031_REG_CONF1 0x0
#define ADM1031_REG_CONF2 0x1
#define ADM1031_REG_EXT_TEMP 0x6
#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */
#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */
#define ADM1031_CONF2_PWM1_ENABLE 0x01
#define ADM1031_CONF2_PWM2_ENABLE 0x02
#define ADM1031_CONF2_TACH1_ENABLE 0x04
#define ADM1031_CONF2_TACH2_ENABLE 0x08
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(adm1030, adm1031);
typedef u8 auto_chan_table_t[8][2];
/* Each client has this additional data */
struct adm1031_data {
struct i2c_client client;
struct semaphore update_lock;
int chip_type;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* The chan_select_table contains the possible configurations for
* auto fan control.
*/
auto_chan_table_t *chan_select_table;
u16 alarm;
u8 conf1;
u8 conf2;
u8 fan[2];
u8 fan_div[2];
u8 fan_min[2];
u8 pwm[2];
u8 old_pwm[2];
s8 temp[3];
u8 ext_temp[3];
u8 auto_temp[3];
u8 auto_temp_min[3];
u8 auto_temp_off[3];
u8 auto_temp_max[3];
s8 temp_min[3];
s8 temp_max[3];
s8 temp_crit[3];
};
static int adm1031_attach_adapter(struct i2c_adapter *adapter);
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1031_init_client(struct i2c_client *client);
static int adm1031_detach_client(struct i2c_client *client);
static struct adm1031_data *adm1031_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver adm1031_driver = {
.owner = THIS_MODULE,
.name = "adm1031",
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1031_attach_adapter,
.detach_client = adm1031_detach_client,
};
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int
adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \
((val + 500) / 1000)))
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)
#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
static int FAN_TO_REG(int reg, int div)
{
int tmp;
tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div);
return tmp > 255 ? 255 : tmp;
}
#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6))
#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4)
#define PWM_FROM_REG(val) ((val) << 4)
#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7)
#define FAN_CHAN_TO_REG(val, reg) \
(((reg) & 0x1F) | (((val) << 5) & 0xe0))
#define AUTO_TEMP_MIN_TO_REG(val, reg) \
((((val)/500) & 0xf8)|((reg) & 0x7))
#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7)))
#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2))
#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2)
#define AUTO_TEMP_OFF_FROM_REG(reg) \
(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
#define AUTO_TEMP_MAX_FROM_REG(reg) \
(AUTO_TEMP_RANGE_FROM_REG(reg) + \
AUTO_TEMP_MIN_FROM_REG(reg))
static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
{
int ret;
int range = val - AUTO_TEMP_MIN_FROM_REG(reg);
range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm);
ret = ((reg & 0xf8) |
(range < 10000 ? 0 :
range < 20000 ? 1 :
range < 40000 ? 2 : range < 80000 ? 3 : 4));
return ret;
}
/* FAN auto control */
#define GET_FAN_AUTO_BITFIELD(data, idx) \
(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]
/* The tables below contains the possible values for the auto fan
* control bitfields. the index in the table is the register value.
* MSb is the auto fan control enable bit, so the four first entries
* in the table disables auto fan control when both bitfields are zero.
*/
static auto_chan_table_t auto_channel_select_table_adm1031 = {
{0, 0}, {0, 0}, {0, 0}, {0, 0},
{2 /*0b010 */ , 4 /*0b100 */ },
{2 /*0b010 */ , 2 /*0b010 */ },
{4 /*0b100 */ , 4 /*0b100 */ },
{7 /*0b111 */ , 7 /*0b111 */ },
};
static auto_chan_table_t auto_channel_select_table_adm1030 = {
{0, 0}, {0, 0}, {0, 0}, {0, 0},
{2 /*0b10 */ , 0},
{0xff /*invalid */ , 0},
{0xff /*invalid */ , 0},
{3 /*0b11 */ , 0},
};
/* That function checks if a bitfield is valid and returns the other bitfield
* nearest match if no exact match where found.
*/
static int
get_fan_auto_nearest(struct adm1031_data *data,
int chan, u8 val, u8 reg, u8 * new_reg)
{
int i;
int first_match = -1, exact_match = -1;
u8 other_reg_val =
(*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];
if (val == 0) {
*new_reg = 0;
return 0;
}
for (i = 0; i < 8; i++) {
if ((val == (*data->chan_select_table)[i][chan]) &&
((*data->chan_select_table)[i][chan ? 0 : 1] ==
other_reg_val)) {
/* We found an exact match */
exact_match = i;
break;
} else if (val == (*data->chan_select_table)[i][chan] &&
first_match == -1) {
/* Save the first match in case of an exact match has not been
* found
*/
first_match = i;
}
}
if (exact_match >= 0) {
*new_reg = exact_match;
} else if (first_match >= 0) {
*new_reg = first_match;
} else {
return -EINVAL;
}
return 0;
}
static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));
}
static ssize_t
set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
u8 reg;
int ret;
u8 old_fan_mode;
old_fan_mode = data->conf1;
down(&data->update_lock);
if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, &reg))) {
up(&data->update_lock);
return ret;
}
if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^
(old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
if (data->conf1 & ADM1031_CONF1_AUTO_MODE){
/* Switch to Auto Fan Mode
* Save PWM registers
* Set PWM registers to 33% Both */
data->old_pwm[0] = data->pwm[0];
data->old_pwm[1] = data->pwm[1];
adm1031_write_value(client, ADM1031_REG_PWM, 0x55);
} else {
/* Switch to Manual Mode */
data->pwm[0] = data->old_pwm[0];
data->pwm[1] = data->old_pwm[1];
/* Restore PWM registers */
adm1031_write_value(client, ADM1031_REG_PWM,
data->pwm[0] | (data->pwm[1] << 4));
}
}
data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);
up(&data->update_lock);
return count;
}
#define fan_auto_channel_offset(offset) \
static ssize_t show_fan_auto_channel_##offset (struct device *dev, char *buf) \
{ \
return show_fan_auto_channel(dev, buf, offset - 1); \
} \
static ssize_t set_fan_auto_channel_##offset (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_fan_auto_channel(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR, \
show_fan_auto_channel_##offset, \
set_fan_auto_channel_##offset)
fan_auto_channel_offset(1);
fan_auto_channel_offset(2);
/* Auto Temps */
static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));
}
static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));
}
static ssize_t
set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
data->auto_temp[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
}
static ssize_t
set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]);
adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
data->temp_max[nr]);
up(&data->update_lock);
return count;
}
#define auto_temp_reg(offset) \
static ssize_t show_auto_temp_##offset##_off (struct device *dev, char *buf) \
{ \
return show_auto_temp_off(dev, buf, offset - 1); \
} \
static ssize_t show_auto_temp_##offset##_min (struct device *dev, char *buf) \
{ \
return show_auto_temp_min(dev, buf, offset - 1); \
} \
static ssize_t show_auto_temp_##offset##_max (struct device *dev, char *buf) \
{ \
return show_auto_temp_max(dev, buf, offset - 1); \
} \
static ssize_t set_auto_temp_##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_auto_temp_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_auto_temp_##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_auto_temp_max(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO, \
show_auto_temp_##offset##_off, NULL); \
static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR, \
show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\
static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR, \
show_auto_temp_##offset##_max, set_auto_temp_##offset##_max)
auto_temp_reg(1);
auto_temp_reg(2);
auto_temp_reg(3);
/* pwm */
static ssize_t show_pwm(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
}
static ssize_t
set_pwm(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
int reg;
down(&data->update_lock);
if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) &&
(((val>>4) & 0xf) != 5)) {
/* In automatic mode, the only PWM accepted is 33% */
up(&data->update_lock);
return -EINVAL;
}
data->pwm[nr] = PWM_TO_REG(val);
reg = adm1031_read_value(client, ADM1031_REG_PWM);
adm1031_write_value(client, ADM1031_REG_PWM,
nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf)
: (data->pwm[nr] & 0xf) | (reg & 0xf0));
up(&data->update_lock);
return count;
}
#define pwm_reg(offset) \
static ssize_t show_pwm_##offset (struct device *dev, char *buf) \
{ \
return show_pwm(dev, buf, offset - 1); \
} \
static ssize_t set_pwm_##offset (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm_##offset, set_pwm_##offset)
pwm_reg(1);
pwm_reg(2);
/* Fans */
/*
* That function checks the cases where the fan reading is not
* relevent. It is used to provide 0 as fan reading when the fan is
* not supposed to run
*/
static int trust_fan_readings(struct adm1031_data *data, int chan)
{
int res = 0;
if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
switch (data->conf1 & 0x60) {
case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */
res = data->temp[chan+1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
break;
case 0x20: /* remote temp1 controls both fans */
res =
data->temp[1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]);
break;
case 0x40: /* remote temp2 controls both fans */
res =
data->temp[2] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]);
break;
case 0x60: /* max controls both fans */
res =
data->temp[0] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0])
|| data->temp[1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1])
|| (data->chip_type == adm1031
&& data->temp[2] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]));
break;
}
} else {
res = data->pwm[chan] > 0;
}
return res;
}
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
int value;
value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
return sprintf(buf, "%d\n", value);
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
FAN_FROM_REG(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
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 adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
if (val) {
data->fan_min[nr] =
FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
} else {
data->fan_min[nr] = 0xff;
}
adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_fan_div(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
u8 tmp;
int old_div;
int new_min;
tmp = val == 8 ? 0xc0 :
val == 4 ? 0x80 :
val == 2 ? 0x40 :
val == 1 ? 0x00 :
0xff;
if (tmp == 0xff)
return -EINVAL;
down(&data->update_lock);
old_div = FAN_DIV_FROM_REG(data->fan_div[nr]);
data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]);
new_min = data->fan_min[nr] * old_div /
FAN_DIV_FROM_REG(data->fan_div[nr]);
data->fan_min[nr] = new_min > 0xff ? 0xff : new_min;
data->fan[nr] = data->fan[nr] * old_div /
FAN_DIV_FROM_REG(data->fan_div[nr]);
adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr),
data->fan_div[nr]);
adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr),
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 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 ssize_t set_fan_##offset##_div (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \
NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div); \
static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \
show_pwm_##offset, set_pwm_##offset)
fan_offset(1);
fan_offset(2);
/* Temps */
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
int ext;
ext = nr == 0 ?
((data->ext_temp[nr] >> 6) & 0x3) * 2 :
(((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
}
static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
}
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
}
static ssize_t show_temp_crit(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
}
static ssize_t
set_temp_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_min[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr),
data->temp_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_temp_max(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_max[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr),
data->temp_max[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_temp_crit(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_crit[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr),
data->temp_crit[nr]);
up(&data->update_lock);
return count;
}
#define temp_reg(offset) \
static ssize_t show_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \
{ \
return show_temp_min(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \
{ \
return show_temp_max(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_crit (struct device *dev, char *buf) \
{ \
return show_temp_crit(dev, buf, offset - 1); \
} \
static ssize_t set_temp_##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_max(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_crit (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_crit(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \
NULL); \
static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp_##offset##_max, set_temp_##offset##_max); \
static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \
show_temp_##offset##_crit, set_temp_##offset##_crit)
temp_reg(1);
temp_reg(2);
temp_reg(3);
/* Alarms */
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", data->alarm);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static int adm1031_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1031_detect);
}
/* This function is called by i2c_detect */
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct adm1031_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm1031_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1031_driver;
new_client->flags = 0;
if (kind < 0) {
int id, co;
id = i2c_smbus_read_byte_data(new_client, 0x3d);
co = i2c_smbus_read_byte_data(new_client, 0x3e);
if (!((id == 0x31 || id == 0x30) && co == 0x41))
goto exit_free;
kind = (id == 0x30) ? adm1030 : adm1031;
}
if (kind <= 0)
kind = adm1031;
/* Given the detected chip type, set the chip name and the
* auto fan control helper table. */
if (kind == adm1030) {
name = "adm1030";
data->chan_select_table = &auto_channel_select_table_adm1030;
} else if (kind == adm1031) {
name = "adm1031";
data->chan_select_table = &auto_channel_select_table_adm1031;
}
data->chip_type = kind;
strlcpy(new_client->name, name, 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 ADM1031 chip */
adm1031_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
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_auto_fan1_channel);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
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_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
device_create_file(&new_client->dev, &dev_attr_alarms);
if (kind == adm1031) {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev,
&dev_attr_auto_fan2_channel);
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_auto_temp3_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
}
return 0;
exit_free:
kfree(new_client);
exit:
return err;
}
static int adm1031_detach_client(struct i2c_client *client)
{
int ret;
if ((ret = i2c_detach_client(client)) != 0) {
return ret;
}
kfree(client);
return 0;
}
static void adm1031_init_client(struct i2c_client *client)
{
unsigned int read_val;
unsigned int mask;
struct adm1031_data *data = i2c_get_clientdata(client);
mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
if (data->chip_type == adm1031) {
mask |= (ADM1031_CONF2_PWM2_ENABLE |
ADM1031_CONF2_TACH2_ENABLE);
}
/* Initialize the ADM1031 chip (enables fan speed reading ) */
read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
if ((read_val | mask) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
}
read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
ADM1031_CONF1_MONITOR_ENABLE);
}
}
static struct adm1031_data *adm1031_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int chan;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting adm1031 update\n");
for (chan = 0;
chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) {
u8 oldh, newh;
oldh =
adm1031_read_value(client, ADM1031_REG_TEMP(chan));
data->ext_temp[chan] =
adm1031_read_value(client, ADM1031_REG_EXT_TEMP);
newh =
adm1031_read_value(client, ADM1031_REG_TEMP(chan));
if (newh != oldh) {
data->ext_temp[chan] =
adm1031_read_value(client,
ADM1031_REG_EXT_TEMP);
#ifdef DEBUG
oldh =
adm1031_read_value(client,
ADM1031_REG_TEMP(chan));
/* oldh is actually newer */
if (newh != oldh)
dev_warn(&client->dev,
"Remote temperature may be "
"wrong.\n");
#endif
}
data->temp[chan] = newh;
data->temp_min[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_MIN(chan));
data->temp_max[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_MAX(chan));
data->temp_crit[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_CRIT(chan));
data->auto_temp[chan] =
adm1031_read_value(client,
ADM1031_REG_AUTO_TEMP(chan));
}
data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
| (adm1031_read_value(client, ADM1031_REG_STATUS(1))
<< 8);
if (data->chip_type == adm1030) {
data->alarm &= 0xc0ff;
}
for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) {
data->fan_div[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan));
data->fan_min[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan));
data->fan[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan));
data->pwm[chan] =
0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >>
(4*chan));
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1031_init(void)
{
return i2c_add_driver(&adm1031_driver);
}
static void __exit sensors_adm1031_exit(void)
{
i2c_del_driver(&adm1031_driver);
}
MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm1031_init);
module_exit(sensors_adm1031_exit);

1066
drivers/i2c/chips/asb100.c Normal file

File diff suppressed because it is too large Load Diff

402
drivers/i2c/chips/ds1337.c Normal file
View File

@@ -0,0 +1,402 @@
/*
* linux/drivers/i2c/chips/ds1337.c
*
* Copyright (C) 2005 James Chapman <jchapman@katalix.com>
*
* based on linux/drivers/acron/char/pcf8583.c
* Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Driver for Dallas Semiconductor DS1337 real time clock chip
*/
#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/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/bcd.h>
#include <linux/list.h>
/* Device registers */
#define DS1337_REG_HOUR 2
#define DS1337_REG_DAY 3
#define DS1337_REG_DATE 4
#define DS1337_REG_MONTH 5
#define DS1337_REG_CONTROL 14
#define DS1337_REG_STATUS 15
/* FIXME - how do we export these interface constants? */
#define DS1337_GET_DATE 0
#define DS1337_SET_DATE 1
/*
* Functions declaration
*/
static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
SENSORS_INSMOD_1(ds1337);
static int ds1337_attach_adapter(struct i2c_adapter *adapter);
static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind);
static void ds1337_init_client(struct i2c_client *client);
static int ds1337_detach_client(struct i2c_client *client);
static int ds1337_command(struct i2c_client *client, unsigned int cmd,
void *arg);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver ds1337_driver = {
.owner = THIS_MODULE,
.name = "ds1337",
.flags = I2C_DF_NOTIFY,
.attach_adapter = ds1337_attach_adapter,
.detach_client = ds1337_detach_client,
.command = ds1337_command,
};
/*
* Client data (each client gets its own)
*/
struct ds1337_data {
struct i2c_client client;
struct list_head list;
int id;
};
/*
* Internal variables
*/
static int ds1337_id;
static LIST_HEAD(ds1337_clients);
static inline int ds1337_read(struct i2c_client *client, u8 reg, u8 *value)
{
s32 tmp = i2c_smbus_read_byte_data(client, reg);
if (tmp < 0)
return -EIO;
*value = tmp;
return 0;
}
/*
* Chip access functions
*/
static int ds1337_get_datetime(struct i2c_client *client, struct rtc_time *dt)
{
struct ds1337_data *data = i2c_get_clientdata(client);
int result;
u8 buf[7];
u8 val;
struct i2c_msg msg[2];
u8 offs = 0;
if (!dt) {
dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n",
__FUNCTION__);
return -EINVAL;
}
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &offs;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = sizeof(buf);
msg[1].buf = &buf[0];
result = client->adapter->algo->master_xfer(client->adapter,
&msg[0], 2);
dev_dbg(&client->adapter->dev,
"%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n",
__FUNCTION__, result, buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6]);
if (result >= 0) {
dt->tm_sec = BCD_TO_BIN(buf[0]);
dt->tm_min = BCD_TO_BIN(buf[1]);
val = buf[2] & 0x3f;
dt->tm_hour = BCD_TO_BIN(val);
dt->tm_wday = BCD_TO_BIN(buf[3]) - 1;
dt->tm_mday = BCD_TO_BIN(buf[4]);
val = buf[5] & 0x7f;
dt->tm_mon = BCD_TO_BIN(val);
dt->tm_year = 1900 + BCD_TO_BIN(buf[6]);
if (buf[5] & 0x80)
dt->tm_year += 100;
dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__, dt->tm_sec, dt->tm_min,
dt->tm_hour, dt->tm_mday,
dt->tm_mon, dt->tm_year, dt->tm_wday);
} else {
dev_err(&client->adapter->dev, "ds1337[%d]: error reading "
"data! %d\n", data->id, result);
result = -EIO;
}
return result;
}
static int ds1337_set_datetime(struct i2c_client *client, struct rtc_time *dt)
{
struct ds1337_data *data = i2c_get_clientdata(client);
int result;
u8 buf[8];
u8 val;
struct i2c_msg msg[1];
if (!dt) {
dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n",
__FUNCTION__);
return -EINVAL;
}
dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__,
dt->tm_sec, dt->tm_min, dt->tm_hour,
dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday);
buf[0] = 0; /* reg offset */
buf[1] = BIN_TO_BCD(dt->tm_sec);
buf[2] = BIN_TO_BCD(dt->tm_min);
buf[3] = BIN_TO_BCD(dt->tm_hour) | (1 << 6);
buf[4] = BIN_TO_BCD(dt->tm_wday) + 1;
buf[5] = BIN_TO_BCD(dt->tm_mday);
buf[6] = BIN_TO_BCD(dt->tm_mon);
if (dt->tm_year >= 2000) {
val = dt->tm_year - 2000;
buf[6] |= (1 << 7);
} else {
val = dt->tm_year - 1900;
}
buf[7] = BIN_TO_BCD(val);
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = sizeof(buf);
msg[0].buf = &buf[0];
result = client->adapter->algo->master_xfer(client->adapter,
&msg[0], 1);
if (result < 0) {
dev_err(&client->adapter->dev, "ds1337[%d]: error "
"writing data! %d\n", data->id, result);
result = -EIO;
} else {
result = 0;
}
return result;
}
static int ds1337_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
dev_dbg(&client->adapter->dev, "%s: cmd=%d\n", __FUNCTION__, cmd);
switch (cmd) {
case DS1337_GET_DATE:
return ds1337_get_datetime(client, arg);
case DS1337_SET_DATE:
return ds1337_set_datetime(client, arg);
default:
return -EINVAL;
}
}
/*
* Public API for access to specific device. Useful for low-level
* RTC access from kernel code.
*/
int ds1337_do_command(int id, int cmd, void *arg)
{
struct list_head *walk;
struct list_head *tmp;
struct ds1337_data *data;
list_for_each_safe(walk, tmp, &ds1337_clients) {
data = list_entry(walk, struct ds1337_data, list);
if (data->id == id)
return ds1337_command(&data->client, cmd, arg);
}
return -ENODEV;
}
static int ds1337_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, ds1337_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct ds1337_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_I2C))
goto exit;
if (!(data = kmalloc(sizeof(struct ds1337_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct ds1337_data));
INIT_LIST_HEAD(&data->list);
/* The common I2C client data is placed right before the
* DS1337-specific data.
*/
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ds1337_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*
* For detection, we read registers that are most likely to cause
* detection failure, i.e. those that have more bits with fixed
* or reserved values.
*/
/* Default to an DS1337 if forced */
if (kind == 0)
kind = ds1337;
if (kind < 0) { /* detection and identification */
u8 data;
/* Check that status register bits 6-2 are zero */
if ((ds1337_read(new_client, DS1337_REG_STATUS, &data) < 0) ||
(data & 0x7c))
goto exit_free;
/* Check for a valid day register value */
if ((ds1337_read(new_client, DS1337_REG_DAY, &data) < 0) ||
(data == 0) || (data & 0xf8))
goto exit_free;
/* Check for a valid date register value */
if ((ds1337_read(new_client, DS1337_REG_DATE, &data) < 0) ||
(data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) ||
(data >= 0x32))
goto exit_free;
/* Check for a valid month register value */
if ((ds1337_read(new_client, DS1337_REG_MONTH, &data) < 0) ||
(data == 0) || (data & 0x60) || ((data & 0x0f) > 9) ||
((data >= 0x13) && (data <= 0x19)))
goto exit_free;
/* Check that control register bits 6-5 are zero */
if ((ds1337_read(new_client, DS1337_REG_CONTROL, &data) < 0) ||
(data & 0x60))
goto exit_free;
kind = ds1337;
}
if (kind == ds1337)
name = "ds1337";
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the DS1337 chip */
ds1337_init_client(new_client);
/* Add client to local list */
data->id = ds1337_id++;
list_add(&data->list, &ds1337_clients);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void ds1337_init_client(struct i2c_client *client)
{
s32 val;
/* Ensure that device is set in 24-hour mode */
val = i2c_smbus_read_byte_data(client, DS1337_REG_HOUR);
if ((val >= 0) && (val & (1 << 6)) == 0)
i2c_smbus_write_byte_data(client, DS1337_REG_HOUR,
val | (1 << 6));
}
static int ds1337_detach_client(struct i2c_client *client)
{
int err;
struct ds1337_data *data = i2c_get_clientdata(client);
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return err;
}
list_del(&data->list);
kfree(data);
return 0;
}
static int __init ds1337_init(void)
{
return i2c_add_driver(&ds1337_driver);
}
static void __exit ds1337_exit(void)
{
i2c_del_driver(&ds1337_driver);
}
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("DS1337 RTC driver");
MODULE_LICENSE("GPL");
module_init(ds1337_init);
module_exit(ds1337_exit);

341
drivers/i2c/chips/ds1621.c Normal file
View File

@@ -0,0 +1,341 @@
/*
ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Christian W. Zuckschwerdt <zany@triq.net> 2000-11-23
based on lm75.c by Frodo Looijaard <frodol@dds.nl>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include "lm75.h"
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(ds1621);
static int polarity = -1;
module_param(polarity, int, 0);
MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
/* Many DS1621 constants specified below */
/* Config register used for detection */
/* 7 6 5 4 3 2 1 0 */
/* |Done|THF |TLF |NVB | X | X |POL |1SHOT| */
#define DS1621_REG_CONFIG_NVB 0x10
#define DS1621_REG_CONFIG_POLARITY 0x02
#define DS1621_REG_CONFIG_1SHOT 0x01
#define DS1621_REG_CONFIG_DONE 0x80
/* The DS1621 registers */
#define DS1621_REG_TEMP 0xAA /* word, RO */
#define DS1621_REG_TEMP_MIN 0xA1 /* word, RW */
#define DS1621_REG_TEMP_MAX 0xA2 /* word, RW */
#define DS1621_REG_CONF 0xAC /* byte, RW */
#define DS1621_COM_START 0xEE /* no data */
#define DS1621_COM_STOP 0x22 /* no data */
/* The DS1621 configuration register */
#define DS1621_ALARM_TEMP_HIGH 0x40
#define DS1621_ALARM_TEMP_LOW 0x20
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. Note that you should be a bit careful with which arguments
these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */
#define ALARMS_FROM_REG(val) ((val) & \
(DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
/* Each client has this additional data */
struct ds1621_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp, temp_min, temp_max; /* Register values, word */
u8 conf; /* Register encoding, combined */
};
static int ds1621_attach_adapter(struct i2c_adapter *adapter);
static int ds1621_detect(struct i2c_adapter *adapter, int address,
int kind);
static void ds1621_init_client(struct i2c_client *client);
static int ds1621_detach_client(struct i2c_client *client);
static struct ds1621_data *ds1621_update_client(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver ds1621_driver = {
.owner = THIS_MODULE,
.name = "ds1621",
.id = I2C_DRIVERID_DS1621,
.flags = I2C_DF_NOTIFY,
.attach_adapter = ds1621_attach_adapter,
.detach_client = ds1621_detach_client,
};
/* All registers are word-sized, except for the configuration register.
DS1621 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int ds1621_read_value(struct i2c_client *client, u8 reg)
{
if (reg == DS1621_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
/* All registers are word-sized, except for the configuration register.
DS1621 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == DS1621_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void ds1621_init_client(struct i2c_client *client)
{
int reg = ds1621_read_value(client, DS1621_REG_CONF);
/* switch to continous conversion mode */
reg &= ~ DS1621_REG_CONFIG_1SHOT;
/* setup output polarity */
if (polarity == 0)
reg &= ~DS1621_REG_CONFIG_POLARITY;
else if (polarity == 1)
reg |= DS1621_REG_CONFIG_POLARITY;
ds1621_write_value(client, DS1621_REG_CONF, reg);
/* start conversion */
i2c_smbus_write_byte(client, DS1621_COM_START);
}
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct ds1621_data *data = ds1621_update_client(dev); \
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \
}
show(temp);
show(temp_min);
show(temp_max);
#define set_temp(suffix, value, reg) \
static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct ds1621_data *data = ds1621_update_client(dev); \
u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
\
down(&data->update_lock); \
data->value = val; \
ds1621_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(min, temp_min, DS1621_REG_TEMP_MIN);
set_temp(max, temp_max, DS1621_REG_TEMP_MAX);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct ds1621_data *data = ds1621_update_client(dev);
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf));
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static int ds1621_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, ds1621_detect);
}
/* This function is called by i2c_detect */
int ds1621_detect(struct i2c_adapter *adapter, int address,
int kind)
{
int conf, temp;
struct i2c_client *new_client;
struct ds1621_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access ds1621_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct ds1621_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ds1621_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. It is lousy. */
if (kind < 0) {
/* The NVB bit should be low if no EEPROM write has been
requested during the latest 10ms, which is highly
improbable in our case. */
conf = ds1621_read_value(new_client, DS1621_REG_CONF);
if (conf & DS1621_REG_CONFIG_NVB)
goto exit_free;
/* The 7 lowest bits of a temperature should always be 0. */
temp = ds1621_read_value(new_client, DS1621_REG_TEMP);
if (temp & 0x007f)
goto exit_free;
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MIN);
if (temp & 0x007f)
goto exit_free;
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MAX);
if (temp & 0x007f)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = ds1621;
/* Fill in remaining client fields and put it into the global list */
strlcpy(new_client->name, "ds1621", 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 DS1621 chip */
ds1621_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(data);
exit:
return err;
}
static int ds1621_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 ds1621_data *ds1621_update_client(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds1621_data *data = i2c_get_clientdata(client);
u8 new_conf;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting ds1621 update\n");
data->conf = ds1621_read_value(client, DS1621_REG_CONF);
data->temp = ds1621_read_value(client, DS1621_REG_TEMP);
data->temp_min = ds1621_read_value(client,
DS1621_REG_TEMP_MIN);
data->temp_max = ds1621_read_value(client,
DS1621_REG_TEMP_MAX);
/* reset alarms if neccessary */
new_conf = data->conf;
if (data->temp < data->temp_min)
new_conf &= ~DS1621_ALARM_TEMP_LOW;
if (data->temp > data->temp_max)
new_conf &= ~DS1621_ALARM_TEMP_HIGH;
if (data->conf != new_conf)
ds1621_write_value(client, DS1621_REG_CONF,
new_conf);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init ds1621_init(void)
{
return i2c_add_driver(&ds1621_driver);
}
static void __exit ds1621_exit(void)
{
i2c_del_driver(&ds1621_driver);
}
MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
MODULE_DESCRIPTION("DS1621 driver");
MODULE_LICENSE("GPL");
module_init(ds1621_init);
module_exit(ds1621_exit);

264
drivers/i2c/chips/eeprom.c Normal file
View File

@@ -0,0 +1,264 @@
/*
eeprom.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
Copyright (C) 2003 IBM Corp.
2004-01-16 Jean Delvare <khali@linux-fr.org>
Divide the eeprom in 32-byte (arbitrary) slices. This significantly
speeds sensors up, as well as various scripts using the eeprom
module.
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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x57, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(eeprom);
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
/* possible types of eeprom devices */
enum eeprom_nature {
UNKNOWN,
VAIO,
};
/* Each client has this additional data */
struct eeprom_data {
struct i2c_client client;
struct semaphore update_lock;
u8 valid; /* bitfield, bit!=0 if slice is valid */
unsigned long last_updated[8]; /* In jiffies, 8 slices */
u8 data[EEPROM_SIZE]; /* Register values */
enum eeprom_nature nature;
};
static int eeprom_attach_adapter(struct i2c_adapter *adapter);
static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind);
static int eeprom_detach_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver eeprom_driver = {
.owner = THIS_MODULE,
.name = "eeprom",
.id = I2C_DRIVERID_EEPROM,
.flags = I2C_DF_NOTIFY,
.attach_adapter = eeprom_attach_adapter,
.detach_client = eeprom_detach_client,
};
static void eeprom_update_client(struct i2c_client *client, u8 slice)
{
struct eeprom_data *data = i2c_get_clientdata(client);
int i, j;
down(&data->update_lock);
if (!(data->valid & (1 << slice)) ||
time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
for (i = slice << 5; i < (slice + 1) << 5; i += I2C_SMBUS_I2C_BLOCK_MAX)
if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_I2C_BLOCK_MAX)
goto exit;
} else {
if (i2c_smbus_write_byte(client, slice << 5)) {
dev_dbg(&client->dev, "eeprom read start has failed!\n");
goto exit;
}
for (i = slice << 5; i < (slice + 1) << 5; i++) {
j = i2c_smbus_read_byte(client);
if (j < 0)
goto exit;
data->data[i] = (u8) j;
}
}
data->last_updated[slice] = jiffies;
data->valid |= (1 << slice);
}
exit:
up(&data->update_lock);
}
static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
struct eeprom_data *data = i2c_get_clientdata(client);
u8 slice;
if (off > EEPROM_SIZE)
return 0;
if (off + count > EEPROM_SIZE)
count = EEPROM_SIZE - off;
/* Only refresh slices which contain requested bytes */
for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
eeprom_update_client(client, slice);
/* Hide Vaio security settings to regular users (16 first bytes) */
if (data->nature == VAIO && off < 16 && !capable(CAP_SYS_ADMIN)) {
size_t in_row1 = 16 - off;
in_row1 = min(in_row1, count);
memset(buf, 0, in_row1);
if (count - in_row1 > 0)
memcpy(buf + in_row1, &data->data[16], count - in_row1);
} else {
memcpy(buf, &data->data[off], count);
}
return count;
}
static struct bin_attribute eeprom_attr = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
.owner = THIS_MODULE,
},
.size = EEPROM_SIZE,
.read = eeprom_read,
};
static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, eeprom_detect);
}
/* This function is called by i2c_detect */
int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct eeprom_data *data;
int err = 0;
/* There are three ways we can read the EEPROM data:
(1) I2C block reads (faster, but unsupported by most adapters)
(2) Consecutive byte reads (100% overhead)
(3) Regular byte data reads (200% overhead)
The third method is not implemented by this driver because all
known adapters support at least the second. */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
| I2C_FUNC_SMBUS_BYTE))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access eeprom_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct eeprom_data));
new_client = &data->client;
memset(data->data, 0xff, EEPROM_SIZE);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &eeprom_driver;
new_client->flags = 0;
/* prevent 24RF08 corruption */
i2c_smbus_write_quick(new_client, 0);
/* Fill in the remaining client fields */
strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
data->nature = UNKNOWN;
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
/* Detect the Vaio nature of EEPROMs.
We use the "PCG-" prefix as the signature. */
if (address == 0x57) {
if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P'
&& i2c_smbus_read_byte(new_client) == 'C'
&& i2c_smbus_read_byte(new_client) == 'G'
&& i2c_smbus_read_byte(new_client) == '-') {
dev_info(&new_client->dev, "Vaio EEPROM detected, "
"enabling password protection\n");
data->nature = VAIO;
}
}
/* create the sysfs eeprom file */
sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
return 0;
exit_kfree:
kfree(data);
exit:
return err;
}
static int eeprom_detach_client(struct i2c_client *client)
{
int err;
err = i2c_detach_client(client);
if (err) {
dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
return err;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static int __init eeprom_init(void)
{
return i2c_add_driver(&eeprom_driver);
}
static void __exit eeprom_exit(void)
{
i2c_del_driver(&eeprom_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com> and "
"Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("I2C EEPROM driver");
MODULE_LICENSE("GPL");
module_init(eeprom_init);
module_exit(eeprom_exit);

692
drivers/i2c/chips/fscher.c Normal file
View File

@@ -0,0 +1,692 @@
/*
* fscher.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* fujitsu siemens hermes chip,
* module based on fscpos.c
* Copyright (C) 2000 Hermann Jung <hej@odn.de>
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
* and Philip Edelbrock <phil@netroedge.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(fscher);
/*
* The FSCHER registers
*/
/* chip identification */
#define FSCHER_REG_IDENT_0 0x00
#define FSCHER_REG_IDENT_1 0x01
#define FSCHER_REG_IDENT_2 0x02
#define FSCHER_REG_REVISION 0x03
/* global control and status */
#define FSCHER_REG_EVENT_STATE 0x04
#define FSCHER_REG_CONTROL 0x05
/* watchdog */
#define FSCHER_REG_WDOG_PRESET 0x28
#define FSCHER_REG_WDOG_STATE 0x23
#define FSCHER_REG_WDOG_CONTROL 0x21
/* fan 0 */
#define FSCHER_REG_FAN0_MIN 0x55
#define FSCHER_REG_FAN0_ACT 0x0e
#define FSCHER_REG_FAN0_STATE 0x0d
#define FSCHER_REG_FAN0_RIPPLE 0x0f
/* fan 1 */
#define FSCHER_REG_FAN1_MIN 0x65
#define FSCHER_REG_FAN1_ACT 0x6b
#define FSCHER_REG_FAN1_STATE 0x62
#define FSCHER_REG_FAN1_RIPPLE 0x6f
/* fan 2 */
#define FSCHER_REG_FAN2_MIN 0xb5
#define FSCHER_REG_FAN2_ACT 0xbb
#define FSCHER_REG_FAN2_STATE 0xb2
#define FSCHER_REG_FAN2_RIPPLE 0xbf
/* voltage supervision */
#define FSCHER_REG_VOLT_12 0x45
#define FSCHER_REG_VOLT_5 0x42
#define FSCHER_REG_VOLT_BATT 0x48
/* temperature 0 */
#define FSCHER_REG_TEMP0_ACT 0x64
#define FSCHER_REG_TEMP0_STATE 0x71
/* temperature 1 */
#define FSCHER_REG_TEMP1_ACT 0x32
#define FSCHER_REG_TEMP1_STATE 0x81
/* temperature 2 */
#define FSCHER_REG_TEMP2_ACT 0x35
#define FSCHER_REG_TEMP2_STATE 0x91
/*
* Functions declaration
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter);
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind);
static int fscher_detach_client(struct i2c_client *client);
static struct fscher_data *fscher_update_device(struct device *dev);
static void fscher_init_client(struct i2c_client *client);
static int fscher_read_value(struct i2c_client *client, u8 reg);
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver fscher_driver = {
.owner = THIS_MODULE,
.name = "fscher",
.id = I2C_DRIVERID_FSCHER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = fscher_attach_adapter,
.detach_client = fscher_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct fscher_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 revision; /* revision of chip */
u8 global_event; /* global event status */
u8 global_control; /* global control register */
u8 watchdog[3]; /* watchdog */
u8 volt[3]; /* 12, 5, battery voltage */
u8 temp_act[3]; /* temperature */
u8 temp_status[3]; /* status of sensor */
u8 fan_act[3]; /* fans revolutions per second */
u8 fan_status[3]; /* fan status */
u8 fan_min[3]; /* fan min value for rps */
u8 fan_ripple[3]; /* divider for rps */
};
/*
* Sysfs stuff
*/
#define sysfs_r(kind, sub, offset, reg) \
static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \
static ssize_t show_##kind##offset##sub (struct device *, char *); \
static ssize_t show_##kind##offset##sub (struct device *dev, char *buf) \
{ \
struct fscher_data *data = fscher_update_device(dev); \
return show_##kind##sub(data, buf, (offset)); \
}
#define sysfs_w(kind, sub, offset, reg) \
static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \
static ssize_t set_##kind##offset##sub (struct device *, const char *, size_t); \
static ssize_t set_##kind##offset##sub (struct device *dev, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscher_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, (offset), reg); \
}
#define sysfs_rw_n(kind, sub, offset, reg) \
sysfs_r(kind, sub, offset, reg) \
sysfs_w(kind, sub, offset, reg) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub);
#define sysfs_rw(kind, sub, reg) \
sysfs_r(kind, sub, 0, reg) \
sysfs_w(kind, sub, 0, reg) \
static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub);
#define sysfs_ro_n(kind, sub, offset, reg) \
sysfs_r(kind, sub, offset, reg) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL);
#define sysfs_ro(kind, sub, reg) \
sysfs_r(kind, sub, 0, reg) \
static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL);
#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \
sysfs_rw_n(pwm, , offset, reg_min) \
sysfs_rw_n(fan, _status, offset, reg_status) \
sysfs_rw_n(fan, _div , offset, reg_ripple) \
sysfs_ro_n(fan, _input , offset, reg_act)
#define sysfs_temp(offset, reg_status, reg_act) \
sysfs_rw_n(temp, _status, offset, reg_status) \
sysfs_ro_n(temp, _input , offset, reg_act)
#define sysfs_in(offset, reg_act) \
sysfs_ro_n(in, _input, offset, reg_act)
#define sysfs_revision(reg_revision) \
sysfs_ro(revision, , reg_revision)
#define sysfs_alarms(reg_events) \
sysfs_ro(alarms, , reg_events)
#define sysfs_control(reg_control) \
sysfs_rw(control, , reg_control)
#define sysfs_watchdog(reg_control, reg_status, reg_preset) \
sysfs_rw(watchdog, _control, reg_control) \
sysfs_rw(watchdog, _status , reg_status) \
sysfs_rw(watchdog, _preset , reg_preset)
sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN,
FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT)
sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN,
FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT)
sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN,
FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT)
sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT)
sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT)
sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT)
sysfs_in(0, FSCHER_REG_VOLT_12)
sysfs_in(1, FSCHER_REG_VOLT_5)
sysfs_in(2, FSCHER_REG_VOLT_BATT)
sysfs_revision(FSCHER_REG_REVISION)
sysfs_alarms(FSCHER_REG_EVENTS)
sysfs_control(FSCHER_REG_CONTROL)
sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET)
#define device_create_file_fan(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_fan##offset##_status); \
device_create_file(&client->dev, &dev_attr_pwm##offset); \
device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
} while (0)
#define device_create_file_temp(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_temp##offset##_status); \
device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
} while (0)
#define device_create_file_in(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_in##offset##_input); \
} while (0)
#define device_create_file_revision(client) \
do { \
device_create_file(&client->dev, &dev_attr_revision); \
} while (0)
#define device_create_file_alarms(client) \
do { \
device_create_file(&client->dev, &dev_attr_alarms); \
} while (0)
#define device_create_file_control(client) \
do { \
device_create_file(&client->dev, &dev_attr_control); \
} while (0)
#define device_create_file_watchdog(client) \
do { \
device_create_file(&client->dev, &dev_attr_watchdog_status); \
device_create_file(&client->dev, &dev_attr_watchdog_control); \
device_create_file(&client->dev, &dev_attr_watchdog_preset); \
} while (0)
/*
* Real code
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, fscher_detect);
}
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct fscher_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
* client structure, even though we cannot fill it completely yet.
* But it allows us to access i2c_smbus_read_byte_data. */
if (!(data = kmalloc(sizeof(struct fscher_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct fscher_data));
/* The common I2C client data is placed right before the
* Hermes-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &fscher_driver;
new_client->flags = 0;
/* Do the remaining detection unless force or force_fscher parameter */
if (kind < 0) {
if ((i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_0) != 0x48) /* 'H' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_1) != 0x45) /* 'E' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */
goto exit_free;
}
/* Fill in the remaining client fields and put it into the
* global list */
strlcpy(new_client->name, "fscher", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
fscher_init_client(new_client);
/* Register sysfs hooks */
device_create_file_revision(new_client);
device_create_file_alarms(new_client);
device_create_file_control(new_client);
device_create_file_watchdog(new_client);
device_create_file_in(new_client, 0);
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
device_create_file_temp(new_client, 3);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int fscher_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static int fscher_read_value(struct i2c_client *client, u8 reg)
{
dev_dbg(&client->dev, "read reg 0x%02x\n", reg);
return i2c_smbus_read_byte_data(client, reg);
}
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value)
{
dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n",
reg, value);
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new FSC Hermes. */
static void fscher_init_client(struct i2c_client *client)
{
struct fscher_data *data = i2c_get_clientdata(client);
/* Read revision from chip */
data->revision = fscher_read_value(client, FSCHER_REG_REVISION);
}
static struct fscher_data *fscher_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fscher_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
dev_dbg(&client->dev, "Starting fscher update\n");
data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT);
data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT);
data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT);
data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE);
data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE);
data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE);
data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12);
data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5);
data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT);
data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT);
data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT);
data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT);
data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE);
data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE);
data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE);
data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN);
data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN);
data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN);
data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE);
data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE);
data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE);
data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET);
data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE);
data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL);
data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04;
down(&data->update_lock);
data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04);
}
static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v;
fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
}
static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* supported values: 2, 4, 8 */
unsigned long v = simple_strtoul(buf, NULL, 10);
switch (v) {
case 2: v = 1; break;
case 4: v = 2; break;
case 8: v = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
/* bits 2..7 reserved => mask with 0x03 */
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03;
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v;
fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03));
}
#define RPM_FROM_REG(val) (val*60)
static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)]));
}
#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 2..7 reserved, 0 read only => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
down(&data->update_lock);
data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03);
}
#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)]));
}
/*
* The final conversion is specified in sensors.conf, as it depends on
* mainboard specific values. We export the registers contents as
* pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much
* sense per se, but it minimizes the conversions count and keeps the
* values within a usual range.
*/
#define VOLT_FROM_REG(val) ((val) * 10)
static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr]));
}
static ssize_t show_revision(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->revision);
}
static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr)
{
/* bits 2, 5..6 reserved => mask with 0x9b */
return sprintf(buf, "%u\n", data->global_event & 0x9b);
}
static ssize_t set_control(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 1..7 reserved => mask with 0x01 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01;
down(&data->update_lock);
data->global_control &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 1..7 reserved => mask with 0x01 */
return sprintf(buf, "%u\n", data->global_control & 0x01);
}
static ssize_t set_watchdog_control(struct i2c_client *client, struct
fscher_data *data, const char *buf, size_t count,
int nr, int reg)
{
/* bits 0..3 reserved => mask with 0xf0 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
down(&data->update_lock);
data->watchdog[2] &= ~0xf0;
data->watchdog[2] |= v;
fscher_write_value(client, reg, data->watchdog[2]);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */
return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0);
}
static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
down(&data->update_lock);
data->watchdog[1] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
return sprintf(buf, "%u\n", data->watchdog[1] & 0x02);
}
static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff;
down(&data->update_lock);
data->watchdog[0] = v;
fscher_write_value(client, reg, data->watchdog[0]);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->watchdog[0]);
}
static int __init sensors_fscher_init(void)
{
return i2c_add_driver(&fscher_driver);
}
static void __exit sensors_fscher_exit(void)
{
i2c_del_driver(&fscher_driver);
}
MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de>");
MODULE_DESCRIPTION("FSC Hermes driver");
MODULE_LICENSE("GPL");
module_init(sensors_fscher_init);
module_exit(sensors_fscher_exit);

641
drivers/i2c/chips/fscpos.c Normal file
View File

@@ -0,0 +1,641 @@
/*
fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips
Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
fujitsu siemens poseidon chip,
module based on the old fscpos module by Hermann Jung <hej@odn.de> and
the fscher module by Reinhard Nissl <rnissl@gmx.de>
original module based on lm80.c
Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
and Philip Edelbrock <phil@netroedge.com>
Thanks to Jean Delvare for reviewing my code and suggesting a lot of
improvements.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(fscpos);
/*
* The FSCPOS registers
*/
/* chip identification */
#define FSCPOS_REG_IDENT_0 0x00
#define FSCPOS_REG_IDENT_1 0x01
#define FSCPOS_REG_IDENT_2 0x02
#define FSCPOS_REG_REVISION 0x03
/* global control and status */
#define FSCPOS_REG_EVENT_STATE 0x04
#define FSCPOS_REG_CONTROL 0x05
/* watchdog */
#define FSCPOS_REG_WDOG_PRESET 0x28
#define FSCPOS_REG_WDOG_STATE 0x23
#define FSCPOS_REG_WDOG_CONTROL 0x21
/* voltages */
#define FSCPOS_REG_VOLT_12 0x45
#define FSCPOS_REG_VOLT_5 0x42
#define FSCPOS_REG_VOLT_BATT 0x48
/* fans - the chip does not support minimum speed for fan2 */
static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 };
static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab };
static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 };
static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf };
/* temperatures */
static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 };
static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 };
/*
* Functions declaration
*/
static int fscpos_attach_adapter(struct i2c_adapter *adapter);
static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind);
static int fscpos_detach_client(struct i2c_client *client);
static int fscpos_read_value(struct i2c_client *client, u8 register);
static int fscpos_write_value(struct i2c_client *client, u8 register, u8 value);
static struct fscpos_data *fscpos_update_device(struct device *dev);
static void fscpos_init_client(struct i2c_client *client);
static void reset_fan_alarm(struct i2c_client *client, int nr);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver fscpos_driver = {
.owner = THIS_MODULE,
.name = "fscpos",
.id = I2C_DRIVERID_FSCPOS,
.flags = I2C_DF_NOTIFY,
.attach_adapter = fscpos_attach_adapter,
.detach_client = fscpos_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct fscpos_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* 0 until following fields are valid */
unsigned long last_updated; /* In jiffies */
/* register values */
u8 revision; /* revision of chip */
u8 global_event; /* global event status */
u8 global_control; /* global control register */
u8 wdog_control; /* watchdog control */
u8 wdog_state; /* watchdog status */
u8 wdog_preset; /* watchdog preset */
u8 volt[3]; /* 12, 5, battery current */
u8 temp_act[3]; /* temperature */
u8 temp_status[3]; /* status of sensor */
u8 fan_act[3]; /* fans revolutions per second */
u8 fan_status[3]; /* fan status */
u8 pwm[2]; /* fan min value for rps */
u8 fan_ripple[3]; /* divider for rps */
};
/* Temperature */
#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1]));
}
static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03);
}
static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "1\n");
}
static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
if (v != 1) {
dev_err(&client->dev, "temp_reset value %ld not supported. "
"Use 1 to reset the alarm!\n", v);
return -EINVAL;
}
dev_info(&client->dev, "You used the temp_reset feature which has not "
"been proplerly tested. Please report your "
"experience to the module author.\n");
/* Supported value: 2 (clears the status) */
fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr], 2);
return count;
}
/* Fans */
#define RPM_FROM_REG(val) ((val) * 60)
static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04);
}
static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1]));
}
static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03);
}
static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int nr, int reg)
{
/* supported values: 2, 4, 8 */
unsigned long v = simple_strtoul(buf, NULL, 10);
switch (v) {
case 2: v = 1; break;
case 4: v = 2; break;
case 8: v = 3; break;
default:
dev_err(&client->dev, "fan_ripple value %ld not supported. "
"Must be one of 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
/* bits 2..7 reserved => mask with 0x03 */
data->fan_ripple[nr - 1] &= ~0x03;
data->fan_ripple[nr - 1] |= v;
fscpos_write_value(client, reg, data->fan_ripple[nr - 1]);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->pwm[nr - 1]);
}
static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
/* Range: 0..255 */
if (v < 0) v = 0;
if (v > 255) v = 255;
down(&data->update_lock);
data->pwm[nr - 1] = v;
fscpos_write_value(client, reg, data->pwm[nr - 1]);
up(&data->update_lock);
return count;
}
static void reset_fan_alarm(struct i2c_client *client, int nr)
{
fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4);
}
/* Volts */
#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255)
static ssize_t show_volt_12(struct device *dev, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200));
}
static ssize_t show_volt_5(struct device *dev, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600));
}
static ssize_t show_volt_batt(struct device *dev, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300));
}
/* Watchdog */
static ssize_t show_wdog_control(struct fscpos_data *data, char *buf)
{
/* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */
return sprintf(buf, "%u\n", data->wdog_control & 0xb0);
}
static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
/* bits 0..3 reserved => mask with 0xf0 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
down(&data->update_lock);
data->wdog_control &= ~0xf0;
data->wdog_control |= v;
fscpos_write_value(client, reg, data->wdog_control);
up(&data->update_lock);
return count;
}
static ssize_t show_wdog_state(struct fscpos_data *data, char *buf)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
return sprintf(buf, "%u\n", data->wdog_state & 0x02);
}
static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
/* Valid values: 2 (clear) */
if (v != 2) {
dev_err(&client->dev, "wdog_state value %ld not supported. "
"Must be 2 to clear the state!\n", v);
return -EINVAL;
}
down(&data->update_lock);
data->wdog_state &= ~v;
fscpos_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf)
{
return sprintf(buf, "%u\n", data->wdog_preset);
}
static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff;
down(&data->update_lock);
data->wdog_preset = v;
fscpos_write_value(client, reg, data->wdog_preset);
up(&data->update_lock);
return count;
}
/* Event */
static ssize_t show_event(struct device *dev, char *buf)
{
/* bits 5..7 reserved => mask with 0x1f */
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", data->global_event & 0x9b);
}
/*
* Sysfs stuff
*/
#define create_getter(kind, sub) \
static ssize_t sysfs_show_##kind##sub(struct device *dev, char *buf) \
{ \
struct fscpos_data *data = fscpos_update_device(dev); \
return show_##kind##sub(data, buf); \
}
#define create_getter_n(kind, offset, sub) \
static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, char\
*buf) \
{ \
struct fscpos_data *data = fscpos_update_device(dev); \
return show_##kind##sub(data, buf, offset); \
}
#define create_setter(kind, sub, reg) \
static ssize_t sysfs_set_##kind##sub (struct device *dev, const char \
*buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscpos_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, reg); \
}
#define create_setter_n(kind, offset, sub, reg) \
static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, \
const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscpos_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, offset, reg);\
}
#define create_sysfs_device_ro(kind, sub, offset) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \
sysfs_show_##kind##offset##sub, NULL);
#define create_sysfs_device_rw(kind, sub, offset) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \
sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub);
#define sysfs_ro_n(kind, sub, offset) \
create_getter_n(kind, offset, sub); \
create_sysfs_device_ro(kind, sub, offset);
#define sysfs_rw_n(kind, sub, offset, reg) \
create_getter_n(kind, offset, sub); \
create_setter_n(kind, offset, sub, reg); \
create_sysfs_device_rw(kind, sub, offset);
#define sysfs_rw(kind, sub, reg) \
create_getter(kind, sub); \
create_setter(kind, sub, reg); \
create_sysfs_device_rw(kind, sub,);
#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \
sysfs_fan(offset, reg_status, reg_ripple); \
sysfs_rw_n(pwm,, offset, reg_min);
#define sysfs_fan(offset, reg_status, reg_ripple) \
sysfs_ro_n(fan, _input, offset); \
sysfs_ro_n(fan, _status, offset); \
sysfs_rw_n(fan, _ripple, offset, reg_ripple);
#define sysfs_temp(offset, reg_status) \
sysfs_ro_n(temp, _input, offset); \
sysfs_ro_n(temp, _status, offset); \
sysfs_rw_n(temp, _reset, offset, reg_status);
#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \
sysfs_rw(wdog, _control, reg_wdog_control); \
sysfs_rw(wdog, _preset, reg_wdog_preset); \
sysfs_rw(wdog, _state, reg_wdog_state);
sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0],
FSCPOS_REG_PWM[0]);
sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1],
FSCPOS_REG_PWM[1]);
sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]);
sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]);
sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]);
sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]);
sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE,
FSCPOS_REG_WDOG_CONTROL);
static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL);
static int fscpos_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, fscpos_detect);
}
int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct fscpos_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/*
* OK. For now, we presume we have a valid client. We now create the
* client structure, even though we cannot fill it completely yet.
* But it allows us to access fscpos_{read,write}_value.
*/
if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct fscpos_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &fscpos_driver;
new_client->flags = 0;
/* Do the remaining detection unless force or force_fscpos parameter */
if (kind < 0) {
if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0)
!= 0x50) /* 'P' */
|| (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1)
!= 0x45) /* 'E' */
|| (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2)
!= 0x47))/* 'G' */
{
dev_dbg(&new_client->dev, "fscpos detection failed\n");
goto exit_free;
}
}
/* Fill in the remaining client fields and put it in the global list */
strlcpy(new_client->name, "fscpos", 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;
/* Inizialize the fscpos chip */
fscpos_init_client(new_client);
/* Announce that the chip was found */
dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_event);
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_wdog_control);
device_create_file(&new_client->dev, &dev_attr_wdog_preset);
device_create_file(&new_client->dev, &dev_attr_wdog_state);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_status);
device_create_file(&new_client->dev, &dev_attr_temp1_reset);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_status);
device_create_file(&new_client->dev, &dev_attr_temp2_reset);
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_status);
device_create_file(&new_client->dev, &dev_attr_temp3_reset);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_status);
device_create_file(&new_client->dev, &dev_attr_fan1_ripple);
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_status);
device_create_file(&new_client->dev, &dev_attr_fan2_ripple);
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev, &dev_attr_fan3_input);
device_create_file(&new_client->dev, &dev_attr_fan3_status);
device_create_file(&new_client->dev, &dev_attr_fan3_ripple);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int fscpos_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 int fscpos_read_value(struct i2c_client *client, u8 reg)
{
dev_dbg(&client->dev, "Read reg 0x%02x\n", reg);
return i2c_smbus_read_byte_data(client, reg);
}
static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value)
{
dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value);
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new FSCPOS chip */
static void fscpos_init_client(struct i2c_client *client)
{
struct fscpos_data *data = i2c_get_clientdata(client);
/* read revision from chip */
data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION);
}
static struct fscpos_data *fscpos_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fscpos_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((jiffies - data->last_updated > 2 * HZ) ||
(jiffies < data->last_updated) || !data->valid) {
int i;
dev_dbg(&client->dev, "Starting fscpos update\n");
for (i = 0; i < 3; i++) {
data->temp_act[i] = fscpos_read_value(client,
FSCPOS_REG_TEMP_ACT[i]);
data->temp_status[i] = fscpos_read_value(client,
FSCPOS_REG_TEMP_STATE[i]);
data->fan_act[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_ACT[i]);
data->fan_status[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_STATE[i]);
data->fan_ripple[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_RIPPLE[i]);
if (i < 2) {
/* fan2_min is not supported by the chip */
data->pwm[i] = fscpos_read_value(client,
FSCPOS_REG_PWM[i]);
}
/* reset fan status if speed is back to > 0 */
if (data->fan_status[i] != 0 && data->fan_act[i] > 0) {
reset_fan_alarm(client, i);
}
}
data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12);
data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5);
data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT);
data->wdog_preset = fscpos_read_value(client,
FSCPOS_REG_WDOG_PRESET);
data->wdog_state = fscpos_read_value(client,
FSCPOS_REG_WDOG_STATE);
data->wdog_control = fscpos_read_value(client,
FSCPOS_REG_WDOG_CONTROL);
data->global_event = fscpos_read_value(client,
FSCPOS_REG_EVENT_STATE);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sm_fscpos_init(void)
{
return i2c_add_driver(&fscpos_driver);
}
static void __exit sm_fscpos_exit(void)
{
i2c_del_driver(&fscpos_driver);
}
MODULE_AUTHOR("Stefan Ott <stefan@desire.ch> based on work from Hermann Jung "
"<hej@odn.de>, Frodo Looijaard <frodol@dds.nl>"
" and Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver");
MODULE_LICENSE("GPL");
module_init(sm_fscpos_init);
module_exit(sm_fscpos_exit);

605
drivers/i2c/chips/gl518sm.c Normal file
View File

@@ -0,0 +1,605 @@
/*
* gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
* Kyosti Malkki <kmalkki@cc.hut.fi>
* Copyright (C) 2004 Hong-Gunn Chew <hglinux@gunnet.org> and
* Jean Delvare <khali@linux-fr.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare
* and advice of Greg Kroah-Hartman.
*
* Notes about the port:
* Release 0x00 of the GL518SM chipset doesn't support reading of in0,
* in1 nor in2. The original driver had an ugly workaround to get them
* anyway (changing limits and watching alarms trigger and wear off).
* We did not keep that part of the original driver in the Linux 2.6
* version, since it was making the driver significantly more complex
* with no real benefit.
*
* History:
* 2004-01-28 Original port. (Hong-Gunn Chew)
* 2004-01-31 Code review and approval. (Jean Delvare)
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80);
/* Many GL518 constants specified below */
/* The GL518 registers */
#define GL518_REG_CHIP_ID 0x00
#define GL518_REG_REVISION 0x01
#define GL518_REG_VENDOR_ID 0x02
#define GL518_REG_CONF 0x03
#define GL518_REG_TEMP_IN 0x04
#define GL518_REG_TEMP_MAX 0x05
#define GL518_REG_TEMP_HYST 0x06
#define GL518_REG_FAN_COUNT 0x07
#define GL518_REG_FAN_LIMIT 0x08
#define GL518_REG_VIN1_LIMIT 0x09
#define GL518_REG_VIN2_LIMIT 0x0a
#define GL518_REG_VIN3_LIMIT 0x0b
#define GL518_REG_VDD_LIMIT 0x0c
#define GL518_REG_VIN3 0x0d
#define GL518_REG_MISC 0x0f
#define GL518_REG_ALARM 0x10
#define GL518_REG_MASK 0x11
#define GL518_REG_INT 0x12
#define GL518_REG_VIN2 0x13
#define GL518_REG_VIN1 0x14
#define GL518_REG_VDD 0x15
/*
* Conversions. Rounding and limit checking is only done on the TO_REG
* variants. Note that you should be a bit careful with which arguments
* these macros are called: arguments may be evaluated more than once.
* Fixing this is just not worth it.
*/
#define RAW_FROM_REG(val) val
#define BOOL_FROM_REG(val) ((val)?0:1)
#define BOOL_TO_REG(val) ((val)?0:1)
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \
(val)-500:(val)+500)/1000)+119),0,255))
#define TEMP_FROM_REG(val) (((val) - 119) * 1000)
static inline u8 FAN_TO_REG(long rpm, int div)
{
long rpmdiv;
if (rpm == 0)
return 0;
rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div;
return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255);
}
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div))))
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
#define IN_FROM_REG(val) ((val)*19)
#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
#define VDD_FROM_REG(val) (((val)*95+2)/4)
#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3)
#define DIV_FROM_REG(val) (1 << (val))
#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask)
#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f)
/* Each client has this additional data */
struct gl518_data {
struct i2c_client client;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 voltage_in[4]; /* Register values; [0] = VDD */
u8 voltage_min[4]; /* Register values; [0] = VDD */
u8 voltage_max[4]; /* Register values; [0] = VDD */
u8 iter_voltage_in[4]; /* Register values; [0] = VDD */
u8 fan_in[2];
u8 fan_min[2];
u8 fan_div[2]; /* Register encoding, shifted right */
u8 fan_auto1; /* Boolean */
u8 temp_in; /* Register values */
u8 temp_max; /* Register values */
u8 temp_hyst; /* Register values */
u8 alarms; /* Register value */
u8 alarm_mask; /* Register value */
u8 beep_mask; /* Register value */
u8 beep_enable; /* Boolean */
};
static int gl518_attach_adapter(struct i2c_adapter *adapter);
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind);
static void gl518_init_client(struct i2c_client *client);
static int gl518_detach_client(struct i2c_client *client);
static int gl518_read_value(struct i2c_client *client, u8 reg);
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct gl518_data *gl518_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver gl518_driver = {
.owner = THIS_MODULE,
.name = "gl518sm",
.id = I2C_DRIVERID_GL518,
.flags = I2C_DF_NOTIFY,
.attach_adapter = gl518_attach_adapter,
.detach_client = gl518_detach_client,
};
/*
* Sysfs stuff
*/
#define show(type, suffix, value) \
static ssize_t show_##suffix(struct device *dev, char *buf) \
{ \
struct gl518_data *data = gl518_update_device(dev); \
return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \
}
#define show_fan(suffix, value, index) \
static ssize_t show_##suffix(struct device *dev, char *buf) \
{ \
struct gl518_data *data = gl518_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \
DIV_FROM_REG(data->fan_div[index]))); \
}
show(TEMP, temp_input1, temp_in);
show(TEMP, temp_max1, temp_max);
show(TEMP, temp_hyst1, temp_hyst);
show(BOOL, fan_auto1, fan_auto1);
show_fan(fan_input1, fan_in, 0);
show_fan(fan_input2, fan_in, 1);
show_fan(fan_min1, fan_min, 0);
show_fan(fan_min2, fan_min, 1);
show(DIV, fan_div1, fan_div[0]);
show(DIV, fan_div2, fan_div[1]);
show(VDD, in_input0, voltage_in[0]);
show(IN, in_input1, voltage_in[1]);
show(IN, in_input2, voltage_in[2]);
show(IN, in_input3, voltage_in[3]);
show(VDD, in_min0, voltage_min[0]);
show(IN, in_min1, voltage_min[1]);
show(IN, in_min2, voltage_min[2]);
show(IN, in_min3, voltage_min[3]);
show(VDD, in_max0, voltage_max[0]);
show(IN, in_max1, voltage_max[1]);
show(IN, in_max2, voltage_max[2]);
show(IN, in_max3, voltage_max[3]);
show(RAW, alarms, alarms);
show(BOOL, beep_enable, beep_enable);
show(BEEP_MASK, beep_mask, beep_mask);
#define set(type, suffix, value, reg) \
static ssize_t set_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = type##_TO_REG(val); \
gl518_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
#define set_bits(type, suffix, value, reg, mask, shift) \
static ssize_t set_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
int regvalue; \
unsigned long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
regvalue = gl518_read_value(client, reg); \
data->value = type##_TO_REG(val); \
regvalue = (regvalue & ~mask) | (data->value << shift); \
gl518_write_value(client, reg, regvalue); \
up(&data->update_lock); \
return count; \
}
#define set_low(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0x00ff, 0)
#define set_high(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0xff00, 8)
set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX);
set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST);
set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3);
set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6);
set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4);
set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT);
set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT);
set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT);
set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT);
set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT);
set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT);
set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT);
set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT);
set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2);
set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM);
static ssize_t set_fan_min1(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue;
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = FAN_TO_REG(val,
DIV_FROM_REG(data->fan_div[0]));
regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8);
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[0] == 0)
data->alarm_mask &= ~0x20;
else
data->alarm_mask |= 0x20;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_min2(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue;
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[1] = FAN_TO_REG(val,
DIV_FROM_REG(data->fan_div[1]));
regvalue = (regvalue & 0xff00) | data->fan_min[1];
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[1] == 0)
data->alarm_mask &= ~0x40;
else
data->alarm_mask |= 0x40;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR|S_IRUGO,
show_temp_hyst1, set_temp_hyst1);
static DEVICE_ATTR(fan1_auto, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL);
static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL);
static DEVICE_ATTR(fan1_min, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1);
static DEVICE_ATTR(fan2_min, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2);
static DEVICE_ATTR(fan1_div, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1);
static DEVICE_ATTR(fan2_div, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2);
static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL);
static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL);
static DEVICE_ATTR(in0_min, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0);
static DEVICE_ATTR(in1_min, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1);
static DEVICE_ATTR(in2_min, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2);
static DEVICE_ATTR(in3_min, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3);
static DEVICE_ATTR(in0_max, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0);
static DEVICE_ATTR(in1_max, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1);
static DEVICE_ATTR(in2_max, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2);
static DEVICE_ATTR(in3_max, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO,
show_beep_enable, set_beep_enable);
static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO,
show_beep_mask, set_beep_mask);
/*
* Real code
*/
static int gl518_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, gl518_detect);
}
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct gl518_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access gl518_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct gl518_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl518_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80)
|| (gl518_read_value(new_client, GL518_REG_CONF) & 0x80))
goto exit_free;
}
/* Determine the chip type. */
if (kind <= 0) {
i = gl518_read_value(new_client, GL518_REG_REVISION);
if (i == 0x00) {
kind = gl518sm_r00;
} else if (i == 0x80) {
kind = gl518sm_r80;
} else {
if (kind <= 0)
dev_info(&adapter->dev,
"Ignoring 'force' parameter for unknown "
"chip at adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto exit_free;
}
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, "gl518sm", I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the GL518SM chip */
data->alarm_mask = 0xff;
data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0;
gl518_init_client((struct i2c_client *) new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_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_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_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_fan1_auto);
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_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_beep_enable);
device_create_file(&new_client->dev, &dev_attr_beep_mask);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(data);
exit:
return err;
}
/* Called when we have found a new GL518SM.
Note that we preserve D4:NoFan2 and D2:beep_enable. */
static void gl518_init_client(struct i2c_client *client)
{
/* Make sure we leave D7:Reset untouched */
u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f;
/* Comparator mode (D3=0), standby mode (D6=0) */
gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37));
/* Never interrupts */
gl518_write_value(client, GL518_REG_MASK, 0x00);
/* Clear status register (D5=1), start (D6=1) */
gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue);
gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue);
}
static int gl518_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev, "Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(i2c_get_clientdata(client));
return 0;
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return swab16(i2c_smbus_read_word_data(client, reg));
else
return i2c_smbus_read_byte_data(client, reg);
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return i2c_smbus_write_word_data(client, reg, swab16(value));
else
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct gl518_data *gl518_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting gl518 update\n");
data->alarms = gl518_read_value(client, GL518_REG_INT);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
data->voltage_min[0] = val & 0xff;
data->voltage_max[0] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
data->voltage_min[1] = val & 0xff;
data->voltage_max[1] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
data->voltage_min[2] = val & 0xff;
data->voltage_max[2] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
data->voltage_min[3] = val & 0xff;
data->voltage_max[3] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_COUNT);
data->fan_in[0] = (val >> 8) & 0xff;
data->fan_in[1] = val & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = (val >> 8) & 0xff;
data->fan_min[1] = val & 0xff;
data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN);
data->temp_max =
gl518_read_value(client, GL518_REG_TEMP_MAX);
data->temp_hyst =
gl518_read_value(client, GL518_REG_TEMP_HYST);
val = gl518_read_value(client, GL518_REG_MISC);
data->fan_div[0] = (val >> 6) & 0x03;
data->fan_div[1] = (val >> 4) & 0x03;
data->fan_auto1 = (val >> 3) & 0x01;
data->alarms &= data->alarm_mask;
val = gl518_read_value(client, GL518_REG_CONF);
data->beep_enable = (val >> 2) & 1;
if (data->type != gl518sm_r00) {
data->voltage_in[0] =
gl518_read_value(client, GL518_REG_VDD);
data->voltage_in[1] =
gl518_read_value(client, GL518_REG_VIN1);
data->voltage_in[2] =
gl518_read_value(client, GL518_REG_VIN2);
}
data->voltage_in[3] =
gl518_read_value(client, GL518_REG_VIN3);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_gl518sm_init(void)
{
return i2c_add_driver(&gl518_driver);
}
static void __exit sensors_gl518sm_exit(void)
{
i2c_del_driver(&gl518_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Kyosti Malkki <kmalkki@cc.hut.fi> and "
"Hong-Gunn Chew <hglinux@gunnet.org>");
MODULE_DESCRIPTION("GL518SM driver");
MODULE_LICENSE("GPL");
module_init(sensors_gl518sm_init);
module_exit(sensors_gl518sm_exit);

769
drivers/i2c/chips/gl520sm.c Normal file
View File

@@ -0,0 +1,769 @@
/*
gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
Copyright (c) 2005 Maarten Deprez <maartendeprez@users.sourceforge.net>
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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/* Type of the extra sensor */
static unsigned short extra_sensor_type;
module_param(extra_sensor_type, ushort, 0);
MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)");
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(gl520sm);
/* Many GL520 constants specified below
One of the inputs can be configured as either temp or voltage.
That's why _TEMP2 and _IN4 access the same register
*/
/* The GL520 registers */
#define GL520_REG_CHIP_ID 0x00
#define GL520_REG_REVISION 0x01
#define GL520_REG_CONF 0x03
#define GL520_REG_MASK 0x11
#define GL520_REG_VID_INPUT 0x02
#define GL520_REG_IN0_INPUT 0x15
#define GL520_REG_IN0_LIMIT 0x0c
#define GL520_REG_IN0_MIN GL520_REG_IN0_LIMIT
#define GL520_REG_IN0_MAX GL520_REG_IN0_LIMIT
#define GL520_REG_IN1_INPUT 0x14
#define GL520_REG_IN1_LIMIT 0x09
#define GL520_REG_IN1_MIN GL520_REG_IN1_LIMIT
#define GL520_REG_IN1_MAX GL520_REG_IN1_LIMIT
#define GL520_REG_IN2_INPUT 0x13
#define GL520_REG_IN2_LIMIT 0x0a
#define GL520_REG_IN2_MIN GL520_REG_IN2_LIMIT
#define GL520_REG_IN2_MAX GL520_REG_IN2_LIMIT
#define GL520_REG_IN3_INPUT 0x0d
#define GL520_REG_IN3_LIMIT 0x0b
#define GL520_REG_IN3_MIN GL520_REG_IN3_LIMIT
#define GL520_REG_IN3_MAX GL520_REG_IN3_LIMIT
#define GL520_REG_IN4_INPUT 0x0e
#define GL520_REG_IN4_MAX 0x17
#define GL520_REG_IN4_MIN 0x18
#define GL520_REG_TEMP1_INPUT 0x04
#define GL520_REG_TEMP1_MAX 0x05
#define GL520_REG_TEMP1_MAX_HYST 0x06
#define GL520_REG_TEMP2_INPUT 0x0e
#define GL520_REG_TEMP2_MAX 0x17
#define GL520_REG_TEMP2_MAX_HYST 0x18
#define GL520_REG_FAN_INPUT 0x07
#define GL520_REG_FAN_MIN 0x08
#define GL520_REG_FAN_DIV 0x0f
#define GL520_REG_FAN_OFF GL520_REG_FAN_DIV
#define GL520_REG_ALARMS 0x12
#define GL520_REG_BEEP_MASK 0x10
#define GL520_REG_BEEP_ENABLE GL520_REG_CONF
/*
* Function declarations
*/
static int gl520_attach_adapter(struct i2c_adapter *adapter);
static int gl520_detect(struct i2c_adapter *adapter, int address, int kind);
static void gl520_init_client(struct i2c_client *client);
static int gl520_detach_client(struct i2c_client *client);
static int gl520_read_value(struct i2c_client *client, u8 reg);
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct gl520_data *gl520_update_device(struct device *dev);
/* Driver data */
static struct i2c_driver gl520_driver = {
.owner = THIS_MODULE,
.name = "gl520sm",
.id = I2C_DRIVERID_GL520,
.flags = I2C_DF_NOTIFY,
.attach_adapter = gl520_attach_adapter,
.detach_client = gl520_detach_client,
};
/* Client data */
struct gl520_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until the following fields are valid */
unsigned long last_updated; /* in jiffies */
u8 vid;
u8 vrm;
u8 in_input[5]; /* [0] = VVD */
u8 in_min[5]; /* [0] = VDD */
u8 in_max[5]; /* [0] = VDD */
u8 fan_input[2];
u8 fan_min[2];
u8 fan_div[2];
u8 fan_off;
u8 temp_input[2];
u8 temp_max[2];
u8 temp_max_hyst[2];
u8 alarms;
u8 beep_enable;
u8 beep_mask;
u8 alarm_mask;
u8 two_temps;
};
/*
* Sysfs stuff
*/
#define sysfs_r(type, n, item, reg) \
static ssize_t get_##type##item (struct gl520_data *, char *, int); \
static ssize_t get_##type##n##item (struct device *, char *); \
static ssize_t get_##type##n##item (struct device *dev, char *buf) \
{ \
struct gl520_data *data = gl520_update_device(dev); \
return get_##type##item(data, buf, (n)); \
}
#define sysfs_w(type, n, item, reg) \
static ssize_t set_##type##item (struct i2c_client *, struct gl520_data *, const char *, size_t, int, int); \
static ssize_t set_##type##n##item (struct device *, const char *, size_t); \
static ssize_t set_##type##n##item (struct device *dev, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl520_data *data = i2c_get_clientdata(client); \
return set_##type##item(client, data, buf, count, (n), reg); \
}
#define sysfs_rw_n(type, n, item, reg) \
sysfs_r(type, n, item, reg) \
sysfs_w(type, n, item, reg) \
static DEVICE_ATTR(type##n##item, S_IRUGO | S_IWUSR, get_##type##n##item, set_##type##n##item);
#define sysfs_ro_n(type, n, item, reg) \
sysfs_r(type, n, item, reg) \
static DEVICE_ATTR(type##n##item, S_IRUGO, get_##type##n##item, NULL);
#define sysfs_rw(type, item, reg) \
sysfs_r(type, 0, item, reg) \
sysfs_w(type, 0, item, reg) \
static DEVICE_ATTR(type##item, S_IRUGO | S_IWUSR, get_##type##0##item, set_##type##0##item);
#define sysfs_ro(type, item, reg) \
sysfs_r(type, 0, item, reg) \
static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL);
#define sysfs_vid(n) \
sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT)
#define device_create_file_vid(client, n) \
device_create_file(&client->dev, &dev_attr_cpu##n##_vid)
#define sysfs_in(n) \
sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \
sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \
sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \
#define device_create_file_in(client, n) \
({device_create_file(&client->dev, &dev_attr_in##n##_input); \
device_create_file(&client->dev, &dev_attr_in##n##_min); \
device_create_file(&client->dev, &dev_attr_in##n##_max);})
#define sysfs_fan(n) \
sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \
sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \
sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV)
#define device_create_file_fan(client, n) \
({device_create_file(&client->dev, &dev_attr_fan##n##_input); \
device_create_file(&client->dev, &dev_attr_fan##n##_min); \
device_create_file(&client->dev, &dev_attr_fan##n##_div);})
#define sysfs_fan_off(n) \
sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \
#define device_create_file_fan_off(client, n) \
device_create_file(&client->dev, &dev_attr_fan##n##_off)
#define sysfs_temp(n) \
sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \
sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \
sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST)
#define device_create_file_temp(client, n) \
({device_create_file(&client->dev, &dev_attr_temp##n##_input); \
device_create_file(&client->dev, &dev_attr_temp##n##_max); \
device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);})
#define sysfs_alarms() \
sysfs_ro(alarms, , GL520_REG_ALARMS) \
sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \
sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK)
#define device_create_file_alarms(client) \
({device_create_file(&client->dev, &dev_attr_alarms); \
device_create_file(&client->dev, &dev_attr_beep_enable); \
device_create_file(&client->dev, &dev_attr_beep_mask);})
sysfs_vid(0)
sysfs_in(0)
sysfs_in(1)
sysfs_in(2)
sysfs_in(3)
sysfs_in(4)
sysfs_fan(1)
sysfs_fan(2)
sysfs_fan_off(1)
sysfs_temp(1)
sysfs_temp(2)
sysfs_alarms()
static ssize_t get_cpu_vid(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
#define VDD_FROM_REG(val) (((val)*95+2)/4)
#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
#define IN_FROM_REG(val) ((val)*19)
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
static ssize_t get_in_input(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_input[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t get_in_min(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_min[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t get_in_max(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_max[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t set_in_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
u8 r;
down(&data->update_lock);
if (n == 0)
r = VDD_TO_REG(v);
else
r = IN_TO_REG(v);
data->in_min[n] = r;
if (n < 4)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
else
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
u8 r;
if (n == 0)
r = VDD_TO_REG(v);
else
r = IN_TO_REG(v);
down(&data->update_lock);
data->in_max[n] = r;
if (n < 4)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
else
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
#define DIV_FROM_REG(val) (1 << (val))
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div))))
#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255));
static ssize_t get_fan_input(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n - 1], data->fan_div[n - 1]));
}
static ssize_t get_fan_min(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n - 1], data->fan_div[n - 1]));
}
static ssize_t get_fan_div(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n - 1]));
}
static ssize_t get_fan_off(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->fan_off);
}
static ssize_t set_fan_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
u8 r;
down(&data->update_lock);
r = FAN_TO_REG(v, data->fan_div[n - 1]);
data->fan_min[n - 1] = r;
if (n == 1)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
else
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
if (data->fan_min[n - 1] == 0)
data->alarm_mask &= (n == 1) ? ~0x20 : ~0x40;
else
data->alarm_mask |= (n == 1) ? 0x20 : 0x40;
data->beep_mask &= data->alarm_mask;
gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_div(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
u8 r;
switch (v) {
case 1: r = 0; break;
case 2: r = 1; break;
case 4: r = 2; break;
case 8: r = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
data->fan_div[n - 1] = r;
if (n == 1)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xc0) | (r << 6));
else
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x30) | (r << 4));
up(&data->update_lock);
return count;
}
static ssize_t set_fan_off(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10)?1:0;
down(&data->update_lock);
data->fan_off = r;
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x0c) | (r << 2));
up(&data->update_lock);
return count;
}
#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255))
static ssize_t get_temp_input(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n - 1]));
}
static ssize_t get_temp_max(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n - 1]));
}
static ssize_t get_temp_max_hyst(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n - 1]));
}
static ssize_t set_temp_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max[n - 1] = TEMP_TO_REG(v);;
gl520_write_value(client, reg, data->temp_max[n - 1]);
up(&data->update_lock);
return count;
}
static ssize_t set_temp_max_hyst(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max_hyst[n - 1] = TEMP_TO_REG(v);
gl520_write_value(client, reg, data->temp_max_hyst[n - 1]);
up(&data->update_lock);
return count;
}
static ssize_t get_alarms(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->alarms);
}
static ssize_t get_beep_enable(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->beep_enable);
}
static ssize_t get_beep_mask(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->beep_mask);
}
static ssize_t set_beep_enable(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10)?0:1;
down(&data->update_lock);
data->beep_enable = !r;
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x04) | (r << 2));
up(&data->update_lock);
return count;
}
static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
r &= data->alarm_mask;
data->beep_mask = r;
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
/*
* Real code
*/
static int gl520_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, gl520_detect);
}
static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct gl520_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access gl520_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct gl520_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl520_driver;
new_client->flags = 0;
/* Determine the chip type. */
if (kind < 0) {
if ((gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) ||
((gl520_read_value(new_client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
((gl520_read_value(new_client, GL520_REG_CONF) & 0x80) != 0x00)) {
dev_dbg(&new_client->dev, "Unknown chip type, skipping\n");
goto exit_free;
}
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, "gl520sm", 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 GL520SM chip */
gl520_init_client(new_client);
/* Register sysfs hooks */
device_create_file_vid(new_client, 0);
device_create_file_in(new_client, 0);
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_in(new_client, 3);
if (!data->two_temps)
device_create_file_in(new_client, 4);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan_off(new_client, 1);
device_create_file_temp(new_client, 1);
if (data->two_temps)
device_create_file_temp(new_client, 2);
device_create_file_alarms(new_client);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
/* Called when we have found a new GL520SM. */
static void gl520_init_client(struct i2c_client *client)
{
struct gl520_data *data = i2c_get_clientdata(client);
u8 oldconf, conf;
conf = oldconf = gl520_read_value(client, GL520_REG_CONF);
data->alarm_mask = 0xff;
data->vrm = i2c_which_vrm();
if (extra_sensor_type == 1)
conf &= ~0x10;
else if (extra_sensor_type == 2)
conf |= 0x10;
data->two_temps = !(conf & 0x10);
/* If IRQ# is disabled, we can safely force comparator mode */
if (!(conf & 0x20))
conf &= 0xf7;
/* Enable monitoring if needed */
conf |= 0x40;
if (conf != oldconf)
gl520_write_value(client, GL520_REG_CONF, conf);
gl520_update_device(&(client->dev));
if (data->fan_min[0] == 0)
data->alarm_mask &= ~0x20;
if (data->fan_min[1] == 0)
data->alarm_mask &= ~0x40;
data->beep_mask &= data->alarm_mask;
gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
}
static int gl520_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;
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL520 uses a high-byte first convention */
static int gl520_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return swab16(i2c_smbus_read_word_data(client, reg));
else
return i2c_smbus_read_byte_data(client, reg);
}
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return i2c_smbus_write_word_data(client, reg, swab16(value));
else
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct gl520_data *gl520_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl520_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
if ((jiffies - data->last_updated > 2 * HZ) ||
(jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting gl520sm update\n");
data->alarms = gl520_read_value(client, GL520_REG_ALARMS);
data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f;
val = gl520_read_value(client, GL520_REG_IN0_LIMIT);
data->in_min[0] = val & 0xff;
data->in_max[0] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN1_LIMIT);
data->in_min[1] = val & 0xff;
data->in_max[1] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN2_LIMIT);
data->in_min[2] = val & 0xff;
data->in_max[2] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN3_LIMIT);
data->in_min[3] = val & 0xff;
data->in_max[3] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_FAN_INPUT);
data->fan_input[0] = (val >> 8) & 0xff;
data->fan_input[1] = val & 0xff;
val = gl520_read_value(client, GL520_REG_FAN_MIN);
data->fan_min[0] = (val >> 8) & 0xff;
data->fan_min[1] = val & 0xff;
data->temp_input[0] = gl520_read_value(client, GL520_REG_TEMP1_INPUT);
data->temp_max[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX);
data->temp_max_hyst[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX_HYST);
val = gl520_read_value(client, GL520_REG_FAN_DIV);
data->fan_div[0] = (val >> 6) & 0x03;
data->fan_div[1] = (val >> 4) & 0x03;
data->fan_off = (val >> 2) & 0x01;
data->alarms &= data->alarm_mask;
val = gl520_read_value(client, GL520_REG_CONF);
data->beep_enable = !((val >> 2) & 1);
data->in_input[0] = gl520_read_value(client, GL520_REG_IN0_INPUT);
data->in_input[1] = gl520_read_value(client, GL520_REG_IN1_INPUT);
data->in_input[2] = gl520_read_value(client, GL520_REG_IN2_INPUT);
data->in_input[3] = gl520_read_value(client, GL520_REG_IN3_INPUT);
/* Temp1 and Vin4 are the same input */
if (data->two_temps) {
data->temp_input[1] = gl520_read_value(client, GL520_REG_TEMP2_INPUT);
data->temp_max[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX);
data->temp_max_hyst[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX_HYST);
} else {
data->in_input[4] = gl520_read_value(client, GL520_REG_IN4_INPUT);
data->in_min[4] = gl520_read_value(client, GL520_REG_IN4_MIN);
data->in_max[4] = gl520_read_value(client, GL520_REG_IN4_MAX);
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_gl520sm_init(void)
{
return i2c_add_driver(&gl520_driver);
}
static void __exit sensors_gl520sm_exit(void)
{
i2c_del_driver(&gl520_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Ky<EFBFBD>sti M<>lkki <kmalkki@cc.hut.fi>, "
"Maarten Deprez <maartendeprez@users.sourceforge.net>");
MODULE_DESCRIPTION("GL520SM driver");
MODULE_LICENSE("GPL");
module_init(sensors_gl520sm_init);
module_exit(sensors_gl520sm_exit);

File diff suppressed because it is too large Load Diff

1208
drivers/i2c/chips/it87.c Normal file

File diff suppressed because it is too large Load Diff

581
drivers/i2c/chips/lm63.c Normal file
View File

@@ -0,0 +1,581 @@
/*
* 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/jiffies.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);
down(&data->update_lock);
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);
up(&data->update_lock);
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);
down(&data->update_lock);
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);
up(&data->update_lock);
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); \
\
down(&data->update_lock); \
data->value = TEMP8_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
up(&data->update_lock); \
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); \
\
down(&data->update_lock); \
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); \
up(&data->update_lock); \
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);
long val = simple_strtol(buf, NULL, 10);
long hyst;
down(&data->update_lock);
hyst = TEMP8_FROM_REG(data->temp2_crit) - val;
i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
HYST_TO_REG(hyst));
up(&data->update_lock);
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 & 0x08) ? "1.4" : "360",
((data->config_fan & 0x08) ? 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 (time_after(jiffies, data->last_updated + HZ) || !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);

297
drivers/i2c/chips/lm75.c Normal file
View File

@@ -0,0 +1,297 @@
/*
lm75.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include "lm75.h"
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm75);
/* Many LM75 constants specified below */
/* The LM75 registers */
#define LM75_REG_TEMP 0x00
#define LM75_REG_CONF 0x01
#define LM75_REG_TEMP_HYST 0x02
#define LM75_REG_TEMP_OS 0x03
/* Each client has this additional data */
struct lm75_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp_input; /* Register values */
u16 temp_max;
u16 temp_hyst;
};
static int lm75_attach_adapter(struct i2c_adapter *adapter);
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm75_init_client(struct i2c_client *client);
static int lm75_detach_client(struct i2c_client *client);
static int lm75_read_value(struct i2c_client *client, u8 reg);
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct lm75_data *lm75_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver lm75_driver = {
.owner = THIS_MODULE,
.name = "lm75",
.id = I2C_DRIVERID_LM75,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm75_attach_adapter,
.detach_client = lm75_detach_client,
};
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm75_data *data = lm75_update_device(dev); \
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
#define set(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 lm75_data *data = i2c_get_clientdata(client); \
int temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = LM75_TEMP_TO_REG(temp); \
lm75_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set(temp_max, LM75_REG_TEMP_OS);
set(temp_hyst, LM75_REG_TEMP_HYST);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL);
static int lm75_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm75_detect);
}
/* This function is called by i2c_detect */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct lm75_data *data;
int err = 0;
const char *name = "";
/* Make sure we aren't probing the ISA bus!! This is just a safety check
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
dev_dbg(&adapter->dev,
"lm75_detect called for an ISA bus adapter?!?\n");
goto exit;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm75_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm75_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm75_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. There is no identification-
dedicated register so we have to rely on several tricks:
unused bits, registers cycling over 8-address boundaries,
addresses 0x04-0x07 returning the last read value.
The cycling+unused addresses combination is not tested,
since it would significantly slow the detection down and would
hardly add any value. */
if (kind < 0) {
int cur, conf, hyst, os;
/* Unused addresses */
cur = i2c_smbus_read_word_data(new_client, 0);
conf = i2c_smbus_read_byte_data(new_client, 1);
hyst = i2c_smbus_read_word_data(new_client, 2);
if (i2c_smbus_read_word_data(new_client, 4) != hyst
|| i2c_smbus_read_word_data(new_client, 5) != hyst
|| i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
goto exit_free;
os = i2c_smbus_read_word_data(new_client, 3);
if (i2c_smbus_read_word_data(new_client, 4) != os
|| i2c_smbus_read_word_data(new_client, 5) != os
|| i2c_smbus_read_word_data(new_client, 6) != os
|| i2c_smbus_read_word_data(new_client, 7) != os)
goto exit_free;
/* Unused bits */
if (conf & 0xe0)
goto exit_free;
/* Addresses cycling */
for (i = 8; i < 0xff; i += 8)
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|| i2c_smbus_read_word_data(new_client, i + 2) != hyst
|| i2c_smbus_read_word_data(new_client, i + 3) != os)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = lm75;
if (kind == lm75) {
name = "lm75";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, 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 LM75 chip */
lm75_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm75_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
/* All registers are word-sized, except for the configuration register.
LM75 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
/* All registers are word-sized, except for the configuration register.
LM75 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void lm75_init_client(struct i2c_client *client)
{
/* Initialize the LM75 chip */
lm75_write_value(client, LM75_REG_CONF, 0);
}
static struct lm75_data *lm75_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm75_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm75 update\n");
data->temp_input = lm75_read_value(client, LM75_REG_TEMP);
data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS);
data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm75_init(void)
{
return i2c_add_driver(&lm75_driver);
}
static void __exit sensors_lm75_exit(void)
{
i2c_del_driver(&lm75_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM75 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm75_init);
module_exit(sensors_lm75_exit);

49
drivers/i2c/chips/lm75.h Normal file
View File

@@ -0,0 +1,49 @@
/*
lm75.h - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.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.
*/
/*
This file contains common code for encoding/decoding LM75 type
temperature readings, which are emulated by many of the chips
we support. As the user is unlikely to load more than one driver
which contains this code, we don't worry about the wasted space.
*/
#include <linux/i2c-sensor.h>
/* straight from the datasheet */
#define LM75_TEMP_MIN (-55000)
#define LM75_TEMP_MAX 125000
/* TEMP: 0.001C/bit (-55C to +125C)
REG: (0.5C/bit, two's complement) << 7 */
static inline u16 LM75_TEMP_TO_REG(int temp)
{
int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
ntemp += (ntemp<0 ? -250 : 250);
return (u16)((ntemp / 500) << 7);
}
static inline int LM75_TEMP_FROM_REG(u16 reg)
{
/* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */
return ((s16)reg / 128) * 500;
}

421
drivers/i2c/chips/lm77.c Normal file
View File

@@ -0,0 +1,421 @@
/*
lm77.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2004 Andras BALI <drewie@freemail.hu>
Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77
is a temperature sensor and thermal window comparator with 0.5 deg
resolution made by National Semiconductor. Complete datasheet can be
obtained at their site:
http://www.national.com/pf/LM/LM77.html
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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm77);
/* The LM77 registers */
#define LM77_REG_TEMP 0x00
#define LM77_REG_CONF 0x01
#define LM77_REG_TEMP_HYST 0x02
#define LM77_REG_TEMP_CRIT 0x03
#define LM77_REG_TEMP_MIN 0x04
#define LM77_REG_TEMP_MAX 0x05
/* Each client has this additional data */
struct lm77_data {
struct i2c_client client;
struct semaphore update_lock;
char valid;
unsigned long last_updated; /* In jiffies */
int temp_input; /* Temperatures */
int temp_crit;
int temp_min;
int temp_max;
int temp_hyst;
u8 alarms;
};
static int lm77_attach_adapter(struct i2c_adapter *adapter);
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm77_init_client(struct i2c_client *client);
static int lm77_detach_client(struct i2c_client *client);
static u16 lm77_read_value(struct i2c_client *client, u8 reg);
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct lm77_data *lm77_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver lm77_driver = {
.owner = THIS_MODULE,
.name = "lm77",
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm77_attach_adapter,
.detach_client = lm77_detach_client,
};
/* straight from the datasheet */
#define LM77_TEMP_MIN (-55000)
#define LM77_TEMP_MAX 125000
/* In the temperature registers, the low 3 bits are not part of the
temperature values; they are the status bits. */
static inline u16 LM77_TEMP_TO_REG(int temp)
{
int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX);
return (u16)((ntemp / 500) * 8);
}
static inline int LM77_TEMP_FROM_REG(u16 reg)
{
return ((int)reg / 8) * 500;
}
/* sysfs stuff */
/* read routines for temperature limits */
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm77_data *data = lm77_update_device(dev); \
return sprintf(buf, "%d\n", data->value); \
}
show(temp_input);
show(temp_crit);
show(temp_min);
show(temp_max);
show(alarms);
/* read routines for hysteresis values */
static ssize_t show_temp_crit_hyst(struct device *dev, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
}
static ssize_t show_temp_min_hyst(struct device *dev, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
}
static ssize_t show_temp_max_hyst(struct device *dev, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
}
/* write routines */
#define set(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 lm77_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = val; \
lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \
up(&data->update_lock); \
return count; \
}
set(temp_min, LM77_REG_TEMP_MIN);
set(temp_max, LM77_REG_TEMP_MAX);
/* hysteresis is stored as a relative value on the chip, so it has to be
converted first */
static ssize_t set_temp_crit_hyst(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = data->temp_crit - val;
lm77_write_value(client, LM77_REG_TEMP_HYST,
LM77_TEMP_TO_REG(data->temp_hyst));
up(&data->update_lock);
return count;
}
/* preserve hysteresis when setting T_crit */
static ssize_t set_temp_crit(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
long val = simple_strtoul(buf, NULL, 10);
int oldcrithyst;
down(&data->update_lock);
oldcrithyst = data->temp_crit - data->temp_hyst;
data->temp_crit = val;
data->temp_hyst = data->temp_crit - oldcrithyst;
lm77_write_value(client, LM77_REG_TEMP_CRIT,
LM77_TEMP_TO_REG(data->temp_crit));
lm77_write_value(client, LM77_REG_TEMP_HYST,
LM77_TEMP_TO_REG(data->temp_hyst));
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO,
show_temp_input, NULL);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
show_temp_crit, set_temp_crit);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
show_temp_crit_hyst, set_temp_crit_hyst);
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
show_temp_min_hyst, NULL);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
show_temp_max_hyst, NULL);
static DEVICE_ATTR(alarms, S_IRUGO,
show_alarms, NULL);
static int lm77_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm77_detect);
}
/* This function is called by i2c_detect */
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm77_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm77_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm77_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm77_driver;
new_client->flags = 0;
/* Here comes the remaining detection. Since the LM77 has no
register dedicated to identification, we have to rely on the
following tricks:
1. the high 4 bits represent the sign and thus they should
always be the same
2. the high 3 bits are unused in the configuration register
3. addresses 0x06 and 0x07 return the last read value
4. registers cycling over 8-address boundaries
Word-sized registers are high-byte first. */
if (kind < 0) {
int i, cur, conf, hyst, crit, min, max;
/* addresses cycling */
cur = i2c_smbus_read_word_data(new_client, 0);
conf = i2c_smbus_read_byte_data(new_client, 1);
hyst = i2c_smbus_read_word_data(new_client, 2);
crit = i2c_smbus_read_word_data(new_client, 3);
min = i2c_smbus_read_word_data(new_client, 4);
max = i2c_smbus_read_word_data(new_client, 5);
for (i = 8; i <= 0xff; i += 8)
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|| i2c_smbus_read_word_data(new_client, i + 2) != hyst
|| i2c_smbus_read_word_data(new_client, i + 3) != crit
|| i2c_smbus_read_word_data(new_client, i + 4) != min
|| i2c_smbus_read_word_data(new_client, i + 5) != max)
goto exit_free;
/* sign bits */
if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
|| ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
|| ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
|| ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
|| ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
goto exit_free;
/* unused bits */
if (conf & 0xe0)
goto exit_free;
/* 0x06 and 0x07 return the last read value */
cur = i2c_smbus_read_word_data(new_client, 0);
if (i2c_smbus_read_word_data(new_client, 6) != cur
|| i2c_smbus_read_word_data(new_client, 7) != cur)
goto exit_free;
hyst = i2c_smbus_read_word_data(new_client, 2);
if (i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
goto exit_free;
min = i2c_smbus_read_word_data(new_client, 4);
if (i2c_smbus_read_word_data(new_client, 6) != min
|| i2c_smbus_read_word_data(new_client, 7) != min)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = lm77;
if (kind == lm77) {
name = "lm77";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, 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 LM77 chip */
lm77_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm77_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
/* All registers are word-sized, except for the configuration register.
The LM77 uses the high-byte first convention. */
static u16 lm77_read_value(struct i2c_client *client, u8 reg)
{
if (reg == LM77_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == LM77_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void lm77_init_client(struct i2c_client *client)
{
/* Initialize the LM77 chip - turn off shutdown mode */
int conf = lm77_read_value(client, LM77_REG_CONF);
if (conf & 1)
lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
}
static struct lm77_data *lm77_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm77 update\n");
data->temp_input =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP));
data->temp_hyst =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_HYST));
data->temp_crit =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_CRIT));
data->temp_min =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_MIN));
data->temp_max =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_MAX));
data->alarms =
lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm77_init(void)
{
return i2c_add_driver(&lm77_driver);
}
static void __exit sensors_lm77_exit(void)
{
i2c_del_driver(&lm77_driver);
}
MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
MODULE_DESCRIPTION("LM77 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm77_init);
module_exit(sensors_lm77_exit);

796
drivers/i2c/chips/lm78.c Normal file
View File

@@ -0,0 +1,796 @@
/*
lm78.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/io.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29,
0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_3(lm78, lm78j, lm79);
/* Many LM78 constants specified below */
/* Length of ISA address segment */
#define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define LM78_ADDR_REG_OFFSET 5
#define LM78_DATA_REG_OFFSET 6
/* The LM78 registers */
#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define LM78_REG_IN(nr) (0x20 + (nr))
#define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
#define LM78_REG_FAN(nr) (0x28 + (nr))
#define LM78_REG_TEMP 0x27
#define LM78_REG_TEMP_OVER 0x39
#define LM78_REG_TEMP_HYST 0x3a
#define LM78_REG_ALARM1 0x41
#define LM78_REG_ALARM2 0x42
#define LM78_REG_VID_FANDIV 0x47
#define LM78_REG_CONFIG 0x40
#define LM78_REG_CHIPID 0x49
#define LM78_REG_I2C_ADDR 0x48
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. */
/* IN: mV, (0V to 4.08V)
REG: 16mV/bit */
static inline u8 IN_TO_REG(unsigned long val)
{
unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
return (nval + 8) / 16;
}
#define IN_FROM_REG(val) ((val) * 16)
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm <= 0)
return 255;
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
static inline int FAN_FROM_REG(u8 val, int div)
{
return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
}
/* TEMP: mC (-128C to +127C)
REG: 1C/bit, two's complement */
static inline s8 TEMP_TO_REG(int val)
{
int nval = SENSORS_LIMIT(val, -128000, 127000) ;
return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
}
static inline int TEMP_FROM_REG(s8 val)
{
return val * 1000;
}
/* VID: mV
REG: (see doc/vid) */
static inline int VID_FROM_REG(u8 val)
{
return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50;
}
#define DIV_FROM_REG(val) (1 << (val))
/* There are some complications in a module like this. First off, LM78 chips
may be both present on the SMBus and the ISA bus, and we have to handle
those cases separately at some places. Second, there might be several
LM78 chips available (well, actually, that is probably never done; but
it is a clean illustration of how to handle a case like that). Finally,
a specific chip may be attached to *both* ISA and SMBus, and we would
not like to detect it double. Fortunately, in the case of the LM78 at
least, a register tells us what SMBus address we are on, so that helps
a bit - except if there could be more than one SMBus. Groan. No solution
for this yet. */
/* This module may seem overly long and complicated. In fact, it is not so
bad. Quite a lot of bookkeeping is done. A real driver can often cut
some corners. */
/* For each registered LM78, we need to keep some data in memory. That
data is pointed to by lm78_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm78 client is
allocated. */
struct lm78_data {
struct i2c_client client;
struct semaphore lock;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
s8 temp; /* Register value */
s8 temp_over; /* Register value */
s8 temp_hyst; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u16 alarms; /* Register encoding, combined */
};
static int lm78_attach_adapter(struct i2c_adapter *adapter);
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
static int lm78_detach_client(struct i2c_client *client);
static int lm78_read_value(struct i2c_client *client, u8 register);
static int lm78_write_value(struct i2c_client *client, u8 register, u8 value);
static struct lm78_data *lm78_update_device(struct device *dev);
static void lm78_init_client(struct i2c_client *client);
static struct i2c_driver lm78_driver = {
.owner = THIS_MODULE,
.name = "lm78",
.id = I2C_DRIVERID_LM78,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm78_attach_adapter,
.detach_client = lm78_detach_client,
};
/* 7 Voltages */
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset, NULL); \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
show_in_offset(5);
show_in_offset(6);
/* Temperature */
static ssize_t show_temp(struct device *dev, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
static ssize_t show_temp_over(struct device *dev, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static ssize_t set_temp_over(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_hyst(struct device *dev, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst);
/* 3 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
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 lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
unsigned long min;
u8 reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
reg = lm78_read_value(client, LM78_REG_VID_FANDIV);
switch (nr) {
case 0:
reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
break;
case 1:
reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
break;
}
lm78_write_value(client, LM78_REG_VID_FANDIV, reg);
data->fan_min[nr] =
FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 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, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min);
static ssize_t set_fan_1_div(struct device *dev, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 0) ;
}
static ssize_t set_fan_2_div(struct device *dev, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 1) ;
}
show_fan_offset(1);
show_fan_offset(2);
show_fan_offset(3);
/* Fan 3 divisor is locked in H/W */
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
show_fan_1_div, set_fan_1_div);
static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
show_fan_2_div, set_fan_2_div);
static DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_3_div, NULL);
/* VID */
static ssize_t show_vid(struct device *dev, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", VID_FROM_REG(data->vid));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
/* Alarms */
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* This function is called when:
* lm78_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and lm78_driver is still present) */
static int lm78_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm78_detect);
}
/* This function is called by i2c_detect */
int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, err;
struct i2c_client *new_client;
struct lm78_data *data;
const char *client_name = "";
int is_isa = i2c_is_isa_adapter(adapter);
if (!is_isa &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
err = -ENODEV;
goto ERROR0;
}
/* Reserve the ISA region */
if (is_isa)
if (!request_region(address, LM78_EXTENT, lm78_driver.name)) {
err = -EBUSY;
goto ERROR0;
}
/* Probe whether there is anything available on this address. Already
done for SMBus clients */
if (kind < 0) {
if (is_isa) {
#define REALLY_SLOW_IO
/* We need the timeouts for at least some LM78-like
chips. But only if we read 'undefined' registers. */
i = inb_p(address + 1);
if (inb_p(address + 2) != i) {
err = -ENODEV;
goto ERROR1;
}
if (inb_p(address + 3) != i) {
err = -ENODEV;
goto ERROR1;
}
if (inb_p(address + 7) != i) {
err = -ENODEV;
goto ERROR1;
}
#undef REALLY_SLOW_IO
/* Let's just hope nothing breaks here */
i = inb_p(address + 5) & 0x7f;
outb_p(~i & 0x7f, address + 5);
if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
outb_p(i, address + 5);
err = -ENODEV;
goto ERROR1;
}
}
}
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm78_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
memset(data, 0, sizeof(struct lm78_data));
new_client = &data->client;
if (is_isa)
init_MUTEX(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm78_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) {
err = -ENODEV;
goto ERROR2;
}
if (!is_isa && (lm78_read_value(
new_client, LM78_REG_I2C_ADDR) != address)) {
err = -ENODEV;
goto ERROR2;
}
}
/* Determine the chip type. */
if (kind <= 0) {
i = lm78_read_value(new_client, LM78_REG_CHIPID);
if (i == 0x00 || i == 0x20)
kind = lm78;
else if (i == 0x40)
kind = lm78j;
else if ((i & 0xfe) == 0xc0)
kind = lm79;
else {
if (kind == 0)
dev_warn(&adapter->dev, "Ignoring 'force' "
"parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
err = -ENODEV;
goto ERROR2;
}
}
if (kind == lm78) {
client_name = "lm78";
} else if (kind == lm78j) {
client_name = "lm78-j";
} else if (kind == lm79) {
client_name = "lm79";
}
/* Fill in the remaining client fields and put into the global list */
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR2;
/* Initialize the LM78 chip */
lm78_init_client(new_client);
/* A few vars need to be filled upon startup */
for (i = 0; i < 3; i++) {
data->fan_min[i] = lm78_read_value(new_client,
LM78_REG_FAN_MIN(i));
}
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in6_max);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
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_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
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_alarms);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
return 0;
ERROR2:
kfree(data);
ERROR1:
if (is_isa)
release_region(address, LM78_EXTENT);
ERROR0:
return err;
}
static int lm78_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;
}
if(i2c_is_isa_client(client))
release_region(client->addr, LM78_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
/* The SMBus locks itself, but ISA access must be locked explicitely!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
would slow down the LM78 access and should not be necessary. */
static int lm78_read_value(struct i2c_client *client, u8 reg)
{
int res;
if (i2c_is_isa_client(client)) {
struct lm78_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
up(&data->lock);
return res;
} else
return i2c_smbus_read_byte_data(client, reg);
}
/* The SMBus locks itself, but ISA access muse be locked explicitely!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
would slow down the LM78 access and should not be necessary.
There are some ugly typecasts here, but the good new is - they should
nowhere else be necessary! */
static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
{
if (i2c_is_isa_client(client)) {
struct lm78_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
up(&data->lock);
return 0;
} else
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new LM78. It should set limits, etc. */
static void lm78_init_client(struct i2c_client *client)
{
u8 config = lm78_read_value(client, LM78_REG_CONFIG);
/* Start monitoring */
if (!(config & 0x01))
lm78_write_value(client, LM78_REG_CONFIG,
(config & 0xf7) | 0x01);
}
static struct lm78_data *lm78_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm78 update\n");
for (i = 0; i <= 6; i++) {
data->in[i] =
lm78_read_value(client, LM78_REG_IN(i));
data->in_min[i] =
lm78_read_value(client, LM78_REG_IN_MIN(i));
data->in_max[i] =
lm78_read_value(client, LM78_REG_IN_MAX(i));
}
for (i = 0; i < 3; i++) {
data->fan[i] =
lm78_read_value(client, LM78_REG_FAN(i));
data->fan_min[i] =
lm78_read_value(client, LM78_REG_FAN_MIN(i));
}
data->temp = lm78_read_value(client, LM78_REG_TEMP);
data->temp_over =
lm78_read_value(client, LM78_REG_TEMP_OVER);
data->temp_hyst =
lm78_read_value(client, LM78_REG_TEMP_HYST);
i = lm78_read_value(client, LM78_REG_VID_FANDIV);
data->vid = i & 0x0f;
if (data->type == lm79)
data->vid |=
(lm78_read_value(client, LM78_REG_CHIPID) &
0x01) << 4;
else
data->vid |= 0x10;
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = lm78_read_value(client, LM78_REG_ALARM1) +
(lm78_read_value(client, LM78_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
data->fan_div[2] = 1;
}
up(&data->update_lock);
return data;
}
static int __init sm_lm78_init(void)
{
return i2c_add_driver(&lm78_driver);
}
static void __exit sm_lm78_exit(void)
{
i2c_del_driver(&lm78_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");
MODULE_LICENSE("GPL");
module_init(sm_lm78_init);
module_exit(sm_lm78_exit);

602
drivers/i2c/chips/lm80.c Normal file
View File

@@ -0,0 +1,602 @@
/*
* lm80.c - From lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
* and Philip Edelbrock <phil@netroedge.com>
*
* Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm80);
/* Many LM80 constants specified below */
/* The LM80 registers */
#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
#define LM80_REG_IN(nr) (0x20 + (nr))
#define LM80_REG_FAN1 0x28
#define LM80_REG_FAN2 0x29
#define LM80_REG_FAN_MIN(nr) (0x3b + (nr))
#define LM80_REG_TEMP 0x27
#define LM80_REG_TEMP_HOT_MAX 0x38
#define LM80_REG_TEMP_HOT_HYST 0x39
#define LM80_REG_TEMP_OS_MAX 0x3a
#define LM80_REG_TEMP_OS_HYST 0x3b
#define LM80_REG_CONFIG 0x00
#define LM80_REG_ALARM1 0x01
#define LM80_REG_ALARM2 0x02
#define LM80_REG_MASK1 0x03
#define LM80_REG_MASK2 0x04
#define LM80_REG_FANDIV 0x05
#define LM80_REG_RES 0x06
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. Note that you should be a bit careful with which arguments
these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */
#define IN_TO_REG(val) (SENSORS_LIMIT(((val)+5)/10,0,255))
#define IN_FROM_REG(val) ((val)*10)
static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div)
{
if (rpm == 0)
return 255;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
return SENSORS_LIMIT((1350000 + rpm*div / 2) / (rpm*div), 1, 254);
}
#define FAN_FROM_REG(val,div) ((val)==0?-1:\
(val)==255?0:1350000/((div)*(val)))
static inline long TEMP_FROM_REG(u16 temp)
{
long res;
temp >>= 4;
if (temp < 0x0800)
res = 625 * (long) temp;
else
res = ((long) temp - 0x01000) * 625;
return res / 10;
}
#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000)
#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\
((val)-500)/1000:((val)+500)/1000,0,255)
#define DIV_FROM_REG(val) (1 << (val))
/*
* Client data (each client gets its own)
*/
struct lm80_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[2]; /* Register value */
u8 fan_min[2]; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u16 temp; /* Register values, shifted right */
u8 temp_hot_max; /* Register value */
u8 temp_hot_hyst; /* Register value */
u8 temp_os_max; /* Register value */
u8 temp_os_hyst; /* Register value */
u16 alarms; /* Register encoding, combined */
};
/*
* Functions declaration
*/
static int lm80_attach_adapter(struct i2c_adapter *adapter);
static int lm80_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm80_init_client(struct i2c_client *client);
static int lm80_detach_client(struct i2c_client *client);
static struct lm80_data *lm80_update_device(struct device *dev);
static int lm80_read_value(struct i2c_client *client, u8 reg);
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm80_driver = {
.owner = THIS_MODULE,
.name = "lm80",
.id = I2C_DRIVERID_LM80,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm80_attach_adapter,
.detach_client = lm80_detach_client,
};
/*
* Sysfs stuff
*/
#define show_in(suffix, value) \
static ssize_t show_in_##suffix(struct device *dev, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", IN_FROM_REG(data->value)); \
}
show_in(min0, in_min[0]);
show_in(min1, in_min[1]);
show_in(min2, in_min[2]);
show_in(min3, in_min[3]);
show_in(min4, in_min[4]);
show_in(min5, in_min[5]);
show_in(min6, in_min[6]);
show_in(max0, in_max[0]);
show_in(max1, in_max[1]);
show_in(max2, in_max[2]);
show_in(max3, in_max[3]);
show_in(max4, in_max[4]);
show_in(max5, in_max[5]);
show_in(max6, in_max[6]);
show_in(input0, in[0]);
show_in(input1, in[1]);
show_in(input2, in[2]);
show_in(input3, in[3]);
show_in(input4, in[4]);
show_in(input5, in[5]);
show_in(input6, in[6]);
#define set_in(suffix, value, reg) \
static ssize_t set_in_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock);\
data->value = IN_TO_REG(val); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock);\
return count; \
}
set_in(min0, in_min[0], LM80_REG_IN_MIN(0));
set_in(min1, in_min[1], LM80_REG_IN_MIN(1));
set_in(min2, in_min[2], LM80_REG_IN_MIN(2));
set_in(min3, in_min[3], LM80_REG_IN_MIN(3));
set_in(min4, in_min[4], LM80_REG_IN_MIN(4));
set_in(min5, in_min[5], LM80_REG_IN_MIN(5));
set_in(min6, in_min[6], LM80_REG_IN_MIN(6));
set_in(max0, in_max[0], LM80_REG_IN_MAX(0));
set_in(max1, in_max[1], LM80_REG_IN_MAX(1));
set_in(max2, in_max[2], LM80_REG_IN_MAX(2));
set_in(max3, in_max[3], LM80_REG_IN_MAX(3));
set_in(max4, in_max[4], LM80_REG_IN_MAX(4));
set_in(max5, in_max[5], LM80_REG_IN_MAX(5));
set_in(max6, in_max[6], LM80_REG_IN_MAX(6));
#define show_fan(suffix, value, div) \
static ssize_t show_fan_##suffix(struct device *dev, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value, \
DIV_FROM_REG(data->div))); \
}
show_fan(min1, fan_min[0], fan_div[0]);
show_fan(min2, fan_min[1], fan_div[1]);
show_fan(input1, fan[0], fan_div[0]);
show_fan(input2, fan[1], fan_div[1]);
#define show_fan_div(suffix, value) \
static ssize_t show_fan_div##suffix(struct device *dev, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", DIV_FROM_REG(data->value)); \
}
show_fan_div(1, fan_div[0]);
show_fan_div(2, fan_div[1]);
#define set_fan(suffix, value, reg, div) \
static ssize_t set_fan_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock);\
data->value = FAN_TO_REG(val, DIV_FROM_REG(data->div)); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock);\
return count; \
}
set_fan(min1, fan_min[0], LM80_REG_FAN_MIN(1), fan_div[0]);
set_fan(min2, fan_min[1], LM80_REG_FAN_MIN(2), fan_div[1]);
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm80_data *data = i2c_get_clientdata(client);
unsigned long min, val = simple_strtoul(buf, NULL, 10);
u8 reg;
/* Save fan_min */
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1))))
| (data->fan_div[nr] << (2 * (nr + 1)));
lm80_write_value(client, LM80_REG_FANDIV, reg);
/* Restore fan_min */
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define set_fan_div(number) \
static ssize_t set_fan_div##number(struct device *dev, const char *buf, \
size_t count) \
{ \
return set_fan_div(dev, buf, count, number - 1); \
}
set_fan_div(1);
set_fan_div(2);
static ssize_t show_temp_input1(struct device *dev, char *buf)
{
struct lm80_data *data = lm80_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp));
}
#define show_temp(suffix, value) \
static ssize_t show_temp_##suffix(struct device *dev, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \
}
show_temp(hot_max, temp_hot_max);
show_temp(hot_hyst, temp_hot_hyst);
show_temp(os_max, temp_os_max);
show_temp(os_hyst, temp_os_hyst);
#define set_temp(suffix, value, reg) \
static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_LIMIT_TO_REG(val); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX);
set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST);
set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX);
set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm80_data *data = lm80_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min0, set_in_min0);
static DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min1, set_in_min1);
static DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min2, set_in_min2);
static DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min3, set_in_min3);
static DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min4, set_in_min4);
static DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min5, set_in_min5);
static DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min6, set_in_min6);
static DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max0, set_in_max0);
static DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max1, set_in_max1);
static DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max2, set_in_max2);
static DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max3, set_in_max3);
static DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max4, set_in_max4);
static DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max5, set_in_max5);
static DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max6, set_in_max6);
static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL);
static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL);
static DEVICE_ATTR(in4_input, S_IRUGO, show_in_input4, NULL);
static DEVICE_ATTR(in5_input, S_IRUGO, show_in_input5, NULL);
static DEVICE_ATTR(in6_input, S_IRUGO, show_in_input6, NULL);
static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min1,
set_fan_min1);
static DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min2,
set_fan_min2);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL);
static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL);
static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div1, set_fan_div1);
static DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div2, set_fan_div2);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max,
set_temp_hot_max);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst,
set_temp_hot_hyst);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max,
set_temp_os_max);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst,
set_temp_os_hyst);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm80_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm80_detect);
}
int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur;
struct i2c_client *new_client;
struct lm80_data *data;
int err = 0;
const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm80_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm80_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm80_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. It is lousy. */
if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0)
goto error_free;
for (i = 0x2a; i <= 0x3d; i++) {
cur = i2c_smbus_read_byte_data(new_client, i);
if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur)
|| (i2c_smbus_read_byte_data(new_client, i + 0x80) != cur)
|| (i2c_smbus_read_byte_data(new_client, i + 0xc0) != cur))
goto error_free;
}
/* Determine the chip type - only one kind supported! */
kind = lm80;
name = "lm80";
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, 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 error_free;
/* Initialize the LM80 chip */
lm80_init_client(new_client);
/* A few vars need to be filled upon startup */
data->fan_min[0] = lm80_read_value(new_client, LM80_REG_FAN_MIN(1));
data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2));
/* Register sysfs hooks */
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_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_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_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
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_div);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
error_free:
kfree(data);
exit:
return err;
}
static int lm80_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 int lm80_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new LM80. */
static void lm80_init_client(struct i2c_client *client)
{
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others. This makes most other
initializations unnecessary */
lm80_write_value(client, LM80_REG_CONFIG, 0x80);
/* Set 11-bit temperature resolution */
lm80_write_value(client, LM80_REG_RES, 0x08);
/* Start monitoring */
lm80_write_value(client, LM80_REG_CONFIG, 0x01);
}
static struct lm80_data *lm80_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm80_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
dev_dbg(&client->dev, "Starting lm80 update\n");
for (i = 0; i <= 6; i++) {
data->in[i] =
lm80_read_value(client, LM80_REG_IN(i));
data->in_min[i] =
lm80_read_value(client, LM80_REG_IN_MIN(i));
data->in_max[i] =
lm80_read_value(client, LM80_REG_IN_MAX(i));
}
data->fan[0] = lm80_read_value(client, LM80_REG_FAN1);
data->fan_min[0] =
lm80_read_value(client, LM80_REG_FAN_MIN(1));
data->fan[1] = lm80_read_value(client, LM80_REG_FAN2);
data->fan_min[1] =
lm80_read_value(client, LM80_REG_FAN_MIN(2));
data->temp =
(lm80_read_value(client, LM80_REG_TEMP) << 8) |
(lm80_read_value(client, LM80_REG_RES) & 0xf0);
data->temp_os_max =
lm80_read_value(client, LM80_REG_TEMP_OS_MAX);
data->temp_os_hyst =
lm80_read_value(client, LM80_REG_TEMP_OS_HYST);
data->temp_hot_max =
lm80_read_value(client, LM80_REG_TEMP_HOT_MAX);
data->temp_hot_hyst =
lm80_read_value(client, LM80_REG_TEMP_HOT_HYST);
i = lm80_read_value(client, LM80_REG_FANDIV);
data->fan_div[0] = (i >> 2) & 0x03;
data->fan_div[1] = (i >> 4) & 0x03;
data->alarms = lm80_read_value(client, LM80_REG_ALARM1) +
(lm80_read_value(client, LM80_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm80_init(void)
{
return i2c_add_driver(&lm80_driver);
}
static void __exit sensors_lm80_exit(void)
{
i2c_del_driver(&lm80_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("LM80 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm80_init);
module_exit(sensors_lm80_exit);

412
drivers/i2c/chips/lm83.c Normal file
View File

@@ -0,0 +1,412 @@
/*
* lm83.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003 Jean Delvare <khali@linux-fr.org>
*
* Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
* a sensor chip made by National Semiconductor. It reports up to four
* temperatures (its own plus up to three external ones) with a 1 deg
* resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
* from National's website at:
* http://www.national.com/pf/LM/LM83.html
* Since the datasheet omits to give the chip stepping code, I give it
* here: 0x03 (at register 0xff).
*
* 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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
* Address is selected using 2 three-level pins, resulting in 9 possible
* addresses.
*/
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm83);
/*
* The LM83 registers
* Manufacturer ID is 0x01 for National Semiconductor.
*/
#define LM83_REG_R_MAN_ID 0xFE
#define LM83_REG_R_CHIP_ID 0xFF
#define LM83_REG_R_CONFIG 0x03
#define LM83_REG_W_CONFIG 0x09
#define LM83_REG_R_STATUS1 0x02
#define LM83_REG_R_STATUS2 0x35
#define LM83_REG_R_LOCAL_TEMP 0x00
#define LM83_REG_R_LOCAL_HIGH 0x05
#define LM83_REG_W_LOCAL_HIGH 0x0B
#define LM83_REG_R_REMOTE1_TEMP 0x30
#define LM83_REG_R_REMOTE1_HIGH 0x38
#define LM83_REG_W_REMOTE1_HIGH 0x50
#define LM83_REG_R_REMOTE2_TEMP 0x01
#define LM83_REG_R_REMOTE2_HIGH 0x07
#define LM83_REG_W_REMOTE2_HIGH 0x0D
#define LM83_REG_R_REMOTE3_TEMP 0x31
#define LM83_REG_R_REMOTE3_HIGH 0x3A
#define LM83_REG_W_REMOTE3_HIGH 0x52
#define LM83_REG_R_TCRIT 0x42
#define LM83_REG_W_TCRIT 0x5A
/*
* Conversions and various macros
* The LM83 uses signed 8-bit values with LSB = 1 degree Celcius.
*/
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
static const u8 LM83_REG_R_TEMP[] = {
LM83_REG_R_LOCAL_TEMP,
LM83_REG_R_REMOTE1_TEMP,
LM83_REG_R_REMOTE2_TEMP,
LM83_REG_R_REMOTE3_TEMP
};
static const u8 LM83_REG_R_HIGH[] = {
LM83_REG_R_LOCAL_HIGH,
LM83_REG_R_REMOTE1_HIGH,
LM83_REG_R_REMOTE2_HIGH,
LM83_REG_R_REMOTE3_HIGH
};
static const u8 LM83_REG_W_HIGH[] = {
LM83_REG_W_LOCAL_HIGH,
LM83_REG_W_REMOTE1_HIGH,
LM83_REG_W_REMOTE2_HIGH,
LM83_REG_W_REMOTE3_HIGH
};
/*
* Functions declaration
*/
static int lm83_attach_adapter(struct i2c_adapter *adapter);
static int lm83_detect(struct i2c_adapter *adapter, int address, int kind);
static int lm83_detach_client(struct i2c_client *client);
static struct lm83_data *lm83_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm83_driver = {
.owner = THIS_MODULE,
.name = "lm83",
.id = I2C_DRIVERID_LM83,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm83_attach_adapter,
.detach_client = lm83_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm83_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 */
s8 temp_input[4];
s8 temp_high[4];
s8 temp_crit;
u16 alarms; /* bitvector, combined */
};
/*
* Sysfs stuff
*/
#define show_temp(suffix, value) \
static ssize_t show_temp_##suffix(struct device *dev, char *buf) \
{ \
struct lm83_data *data = lm83_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show_temp(input1, temp_input[0]);
show_temp(input2, temp_input[1]);
show_temp(input3, temp_input[2]);
show_temp(input4, temp_input[3]);
show_temp(high1, temp_high[0]);
show_temp(high2, temp_high[1]);
show_temp(high3, temp_high[2]);
show_temp(high4, temp_high[3]);
show_temp(crit, temp_crit);
#define set_temp(suffix, value, reg) \
static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm83_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(high1, temp_high[0], LM83_REG_W_LOCAL_HIGH);
set_temp(high2, temp_high[1], LM83_REG_W_REMOTE1_HIGH);
set_temp(high3, temp_high[2], LM83_REG_W_REMOTE2_HIGH);
set_temp(high4, temp_high[3], LM83_REG_W_REMOTE3_HIGH);
set_temp(crit, temp_crit, LM83_REG_W_TCRIT);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm83_data *data = lm83_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);
static DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input3, NULL);
static DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input4, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_high1,
set_temp_high1);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2,
set_temp_high2);
static DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_high3,
set_temp_high3);
static DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp_high4,
set_temp_high4);
static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL);
static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL);
static DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp_crit,
set_temp_crit);
static DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm83_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm83_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm83_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm83_data));
/* The common I2C client data is placed right after the
* LM83-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm83_driver;
new_client->flags = 0;
/* Now we do the detection and identification. A negative kind
* means that the driver was loaded with no force parameter
* (default), so we must both detect and identify the chip
* (actually there is only one possible kind of chip for now, LM83).
* A zero kind means that the driver was loaded with the force
* parameter, the detection step shall be skipped. A positive kind
* means that the driver was loaded with the force parameter and a
* given kind of chip is requested, so both the detection and the
* identification steps are skipped. */
/* Default to an LM83 if forced */
if (kind == 0)
kind = lm83;
if (kind < 0) { /* detection */
if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
& 0xA8) != 0x00) ||
((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
& 0x48) != 0x00) ||
((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
& 0x41) != 0x00)) {
dev_dbg(&adapter->dev,
"LM83 detection failed at 0x%02x.\n", address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
u8 man_id, chip_id;
man_id = i2c_smbus_read_byte_data(new_client,
LM83_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM83_REG_R_CHIP_ID);
if (man_id == 0x01) { /* National Semiconductor */
if (chip_id == 0x03) {
kind = lm83;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == lm83) {
name = "lm83";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, 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 LM83 chip
* (Nothing to do for this one.)
*/
/* Register sysfs hooks */
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_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp4_input);
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_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp4_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_temp3_crit);
device_create_file(&new_client->dev, &dev_attr_temp4_crit);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm83_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 lm83_data *lm83_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm83_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
int nr;
dev_dbg(&client->dev, "Updating lm83 data.\n");
for (nr = 0; nr < 4 ; nr++) {
data->temp_input[nr] =
i2c_smbus_read_byte_data(client,
LM83_REG_R_TEMP[nr]);
data->temp_high[nr] =
i2c_smbus_read_byte_data(client,
LM83_REG_R_HIGH[nr]);
}
data->temp_crit =
i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
data->alarms =
i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
+ (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
<< 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm83_init(void)
{
return i2c_add_driver(&lm83_driver);
}
static void __exit sensors_lm83_exit(void)
{
i2c_del_driver(&lm83_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM83 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm83_init);
module_exit(sensors_lm83_exit);

1578
drivers/i2c/chips/lm85.c Normal file

File diff suppressed because it is too large Load Diff

829
drivers/i2c/chips/lm87.c Normal file
View File

@@ -0,0 +1,829 @@
/*
* lm87.c
*
* Copyright (C) 2000 Frodo Looijaard <frodol@dds.nl>
* Philip Edelbrock <phil@netroedge.com>
* Stephen Rousset <stephen.rousset@rocketlogix.com>
* Dan Eaton <dan.eaton@rocketlogix.com>
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
*
* Original port to Linux 2.6 by Jeff Oliver.
*
* The LM87 is a sensor chip made by National Semiconductor. It monitors up
* to 8 voltages (including its own power source), up to three temperatures
* (its own plus up to two external ones) and up to two fans. The default
* configuration is 6 voltages, two temperatures and two fans (see below).
* Voltages are scaled internally with ratios such that the nominal value of
* each voltage correspond to a register value of 192 (which means a
* resolution of about 0.5% of the nominal value). Temperature values are
* reported with a 1 deg resolution and a 3-4 deg accuracy. Complete
* datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM87.html
*
* Some functions share pins, so not all functions are available at the same
* time. Which are depends on the hardware setup. This driver assumes that
* the BIOS configured the chip correctly. In that respect, it differs from
* the original driver (from lm_sensors for Linux 2.4), which would force the
* LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
* chipset wiring.
* For reference, here is the list of exclusive functions:
* - in0+in5 (default) or temp3
* - fan1 (default) or in6
* - fan2 (default) or in7
* - VID lines (default) or IRQ lines (not handled by this driver)
*
* The LM87 additionally features an analog output, supposedly usable to
* control the speed of a fan. All new chips use pulse width modulation
* instead. The LM87 is the only hardware monitoring chipset I know of
* which uses amplitude modulation. Be careful when using this feature.
*
* 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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/*
* Addresses to scan
* LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
*/
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm87);
/*
* The LM87 registers
*/
/* nr in 0..5 */
#define LM87_REG_IN(nr) (0x20 + (nr))
#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2)
#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2)
/* nr in 0..1 */
#define LM87_REG_AIN(nr) (0x28 + (nr))
#define LM87_REG_AIN_MIN(nr) (0x1A + (nr))
#define LM87_REG_AIN_MAX(nr) (0x3B + (nr))
static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 };
static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B };
static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
#define LM87_REG_TEMP_HW_INT_LOCK 0x13
#define LM87_REG_TEMP_HW_EXT_LOCK 0x14
#define LM87_REG_TEMP_HW_INT 0x17
#define LM87_REG_TEMP_HW_EXT 0x18
/* nr in 0..1 */
#define LM87_REG_FAN(nr) (0x28 + (nr))
#define LM87_REG_FAN_MIN(nr) (0x3B + (nr))
#define LM87_REG_AOUT 0x19
#define LM87_REG_CONFIG 0x40
#define LM87_REG_CHANNEL_MODE 0x16
#define LM87_REG_VID_FAN_DIV 0x47
#define LM87_REG_VID4 0x49
#define LM87_REG_ALARMS1 0x41
#define LM87_REG_ALARMS2 0x42
#define LM87_REG_COMPANY_ID 0x3E
#define LM87_REG_REVISION 0x3F
/*
* Conversions and various macros
* The LM87 uses signed 8-bit values for temperatures.
*/
#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \
((val) * 192 + (scale)/2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000)
#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
(val) >= 126500 ? 127 : \
(((val) < 0 ? (val)-500 : (val)+500) / 1000))
#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \
1350000 + (reg)*(div) / 2) / ((reg)*(div))
#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \
(1350000 + (val)*(div) / 2) / ((val)*(div)))
#define FAN_DIV_FROM_REG(reg) (1 << (reg))
/* analog out is 9.80mV/LSB */
#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10)
#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \
(val) >= 2500 ? 255 : \
((val) * 10 + 49) / 98)
/* nr in 0..1 */
#define CHAN_NO_FAN(nr) (1 << (nr))
#define CHAN_TEMP3 (1 << 2)
#define CHAN_VCC_5V (1 << 3)
#define CHAN_NO_VID (1 << 8)
/*
* Functions declaration
*/
static int lm87_attach_adapter(struct i2c_adapter *adapter);
static int lm87_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm87_init_client(struct i2c_client *client);
static int lm87_detach_client(struct i2c_client *client);
static struct lm87_data *lm87_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm87_driver = {
.owner = THIS_MODULE,
.name = "lm87",
.id = I2C_DRIVERID_LM87,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm87_attach_adapter,
.detach_client = lm87_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm87_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 channel; /* register value */
u8 in[8]; /* register value */
u8 in_max[8]; /* register value */
u8 in_min[8]; /* register value */
u16 in_scale[8];
s8 temp[3]; /* register value */
s8 temp_high[3]; /* register value */
s8 temp_low[3]; /* register value */
s8 temp_crit_int; /* min of two register values */
s8 temp_crit_ext; /* min of two register values */
u8 fan[2]; /* register value */
u8 fan_min[2]; /* register value */
u8 fan_div[2]; /* register value, shifted right */
u8 aout; /* register value */
u16 alarms; /* register values, combined */
u8 vid; /* register values, combined */
u8 vrm;
};
/*
* Sysfs stuff
*/
static inline int lm87_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
#define show_in(offset) \
static ssize_t show_in##offset##_input(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
data->in_scale[offset])); \
} \
static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
data->in_scale[offset])); \
} \
static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
data->in_scale[offset])); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset##_input, NULL);
show_in(0);
show_in(1);
show_in(2);
show_in(3);
show_in(4);
show_in(5);
show_in(6);
show_in(7);
static void set_in_min(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]);
lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) :
LM87_REG_AIN_MIN(nr-6), data->in_min[nr]);
up(&data->update_lock);
}
static void set_in_max(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]);
lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) :
LM87_REG_AIN_MAX(nr-6), data->in_max[nr]);
up(&data->update_lock);
}
#define set_in(offset) \
static ssize_t set_in##offset##_min(struct device *dev, \
const char *buf, size_t count) \
{ \
set_in_min(dev, buf, offset); \
return count; \
} \
static ssize_t set_in##offset##_max(struct device *dev, \
const char *buf, size_t count) \
{ \
set_in_max(dev, buf, offset); \
return count; \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
set_in(0);
set_in(1);
set_in(2);
set_in(3);
set_in(4);
set_in(5);
set_in(6);
set_in(7);
#define show_temp(offset) \
static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
} \
static ssize_t show_temp##offset##_low(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \
} \
static ssize_t show_temp##offset##_high(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \
}\
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
show_temp##offset##_input, NULL);
show_temp(1);
show_temp(2);
show_temp(3);
static void set_temp_low(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_low[nr] = TEMP_TO_REG(val);
lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);
up(&data->update_lock);
}
static void set_temp_high(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_high[nr] = TEMP_TO_REG(val);
lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);
up(&data->update_lock);
}
#define set_temp(offset) \
static ssize_t set_temp##offset##_low(struct device *dev, \
const char *buf, size_t count) \
{ \
set_temp_low(dev, buf, offset-1); \
return count; \
} \
static ssize_t set_temp##offset##_high(struct device *dev, \
const char *buf, size_t count) \
{ \
set_temp_high(dev, buf, offset-1); \
return count; \
} \
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp##offset##_high, set_temp##offset##_high); \
static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
show_temp##offset##_low, set_temp##offset##_low);
set_temp(1);
set_temp(2);
set_temp(3);
static ssize_t show_temp_crit_int(struct device *dev, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
}
static ssize_t show_temp_crit_ext(struct device *dev, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
}
static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL);
static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL);
static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL);
#define show_fan(offset) \
static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \
FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
} \
static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \
FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
} \
static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
show_fan##offset##_input, NULL);
show_fan(1);
show_fan(2);
static void set_fan_min(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val,
FAN_DIV_FROM_REG(data->fan_div[nr]));
lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
of least suprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
unsigned long min;
u8 reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
up(&data->update_lock);
return -EINVAL;
}
reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
switch (nr) {
case 0:
reg = (reg & 0xCF) | (data->fan_div[0] << 4);
break;
case 1:
reg = (reg & 0x3F) | (data->fan_div[1] << 6);
break;
}
lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
data->fan_min[nr] = FAN_TO_REG(min, val);
lm87_write_value(client, LM87_REG_FAN_MIN(nr),
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define set_fan(offset) \
static ssize_t set_fan##offset##_min(struct device *dev, const char *buf, \
size_t count) \
{ \
set_fan_min(dev, buf, offset-1); \
return count; \
} \
static ssize_t set_fan##offset##_div(struct device *dev, const char *buf, \
size_t count) \
{ \
return set_fan_div(dev, buf, count, offset-1); \
} \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan##offset##_min, set_fan##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan##offset##_div, set_fan##offset##_div);
set_fan(1);
set_fan(2);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static ssize_t show_vid(struct device *dev, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\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 lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\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 lm87_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_aout(struct device *dev, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
static ssize_t set_aout(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->aout = AOUT_TO_REG(val);
lm87_write_value(client, LM87_REG_AOUT, data->aout);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
/*
* Real code
*/
static int lm87_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm87_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm87_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm87_data));
/* The common I2C client data is placed right before the
LM87-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm87_driver;
new_client->flags = 0;
/* Default to an LM87 if forced */
if (kind == 0)
kind = lm87;
/* Now, we do the remaining detection. */
if (kind < 0) {
u8 rev = lm87_read_value(new_client, LM87_REG_REVISION);
if (rev < 0x01 || rev > 0x08
|| (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
|| lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02) {
dev_dbg(&adapter->dev,
"LM87 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, "lm87", 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 LM87 chip */
lm87_init_client(new_client);
data->in_scale[0] = 2500;
data->in_scale[1] = 2700;
data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300;
data->in_scale[3] = 5000;
data->in_scale[4] = 12000;
data->in_scale[5] = 2700;
data->in_scale[6] = 1875;
data->in_scale[7] = 1875;
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
if (data->channel & CHAN_NO_FAN(0)) {
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in6_max);
} else {
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_fan1_div);
}
if (data->channel & CHAN_NO_FAN(1)) {
device_create_file(&new_client->dev, &dev_attr_in7_input);
device_create_file(&new_client->dev, &dev_attr_in7_min);
device_create_file(&new_client->dev, &dev_attr_in7_max);
} else {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
}
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
if (data->channel & CHAN_TEMP3) {
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp3_min);
device_create_file(&new_client->dev, &dev_attr_temp3_crit);
} else {
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in5_max);
}
if (!(data->channel & CHAN_NO_VID)) {
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);
device_create_file(&new_client->dev, &dev_attr_aout_output);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void lm87_init_client(struct i2c_client *client)
{
struct lm87_data *data = i2c_get_clientdata(client);
u8 config;
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
data->vrm = i2c_which_vrm();
config = lm87_read_value(client, LM87_REG_CONFIG);
if (!(config & 0x01)) {
int i;
/* Limits are left uninitialized after power-up */
for (i = 1; i < 6; i++) {
lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
}
for (i = 0; i < 2; i++) {
lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F);
lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00);
lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
}
if (data->channel & CHAN_TEMP3) {
lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F);
lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00);
} else {
lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
}
}
if ((config & 0x81) != 0x01) {
/* Start monitoring */
lm87_write_value(client, LM87_REG_CONFIG,
(config & 0xF7) | 0x01);
}
}
static int lm87_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 lm87_data *lm87_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
int i, j;
dev_dbg(&client->dev, "Updating data.\n");
i = (data->channel & CHAN_TEMP3) ? 1 : 0;
j = (data->channel & CHAN_TEMP3) ? 5 : 6;
for (; i < j; i++) {
data->in[i] = lm87_read_value(client,
LM87_REG_IN(i));
data->in_min[i] = lm87_read_value(client,
LM87_REG_IN_MIN(i));
data->in_max[i] = lm87_read_value(client,
LM87_REG_IN_MAX(i));
}
for (i = 0; i < 2; i++) {
if (data->channel & CHAN_NO_FAN(i)) {
data->in[6+i] = lm87_read_value(client,
LM87_REG_AIN(i));
data->in_max[6+i] = lm87_read_value(client,
LM87_REG_AIN_MAX(i));
data->in_min[6+i] = lm87_read_value(client,
LM87_REG_AIN_MIN(i));
} else {
data->fan[i] = lm87_read_value(client,
LM87_REG_FAN(i));
data->fan_min[i] = lm87_read_value(client,
LM87_REG_FAN_MIN(i));
}
}
j = (data->channel & CHAN_TEMP3) ? 3 : 2;
for (i = 0 ; i < j; i++) {
data->temp[i] = lm87_read_value(client,
LM87_REG_TEMP[i]);
data->temp_high[i] = lm87_read_value(client,
LM87_REG_TEMP_HIGH[i]);
data->temp_low[i] = lm87_read_value(client,
LM87_REG_TEMP_LOW[i]);
}
i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK);
j = lm87_read_value(client, LM87_REG_TEMP_HW_INT);
data->temp_crit_int = min(i, j);
i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK);
j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT);
data->temp_crit_ext = min(i, j);
i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = (i >> 6) & 0x03;
data->vid = (i & 0x0F)
| (lm87_read_value(client, LM87_REG_VID4) & 0x01)
<< 4;
data->alarms = lm87_read_value(client, LM87_REG_ALARMS1)
| (lm87_read_value(client, LM87_REG_ALARMS2)
<< 8);
data->aout = lm87_read_value(client, LM87_REG_AOUT);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm87_init(void)
{
return i2c_add_driver(&lm87_driver);
}
static void __exit sensors_lm87_exit(void)
{
i2c_del_driver(&lm87_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org> and others");
MODULE_DESCRIPTION("LM87 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm87_init);
module_exit(sensors_lm87_exit);

626
drivers/i2c/chips/lm90.c Normal file
View File

@@ -0,0 +1,626 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
* one external one) with a 0.125 deg resolution (1 deg for local
* temperature) and a 3-4 deg accuracy. Complete datasheet can be
* obtained from National's website at:
* http://www.national.com/pf/LM/LM90.html
*
* This driver also supports the LM89 and LM99, two other sensor chips
* made by National Semiconductor. Both have an increased remote
* temperature measurement accuracy (1 degree), and the LM99
* additionally shifts remote temperatures (measured and limits) by 16
* degrees, which allows for higher temperatures measurement. The
* driver doesn't handle it since it can be done easily in user-space.
* Complete datasheets can be obtained from National's website at:
* http://www.national.com/pf/LM/LM89.html
* http://www.national.com/pf/LM/LM99.html
* Note that there is no way to differenciate between both chips.
*
* This driver also supports the LM86, another sensor chip made by
* National Semiconductor. It is exactly similar to the LM90 except it
* has a higher accuracy.
* Complete datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM86.html
*
* This driver also supports the ADM1032, a sensor chip made by Analog
* Devices. That chip is similar to the LM90, with a few differences
* that are not handled by this driver. Complete datasheet can be
* obtained from Analog's website at:
* http://products.analog.com/products/info.asp?product=ADM1032
* Among others, it has a higher accuracy than the LM90, much like the
* LM86 does.
*
* This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
* chips made by Maxim. These chips are similar to the LM86. Complete
* datasheet can be obtained at Maxim's website at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
* Note that there is no easy way to differenciate between the three
* variants. The extra address and features of the MAX6659 are not
* supported by this driver.
*
* This driver also supports the ADT7461 chip from Analog Devices but
* only in its "compatability mode". If an ADT7461 chip is found but
* is configured in non-compatible mode (where its temperature
* register values are decoded differently) it is ignored by this
* driver. Complete datasheet can be obtained from Analog's website
* at:
* http://products.analog.com/products/info.asp?product=ADT7461
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* 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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
* Address is fully defined internally and cannot be changed except for
* MAX6659.
* LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c.
* LM89-1, and LM99-1 have address 0x4d.
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
* ADT7461 always has address 0x4c.
*/
static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461);
/*
* The LM90 registers
*/
#define LM90_REG_R_MAN_ID 0xFE
#define LM90_REG_R_CHIP_ID 0xFF
#define LM90_REG_R_CONFIG1 0x03
#define LM90_REG_W_CONFIG1 0x09
#define LM90_REG_R_CONFIG2 0xBF
#define LM90_REG_W_CONFIG2 0xBF
#define LM90_REG_R_CONVRATE 0x04
#define LM90_REG_W_CONVRATE 0x0A
#define LM90_REG_R_STATUS 0x02
#define LM90_REG_R_LOCAL_TEMP 0x00
#define LM90_REG_R_LOCAL_HIGH 0x05
#define LM90_REG_W_LOCAL_HIGH 0x0B
#define LM90_REG_R_LOCAL_LOW 0x06
#define LM90_REG_W_LOCAL_LOW 0x0C
#define LM90_REG_R_LOCAL_CRIT 0x20
#define LM90_REG_W_LOCAL_CRIT 0x20
#define LM90_REG_R_REMOTE_TEMPH 0x01
#define LM90_REG_R_REMOTE_TEMPL 0x10
#define LM90_REG_R_REMOTE_OFFSH 0x11
#define LM90_REG_W_REMOTE_OFFSH 0x11
#define LM90_REG_R_REMOTE_OFFSL 0x12
#define LM90_REG_W_REMOTE_OFFSL 0x12
#define LM90_REG_R_REMOTE_HIGHH 0x07
#define LM90_REG_W_REMOTE_HIGHH 0x0D
#define LM90_REG_R_REMOTE_HIGHL 0x13
#define LM90_REG_W_REMOTE_HIGHL 0x13
#define LM90_REG_R_REMOTE_LOWH 0x08
#define LM90_REG_W_REMOTE_LOWH 0x0E
#define LM90_REG_R_REMOTE_LOWL 0x14
#define LM90_REG_W_REMOTE_LOWL 0x14
#define LM90_REG_R_REMOTE_CRIT 0x19
#define LM90_REG_W_REMOTE_CRIT 0x19
#define LM90_REG_R_TCRIT_HYST 0x21
#define LM90_REG_W_TCRIT_HYST 0x21
/*
* Conversions and various macros
* For local temperatures and limits, critical limits and the hysteresis
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celcius.
* For remote temperatures and limits, it uses signed 11-bit values with
* LSB = 0.125 degree Celcius, left-justified in 16-bit registers.
*/
#define TEMP1_FROM_REG(val) ((val) * 1000)
#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
#define TEMP2_FROM_REG(val) ((val) / 32 * 125)
#define TEMP2_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) >= 30500 ? 31 : \
((val) + 500) / 1000)
/*
* ADT7461 is almost identical to LM90 except that attempts to write
* values that are outside the range 0 < temp < 127 are treated as
* the boundary value.
*/
#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
(val) >= 127750 ? 0x7FC0 : \
((val) + 125) / 250 * 64)
/*
* Functions declaration
*/
static int lm90_attach_adapter(struct i2c_adapter *adapter);
static int lm90_detect(struct i2c_adapter *adapter, int address,
int kind);
static void lm90_init_client(struct i2c_client *client);
static int lm90_detach_client(struct i2c_client *client);
static struct lm90_data *lm90_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm90_driver = {
.owner = THIS_MODULE,
.name = "lm90",
.id = I2C_DRIVERID_LM90,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm90_attach_adapter,
.detach_client = lm90_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm90_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int kind;
/* registers values */
s8 temp_input1, temp_low1, temp_high1; /* local */
s16 temp_input2, temp_low2, temp_high2; /* remote, combined */
s8 temp_crit1, temp_crit2;
u8 temp_hyst;
u8 alarms; /* bitvector */
};
/*
* Sysfs stuff
*/
#define show_temp(value, converter) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm90_data *data = lm90_update_device(dev); \
return sprintf(buf, "%d\n", converter(data->value)); \
}
show_temp(temp_input1, TEMP1_FROM_REG);
show_temp(temp_input2, TEMP2_FROM_REG);
show_temp(temp_low1, TEMP1_FROM_REG);
show_temp(temp_low2, TEMP2_FROM_REG);
show_temp(temp_high1, TEMP1_FROM_REG);
show_temp(temp_high2, TEMP2_FROM_REG);
show_temp(temp_crit1, TEMP1_FROM_REG);
show_temp(temp_crit2, TEMP1_FROM_REG);
#define set_temp1(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 lm90_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
if (data->kind == adt7461) \
data->value = TEMP1_TO_REG_ADT7461(val); \
else \
data->value = TEMP1_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
#define set_temp2(value, regh, regl) \
static ssize_t set_##value(struct device *dev, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm90_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
if (data->kind == adt7461) \
data->value = TEMP2_TO_REG_ADT7461(val); \
else \
data->value = TEMP2_TO_REG(val); \
i2c_smbus_write_byte_data(client, regh, data->value >> 8); \
i2c_smbus_write_byte_data(client, regl, data->value & 0xff); \
up(&data->update_lock); \
return count; \
}
set_temp1(temp_low1, LM90_REG_W_LOCAL_LOW);
set_temp2(temp_low2, LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL);
set_temp1(temp_high1, LM90_REG_W_LOCAL_HIGH);
set_temp2(temp_high2, LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL);
set_temp1(temp_crit1, LM90_REG_W_LOCAL_CRIT);
set_temp1(temp_crit2, LM90_REG_W_REMOTE_CRIT);
#define show_temp_hyst(value, basereg) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm90_data *data = lm90_update_device(dev); \
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->basereg) \
- TEMP1_FROM_REG(data->temp_hyst)); \
}
show_temp_hyst(temp_hyst1, temp_crit1);
show_temp_hyst(temp_hyst2, temp_crit2);
static ssize_t set_temp_hyst1(struct device *dev, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
long hyst;
down(&data->update_lock);
hyst = TEMP1_FROM_REG(data->temp_crit1) - val;
i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
HYST_TO_REG(hyst));
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_low1,
set_temp_low1);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2,
set_temp_low2);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_high1,
set_temp_high1);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2,
set_temp_high2);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit1,
set_temp_crit1);
static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2,
set_temp_crit2);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst1,
set_temp_hyst1);
static DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_hyst2, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm90_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm90_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm90_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm90_data));
/* The common I2C client data is placed right before the
LM90-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm90_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
/* Default to an LM90 if forced */
if (kind == 0)
kind = lm90;
if (kind < 0) { /* detection and identification */
u8 man_id, chip_id, reg_config1, reg_convrate;
man_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CHIP_ID);
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG1);
reg_convrate = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONVRATE);
if (man_id == 0x01) { /* National Semiconductor */
u8 reg_config2;
reg_config2 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG2);
if ((reg_config1 & 0x2A) == 0x00
&& (reg_config2 & 0xF8) == 0x00
&& reg_convrate <= 0x09) {
if (address == 0x4C
&& (chip_id & 0xF0) == 0x20) { /* LM90 */
kind = lm90;
} else
if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
kind = lm99;
} else
if (address == 0x4C
&& (chip_id & 0xF0) == 0x10) { /* LM86 */
kind = lm86;
}
}
} else
if (man_id == 0x41) { /* Analog Devices */
if (address == 0x4C
&& (chip_id & 0xF0) == 0x40 /* ADM1032 */
&& (reg_config1 & 0x3F) == 0x00
&& reg_convrate <= 0x0A) {
kind = adm1032;
} else
if (address == 0x4c
&& chip_id == 0x51 /* ADT7461 */
&& (reg_config1 & 0x1F) == 0x00 /* check compat mode */
&& reg_convrate <= 0x0A) {
kind = adt7461;
}
} else
if (man_id == 0x4D) { /* Maxim */
/*
* The Maxim variants do NOT have a chip_id register.
* Reading from that address will return the last read
* value, which in our case is those of the man_id
* register. Likewise, the config1 register seems to
* lack a low nibble, so the value will be those of the
* previous read, so in our case those of the man_id
* register.
*/
if (chip_id == man_id
&& (reg_config1 & 0x1F) == (man_id & 0x0F)
&& reg_convrate <= 0x09) {
kind = max6657;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == lm90) {
name = "lm90";
} else if (kind == adm1032) {
name = "adm1032";
} else if (kind == lm99) {
name = "lm99";
} else if (kind == lm86) {
name = "lm86";
} else if (kind == max6657) {
name = "max6657";
} else if (kind == adt7461) {
name = "adt7461";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->valid = 0;
data->kind = kind;
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 LM90 chip */
lm90_init_client(new_client);
/* Register sysfs hooks */
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_crit_hyst);
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;
}
static void lm90_init_client(struct i2c_client *client)
{
u8 config;
/*
* Start the conversions.
*/
i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
5); /* 2 Hz */
config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
if (config & 0x40)
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
config & 0xBF); /* run */
}
static int lm90_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 lm90_data *lm90_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
u8 oldh, newh;
dev_dbg(&client->dev, "Updating lm90 data.\n");
data->temp_input1 = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_TEMP);
data->temp_high1 = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_HIGH);
data->temp_low1 = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_LOW);
data->temp_crit1 = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_CRIT);
data->temp_crit2 = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_CRIT);
data->temp_hyst = i2c_smbus_read_byte_data(client,
LM90_REG_R_TCRIT_HYST);
/*
* There is a trick here. We have to read two registers to
* have the remote sensor temperature, but we have to beware
* a conversion could occur inbetween the readings. The
* datasheet says we should either use the one-shot
* conversion register, which we don't want to do (disables
* hardware monitoring) or monitor the busy bit, which is
* impossible (we can't read the values and monitor that bit
* at the exact same time). So the solution used here is to
* read the high byte once, then the low byte, then the high
* byte again. If the new high byte matches the old one,
* then we have a valid reading. Else we have to read the low
* byte again, and now we believe we have a correct reading.
*/
oldh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
data->temp_input2 = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPL);
newh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
if (newh != oldh) {
data->temp_input2 = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPL);
#ifdef DEBUG
oldh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
/* oldh is actually newer */
if (newh != oldh)
dev_warn(&client->dev, "Remote temperature may be "
"wrong.\n");
#endif
}
data->temp_input2 |= (newh << 8);
data->temp_high2 = (i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_HIGHH) << 8) +
i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_HIGHL);
data->temp_low2 = (i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_LOWH) << 8) +
i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_LOWL);
data->alarms = i2c_smbus_read_byte_data(client,
LM90_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm90_init(void)
{
return i2c_add_driver(&lm90_driver);
}
static void __exit sensors_lm90_exit(void)
{
i2c_del_driver(&lm90_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM90/ADM1032 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm90_init);
module_exit(sensors_lm90_exit);

429
drivers/i2c/chips/lm92.c Normal file
View File

@@ -0,0 +1,429 @@
/*
* lm92 - Hardware monitoring driver
* Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm90 driver, with some ideas taken from the lm_sensors
* lm92 driver as well.
*
* The LM92 is a sensor chip made by National Semiconductor. It reports
* its own temperature with a 0.0625 deg resolution and a 0.33 deg
* accuracy. Complete datasheet can be obtained from National's website
* at:
* http://www.national.com/pf/LM/LM92.html
*
* This driver also supports the MAX6635 sensor chip made by Maxim.
* This chip is compatible with the LM92, but has a lesser accuracy
* (1.0 deg). Complete datasheet can be obtained from Maxim's website
* at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074
*
* Since the LM92 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* Support could easily be added for the National Semiconductor LM76
* and Maxim MAX6633 and MAX6634 chips, which are mostly compatible
* with the LM92.
*
* 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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* The LM92 and MAX6635 have 2 two-state pins for address selection,
resulting in 4 possible addresses. */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm92);
/* The LM92 registers */
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
#define LM92_REG_TEMP 0x00 /* 16-bit, RO */
#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */
#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */
#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */
#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */
#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */
/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius,
left-justified in 16-bit registers. No rounding is done, with such
a resolution it's just not worth it. Note that the MAX6635 doesn't
make use of the 4 lower bits for limits (i.e. effective resolution
for limits is 1 degree Celsius). */
static inline int TEMP_FROM_REG(s16 reg)
{
return reg / 8 * 625 / 10;
}
static inline s16 TEMP_TO_REG(int val)
{
if (val <= -60000)
return -60000 * 10 / 625 * 8;
if (val >= 160000)
return 160000 * 10 / 625 * 8;
return val * 10 / 625 * 8;
}
/* Alarm flags are stored in the 3 LSB of the temperature register */
static inline u8 ALARMS_FROM_REG(s16 reg)
{
return reg & 0x0007;
}
/* Driver data (common to all clients) */
static struct i2c_driver lm92_driver;
/* Client data (each client gets its own) */
struct lm92_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 */
s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst;
};
/*
* Sysfs attributes and callback functions
*/
static struct lm92_data *lm92_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm92_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) {
dev_dbg(&client->dev, "Updating lm92 data\n");
data->temp1_input = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP));
data->temp1_hyst = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HYST));
data->temp1_crit = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_CRIT));
data->temp1_min = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_LOW));
data->temp1_max = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HIGH));
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
#define show_temp(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct lm92_data *data = lm92_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show_temp(temp1_input);
show_temp(temp1_crit);
show_temp(temp1_min);
show_temp(temp1_max);
#define set_temp(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 lm92_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
i2c_smbus_write_word_data(client, reg, swab16(data->value)); \
up(&data->update_lock); \
return count; \
}
set_temp(temp1_crit, LM92_REG_TEMP_CRIT);
set_temp(temp1_min, LM92_REG_TEMP_LOW);
set_temp(temp1_max, LM92_REG_TEMP_HIGH);
static ssize_t show_temp1_crit_hyst(struct device *dev, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit)
- TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t show_temp1_max_hyst(struct device *dev, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max)
- TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t show_temp1_min_hyst(struct device *dev, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min)
+ TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t set_temp1_crit_hyst(struct device *dev, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm92_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST,
swab16(TEMP_TO_REG(data->temp1_hyst)));
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input));
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit,
set_temp1_crit);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst,
set_temp1_crit_hyst);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min,
set_temp1_min);
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max,
set_temp1_max);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Detection and registration
*/
static void lm92_init_client(struct i2c_client *client)
{
u8 config;
/* Start the conversions if needed */
config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
if (config & 0x01)
i2c_smbus_write_byte_data(client, LM92_REG_CONFIG,
config & 0xFE);
}
/* The MAX6635 has no identification register, so we have to use tricks
to identify it reliably. This is somewhat slow.
Note that we do NOT rely on the 2 MSB of the configuration register
always reading 0, as suggested by the datasheet, because it was once
reported not to be true. */
static int max6635_check(struct i2c_client *client)
{
u16 temp_low, temp_high, temp_hyst, temp_crit;
u8 conf;
int i;
/* No manufacturer ID register, so a read from this address will
always return the last read value. */
temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW);
if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low)
return 0;
temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH);
if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high)
return 0;
/* Limits are stored as integer values (signed, 9-bit). */
if ((temp_low & 0x7f00) || (temp_high & 0x7f00))
return 0;
temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST);
temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT);
if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00))
return 0;
/* Registers addresses were found to cycle over 16-byte boundaries.
We don't test all registers with all offsets so as to save some
reads and time, but this should still be sufficient to dismiss
non-MAX6635 chips. */
conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
for (i=16; i<96; i*=2) {
if (temp_hyst != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HYST + i - 16)
|| temp_crit != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_CRIT + i)
|| temp_low != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_LOW + i + 16)
|| temp_high != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HIGH + i + 32)
|| conf != i2c_smbus_read_byte_data(client,
LM92_REG_CONFIG + i))
return 0;
}
return 1;
}
/* The following function does more than just detection. If detection
succeeds, it also registers the new chip. */
static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm92_data *data;
int err = 0;
char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm92_data));
/* Fill in enough client fields so that we can read from the chip,
which is required for identication */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm92_driver;
new_client->flags = 0;
/* A negative kind means that the driver was loaded with no force
parameter (default), so we must identify the chip. */
if (kind < 0) {
u8 config = i2c_smbus_read_byte_data(new_client,
LM92_REG_CONFIG);
u16 man_id = i2c_smbus_read_word_data(new_client,
LM92_REG_MAN_ID);
if ((config & 0xe0) == 0x00
&& man_id == 0x0180) {
pr_info("lm92: Found National Semiconductor LM92 chip\n");
kind = lm92;
} else
if (max6635_check(new_client)) {
pr_info("lm92: Found Maxim MAX6635 chip\n");
kind = lm92; /* No separate prefix */
}
else
goto exit_free;
} else
if (kind == 0) /* Default to an LM92 if forced */
kind = lm92;
/* Give it the proper name */
if (kind == lm92) {
name = "lm92";
} else { /* Supposedly cannot happen */
dev_dbg(&new_client->dev, "Kind out of range?\n");
goto exit_free;
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the i2c subsystem a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the chipset */
lm92_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm92_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm92_detect);
}
static int lm92_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;
}
/*
* Module and driver stuff
*/
static struct i2c_driver lm92_driver = {
.owner = THIS_MODULE,
.name = "lm92",
.id = I2C_DRIVERID_LM92,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm92_attach_adapter,
.detach_client = lm92_detach_client,
};
static int __init sensors_lm92_init(void)
{
return i2c_add_driver(&lm92_driver);
}
static void __exit sensors_lm92_exit(void)
{
i2c_del_driver(&lm92_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM92/MAX6635 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm92_init);
module_exit(sensors_lm92_exit);

246
drivers/i2c/chips/m41t00.c Normal file
View File

@@ -0,0 +1,246 @@
/*
* drivers/i2c/chips/m41t00.c
*
* I2C client/driver for the ST M41T00 Real-Time Clock chip.
*
* Author: Mark A. Greer <mgreer@mvista.com>
*
* 2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
/*
* This i2c client/driver wedges between the drivers/char/genrtc.c RTC
* interface and the SMBus interface of the i2c subsystem.
* It would be more efficient to use i2c msgs/i2c_transfer directly but, as
* recommened in .../Documentation/i2c/writing-clients section
* "Sending and receiving", using SMBus level communication is preferred.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <asm/time.h>
#include <asm/rtc.h>
#define M41T00_DRV_NAME "m41t00"
static DECLARE_MUTEX(m41t00_mutex);
static struct i2c_driver m41t00_driver;
static struct i2c_client *save_client;
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.normal_i2c_range = ignore,
.probe = ignore,
.probe_range = ignore,
.ignore = ignore,
.ignore_range = ignore,
.force = ignore,
};
ulong
m41t00_get_rtc_time(void)
{
s32 sec, min, hour, day, mon, year;
s32 sec1, min1, hour1, day1, mon1, year1;
ulong limit = 10;
sec = min = hour = day = mon = year = 0;
sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
down(&m41t00_mutex);
do {
if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0)
&& ((min = i2c_smbus_read_byte_data(save_client, 1))
>= 0)
&& ((hour = i2c_smbus_read_byte_data(save_client, 2))
>= 0)
&& ((day = i2c_smbus_read_byte_data(save_client, 4))
>= 0)
&& ((mon = i2c_smbus_read_byte_data(save_client, 5))
>= 0)
&& ((year = i2c_smbus_read_byte_data(save_client, 6))
>= 0)
&& ((sec == sec1) && (min == min1) && (hour == hour1)
&& (day == day1) && (mon == mon1)
&& (year == year1)))
break;
sec1 = sec;
min1 = min;
hour1 = hour;
day1 = day;
mon1 = mon;
year1 = year;
} while (--limit > 0);
up(&m41t00_mutex);
if (limit == 0) {
dev_warn(&save_client->dev,
"m41t00: can't read rtc chip\n");
sec = min = hour = day = mon = year = 0;
}
sec &= 0x7f;
min &= 0x7f;
hour &= 0x3f;
day &= 0x3f;
mon &= 0x1f;
year &= 0xff;
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
year += 1900;
if (year < 1970)
year += 100;
return mktime(year, mon, day, hour, min, sec);
}
static void
m41t00_set_tlet(ulong arg)
{
struct rtc_time tm;
ulong nowtime = *(ulong *)arg;
to_tm(nowtime, &tm);
tm.tm_year = (tm.tm_year - 1900) % 100;
BIN_TO_BCD(tm.tm_sec);
BIN_TO_BCD(tm.tm_min);
BIN_TO_BCD(tm.tm_hour);
BIN_TO_BCD(tm.tm_mon);
BIN_TO_BCD(tm.tm_mday);
BIN_TO_BCD(tm.tm_year);
down(&m41t00_mutex);
if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
|| (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
< 0)
|| (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x7f)
< 0)
|| (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x7f)
< 0)
|| (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x7f)
< 0)
|| (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0x7f)
< 0))
dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
up(&m41t00_mutex);
return;
}
ulong new_time;
DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time);
int
m41t00_set_rtc_time(ulong nowtime)
{
new_time = nowtime;
if (in_interrupt())
tasklet_schedule(&m41t00_tasklet);
else
m41t00_set_tlet((ulong)&new_time);
return 0;
}
/*
*****************************************************************************
*
* Driver Interface
*
*****************************************************************************
*/
static int
m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
{
struct i2c_client *client;
int rc;
client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!client)
return -ENOMEM;
memset(client, 0, sizeof(struct i2c_client));
strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE);
client->flags = I2C_DF_NOTIFY;
client->addr = addr;
client->adapter = adap;
client->driver = &m41t00_driver;
if ((rc = i2c_attach_client(client)) != 0) {
kfree(client);
return rc;
}
save_client = client;
return 0;
}
static int
m41t00_attach(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, m41t00_probe);
}
static int
m41t00_detach(struct i2c_client *client)
{
int rc;
if ((rc = i2c_detach_client(client)) == 0) {
kfree(i2c_get_clientdata(client));
tasklet_kill(&m41t00_tasklet);
}
return rc;
}
static struct i2c_driver m41t00_driver = {
.owner = THIS_MODULE,
.name = M41T00_DRV_NAME,
.id = I2C_DRIVERID_STM41T00,
.flags = I2C_DF_NOTIFY,
.attach_adapter = m41t00_attach,
.detach_client = m41t00_detach,
};
static int __init
m41t00_init(void)
{
return i2c_add_driver(&m41t00_driver);
}
static void __exit
m41t00_exit(void)
{
i2c_del_driver(&m41t00_driver);
return;
}
module_init(m41t00_init);
module_exit(m41t00_exit);
MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
MODULE_LICENSE("GPL");

373
drivers/i2c/chips/max1619.c Normal file
View File

@@ -0,0 +1,373 @@
/*
* max1619.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru>
* Jean Delvare <khali@linux-fr.org>
*
* Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim.
* It reports up to two temperatures (its own plus up to
* one external one). Complete datasheet can be
* obtained from Maxim's website at:
* http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
*
* 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/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(max1619);
/*
* The MAX1619 registers
*/
#define MAX1619_REG_R_MAN_ID 0xFE
#define MAX1619_REG_R_CHIP_ID 0xFF
#define MAX1619_REG_R_CONFIG 0x03
#define MAX1619_REG_W_CONFIG 0x09
#define MAX1619_REG_R_CONVRATE 0x04
#define MAX1619_REG_W_CONVRATE 0x0A
#define MAX1619_REG_R_STATUS 0x02
#define MAX1619_REG_R_LOCAL_TEMP 0x00
#define MAX1619_REG_R_REMOTE_TEMP 0x01
#define MAX1619_REG_R_REMOTE_HIGH 0x07
#define MAX1619_REG_W_REMOTE_HIGH 0x0D
#define MAX1619_REG_R_REMOTE_LOW 0x08
#define MAX1619_REG_W_REMOTE_LOW 0x0E
#define MAX1619_REG_R_REMOTE_CRIT 0x10
#define MAX1619_REG_W_REMOTE_CRIT 0x12
#define MAX1619_REG_R_TCRIT_HYST 0x11
#define MAX1619_REG_W_TCRIT_HYST 0x13
/*
* Conversions and various macros
*/
#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)
#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000)
/*
* Functions declaration
*/
static int max1619_attach_adapter(struct i2c_adapter *adapter);
static int max1619_detect(struct i2c_adapter *adapter, int address,
int kind);
static void max1619_init_client(struct i2c_client *client);
static int max1619_detach_client(struct i2c_client *client);
static struct max1619_data *max1619_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver max1619_driver = {
.owner = THIS_MODULE,
.name = "max1619",
.flags = I2C_DF_NOTIFY,
.attach_adapter = max1619_attach_adapter,
.detach_client = max1619_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct max1619_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 temp_input1; /* local */
u8 temp_input2, temp_low2, temp_high2; /* remote */
u8 temp_crit2;
u8 temp_hyst2;
u8 alarms;
};
/*
* Sysfs stuff
*/
#define show_temp(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct max1619_data *data = max1619_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show_temp(temp_input1);
show_temp(temp_input2);
show_temp(temp_low2);
show_temp(temp_high2);
show_temp(temp_crit2);
show_temp(temp_hyst2);
#define set_temp2(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 max1619_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW);
set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH);
set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT);
set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST);
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct max1619_data *data = max1619_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2,
set_temp_low2);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2,
set_temp_high2);
static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2,
set_temp_crit2);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2,
set_temp_hyst2);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int max1619_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, max1619_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int max1619_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct max1619_data *data;
int err = 0;
const char *name = "";
u8 reg_config=0, reg_convrate=0, reg_status=0;
u8 man_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct max1619_data));
/* The common I2C client data is placed right before the
MAX1619-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &max1619_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
if (kind < 0) { /* detection */
reg_config = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CONFIG);
reg_convrate = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CONVRATE);
reg_status = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_STATUS);
if ((reg_config & 0x03) != 0x00
|| reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) {
dev_dbg(&adapter->dev,
"MAX1619 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
man_id = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CHIP_ID);
if ((man_id == 0x4D) && (chip_id == 0x04)){
kind = max1619;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
if (kind == max1619){
name = "max1619";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, 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 MAX1619 chip */
max1619_init_client(new_client);
/* Register sysfs hooks */
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_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;
}
static void max1619_init_client(struct i2c_client *client)
{
u8 config;
/*
* Start the conversions.
*/
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
5); /* 2 Hz */
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
if (config & 0x40)
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
config & 0xBF); /* run */
}
static int max1619_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 max1619_data *max1619_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max1619_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
dev_dbg(&client->dev, "Updating max1619 data.\n");
data->temp_input1 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_LOCAL_TEMP);
data->temp_input2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_TEMP);
data->temp_high2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_HIGH);
data->temp_low2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_LOW);
data->temp_crit2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_CRIT);
data->temp_hyst2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_TCRIT_HYST);
data->alarms = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_max1619_init(void)
{
return i2c_add_driver(&max1619_driver);
}
static void __exit sensors_max1619_exit(void)
{
i2c_del_driver(&max1619_driver);
}
MODULE_AUTHOR("Alexey Fisher <fishor@mail.ru> and"
"Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("MAX1619 sensor driver");
MODULE_LICENSE("GPL");
module_init(sensors_max1619_init);
module_exit(sensors_max1619_exit);

1349
drivers/i2c/chips/pc87360.c Normal file

File diff suppressed because it is too large Load Diff

229
drivers/i2c/chips/pcf8574.c Normal file
View File

@@ -0,0 +1,229 @@
/*
pcf8574.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Dan Eaton <dan.eaton@rocketlogix.com>
Ported to Linux 2.6 by Aurelien Jarno <aurel32@debian.org> with
the help of 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.
*/
/* A few notes about the PCF8574:
* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by
Philips Semiconductors. It is designed to provide a byte I2C
interface to up to 8 separate devices.
* The PCF8574 appears as a very simple SMBus device which can be
read from or written to with SMBUS byte read/write accesses.
--Dan
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(pcf8574, pcf8574a);
/* Initial values */
#define PCF8574_INIT 255 /* All outputs on (input mode) */
/* Each client has this additional data */
struct pcf8574_data {
struct i2c_client client;
u8 read, write; /* Register values */
};
static int pcf8574_attach_adapter(struct i2c_adapter *adapter);
static int pcf8574_detect(struct i2c_adapter *adapter, int address, int kind);
static int pcf8574_detach_client(struct i2c_client *client);
static void pcf8574_init_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver pcf8574_driver = {
.owner = THIS_MODULE,
.name = "pcf8574",
.id = I2C_DRIVERID_PCF8574,
.flags = I2C_DF_NOTIFY,
.attach_adapter = pcf8574_attach_adapter,
.detach_client = pcf8574_detach_client,
};
/* following are the sysfs callback functions */
static ssize_t show_read(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf8574_data *data = i2c_get_clientdata(client);
data->read = i2c_smbus_read_byte(client);
return sprintf(buf, "%u\n", data->read);
}
static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
static ssize_t show_write(struct device *dev, char *buf)
{
struct pcf8574_data *data = i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%u\n", data->write);
}
static ssize_t set_write(struct device *dev, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf8574_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
if (val > 0xff)
return -EINVAL;
data->write = val;
i2c_smbus_write_byte(client, data->write);
return count;
}
static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
/*
* Real code
*/
static int pcf8574_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, pcf8574_detect);
}
/* This function is called by i2c_detect */
int pcf8574_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct pcf8574_data *data;
int err = 0;
const char *client_name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. */
if (!(data = kmalloc(sizeof(struct pcf8574_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct pcf8574_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &pcf8574_driver;
new_client->flags = 0;
/* Now, we would do the remaining detection. But the PCF8574 is plainly
impossible to detect! Stupid chip. */
/* Determine the chip type */
if (kind <= 0) {
if (address >= 0x38 && address <= 0x3f)
kind = pcf8574a;
else
kind = pcf8574;
}
if (kind == pcf8574a)
client_name = "pcf8574a";
else
client_name = "pcf8574";
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the PCF8574 chip */
pcf8574_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_read);
device_create_file(&new_client->dev, &dev_attr_write);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(data);
exit:
return err;
}
static int pcf8574_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;
}
/* Called when we have found a new PCF8574. */
static void pcf8574_init_client(struct i2c_client *client)
{
struct pcf8574_data *data = i2c_get_clientdata(client);
data->write = PCF8574_INIT;
i2c_smbus_write_byte(client, data->write);
}
static int __init pcf8574_init(void)
{
return i2c_add_driver(&pcf8574_driver);
}
static void __exit pcf8574_exit(void)
{
i2c_del_driver(&pcf8574_driver);
}
MODULE_AUTHOR
("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"Dan Eaton <dan.eaton@rocketlogix.com> "
"and Aurelien Jarno <aurelien@aurel32.net>");
MODULE_DESCRIPTION("PCF8574 driver");
MODULE_LICENSE("GPL");
module_init(pcf8574_init);
module_exit(pcf8574_exit);

316
drivers/i2c/chips/pcf8591.c Normal file
View File

@@ -0,0 +1,316 @@
/*
pcf8591.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(pcf8591);
static int input_mode;
module_param(input_mode, int, 0);
MODULE_PARM_DESC(input_mode,
"Analog input mode:\n"
" 0 = four single ended inputs\n"
" 1 = three differential inputs\n"
" 2 = single ended and differential mixed\n"
" 3 = two differential inputs\n");
/* The PCF8591 control byte
7 6 5 4 3 2 1 0
| 0 |AOEF| AIP | 0 |AINC| AICH | */
/* Analog Output Enable Flag (analog output active if 1) */
#define PCF8591_CONTROL_AOEF 0x40
/* Analog Input Programming
0x00 = four single ended inputs
0x10 = three differential inputs
0x20 = single ended and differential mixed
0x30 = two differential inputs */
#define PCF8591_CONTROL_AIP_MASK 0x30
/* Autoincrement Flag (switch on if 1) */
#define PCF8591_CONTROL_AINC 0x04
/* Channel selection
0x00 = channel 0
0x01 = channel 1
0x02 = channel 2
0x03 = channel 3 */
#define PCF8591_CONTROL_AICH_MASK 0x03
/* Initial values */
#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF)
#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */
/* Conversions */
#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg))
struct pcf8591_data {
struct i2c_client client;
struct semaphore update_lock;
u8 control;
u8 aout;
};
static int pcf8591_attach_adapter(struct i2c_adapter *adapter);
static int pcf8591_detect(struct i2c_adapter *adapter, int address, int kind);
static int pcf8591_detach_client(struct i2c_client *client);
static void pcf8591_init_client(struct i2c_client *client);
static int pcf8591_read_channel(struct device *dev, int channel);
/* This is the driver that will be inserted */
static struct i2c_driver pcf8591_driver = {
.owner = THIS_MODULE,
.name = "pcf8591",
.id = I2C_DRIVERID_PCF8591,
.flags = I2C_DF_NOTIFY,
.attach_adapter = pcf8591_attach_adapter,
.detach_client = pcf8591_detach_client,
};
/* following are the sysfs callback functions */
#define show_in_channel(channel) \
static ssize_t show_in##channel##_input(struct device *dev, char *buf) \
{ \
return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\
} \
static DEVICE_ATTR(in##channel##_input, S_IRUGO, \
show_in##channel##_input, NULL);
show_in_channel(0);
show_in_channel(1);
show_in_channel(2);
show_in_channel(3);
static ssize_t show_out0_ouput(struct device *dev, char *buf)
{
struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%d\n", data->aout * 10);
}
static ssize_t set_out0_output(struct device *dev, const char *buf, size_t count)
{
unsigned int value;
struct i2c_client *client = to_i2c_client(dev);
struct pcf8591_data *data = i2c_get_clientdata(client);
if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) {
data->aout = value;
i2c_smbus_write_byte_data(client, data->control, data->aout);
return count;
}
return -EINVAL;
}
static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO,
show_out0_ouput, set_out0_output);
static ssize_t show_out0_enable(struct device *dev, char *buf)
{
struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
}
static ssize_t set_out0_enable(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct pcf8591_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
if (val)
data->control |= PCF8591_CONTROL_AOEF;
else
data->control &= ~PCF8591_CONTROL_AOEF;
i2c_smbus_write_byte(client, data->control);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
show_out0_enable, set_out0_enable);
/*
* Real code
*/
static int pcf8591_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, pcf8591_detect);
}
/* This function is called by i2c_detect */
int pcf8591_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct pcf8591_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE
| I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. */
if (!(data = kmalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct pcf8591_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &pcf8591_driver;
new_client->flags = 0;
/* Now, we would do the remaining detection. But the PCF8591 is plainly
impossible to detect! Stupid chip. */
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = pcf8591;
/* Fill in the remaining client fields and put it into the global
list */
strlcpy(new_client->name, "pcf8591", I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
/* Initialize the PCF8591 chip */
pcf8591_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_out0_enable);
device_create_file(&new_client->dev, &dev_attr_out0_output);
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
/* Register input2 if not in "two differential inputs" mode */
if (input_mode != 3 )
device_create_file(&new_client->dev, &dev_attr_in2_input);
/* Register input3 only in "four single ended inputs" mode */
if (input_mode == 0)
device_create_file(&new_client->dev, &dev_attr_in3_input);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_kfree:
kfree(data);
exit:
return err;
}
static int pcf8591_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;
}
/* Called when we have found a new PCF8591. */
static void pcf8591_init_client(struct i2c_client *client)
{
struct pcf8591_data *data = i2c_get_clientdata(client);
data->control = PCF8591_INIT_CONTROL;
data->aout = PCF8591_INIT_AOUT;
i2c_smbus_write_byte_data(client, data->control, data->aout);
/* The first byte transmitted contains the conversion code of the
previous read cycle. FLUSH IT! */
i2c_smbus_read_byte(client);
}
static int pcf8591_read_channel(struct device *dev, int channel)
{
u8 value;
struct i2c_client *client = to_i2c_client(dev);
struct pcf8591_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) {
data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
| channel;
i2c_smbus_write_byte(client, data->control);
/* The first byte transmitted contains the conversion code of
the previous read cycle. FLUSH IT! */
i2c_smbus_read_byte(client);
}
value = i2c_smbus_read_byte(client);
up(&data->update_lock);
if ((channel == 2 && input_mode == 2) ||
(channel != 3 && (input_mode == 1 || input_mode == 3)))
return (10 * REG_TO_SIGNED(value));
else
return (10 * value);
}
static int __init pcf8591_init(void)
{
if (input_mode < 0 || input_mode > 3) {
printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n",
input_mode);
input_mode = 0;
}
return i2c_add_driver(&pcf8591_driver);
}
static void __exit pcf8591_exit(void)
{
i2c_del_driver(&pcf8591_driver);
}
MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
MODULE_DESCRIPTION("PCF8591 driver");
MODULE_LICENSE("GPL");
module_init(pcf8591_init);
module_exit(pcf8591_exit);

394
drivers/i2c/chips/rtc8564.c Normal file
View File

@@ -0,0 +1,394 @@
/*
* linux/drivers/i2c/chips/rtc8564.c
*
* Copyright (C) 2002-2004 Stefan Eletzhofer
*
* based on linux/drivers/acron/char/pcf8583.c
* Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Driver for system3's EPSON RTC 8564 chip
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/init.h>
#include <linux/init.h>
#include "rtc8564.h"
#ifdef DEBUG
# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0);
#else
# define _DBG(x, fmt, args...) do { } while(0);
#endif
#define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \
"mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \
(rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \
(rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl);
struct rtc8564_data {
struct i2c_client client;
u16 ctrl;
};
static inline u8 _rtc8564_ctrl1(struct i2c_client *client)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
return data->ctrl & 0xff;
}
static inline u8 _rtc8564_ctrl2(struct i2c_client *client)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
return (data->ctrl & 0xff00) >> 8;
}
#define CTRL1(c) _rtc8564_ctrl1(c)
#define CTRL2(c) _rtc8564_ctrl2(c)
#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
static int debug;;
module_param(debug, int, S_IRUGO | S_IWUSR);
static struct i2c_driver rtc8564_driver;
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.normal_i2c_range = ignore,
.probe = ignore,
.probe_range = ignore,
.ignore = ignore,
.ignore_range = ignore,
.force = ignore,
};
static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem);
static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem);
static int rtc8564_read(struct i2c_client *client, unsigned char adr,
unsigned char *buf, unsigned char len)
{
int ret = -EIO;
unsigned char addr[1] = { adr };
struct i2c_msg msgs[2] = {
{client->addr, 0, 1, addr},
{client->addr, I2C_M_RD, len, buf}
};
_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len);
if (!buf) {
ret = -EINVAL;
goto done;
}
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2) {
ret = 0;
}
done:
return ret;
}
static int rtc8564_write(struct i2c_client *client, unsigned char adr,
unsigned char *data, unsigned char len)
{
int ret = 0;
unsigned char _data[16];
struct i2c_msg wr;
int i;
if (!data || len > 15) {
ret = -EINVAL;
goto done;
}
_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len);
_data[0] = adr;
for (i = 0; i < len; i++) {
_data[i + 1] = data[i];
_DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]);
}
wr.addr = client->addr;
wr.flags = 0;
wr.len = len + 1;
wr.buf = _data;
ret = i2c_transfer(client->adapter, &wr, 1);
if (ret == 1) {
ret = 0;
}
done:
return ret;
}
static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind)
{
int ret;
struct i2c_client *new_client;
struct rtc8564_data *d;
unsigned char data[10];
unsigned char ad[1] = { 0 };
struct i2c_msg ctrl_wr[1] = {
{addr, 0, 2, data}
};
struct i2c_msg ctrl_rd[2] = {
{addr, 0, 1, ad},
{addr, I2C_M_RD, 2, data}
};
d = kmalloc(sizeof(struct rtc8564_data), GFP_KERNEL);
if (!d) {
ret = -ENOMEM;
goto done;
}
memset(d, 0, sizeof(struct rtc8564_data));
new_client = &d->client;
strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE);
i2c_set_clientdata(new_client, d);
new_client->flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY;
new_client->addr = addr;
new_client->adapter = adap;
new_client->driver = &rtc8564_driver;
_DBG(1, "client=%p", new_client);
/* init ctrl1 reg */
data[0] = 0;
data[1] = 0;
ret = i2c_transfer(new_client->adapter, ctrl_wr, 1);
if (ret != 1) {
printk(KERN_INFO "rtc8564: cant init ctrl1\n");
ret = -ENODEV;
goto done;
}
/* read back ctrl1 and ctrl2 */
ret = i2c_transfer(new_client->adapter, ctrl_rd, 2);
if (ret != 2) {
printk(KERN_INFO "rtc8564: cant read ctrl\n");
ret = -ENODEV;
goto done;
}
d->ctrl = data[0] | (data[1] << 8);
_DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x",
data[0], data[1]);
ret = i2c_attach_client(new_client);
done:
if (ret) {
kfree(d);
}
return ret;
}
static int rtc8564_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, rtc8564_attach);
}
static int rtc8564_detach(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt)
{
int ret = -EIO;
unsigned char buf[15];
_DBG(1, "client=%p, dt=%p", client, dt);
if (!dt)
return -EINVAL;
memset(buf, 0, sizeof(buf));
ret = rtc8564_read(client, 0, buf, 15);
if (ret)
return ret;
/* century stored in minute alarm reg */
dt->year = BCD_TO_BIN(buf[RTC8564_REG_YEAR]);
dt->year += 100 * BCD_TO_BIN(buf[RTC8564_REG_AL_MIN] & 0x3f);
dt->mday = BCD_TO_BIN(buf[RTC8564_REG_DAY] & 0x3f);
dt->wday = BCD_TO_BIN(buf[RTC8564_REG_WDAY] & 7);
dt->mon = BCD_TO_BIN(buf[RTC8564_REG_MON_CENT] & 0x1f);
dt->secs = BCD_TO_BIN(buf[RTC8564_REG_SEC] & 0x7f);
dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80;
dt->mins = BCD_TO_BIN(buf[RTC8564_REG_MIN] & 0x7f);
dt->hours = BCD_TO_BIN(buf[RTC8564_REG_HR] & 0x3f);
_DBGRTCTM(2, *dt);
return 0;
}
static int
rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo)
{
int ret, len = 5;
unsigned char buf[15];
_DBG(1, "client=%p, dt=%p", client, dt);
if (!dt)
return -EINVAL;
_DBGRTCTM(2, *dt);
buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP;
buf[RTC8564_REG_CTRL2] = CTRL2(client);
buf[RTC8564_REG_SEC] = BIN_TO_BCD(dt->secs);
buf[RTC8564_REG_MIN] = BIN_TO_BCD(dt->mins);
buf[RTC8564_REG_HR] = BIN_TO_BCD(dt->hours);
if (datetoo) {
len += 5;
buf[RTC8564_REG_DAY] = BIN_TO_BCD(dt->mday);
buf[RTC8564_REG_WDAY] = BIN_TO_BCD(dt->wday);
buf[RTC8564_REG_MON_CENT] = BIN_TO_BCD(dt->mon) & 0x1f;
/* century stored in minute alarm reg */
buf[RTC8564_REG_YEAR] = BIN_TO_BCD(dt->year % 100);
buf[RTC8564_REG_AL_MIN] = BIN_TO_BCD(dt->year / 100);
}
ret = rtc8564_write(client, 0, buf, len);
if (ret) {
_DBG(1, "error writing data! %d", ret);
}
buf[RTC8564_REG_CTRL1] = CTRL1(client);
ret = rtc8564_write(client, 0, buf, 1);
if (ret) {
_DBG(1, "error writing data! %d", ret);
}
return ret;
}
static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
if (!ctrl)
return -1;
*ctrl = data->ctrl;
return 0;
}
static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl)
{
struct rtc8564_data *data = i2c_get_clientdata(client);
unsigned char buf[2];
if (!ctrl)
return -1;
buf[0] = *ctrl & 0xff;
buf[1] = (*ctrl & 0xff00) >> 8;
data->ctrl = *ctrl;
return rtc8564_write(client, 0, buf, 2);
}
static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem)
{
if (!mem)
return -EINVAL;
return rtc8564_read(client, mem->loc, mem->data, mem->nr);
}
static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem)
{
if (!mem)
return -EINVAL;
return rtc8564_write(client, mem->loc, mem->data, mem->nr);
}
static int
rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
_DBG(1, "cmd=%d", cmd);
switch (cmd) {
case RTC_GETDATETIME:
return rtc8564_get_datetime(client, arg);
case RTC_SETTIME:
return rtc8564_set_datetime(client, arg, 0);
case RTC_SETDATETIME:
return rtc8564_set_datetime(client, arg, 1);
case RTC_GETCTRL:
return rtc8564_get_ctrl(client, arg);
case RTC_SETCTRL:
return rtc8564_set_ctrl(client, arg);
case MEM_READ:
return rtc8564_read_mem(client, arg);
case MEM_WRITE:
return rtc8564_write_mem(client, arg);
default:
return -EINVAL;
}
}
static struct i2c_driver rtc8564_driver = {
.owner = THIS_MODULE,
.name = "RTC8564",
.id = I2C_DRIVERID_RTC8564,
.flags = I2C_DF_NOTIFY,
.attach_adapter = rtc8564_probe,
.detach_client = rtc8564_detach,
.command = rtc8564_command
};
static __init int rtc8564_init(void)
{
return i2c_add_driver(&rtc8564_driver);
}
static __exit void rtc8564_exit(void)
{
i2c_del_driver(&rtc8564_driver);
}
MODULE_AUTHOR("Stefan Eletzhofer <Stefan.Eletzhofer@eletztrick.de>");
MODULE_DESCRIPTION("EPSON RTC8564 Driver");
MODULE_LICENSE("GPL");
module_init(rtc8564_init);
module_exit(rtc8564_exit);

View File

@@ -0,0 +1,78 @@
/*
* linux/drivers/i2c/chips/rtc8564.h
*
* Copyright (C) 2002-2004 Stefan Eletzhofer
*
* based on linux/drivers/acron/char/pcf8583.h
* Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
struct rtc_tm {
unsigned char secs;
unsigned char mins;
unsigned char hours;
unsigned char mday;
unsigned char mon;
unsigned short year; /* xxxx 4 digits :) */
unsigned char wday;
unsigned char vl;
};
struct mem {
unsigned int loc;
unsigned int nr;
unsigned char *data;
};
#define RTC_GETDATETIME 0
#define RTC_SETTIME 1
#define RTC_SETDATETIME 2
#define RTC_GETCTRL 3
#define RTC_SETCTRL 4
#define MEM_READ 5
#define MEM_WRITE 6
#define RTC8564_REG_CTRL1 0x0 /* T 0 S 0 | T 0 0 0 */
#define RTC8564_REG_CTRL2 0x1 /* 0 0 0 TI/TP | AF TF AIE TIE */
#define RTC8564_REG_SEC 0x2 /* VL 4 2 1 | 8 4 2 1 */
#define RTC8564_REG_MIN 0x3 /* x 4 2 1 | 8 4 2 1 */
#define RTC8564_REG_HR 0x4 /* x x 2 1 | 8 4 2 1 */
#define RTC8564_REG_DAY 0x5 /* x x 2 1 | 8 4 2 1 */
#define RTC8564_REG_WDAY 0x6 /* x x x x | x 4 2 1 */
#define RTC8564_REG_MON_CENT 0x7 /* C x x 1 | 8 4 2 1 */
#define RTC8564_REG_YEAR 0x8 /* 8 4 2 1 | 8 4 2 1 */
#define RTC8564_REG_AL_MIN 0x9 /* AE 4 2 1 | 8 4 2 1 */
#define RTC8564_REG_AL_HR 0xa /* AE 4 2 1 | 8 4 2 1 */
#define RTC8564_REG_AL_DAY 0xb /* AE x 2 1 | 8 4 2 1 */
#define RTC8564_REG_AL_WDAY 0xc /* AE x x x | x 4 2 1 */
#define RTC8564_REG_CLKOUT 0xd /* FE x x x | x x FD1 FD0 */
#define RTC8564_REG_TCTL 0xe /* TE x x x | x x FD1 FD0 */
#define RTC8564_REG_TIMER 0xf /* 8 bit binary */
/* Control reg */
#define RTC8564_CTRL1_TEST1 (1<<3)
#define RTC8564_CTRL1_STOP (1<<5)
#define RTC8564_CTRL1_TEST2 (1<<7)
#define RTC8564_CTRL2_TIE (1<<0)
#define RTC8564_CTRL2_AIE (1<<1)
#define RTC8564_CTRL2_TF (1<<2)
#define RTC8564_CTRL2_AF (1<<3)
#define RTC8564_CTRL2_TI_TP (1<<4)
/* CLKOUT frequencies */
#define RTC8564_FD_32768HZ (0x0)
#define RTC8564_FD_1024HZ (0x1)
#define RTC8564_FD_32 (0x2)
#define RTC8564_FD_1HZ (0x3)
/* Timer CTRL */
#define RTC8564_TD_4096HZ (0x0)
#define RTC8564_TD_64HZ (0x1)
#define RTC8564_TD_1HZ (0x2)
#define RTC8564_TD_1_60HZ (0x3)
#define I2C_DRIVERID_RTC8564 0xf000

816
drivers/i2c/chips/sis5595.c Normal file
View File

@@ -0,0 +1,816 @@
/*
sis5595.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of 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.
*/
/*
SiS southbridge has a LM78-like chip integrated on the same IC.
This driver is a customized copy of lm78.c
Supports following revisions:
Version PCI ID PCI Revision
1 1039/0008 AF or less
2 1039/0008 B0 or greater
Note: these chips contain a 0008 device which is incompatible with the
5595. We recognize these by the presence of the listed
"blacklist" PCI ID and refuse to load.
NOT SUPPORTED PCI ID BLACKLIST PCI ID
540 0008 0540
550 0008 0550
5513 0008 5511
5581 0008 5597
5582 0008 5597
5597 0008 5597
5598 0008 5597/5598
630 0008 0630
645 0008 0645
730 0008 0730
735 0008 0735
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static u16 force_addr;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr,
"Initialize the base address of the sensors");
/* Addresses to scan.
Note that we can't determine the ISA address until we have initialized
our module */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(sis5595);
/* Many SIS5595 constants specified below */
/* Length of ISA address segment */
#define SIS5595_EXTENT 8
/* PCI Config Registers */
#define SIS5595_REVISION_REG 0x08
#define SIS5595_BASE_REG 0x68
#define SIS5595_PIN_REG 0x7A
#define SIS5595_ENABLE_REG 0x7B
/* Where are the ISA address/data registers relative to the base address */
#define SIS5595_ADDR_REG_OFFSET 5
#define SIS5595_DATA_REG_OFFSET 6
/* The SIS5595 registers */
#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define SIS5595_REG_IN(nr) (0x20 + (nr))
#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr))
#define SIS5595_REG_FAN(nr) (0x28 + (nr))
/* On the first version of the chip, the temp registers are separate.
On the second version,
TEMP pin is shared with IN4, configured in PCI register 0x7A.
The registers are the same as well.
OVER and HYST are really MAX and MIN. */
#define REV2MIN 0xb0
#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN(4) : 0x27
#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN_MAX(4) : 0x39
#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN_MIN(4) : 0x3a
#define SIS5595_REG_CONFIG 0x40
#define SIS5595_REG_ALARM1 0x41
#define SIS5595_REG_ALARM2 0x42
#define SIS5595_REG_FANDIV 0x47
/* Conversions. Limit checking is only done on the TO_REG
variants. */
/* IN: mV, (0V to 4.08V)
REG: 16mV/bit */
static inline u8 IN_TO_REG(unsigned long val)
{
unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
return (nval + 8) / 16;
}
#define IN_FROM_REG(val) ((val) * 16)
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm <= 0)
return 255;
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
static inline int FAN_FROM_REG(u8 val, int div)
{
return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
}
/* TEMP: mC (-54.12C to +157.53C)
REG: 0.83C/bit + 52.12, two's complement */
static inline int TEMP_FROM_REG(s8 val)
{
return val * 830 + 52120;
}
static inline s8 TEMP_TO_REG(int val)
{
int nval = SENSORS_LIMIT(val, -54120, 157530) ;
return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830;
}
/* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */
static inline u8 DIV_TO_REG(int val)
{
return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1;
}
#define DIV_FROM_REG(val) (1 << (val))
/* For the SIS5595, we need to keep some data in memory. That
data is pointed to by sis5595_list[NR]->data. The structure itself is
dynamically allocated, at the time when the new sis5595 client is
allocated. */
struct sis5595_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 */
char maxins; /* == 3 if temp enabled, otherwise == 4 */
u8 revision; /* Reg. value */
u8 in[5]; /* Register value */
u8 in_max[5]; /* Register value */
u8 in_min[5]; /* Register value */
u8 fan[2]; /* Register value */
u8 fan_min[2]; /* Register value */
s8 temp; /* Register value */
s8 temp_over; /* Register value */
s8 temp_hyst; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u16 alarms; /* Register encoding, combined */
};
static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */
static int sis5595_attach_adapter(struct i2c_adapter *adapter);
static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind);
static int sis5595_detach_client(struct i2c_client *client);
static int sis5595_read_value(struct i2c_client *client, u8 register);
static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value);
static struct sis5595_data *sis5595_update_device(struct device *dev);
static void sis5595_init_client(struct i2c_client *client);
static struct i2c_driver sis5595_driver = {
.owner = THIS_MODULE,
.name = "sis5595",
.id = I2C_DRIVERID_SIS5595,
.flags = I2C_DF_NOTIFY,
.attach_adapter = sis5595_attach_adapter,
.detach_client = sis5595_detach_client,
};
/* 4 Voltages */
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset, NULL); \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
/* Temperature */
static ssize_t show_temp(struct device *dev, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
static ssize_t show_temp_over(struct device *dev, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static ssize_t set_temp_over(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over = TEMP_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_hyst(struct device *dev, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = TEMP_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst);
/* 2 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
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 sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long min;
unsigned long val = simple_strtoul(buf, NULL, 10);
int reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
reg = sis5595_read_value(client, SIS5595_REG_FANDIV);
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
switch (nr) {
case 0:
reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
break;
case 1:
reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
break;
}
sis5595_write_value(client, SIS5595_REG_FANDIV, reg);
data->fan_min[nr] =
FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 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, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min);
show_fan_offset(1);
show_fan_offset(2);
static ssize_t set_fan_1_div(struct device *dev, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 0) ;
}
static ssize_t set_fan_2_div(struct device *dev, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 1) ;
}
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
show_fan_1_div, set_fan_1_div);
static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
show_fan_2_div, set_fan_2_div);
/* Alarms */
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* This is called when the module is loaded */
static int sis5595_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, sis5595_detect);
}
int sis5595_detect(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
int i;
struct i2c_client *new_client;
struct sis5595_data *data;
char val;
u16 a;
/* Make sure we are probing the ISA bus!! */
if (!i2c_is_isa_adapter(adapter))
goto exit;
if (force_addr)
address = force_addr & ~(SIS5595_EXTENT - 1);
/* Reserve the ISA region */
if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) {
err = -EBUSY;
goto exit;
}
if (force_addr) {
dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address);
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
goto exit_release;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
goto exit_release;
if ((a & ~(SIS5595_EXTENT - 1)) != address)
/* doesn't work for some chips? */
goto exit_release;
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) {
goto exit_release;
}
if ((val & 0x80) == 0) {
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
val | 0x80))
goto exit_release;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
goto exit_release;
if ((val & 0x80) == 0)
/* doesn't work for some chips! */
goto exit_release;
}
if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit_release;
}
memset(data, 0, sizeof(struct sis5595_data));
new_client = &data->client;
new_client->addr = address;
init_MUTEX(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->adapter = adapter;
new_client->driver = &sis5595_driver;
new_client->flags = 0;
/* Check revision and pin registers to determine whether 4 or 5 voltages */
pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
/* 4 voltages, 1 temp */
data->maxins = 3;
if (data->revision >= REV2MIN) {
pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
if (!(val & 0x80))
/* 5 voltages, no temps */
data->maxins = 4;
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, "sis5595", 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 SIS5595 chip */
sis5595_init_client(new_client);
/* A few vars need to be filled upon startup */
for (i = 0; i < 2; i++) {
data->fan_min[i] = sis5595_read_value(new_client,
SIS5595_REG_FAN_MIN(i));
}
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
if (data->maxins == 4) {
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
}
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_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_alarms);
if (data->maxins == 3) {
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
}
return 0;
exit_free:
kfree(data);
exit_release:
release_region(address, SIS5595_EXTENT);
exit:
return err;
}
static int sis5595_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;
}
if (i2c_is_isa_client(client))
release_region(client->addr, SIS5595_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
/* ISA access must be locked explicitly. */
static int sis5595_read_value(struct i2c_client *client, u8 reg)
{
int res;
struct sis5595_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
up(&data->lock);
return res;
}
static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
{
struct sis5595_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
up(&data->lock);
return 0;
}
/* Called when we have found a new SIS5595. */
static void sis5595_init_client(struct i2c_client *client)
{
u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG);
if (!(config & 0x01))
sis5595_write_value(client, SIS5595_REG_CONFIG,
(config & 0xf7) | 0x01);
}
static struct sis5595_data *sis5595_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
for (i = 0; i <= data->maxins; i++) {
data->in[i] =
sis5595_read_value(client, SIS5595_REG_IN(i));
data->in_min[i] =
sis5595_read_value(client,
SIS5595_REG_IN_MIN(i));
data->in_max[i] =
sis5595_read_value(client,
SIS5595_REG_IN_MAX(i));
}
for (i = 0; i < 2; i++) {
data->fan[i] =
sis5595_read_value(client, SIS5595_REG_FAN(i));
data->fan_min[i] =
sis5595_read_value(client,
SIS5595_REG_FAN_MIN(i));
}
if (data->maxins == 3) {
data->temp =
sis5595_read_value(client, SIS5595_REG_TEMP);
data->temp_over =
sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
data->temp_hyst =
sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
}
i = sis5595_read_value(client, SIS5595_REG_FANDIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms =
sis5595_read_value(client, SIS5595_REG_ALARM1) |
(sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static struct pci_device_id sis5595_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sis5595_pci_ids);
static int blacklist[] __devinitdata = {
PCI_DEVICE_ID_SI_540,
PCI_DEVICE_ID_SI_550,
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_645,
PCI_DEVICE_ID_SI_730,
PCI_DEVICE_ID_SI_735,
PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
that ID shows up in other chips so we
use the 5511 ID for recognition */
PCI_DEVICE_ID_SI_5597,
PCI_DEVICE_ID_SI_5598,
0 };
static int __devinit sis5595_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 val;
int *i;
int addr = 0;
for (i = blacklist; *i != 0; i++) {
struct pci_dev *dev;
dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
if (dev) {
dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
pci_dev_put(dev);
return -ENODEV;
}
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(dev, SIS5595_BASE_REG, &val))
return -ENODEV;
addr = val & ~(SIS5595_EXTENT - 1);
if (addr == 0 && force_addr == 0) {
dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if (force_addr)
addr = force_addr; /* so detect will get called */
if (!addr) {
dev_err(&dev->dev,"No SiS 5595 sensors found.\n");
return -ENODEV;
}
normal_isa[0] = addr;
s_bridge = pci_dev_get(dev);
if (i2c_add_driver(&sis5595_driver)) {
pci_dev_put(s_bridge);
s_bridge = NULL;
}
/* Always return failure here. This is to allow other drivers to bind
* to this pci device. We don't really want to have control over the
* pci device, we only wanted to read as few register values from it.
*/
return -ENODEV;
}
static struct pci_driver sis5595_pci_driver = {
.name = "sis5595",
.id_table = sis5595_pci_ids,
.probe = sis5595_pci_probe,
};
static int __init sm_sis5595_init(void)
{
return pci_register_driver(&sis5595_pci_driver);
}
static void __exit sm_sis5595_exit(void)
{
pci_unregister_driver(&sis5595_pci_driver);
if (s_bridge != NULL) {
i2c_del_driver(&sis5595_driver);
pci_dev_put(s_bridge);
s_bridge = NULL;
}
}
MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
MODULE_DESCRIPTION("SiS 5595 Sensor device");
MODULE_LICENSE("GPL");
module_init(sm_sis5595_init);
module_exit(sm_sis5595_exit);

View File

@@ -0,0 +1,352 @@
/*
smsc47b397.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Supports the SMSC LPC47B397-NC Super-I/O chip.
Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com>
Copyright (C) 2004 Utilitek Systems, Inc.
derived in part from smsc47m1.c:
Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Address is autodetected, there is no default value */
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{NULL}};
enum chips { any_chip, smsc47b397 };
static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_isa = normal_isa,
.probe = normal_i2c, /* cheat */
.ignore = normal_i2c, /* cheat */
.forces = forces,
};
/* Super-I/0 registers and commands */
#define REG 0x2e /* The register to read/write */
#define VAL 0x2f /* The value to read/write */
static inline void superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int superio_inb(int reg)
{
outb(reg, REG);
return inb(VAL);
}
/* select superio logical device */
static inline void superio_select(int ld)
{
superio_outb(0x07, ld);
}
static inline void superio_enter(void)
{
outb(0x55, REG);
}
static inline void superio_exit(void)
{
outb(0xAA, REG);
}
#define SUPERIO_REG_DEVID 0x20
#define SUPERIO_REG_DEVREV 0x21
#define SUPERIO_REG_BASE_MSB 0x60
#define SUPERIO_REG_BASE_LSB 0x61
#define SUPERIO_REG_LD8 0x08
#define SMSC_EXTENT 0x02
/* 0 <= nr <= 3 */
static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
/* 0 <= nr <= 3 */
#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
struct smsc47b397_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
unsigned long last_updated; /* in jiffies */
int valid;
/* register values */
u16 fan[4];
u8 temp[4];
};
static int smsc47b397_read_value(struct i2c_client *client, u8 reg)
{
struct smsc47b397_data *data = i2c_get_clientdata(client);
int res;
down(&data->lock);
outb(reg, client->addr);
res = inb_p(client->addr + 1);
up(&data->lock);
return res;
}
static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47b397_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
dev_dbg(&client->dev, "starting device update...\n");
/* 4 temperature inputs, 4 fan inputs */
for (i = 0; i < 4; i++) {
data->temp[i] = smsc47b397_read_value(client,
SMSC47B397_REG_TEMP(i));
/* must read LSB first */
data->fan[i] = smsc47b397_read_value(client,
SMSC47B397_REG_FAN_LSB(i));
data->fan[i] |= smsc47b397_read_value(client,
SMSC47B397_REG_FAN_MSB(i)) << 8;
}
data->last_updated = jiffies;
data->valid = 1;
dev_dbg(&client->dev, "... device update complete\n");
}
up(&data->update_lock);
return data;
}
/* TEMP: 0.001C/bit (-128C to +127C)
REG: 1C/bit, two's complement */
static int temp_from_reg(u8 reg)
{
return (s8)reg * 1000;
}
/* 0 <= nr <= 3 */
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
}
#define sysfs_temp(num) \
static ssize_t show_temp##num(struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, num-1); \
} \
static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
sysfs_temp(1);
sysfs_temp(2);
sysfs_temp(3);
sysfs_temp(4);
#define device_create_file_temp(client, num) \
device_create_file(&client->dev, &dev_attr_temp##num##_input)
/* FAN: 1 RPM/bit
REG: count of 90kHz pulses / revolution */
static int fan_from_reg(u16 reg)
{
return 90000 * 60 / reg;
}
/* 0 <= nr <= 3 */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
}
#define sysfs_fan(num) \
static ssize_t show_fan##num(struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, num-1); \
} \
static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL)
sysfs_fan(1);
sysfs_fan(2);
sysfs_fan(3);
sysfs_fan(4);
#define device_create_file_fan(client, num) \
device_create_file(&client->dev, &dev_attr_fan##num##_input)
static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind);
static int smsc47b397_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, smsc47b397_detect);
}
static int smsc47b397_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;
}
release_region(client->addr, SMSC_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
static struct i2c_driver smsc47b397_driver = {
.owner = THIS_MODULE,
.name = "smsc47b397",
.id = I2C_DRIVERID_SMSC47B397,
.flags = I2C_DF_NOTIFY,
.attach_adapter = smsc47b397_attach_adapter,
.detach_client = smsc47b397_detach_client,
};
static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *new_client;
struct smsc47b397_data *data;
int err = 0;
if (!i2c_is_isa_adapter(adapter)) {
return 0;
}
if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) {
dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr);
return -EBUSY;
}
if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error_release;
}
memset(data, 0x00, sizeof(struct smsc47b397_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = addr;
init_MUTEX(&data->lock);
new_client->adapter = adapter;
new_client->driver = &smsc47b397_driver;
new_client->flags = 0;
strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
if ((err = i2c_attach_client(new_client)))
goto error_free;
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
device_create_file_temp(new_client, 3);
device_create_file_temp(new_client, 4);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
device_create_file_fan(new_client, 4);
return 0;
error_free:
kfree(new_client);
error_release:
release_region(addr, SMSC_EXTENT);
return err;
}
static int __init smsc47b397_find(unsigned int *addr)
{
u8 id, rev;
superio_enter();
id = superio_inb(SUPERIO_REG_DEVID);
if (id != 0x6f) {
superio_exit();
return -ENODEV;
}
rev = superio_inb(SUPERIO_REG_DEVREV);
superio_select(SUPERIO_REG_LD8);
*addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
| superio_inb(SUPERIO_REG_BASE_LSB);
printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
"(base address 0x%04x, revision %u)\n", *addr, rev);
superio_exit();
return 0;
}
static int __init smsc47b397_init(void)
{
int ret;
if ((ret = smsc47b397_find(normal_isa)))
return ret;
return i2c_add_driver(&smsc47b397_driver);
}
static void __exit smsc47b397_exit(void)
{
i2c_del_driver(&smsc47b397_driver);
}
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
MODULE_DESCRIPTION("SMSC LPC47B397 driver");
MODULE_LICENSE("GPL");
module_init(smsc47b397_init);
module_exit(smsc47b397_exit);

View File

@@ -0,0 +1,591 @@
/*
smsc47m1.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x
Super-I/O chips.
Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
and Jean Delvare
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/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Address is autodetected, there is no default value */
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{NULL}};
enum chips { any_chip, smsc47m1 };
static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_isa = normal_isa,
.forces = forces,
};
/* Super-I/0 registers and commands */
#define REG 0x2e /* The register to read/write */
#define VAL 0x2f /* The value to read/write */
static inline void
superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int
superio_inb(int reg)
{
outb(reg, REG);
return inb(VAL);
}
/* logical device for fans is 0x0A */
#define superio_select() superio_outb(0x07, 0x0A)
static inline void
superio_enter(void)
{
outb(0x55, REG);
}
static inline void
superio_exit(void)
{
outb(0xAA, REG);
}
#define SUPERIO_REG_ACT 0x30
#define SUPERIO_REG_BASE 0x60
#define SUPERIO_REG_DEVID 0x20
/* Logical device registers */
#define SMSC_EXTENT 0x80
/* nr is 0 or 1 in the macros below */
#define SMSC47M1_REG_ALARM 0x04
#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
#define SMSC47M1_REG_PWM(nr) (0x56 + (nr))
#define SMSC47M1_REG_FANDIV 0x58
#define SMSC47M1_REG_FAN(nr) (0x59 + (nr))
#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr))
#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
983040/((192-(reg))*(div)))
#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
983040/(((reg)-(preload))*(div)))
#define DIV_FROM_REG(reg) (1 << (reg))
#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
struct smsc47m1_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
unsigned long last_updated; /* In jiffies */
u8 fan[2]; /* Register value */
u8 fan_preload[2]; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u8 alarms; /* Register encoding */
u8 pwm[2]; /* Register value (bit 7 is enable) */
};
static int smsc47m1_attach_adapter(struct i2c_adapter *adapter);
static int smsc47m1_find(int *address);
static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind);
static int smsc47m1_detach_client(struct i2c_client *client);
static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
int init);
static struct i2c_driver smsc47m1_driver = {
.owner = THIS_MODULE,
.name = "smsc47m1",
.id = I2C_DRIVERID_SMSC47M1,
.flags = I2C_DF_NOTIFY,
.attach_adapter = smsc47m1_attach_adapter,
.detach_client = smsc47m1_detach_client,
};
/* nr is 0 or 1 in the callback functions below */
static ssize_t get_fan(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
/* This chip (stupidly) stops monitoring fan speed if PWM is
enabled and duty cycle is 0%. This is fine if the monitoring
and control concern the same fan, but troublesome if they are
not (which could as well happen). */
int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr]),
data->fan_preload[nr]);
return sprintf(buf, "%d\n", rpm);
}
static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
int rpm = MIN_FROM_REG(data->fan_preload[nr],
DIV_FROM_REG(data->fan_div[nr]));
return sprintf(buf, "%d\n", rpm);
}
static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
}
static ssize_t get_pwm(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
}
static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
}
static ssize_t get_alarms(struct device *dev, char *buf)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", data->alarms);
}
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 smsc47m1_data *data = i2c_get_clientdata(client);
long rpmdiv, val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
up(&data->update_lock);
return -EINVAL;
}
data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
data->fan_preload[nr]);
up(&data->update_lock);
return count;
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
of least suprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long new_div = simple_strtol(buf, NULL, 10), tmp;
u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
if (new_div == old_div) /* No change */
return count;
down(&data->update_lock);
switch (new_div) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
up(&data->update_lock);
return -EINVAL;
}
tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;
tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);
smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
/* Preserve fan min */
tmp = 192 - (old_div * (192 - data->fan_preload[nr])
+ new_div / 2) / new_div;
data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
data->fan_preload[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_pwm(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
if (val < 0 || val > 255)
return -EINVAL;
down(&data->update_lock);
data->pwm[nr] &= 0x81; /* Preserve additional bits */
data->pwm[nr] |= PWM_TO_REG(val);
smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
data->pwm[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_pwm_en(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
if (val != 0 && val != 1)
return -EINVAL;
down(&data->update_lock);
data->pwm[nr] &= 0xFE; /* preserve the other bits */
data->pwm[nr] |= !val;
smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
data->pwm[nr]);
up(&data->update_lock);
return count;
}
#define fan_present(offset) \
static ssize_t get_fan##offset (struct device *dev, char *buf) \
{ \
return get_fan(dev, buf, offset - 1); \
} \
static ssize_t get_fan##offset##_min (struct device *dev, char *buf) \
{ \
return get_fan_min(dev, buf, 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 ssize_t get_fan##offset##_div (struct device *dev, char *buf) \
{ \
return get_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan##offset##_div (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static ssize_t get_pwm##offset (struct device *dev, char *buf) \
{ \
return get_pwm(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm(dev, buf, count, offset - 1); \
} \
static ssize_t get_pwm##offset##_en (struct device *dev, char *buf) \
{ \
return get_pwm_en(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_en (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm_en(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
get_fan##offset##_min, set_fan##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
get_fan##offset##_div, set_fan##offset##_div); \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
get_pwm##offset, set_pwm##offset); \
static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
get_pwm##offset##_en, set_pwm##offset##_en);
fan_present(1);
fan_present(2);
static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
static int smsc47m1_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, smsc47m1_detect);
}
static int smsc47m1_find(int *address)
{
u8 val;
superio_enter();
val = superio_inb(SUPERIO_REG_DEVID);
/*
* SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id
* 0x5F) and LPC47B27x (device id 0x51) have fan control.
* The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
* can do much more besides (device id 0x60, unsupported).
*/
if (val == 0x51)
printk(KERN_INFO "smsc47m1: Found SMSC47B27x\n");
else if (val == 0x59)
printk(KERN_INFO "smsc47m1: Found SMSC47M10x/SMSC47M13x\n");
else if (val == 0x5F)
printk(KERN_INFO "smsc47m1: Found SMSC47M14x\n");
else {
superio_exit();
return -ENODEV;
}
superio_select();
*address = (superio_inb(SUPERIO_REG_BASE) << 8)
| superio_inb(SUPERIO_REG_BASE + 1);
val = superio_inb(SUPERIO_REG_ACT);
if (*address == 0 || (val & 0x01) == 0) {
printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
superio_exit();
return -ENODEV;
}
superio_exit();
return 0;
}
static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct smsc47m1_data *data;
int err = 0;
int fan1, fan2, pwm1, pwm2;
if (!i2c_is_isa_adapter(adapter)) {
return 0;
}
if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.name)) {
dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
return -EBUSY;
}
if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error_release;
}
memset(data, 0x00, sizeof(struct smsc47m1_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 = &smsc47m1_driver;
new_client->flags = 0;
strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
/* If no function is properly configured, there's no point in
actually registering the chip. */
fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
== 0x05;
fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
== 0x05;
pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
== 0x04;
pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
== 0x04;
if (!(fan1 || fan2 || pwm1 || pwm2)) {
dev_warn(&new_client->dev, "Device is not configured, will not use\n");
err = -ENODEV;
goto error_free;
}
if ((err = i2c_attach_client(new_client)))
goto error_free;
/* Some values (fan min, clock dividers, pwm registers) may be
needed before any update is triggered, so we better read them
at least once here. We don't usually do it that way, but in
this particular case, manually reading 5 registers out of 8
doesn't make much sense and we're better using the existing
function. */
smsc47m1_update_device(&new_client->dev, 1);
if (fan1) {
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_fan1_div);
} else
dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
"skipping\n");
if (fan2) {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
} else
dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
"skipping\n");
if (pwm1) {
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
} else
dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
"skipping\n");
if (pwm2) {
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
} else
dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
"skipping\n");
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
error_free:
kfree(new_client);
error_release:
release_region(address, SMSC_EXTENT);
return err;
}
static int smsc47m1_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;
}
release_region(client->addr, SMSC_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
{
int res;
down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
res = inb_p(client->addr + reg);
up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
return res;
}
static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
{
down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
outb_p(value, client->addr + reg);
up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
}
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
int init)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
int i;
for (i = 0; i < 2; i++) {
data->fan[i] = smsc47m1_read_value(client,
SMSC47M1_REG_FAN(i));
data->fan_preload[i] = smsc47m1_read_value(client,
SMSC47M1_REG_FAN_PRELOAD(i));
data->pwm[i] = smsc47m1_read_value(client,
SMSC47M1_REG_PWM(i));
}
i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = smsc47m1_read_value(client,
SMSC47M1_REG_ALARM) >> 6;
/* Clear alarms if needed */
if (data->alarms)
smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
data->last_updated = jiffies;
}
up(&data->update_lock);
return data;
}
static int __init sm_smsc47m1_init(void)
{
if (smsc47m1_find(normal_isa)) {
return -ENODEV;
}
return i2c_add_driver(&smsc47m1_driver);
}
static void __exit sm_smsc47m1_exit(void)
{
i2c_del_driver(&smsc47m1_driver);
}
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
MODULE_LICENSE("GPL");
module_init(sm_smsc47m1_init);
module_exit(sm_smsc47m1_exit);

879
drivers/i2c/chips/via686a.c Normal file
View File

@@ -0,0 +1,879 @@
/*
via686a.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>,
Mark Studebaker <mdsxyz123@yahoo.com>,
and Bob Dougherty <bobd@stanford.edu>
(Some conversion-factor data were contributed by Jonathan Teh Soon Yew
<j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
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 Via VT82C686A, VT82C686B south bridges.
Reports all as a 686A.
Warning - only supports a single device.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static unsigned short force_addr = 0;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr,
"Initialize the base address of the sensors");
/* Addresses to scan.
Note that we can't determine the ISA address until we have initialized
our module */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(via686a);
/*
The Via 686a southbridge has a LM78-like chip integrated on the same IC.
This driver is a customized copy of lm78.c
*/
/* Many VIA686A constants specified below */
/* Length of ISA address segment */
#define VIA686A_EXTENT 0x80
#define VIA686A_BASE_REG 0x70
#define VIA686A_ENABLE_REG 0x74
/* The VIA686A registers */
/* ins numbered 0-4 */
#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
#define VIA686A_REG_IN(nr) (0x22 + (nr))
/* fans numbered 1-2 */
#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
#define VIA686A_REG_FAN(nr) (0x28 + (nr))
/* the following values are as speced by VIA: */
static const u8 regtemp[] = { 0x20, 0x21, 0x1f };
static const u8 regover[] = { 0x39, 0x3d, 0x1d };
static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e };
/* temps numbered 1-3 */
#define VIA686A_REG_TEMP(nr) (regtemp[nr])
#define VIA686A_REG_TEMP_OVER(nr) (regover[nr])
#define VIA686A_REG_TEMP_HYST(nr) (reghyst[nr])
#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6
#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6
#define VIA686A_REG_ALARM1 0x41
#define VIA686A_REG_ALARM2 0x42
#define VIA686A_REG_FANDIV 0x47
#define VIA686A_REG_CONFIG 0x40
/* The following register sets temp interrupt mode (bits 1-0 for temp1,
3-2 for temp2, 5-4 for temp3). Modes are:
00 interrupt stays as long as value is out-of-range
01 interrupt is cleared once register is read (default)
10 comparator mode- like 00, but ignores hysteresis
11 same as 00 */
#define VIA686A_REG_TEMP_MODE 0x4b
/* We'll just assume that you want to set all 3 simultaneously: */
#define VIA686A_TEMP_MODE_MASK 0x3F
#define VIA686A_TEMP_MODE_CONTINUOUS (0x00)
/* Conversions. Limit checking is only done on the TO_REG
variants.
********* VOLTAGE CONVERSIONS (Bob Dougherty) ********
From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp
voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V
voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V
voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V
voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V
in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
That is:
volts = (25*regVal+133)*factor
regVal = (volts/factor-133)/25
(These conversions were contributed by Jonathan Teh Soon Yew
<j.teh@iname.com>) */
static inline u8 IN_TO_REG(long val, int inNum)
{
/* To avoid floating point, we multiply constants by 10 (100 for +12V).
Rounding is done (120500 is actually 133000 - 12500).
Remember that val is expressed in 0.001V/bit, which is why we divide
by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
for the constants. */
if (inNum <= 1)
return (u8)
SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255);
else if (inNum == 2)
return (u8)
SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255);
else if (inNum == 3)
return (u8)
SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255);
else
return (u8)
SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255);
}
static inline long IN_FROM_REG(u8 val, int inNum)
{
/* To avoid floating point, we multiply constants by 10 (100 for +12V).
We also multiply them by 1000 because we want 0.001V/bit for the
output value. Rounding is done. */
if (inNum <= 1)
return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
else if (inNum == 2)
return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
else if (inNum == 3)
return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
else
return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
}
/********* FAN RPM CONVERSIONS ********/
/* Higher register values = slower fans (the fan's strobe gates a counter).
But this chip saturates back at 0, not at 255 like all the other chips.
So, 0 means 0 RPM */
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm == 0)
return 0;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
}
#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
/******** TEMP CONVERSIONS (Bob Dougherty) *********/
/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
if(temp<169)
return double(temp)*0.427-32.08;
else if(temp>=169 && temp<=202)
return double(temp)*0.582-58.16;
else
return double(temp)*0.924-127.33;
A fifth-order polynomial fits the unofficial data (provided by Alex van
Kaam <darkside@chello.nl>) a bit better. It also give more reasonable
numbers on my machine (ie. they agree with what my BIOS tells me).
Here's the fifth-order fit to the 8-bit data:
temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
(2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
finding my typos in this formula!)
Alas, none of the elegant function-fit solutions will work because we
aren't allowed to use floating point in the kernel and doing it with
integers doesn't rpovide enough precision. So we'll do boring old
look-up table stuff. The unofficial data (see below) have effectively
7-bit resolution (they are rounded to the nearest degree). I'm assuming
that the transfer function of the device is monotonic and smooth, so a
smooth function fit to the data will allow us to get better precision.
I used the 5th-order poly fit described above and solved for
VIA register values 0-255. I *10 before rounding, so we get tenth-degree
precision. (I could have done all 1024 values for our 10-bit readings,
but the function is very linear in the useful range (0-80 deg C), so
we'll just use linear interpolation for 10-bit readings.) So, tempLUT
is the temp at via register values 0-255: */
static const long tempLUT[] =
{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
-503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
-362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
-255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
-173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
-108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
-44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
};
/* the original LUT values from Alex van Kaam <darkside@chello.nl>
(for via register values 12-240):
{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed
an extra term for a good fit to these inverse data!) and then
solving for each temp value from -50 to 110 (the useable range for
this chip). Here's the fit:
viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
- 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
Note that n=161: */
static const u8 viaLUT[] =
{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
239, 240
};
/* Converting temps to (8-bit) hyst and over registers
No interpolation here.
The +50 is because the temps start at -50 */
static inline u8 TEMP_TO_REG(long val)
{
return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
(val < 0 ? val - 500 : val + 500) / 1000 + 50];
}
/* for 8-bit temperature hyst and over registers */
#define TEMP_FROM_REG(val) (tempLUT[(val)] * 100)
/* for 10-bit temperature readings */
static inline long TEMP_FROM_REG10(u16 val)
{
u16 eightBits = val >> 2;
u16 twoBits = val & 3;
/* no interpolation for these */
if (twoBits == 0 || eightBits == 255)
return TEMP_FROM_REG(eightBits);
/* do some linear interpolation */
return (tempLUT[eightBits] * (4 - twoBits) +
tempLUT[eightBits + 1] * twoBits) * 25;
}
#define ALARMS_FROM_REG(val) (val)
#define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
/* For the VIA686A, we need to keep some data in memory.
The structure is dynamically allocated, at the same time when a new
via686a client is allocated. */
struct via686a_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[5]; /* Register value */
u8 in_max[5]; /* Register value */
u8 in_min[5]; /* Register value */
u8 fan[2]; /* Register value */
u8 fan_min[2]; /* Register value */
u16 temp[3]; /* Register value 10 bit */
u8 temp_over[3]; /* Register value */
u8 temp_hyst[3]; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u16 alarms; /* Register encoding, combined */
};
static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
static int via686a_attach_adapter(struct i2c_adapter *adapter);
static int via686a_detect(struct i2c_adapter *adapter, int address, int kind);
static int via686a_detach_client(struct i2c_client *client);
static inline int via686a_read_value(struct i2c_client *client, u8 reg)
{
return (inb_p(client->addr + reg));
}
static inline void via686a_write_value(struct i2c_client *client, u8 reg,
u8 value)
{
outb_p(value, client->addr + reg);
}
static struct via686a_data *via686a_update_device(struct device *dev);
static void via686a_init_client(struct i2c_client *client);
/* following are the sysfs callback functions */
/* 7 voltage sensors */
static ssize_t show_in(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val,nr);
via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val,nr);
via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
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##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
/* 3 temperatures */
static ssize_t show_temp(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
}
static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
}
static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
}
static ssize_t set_temp_over(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_OVER(nr), data->temp_over[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_temp_hyst(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_HYST(nr), data->temp_hyst[nr]);
up(&data->update_lock);
return count;
}
#define show_temp_offset(offset) \
static ssize_t show_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, offset - 1); \
} \
static ssize_t \
show_temp_##offset##_over (struct device *dev, char *buf) \
{ \
return show_temp_over(dev, buf, offset - 1); \
} \
static ssize_t \
show_temp_##offset##_hyst (struct device *dev, char *buf) \
{ \
return show_temp_hyst(dev, buf, offset - 1); \
} \
static ssize_t set_temp_##offset##_over (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_over(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_hyst (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_temp_hyst(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp_##offset##_over, set_temp_##offset##_over); \
static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
show_temp_##offset##_hyst, set_temp_##offset##_hyst);
show_temp_offset(1);
show_temp_offset(2);
show_temp_offset(3);
/* 2 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf,"%d\n",
FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf,"%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
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 via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
int old;
down(&data->update_lock);
old = via686a_read_value(client, VIA686A_REG_FANDIV);
data->fan_div[nr] = DIV_TO_REG(val);
old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
via686a_write_value(client, VIA686A_REG_FANDIV, old);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 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 ssize_t set_fan_##offset##_div (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div);
show_fan_offset(1);
show_fan_offset(2);
/* Alarms */
static ssize_t show_alarms(struct device *dev, char *buf) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
}
static DEVICE_ATTR(alarms, S_IRUGO | S_IWUSR, show_alarms, NULL);
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
static struct i2c_driver via686a_driver = {
.owner = THIS_MODULE,
.name = "via686a",
.id = I2C_DRIVERID_VIA686A,
.flags = I2C_DF_NOTIFY,
.attach_adapter = via686a_attach_adapter,
.detach_client = via686a_detach_client,
};
/* This is called when the module is loaded */
static int via686a_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, via686a_detect);
}
static int via686a_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct via686a_data *data;
int err = 0;
const char client_name[] = "via686a";
u16 val;
/* Make sure we are probing the ISA bus!! */
if (!i2c_is_isa_adapter(adapter)) {
dev_err(&adapter->dev,
"via686a_detect called for an I2C bus adapter?!?\n");
return 0;
}
/* 8231 requires multiple of 256, we enforce that on 686 as well */
if(force_addr)
address = force_addr & 0xFF00;
if(force_addr) {
dev_warn(&adapter->dev,"forcing ISA address 0x%04X\n", address);
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(s_bridge, VIA686A_BASE_REG, address))
return -ENODEV;
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
return -ENODEV;
if (!(val & 0x0001)) {
dev_warn(&adapter->dev,"enabling sensors\n");
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
val | 0x0001))
return -ENODEV;
}
/* Reserve the ISA region */
if (!request_region(address, VIA686A_EXTENT, via686a_driver.name)) {
dev_err(&adapter->dev,"region 0x%x already in use!\n",
address);
return -ENODEV;
}
if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
memset(data, 0, sizeof(struct via686a_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &via686a_driver;
new_client->flags = 0;
new_client->dev.parent = &adapter->dev;
/* Fill in the remaining client fields and put into the global list */
snprintf(new_client->name, I2C_NAME_SIZE, client_name);
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 ERROR3;
/* Initialize the VIA686A chip */
via686a_init_client(new_client);
/* Register sysfs hooks */
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_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_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_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp3_input);
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_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst);
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_alarms);
return 0;
ERROR3:
kfree(data);
ERROR0:
release_region(address, VIA686A_EXTENT);
return err;
}
static int via686a_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;
}
release_region(client->addr, VIA686A_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
/* Called when we have found a new VIA686A. Set limits, etc. */
static void via686a_init_client(struct i2c_client *client)
{
u8 reg;
/* Start monitoring */
reg = via686a_read_value(client, VIA686A_REG_CONFIG);
via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F);
/* Configure temp interrupt mode for continuous-interrupt operation */
via686a_write_value(client, VIA686A_REG_TEMP_MODE,
via686a_read_value(client, VIA686A_REG_TEMP_MODE) &
!(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS));
}
static struct via686a_data *via686a_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
for (i = 0; i <= 4; i++) {
data->in[i] =
via686a_read_value(client, VIA686A_REG_IN(i));
data->in_min[i] = via686a_read_value(client,
VIA686A_REG_IN_MIN
(i));
data->in_max[i] =
via686a_read_value(client, VIA686A_REG_IN_MAX(i));
}
for (i = 1; i <= 2; i++) {
data->fan[i - 1] =
via686a_read_value(client, VIA686A_REG_FAN(i));
data->fan_min[i - 1] = via686a_read_value(client,
VIA686A_REG_FAN_MIN(i));
}
for (i = 0; i <= 2; i++) {
data->temp[i] = via686a_read_value(client,
VIA686A_REG_TEMP(i)) << 2;
data->temp_over[i] =
via686a_read_value(client,
VIA686A_REG_TEMP_OVER(i));
data->temp_hyst[i] =
via686a_read_value(client,
VIA686A_REG_TEMP_HYST(i));
}
/* add in lower 2 bits
temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
*/
data->temp[0] |= (via686a_read_value(client,
VIA686A_REG_TEMP_LOW1)
& 0xc0) >> 6;
data->temp[1] |=
(via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
0x30) >> 4;
data->temp[2] |=
(via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
0xc0) >> 6;
i = via686a_read_value(client, VIA686A_REG_FANDIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms =
via686a_read_value(client,
VIA686A_REG_ALARM1) |
(via686a_read_value(client, VIA686A_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static struct pci_device_id via686a_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
static int __devinit via686a_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 val;
int addr = 0;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(dev, VIA686A_BASE_REG, &val))
return -ENODEV;
addr = val & ~(VIA686A_EXTENT - 1);
if (addr == 0 && force_addr == 0) {
dev_err(&dev->dev,"base address not set - upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if (force_addr)
addr = force_addr; /* so detect will get called */
if (!addr) {
dev_err(&dev->dev,"No Via 686A sensors found.\n");
return -ENODEV;
}
normal_isa[0] = addr;
s_bridge = pci_dev_get(dev);
if (i2c_add_driver(&via686a_driver)) {
pci_dev_put(s_bridge);
s_bridge = NULL;
}
/* Always return failure here. This is to allow other drivers to bind
* to this pci device. We don't really want to have control over the
* pci device, we only wanted to read as few register values from it.
*/
return -ENODEV;
}
static struct pci_driver via686a_pci_driver = {
.name = "via686a",
.id_table = via686a_pci_ids,
.probe = via686a_pci_probe,
};
static int __init sm_via686a_init(void)
{
return pci_register_driver(&via686a_pci_driver);
}
static void __exit sm_via686a_exit(void)
{
pci_unregister_driver(&via686a_pci_driver);
if (s_bridge != NULL) {
i2c_del_driver(&via686a_driver);
pci_dev_put(s_bridge);
s_bridge = NULL;
}
}
MODULE_AUTHOR("Ky<EFBFBD>sti M<>lkki <kmalkki@cc.hut.fi>, "
"Mark Studebaker <mdsxyz123@yahoo.com> "
"and Bob Dougherty <bobd@stanford.edu>");
MODULE_DESCRIPTION("VIA 686A Sensor device");
MODULE_LICENSE("GPL");
module_init(sm_via686a_init);
module_exit(sm_via686a_exit);

1511
drivers/i2c/chips/w83627hf.c Normal file

File diff suppressed because it is too large Load Diff

1664
drivers/i2c/chips/w83781d.c Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More