Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 419 → Rev 420

/shark/trunk/drivers/i2c/busses/i2c-ali15x3.c
0,0 → 1,535
/*
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 */
 
/* #define DEBUG 1 */
 
#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/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 int force_addr = 0;
MODULE_PARM(force_addr, "i");
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 {
i2c_delay(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++) {
i2c_delay(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_ADAP_CLASS_SMBUS,
.algo = &smbus_algorithm,
.name = "unset",
};
 
static struct pci_device_id ali15x3_ids[] = {
{
.vendor = PCI_VENDOR_ID_AL,
.device = PCI_DEVICE_ID_AL_M7101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
 
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_module_init(&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);
/shark/trunk/drivers/i2c/busses/i2c-sis630.c
0,0 → 1,520
/*
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.
*/
 
/* #define DEBUG 1 */
 
#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 <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 = 0;
static int force = 0;
MODULE_PARM(high_clock, "i");
MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
MODULE_PARM(force, "i");
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 > 0)
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 {
i2c_delay(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 > 0 && !(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 > 0) {
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_ADAP_CLASS_SMBUS,
.name = "unset",
.algo = &smbus_algorithm,
};
 
static struct pci_device_id sis630_ids[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ 0, }
};
 
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_module_init(&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);
/shark/trunk/drivers/i2c/busses/i2c-philips-par.c
0,0 → 1,243
/* ------------------------------------------------------------------------- */
/* i2c-philips-par.c i2c-hw access for philips style parallel port 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 Kyösti Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
 
/* $Id: i2c-philips-par.c,v 1.1 2004-01-28 15:12:06 giacomo Exp $ */
 
#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>
 
static int type;
 
struct i2c_par
{
struct pardevice *pdev;
struct i2c_adapter adapter;
struct i2c_algo_bit_data bit_lp_data;
struct i2c_par *next;
};
 
static struct i2c_par *adapter_list;
 
 
/* ----- printer port defines ------------------------------------------*/
/* Pin Port Inverted name */
#define I2C_ON 0x20 /* 12 status N paper */
/* ... only for phil. not used */
#define I2C_SDA 0x80 /* 9 data N data7 */
#define I2C_SCL 0x08 /* 17 ctrl N dsel */
 
#define I2C_SDAIN 0x80 /* 11 stat Y busy */
#define I2C_SCLIN 0x08 /* 15 stat Y enable */
 
#define I2C_DMASK 0x7f
#define I2C_CMASK 0xf7
 
/* ----- local functions ---------------------------------------------- */
 
static void bit_lp_setscl(void *data, int state)
{
/*be cautious about state of the control register -
touch only the one bit needed*/
if (state) {
parport_write_control((struct parport *) data,
parport_read_control((struct parport *) data)|I2C_SCL);
} else {
parport_write_control((struct parport *) data,
parport_read_control((struct parport *) data)&I2C_CMASK);
}
}
 
static void bit_lp_setsda(void *data, int state)
{
if (state) {
parport_write_data((struct parport *) data, I2C_DMASK);
} else {
parport_write_data((struct parport *) data, I2C_SDA);
}
}
 
static int bit_lp_getscl(void *data)
{
return parport_read_status((struct parport *) data) & I2C_SCLIN;
}
 
static int bit_lp_getsda(void *data)
{
return parport_read_status((struct parport *) data) & I2C_SDAIN;
}
 
static void bit_lp_setscl2(void *data, int state)
{
if (state) {
parport_write_data((struct parport *) data,
parport_read_data((struct parport *) data)|0x1);
} else {
parport_write_data((struct parport *) data,
parport_read_data((struct parport *) data)&0xfe);
}
}
 
static void bit_lp_setsda2(void *data, int state)
{
if (state) {
parport_write_data((struct parport *) data,
parport_read_data((struct parport *) data)|0x2);
} else {
parport_write_data((struct parport *) data,
parport_read_data((struct parport *) data)&0xfd);
}
}
 
static int bit_lp_getsda2(void *data)
{
return (parport_read_status((struct parport *) data) &
PARPORT_STATUS_BUSY) ? 0 : 1;
}
 
/* ------------------------------------------------------------------------
* 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 bit_lp_data = {
.setsda = bit_lp_setsda,
.setscl = bit_lp_setscl,
.getsda = bit_lp_getsda,
.getscl = bit_lp_getscl,
.udelay = 80,
.mdelay = 80,
.timeout = HZ
};
 
static struct i2c_algo_bit_data bit_lp_data2 = {
.setsda = bit_lp_setsda2,
.setscl = bit_lp_setscl2,
.getsda = bit_lp_getsda2,
.udelay = 80,
.mdelay = 80,
.timeout = HZ
};
 
static struct i2c_adapter bit_lp_ops = {
.owner = THIS_MODULE,
.id = I2C_HW_B_LP,
.name = "Philips Parallel port adapter",
};
 
static void i2c_parport_attach (struct parport *port)
{
struct i2c_par *adapter = kmalloc(sizeof(struct i2c_par),
GFP_KERNEL);
if (!adapter) {
printk(KERN_ERR "i2c-philips-par: Unable to malloc.\n");
return;
}
memset (adapter, 0x00, sizeof(struct i2c_par));
 
/* printk(KERN_DEBUG "i2c-philips-par.o: attaching to %s\n", port->name); */
 
adapter->pdev = parport_register_device(port, "i2c-philips-par",
NULL, NULL, NULL,
PARPORT_FLAG_EXCL,
NULL);
if (!adapter->pdev) {
printk(KERN_ERR "i2c-philips-par: Unable to register with parport.\n");
kfree(adapter);
return;
}
 
adapter->adapter = bit_lp_ops;
adapter->adapter.algo_data = &adapter->bit_lp_data;
adapter->bit_lp_data = type ? bit_lp_data2 : bit_lp_data;
adapter->bit_lp_data.data = port;
 
if (parport_claim_or_block(adapter->pdev) < 0 ) {
printk(KERN_ERR "i2c-philips-par: Could not claim parallel port.\n");
kfree(adapter);
return;
}
/* reset hardware to sane state */
bit_lp_setsda(port, 1);
bit_lp_setscl(port, 1);
parport_release(adapter->pdev);
 
if (i2c_bit_add_bus(&adapter->adapter) < 0) {
printk(KERN_ERR "i2c-philips-par: Unable to register with I2C.\n");
parport_unregister_device(adapter->pdev);
kfree(adapter);
return; /* No good */
}
 
adapter->next = adapter_list;
adapter_list = adapter;
}
 
static void i2c_parport_detach (struct parport *port)
{
struct i2c_par *adapter, *prev = NULL;
 
for (adapter = adapter_list; adapter; adapter = adapter->next) {
if (adapter->pdev->port == port) {
parport_unregister_device(adapter->pdev);
i2c_bit_del_bus(&adapter->adapter);
if (prev)
prev->next = adapter->next;
else
adapter_list = adapter->next;
kfree(adapter);
return;
}
prev = adapter;
}
}
 
static struct parport_driver i2c_driver = {
.name = "i2c-philips-par",
.attach = i2c_parport_attach,
.detach = i2c_parport_detach,
};
 
int __init i2c_bitlp_init(void)
{
printk(KERN_INFO "i2c Philips parallel port adapter driver\n");
 
return parport_register_driver(&i2c_driver);
}
 
void __exit i2c_bitlp_exit(void)
{
parport_unregister_driver(&i2c_driver);
}
 
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter");
MODULE_LICENSE("GPL");
 
MODULE_PARM(type, "i");
 
module_init(i2c_bitlp_init);
module_exit(i2c_bitlp_exit);
/shark/trunk/drivers/i2c/busses/i2c-iop3xx.c
0,0 → 1,536
/* ------------------------------------------------------------------------- */
/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx */
/* ------------------------------------------------------------------------- */
/* 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. */
/* ------------------------------------------------------------------------- */
/*
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östi Mälkki <kmalkki@cc.hut.fi>,
Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com>
 
---------------------------------------------------------------------------*/
 
 
#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/i2c.h>
 
 
#include <asm/arch-iop3xx/iop321.h>
#include <asm/arch-iop3xx/iop321-irqs.h>
#include "i2c-iop3xx.h"
 
 
/* ----- global defines ----------------------------------------------- */
#define PASSERT(x) do { if (!(x) ) \
printk(KERN_CRIT "PASSERT %s in %s:%d\n", #x, __FILE__, __LINE__ );\
} while (0)
 
 
/* ----- global variables --------------------------------------------- */
 
 
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;
 
/* PGM: what is M_REV_DIR_ADDR - do we need it ?? */
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
 
return addr;
}
 
 
static inline void iop3xx_adap_reset(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
/* Follows devman 9.3 */
*iop3xx_adap->biu->CR = IOP321_ICR_UNIT_RESET;
*iop3xx_adap->biu->SR = IOP321_ISR_CLEARBITS;
*iop3xx_adap->biu->CR = 0;
}
 
static inline void iop3xx_adap_set_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
*iop3xx_adap->biu->SAR = MYSAR;
}
 
static inline void iop3xx_adap_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
u32 cr = IOP321_ICR_GCD|IOP321_ICR_SCLEN|IOP321_ICR_UE;
 
/* NB SR bits not same position as CR IE bits :-( */
iop3xx_adap->biu->SR_enabled =
IOP321_ISR_ALD | IOP321_ISR_BERRD |
IOP321_ISR_RXFULL | IOP321_ISR_TXEMPTY;
 
cr |= IOP321_ICR_ALDIE | IOP321_ICR_BERRIE |
IOP321_ICR_RXFULLIE | IOP321_ICR_TXEMPTYIE;
 
*iop3xx_adap->biu->CR = cr;
}
 
static void iop3xx_adap_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
unsigned cr = *iop3xx_adap->biu->CR;
cr &= ~(IOP321_ICR_MSTART | IOP321_ICR_TBYTE |
IOP321_ICR_MSTOP | IOP321_ICR_SCLEN);
*iop3xx_adap->biu->CR = cr;
}
 
static void iop3xx_adap_final_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
unsigned cr = *iop3xx_adap->biu->CR;
cr &= ~(IOP321_ICR_ALDIE | IOP321_ICR_BERRIE |
IOP321_ICR_RXFULLIE | IOP321_ICR_TXEMPTYIE);
iop3xx_adap->biu->SR_enabled = 0;
*iop3xx_adap->biu->CR = cr;
}
 
/*
* 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 void iop3xx_i2c_handler(int this_irq,
void *dev_id,
struct pt_regs *regs)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id;
 
u32 sr = *iop3xx_adap->biu->SR;
 
if ((sr &= iop3xx_adap->biu->SR_enabled)) {
*iop3xx_adap->biu->SR = sr;
iop3xx_adap->biu->SR_received |= sr;
wake_up_interruptible(&iop3xx_adap->waitq);
}
}
 
/* check all error conditions, clear them , report most important */
static int iop3xx_adap_error(u32 sr)
{
int rc = 0;
 
if ((sr&IOP321_ISR_BERRD)) {
if ( !rc ) rc = -I2C_ERR_BERR;
}
if ((sr&IOP321_ISR_ALD)) {
if ( !rc ) rc = -I2C_ERR_ALD;
}
return rc;
}
 
static inline u32 get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
{
unsigned long flags;
u32 sr;
 
spin_lock_irqsave(&iop3xx_adap->lock, flags);
sr = iop3xx_adap->biu->SR_received;
iop3xx_adap->biu->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_adap_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;
 
do {
interrupted = wait_event_interruptible_timeout (
iop3xx_adap->waitq,
(done = compare( sr = get_srstat(iop3xx_adap),flags )),
iop3xx_adap->timeout
);
if ((rc = iop3xx_adap_error(sr)) < 0) {
*status = sr;
return rc;
}else if (!interrupted) {
*status = sr;
return rc = -ETIMEDOUT;
}
} while(!done);
 
*status = sr;
 
return rc = 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_adap_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_adap_wait_event(
iop3xx_adap,
IOP321_ISR_TXEMPTY|IOP321_ISR_ALD|IOP321_ISR_BERRD,
status, any_bits_set);
}
 
static int iop3xx_adap_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_adap_wait_event(
iop3xx_adap,
IOP321_ISR_RXFULL|IOP321_ISR_ALD|IOP321_ISR_BERRD,
status, any_bits_set);
}
 
static int iop3xx_adap_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
{
return iop3xx_adap_wait_event(
iop3xx_adap, IOP321_ISR_UNITBUSY, status, all_bits_clear);
}
 
/*
* Description: This performs the IOP3xx initialization sequence
* Valid for IOP321. Maybe valid for IOP310?.
*/
static int iop3xx_adap_init (struct i2c_algo_iop3xx_data *iop3xx_adap)
{
*IOP321_GPOD &= ~(iop3xx_adap->channel==0 ?
IOP321_GPOD_I2C0:
IOP321_GPOD_I2C1);
 
iop3xx_adap_reset(iop3xx_adap);
iop3xx_adap_set_slave_addr(iop3xx_adap);
iop3xx_adap_enable(iop3xx_adap);
return 0;
}
 
static int iop3xx_adap_send_target_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
struct i2c_msg* msg)
{
unsigned cr = *iop3xx_adap->biu->CR;
int status;
int rc;
 
*iop3xx_adap->biu->DBR = iic_cook_addr(msg);
cr &= ~(IOP321_ICR_MSTOP | IOP321_ICR_NACK);
cr |= IOP321_ICR_MSTART | IOP321_ICR_TBYTE;
 
*iop3xx_adap->biu->CR = cr;
rc = iop3xx_adap_wait_tx_done(iop3xx_adap, &status);
/* this assert fires every time, contrary to IOP manual
PASSERT((status&IOP321_ISR_UNITBUSY)!=0);
*/
PASSERT((status&IOP321_ISR_RXREAD)==0);
return rc;
}
 
static int iop3xx_adap_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, int stop)
{
unsigned cr = *iop3xx_adap->biu->CR;
int status;
int rc;
 
*iop3xx_adap->biu->DBR = byte;
cr &= ~IOP321_ICR_MSTART;
if (stop) {
cr |= IOP321_ICR_MSTOP;
} else {
cr &= ~IOP321_ICR_MSTOP;
}
*iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE;
rc = iop3xx_adap_wait_tx_done(iop3xx_adap, &status);
 
return rc;
}
 
static int iop3xx_adap_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap,
char* byte, int stop)
{
unsigned cr = *iop3xx_adap->biu->CR;
int status;
int rc;
 
cr &= ~IOP321_ICR_MSTART;
 
if (stop) {
cr |= IOP321_ICR_MSTOP|IOP321_ICR_NACK;
} else {
cr &= ~(IOP321_ICR_MSTOP|IOP321_ICR_NACK);
}
*iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE;
 
rc = iop3xx_adap_wait_rx_done(iop3xx_adap, &status);
 
*byte = *iop3xx_adap->biu->DBR;
 
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_adap_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_adap_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_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_adap_send_target_slave_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_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_adap_wait_idle(iop3xx_adap, &status);
iop3xx_adap_reset(iop3xx_adap);
iop3xx_adap_enable(iop3xx_adap);
 
for (im = 0; ret == 0 && im != num; ++im) {
ret = iop3xx_handle_msg(i2c_adap, &msgs[im]);
}
 
iop3xx_adap_transaction_cleanup(iop3xx_adap);
 
return ret;
}
 
static int algo_control(struct i2c_adapter *adapter, unsigned int cmd,
unsigned long arg)
{
return 0;
}
 
static u32 iic_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
 
 
/* -----exported algorithm data: ------------------------------------- */
 
static struct i2c_algorithm iic_algo = {
.name = "IOP3xx I2C algorithm",
.id = I2C_ALGO_OCP_IOP3XX,
.master_xfer = iop3xx_master_xfer,
.algo_control = algo_control,
.functionality = iic_func,
};
 
/*
* registering functions to load algorithms at runtime
*/
static int i2c_iop3xx_add_bus(struct i2c_adapter *iic_adap)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data;
 
if (!request_region( REGION_START(iop3xx_adap),
REGION_LENGTH(iop3xx_adap),
iic_adap->name)) {
return -ENODEV;
}
 
init_waitqueue_head(&iop3xx_adap->waitq);
spin_lock_init(&iop3xx_adap->lock);
 
if (request_irq(
iop3xx_adap->biu->irq,
iop3xx_i2c_handler,
/* SA_SAMPLE_RANDOM */ 0,
iic_adap->name,
iop3xx_adap)) {
return -ENODEV;
}
 
/* register new iic_adapter to i2c module... */
iic_adap->id |= iic_algo.id;
iic_adap->algo = &iic_algo;
 
iic_adap->timeout = 100; /* default values, should */
iic_adap->retries = 3; /* be replaced by defines */
 
iop3xx_adap_init(iic_adap->algo_data);
i2c_add_adapter(iic_adap);
return 0;
}
 
static int i2c_iop3xx_del_bus(struct i2c_adapter *iic_adap)
{
struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data;
 
iop3xx_adap_final_cleanup(iop3xx_adap);
free_irq(iop3xx_adap->biu->irq, iop3xx_adap);
 
release_region(REGION_START(iop3xx_adap), REGION_LENGTH(iop3xx_adap));
 
return i2c_del_adapter(iic_adap);
}
 
#ifdef CONFIG_ARCH_IOP321
 
static struct iop3xx_biu biu0 = {
.CR = IOP321_ICR0,
.SR = IOP321_ISR0,
.SAR = IOP321_ISAR0,
.DBR = IOP321_IDBR0,
.BMR = IOP321_IBMR0,
.irq = IRQ_IOP321_I2C_0,
};
 
static struct iop3xx_biu biu1 = {
.CR = IOP321_ICR1,
.SR = IOP321_ISR1,
.SAR = IOP321_ISAR1,
.DBR = IOP321_IDBR1,
.BMR = IOP321_IBMR1,
.irq = IRQ_IOP321_I2C_1,
};
 
#define ADAPTER_NAME_ROOT "IOP321 i2c biu adapter "
#else
#error Please define the BIU struct iop3xx_biu for your processor arch
#endif
 
static struct i2c_algo_iop3xx_data algo_iop3xx_data0 = {
.channel = 0,
.biu = &biu0,
.timeout = 1*HZ,
};
static struct i2c_algo_iop3xx_data algo_iop3xx_data1 = {
.channel = 1,
.biu = &biu1,
.timeout = 1*HZ,
};
 
static struct i2c_adapter iop3xx_ops0 = {
.owner = THIS_MODULE,
.name = ADAPTER_NAME_ROOT "0",
.id = I2C_HW_IOP321,
.algo_data = &algo_iop3xx_data0,
};
static struct i2c_adapter iop3xx_ops1 = {
.owner = THIS_MODULE,
.name = ADAPTER_NAME_ROOT "1",
.id = I2C_HW_IOP321,
.algo_data = &algo_iop3xx_data1,
};
 
static int __init i2c_iop3xx_init (void)
{
return i2c_iop3xx_add_bus(&iop3xx_ops0) ||
i2c_iop3xx_add_bus(&iop3xx_ops1);
}
 
static void __exit i2c_iop3xx_exit (void)
{
i2c_iop3xx_del_bus(&iop3xx_ops0);
i2c_iop3xx_del_bus(&iop3xx_ops1);
}
 
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");
 
MODULE_PARM(i2c_debug,"i");
 
MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol");
 
/shark/trunk/drivers/i2c/busses/i2c-keywest.c
0,0 → 1,653
/*
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.
 
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/module.h>
#include <linux/config.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 "i2c-keywest.h"
 
#define DBG(x...) do {\
if (debug > 0) \
printk(KERN_DEBUG "KW:" x); \
} while(0)
 
 
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
MODULE_LICENSE("GPL");
MODULE_PARM(probe, "i");
MODULE_PARM(debug, "i");
 
int probe = 0;
int debug = 0;
 
static void
do_stop(struct keywest_iface* iface, int result)
{
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
 
/* Main state machine for standard & standard sub mode */
static int
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
int rearm_timer = 1;
DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
isr, read_reg(reg_status), iface->state);
if (isr == 0 && iface->state != state_stop) {
do_stop(iface, -1);
return rearm_timer;
}
if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
iface->result = -1;
iface->state = state_stop;
}
switch(iface->state) {
case state_addr:
if (!(isr & KW_I2C_IRQ_ADDR)) {
do_stop(iface, -1);
break;
}
ack = read_reg(reg_status);
DBG("ack on set address: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
/* 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, read_reg(reg_control)
| KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
break;
case state_read:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
*(iface->data++) = read_reg(reg_data);
DBG("read byte: %x\n", *(iface->data-1));
iface->datalen--;
if (iface->datalen == 0)
iface->state = state_stop;
else
write_reg(reg_control, 0);
break;
case state_write:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
/* Check ack status */
ack = read_reg(reg_status);
DBG("ack on data write: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
if (iface->datalen) {
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
} else
do_stop(iface, 0);
break;
case state_stop:
if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
do_stop(iface, -1);
else {
rearm_timer = 0;
iface->state = state_idle;
write_reg(reg_control, 0x00);
write_reg(reg_ier, 0x00);
complete(&iface->complete);
}
break;
}
write_reg(reg_isr, isr);
 
return rearm_timer;
}
 
/* 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;
 
spin_lock(&iface->lock);
del_timer(&iface->timeout_timer);
if (handle_interrupt(iface, read_reg(reg_isr)))
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
spin_unlock(&iface->lock);
return IRQ_HANDLED;
}
 
static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
 
DBG("timeout !\n");
spin_lock_irq(&iface->lock);
if (handle_interrupt(iface, read_reg(reg_isr)))
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
spin_unlock(&iface->lock);
}
 
/*
* 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 -1;
/* 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;
}
 
/* Original driver had this limitation */
if (len > 32)
len = 32;
 
down(&iface->sem);
 
DBG("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->stopretry = 0;
iface->read_write = read_write;
/* 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);
 
/* 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);
 
/* Arm timeout */
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
 
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
 
/* Wait interrupt operations completion */
wait_for_completion(&iface->complete);
 
rc = iface->result;
DBG("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 */
up(&iface->sem);
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;
 
down(&iface->sem);
 
/* 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;
}
DBG("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;
iface->stopretry = 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));
 
/* Arm timeout */
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
 
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
 
/* Wait interrupt operations completion */
wait_for_completion(&iface->complete);
 
rc = iface->result;
if (rc == 0)
completed++;
DBG("transfer done, result: %d\n", rc);
}
 
/* Release sem */
up(&iface->sem);
 
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, *psteps, *prate;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
int rc;
 
psteps = (unsigned long *)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 (!strcmp(np->parent->name, "uni-n")) {
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");
return -ENOMEM;
}
memset(iface, 0, tsize);
init_MUTEX(&iface->sem);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
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 = (unsigned long)ioremap(np->addrs[0].address + addroffset,
np->addrs[0].size);
if (iface->base == 0) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
return -ENOMEM;
}
 
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
 
/* Select interface rate */
iface->cur_mode = KW_I2C_MODE_100KHZ;
prate = (unsigned long *)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",
*prate);
}
/* Select standard sub mode */
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
/* 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);
 
/* Request chip interrupt */
rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
if (rc) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap((void *)iface->base);
kfree(iface);
return -ENODEV;
}
 
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 */
down(&iface->sem);
 
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
set_task_state(current,TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
spin_lock_irq(&iface->lock);
}
iface->state = state_dead;
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
up(&iface->sem);
 
/* 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((void *)iface->base);
dev_set_drvdata(dev, NULL);
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)
{
macio_register_driver(&i2c_keywest_macio_driver);
of_register_driver(&i2c_keywest_of_platform_driver);
 
return 0;
}
 
static void __exit
i2c_keywest_cleanup(void)
{
macio_unregister_driver(&i2c_keywest_macio_driver);
of_unregister_driver(&i2c_keywest_of_platform_driver);
}
 
module_init(i2c_keywest_init);
module_exit(i2c_keywest_cleanup);
/shark/trunk/drivers/i2c/busses/i2c-iop3xx.h
0,0 → 1,118
/* ------------------------------------------------------------------------- */
/* 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 IOP321_ICR_FAST_MODE 0x8000 /* 1=400kBps, 0=100kBps */
#define IOP321_ICR_UNIT_RESET 0x4000 /* 1=RESET */
#define IOP321_ICR_SADIE 0x2000 /* 1=Slave Detect Interrupt Enable */
#define IOP321_ICR_ALDIE 0x1000 /* 1=Arb Loss Detect Interrupt Enable */
#define IOP321_ICR_SSDIE 0x0800 /* 1=Slave STOP Detect Interrupt Enable */
#define IOP321_ICR_BERRIE 0x0400 /* 1=Bus Error Interrupt Enable */
#define IOP321_ICR_RXFULLIE 0x0200 /* 1=Receive Full Interrupt Enable */
#define IOP321_ICR_TXEMPTYIE 0x0100 /* 1=Transmit Empty Interrupt Enable */
#define IOP321_ICR_GCD 0x0080 /* 1=General Call Disable */
/*
* IOP321_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 IOP321_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 IOP321_ICR_SCLEN 0x0020 /* 1=SCL enable for master mode */
#define IOP321_ICR_MABORT 0x0010 /* 1=Send a STOP with no data
* NB TBYTE must be clear */
#define IOP321_ICR_TBYTE 0x0008 /* 1=Send/Receive a byte. i2c clears */
#define IOP321_ICR_NACK 0x0004 /* 1=reply with NACK */
#define IOP321_ICR_MSTOP 0x0002 /* 1=send a STOP after next data byte */
#define IOP321_ICR_MSTART 0x0001 /* 1=initiate a START */
 
 
#define IOP321_ISR_BERRD 0x0400 /* 1=BUS ERROR Detected */
#define IOP321_ISR_SAD 0x0200 /* 1=Slave ADdress Detected */
#define IOP321_ISR_GCAD 0x0100 /* 1=General Call Address Detected */
#define IOP321_ISR_RXFULL 0x0080 /* 1=Receive Full */
#define IOP321_ISR_TXEMPTY 0x0040 /* 1=Transmit Empty */
#define IOP321_ISR_ALD 0x0020 /* 1=Arbitration Loss Detected */
#define IOP321_ISR_SSD 0x0010 /* 1=Slave STOP Detected */
#define IOP321_ISR_BBUSY 0x0008 /* 1=Bus BUSY */
#define IOP321_ISR_UNITBUSY 0x0004 /* 1=Unit Busy */
#define IOP321_ISR_NACK 0x0002 /* 1=Unit Rx or Tx a NACK */
#define IOP321_ISR_RXREAD 0x0001 /* 1=READ 0=WRITE (R/W bit of slave addr */
 
#define IOP321_ISR_CLEARBITS 0x07f0
 
#define IOP321_ISAR_SAMASK 0x007f
 
#define IOP321_IDBR_MASK 0x00ff
 
#define IOP321_IBMR_SCL 0x0002
#define IOP321_IBMR_SDA 0x0001
 
#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */
#define IOP321_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)
 
 
struct iop3xx_biu { /* Bus Interface Unit - the hardware */
/* physical hardware defs - regs*/
u32 *CR;
u32 *SR;
u32 *SAR;
u32 *DBR;
u32 *BMR;
/* irq bit vector */
u32 irq;
/* stored flags */
u32 SR_enabled, SR_received;
};
 
struct i2c_algo_iop3xx_data {
int channel;
 
wait_queue_head_t waitq;
spinlock_t lock;
int timeout;
struct iop3xx_biu* biu;
};
 
#define REGION_START(adap) ((u32)((adap)->biu->CR))
#define REGION_END(adap) ((u32)((adap)->biu->BMR+1))
#define REGION_LENGTH(adap) (REGION_END(adap)-REGION_START(adap))
 
#define IRQ_STATUS_MASK(adap) (1<<adap->biu->irq)
 
#endif /* I2C_IOP3XX_H */
/shark/trunk/drivers/i2c/busses/i2c-keywest.h
0,0 → 1,110
#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
{
unsigned long base;
unsigned bsteps;
int irq;
struct semaphore sem;
spinlock_t lock;
struct keywest_chan* channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
u8* data;
unsigned datalen;
int state;
int result;
int stopretry;
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(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps));
}
 
static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps), val);
(void)__read_reg(iface, reg);
udelay(10);
}
 
#define write_reg(reg, val) __write_reg(iface, reg, val)
#define read_reg(reg) __read_reg(iface, reg)
 
 
 
#endif /* __I2C_KEYWEST_H__ */
/shark/trunk/drivers/i2c/busses/scx200_i2c.c
0,0 → 1,132
/* 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");
 
MODULE_PARM(scl, "i");
MODULE_PARM_DESC(scl, "GPIO line for SCL");
MODULE_PARM(sda, "i");
MODULE_PARM_DESC(sda, "GPIO line for SDA");
 
static int scl = CONFIG_SCx200_I2C_SCL;
static int sda = CONFIG_SCx200_I2C_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",
};
 
int scx200_i2c_init(void)
{
printk(KERN_DEBUG NAME ": NatSemi SCx200 I2C Driver\n");
 
if (!scx200_gpio_present()) {
printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
return -ENODEV;
}
 
printk(KERN_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;
}
 
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:
*/
/shark/trunk/drivers/i2c/busses/i2c-amd756.c
0,0 → 1,415
/*
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)
*/
 
/*
Supports AMD756, AMD766, AMD768 and nVidia nForce
Note: we assume there can only be one device, with one SMBus interface.
*/
 
/* #define DEBUG 1 */
 
#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/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 {
i2c_delay(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 {
i2c_delay(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);
i2c_delay(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);
/* TODO: Why only during write? */
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMB_HOST_COMMAND);
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,
};
 
static struct i2c_adapter amd756_adapter = {
.owner = THIS_MODULE,
.class = I2C_ADAP_CLASS_SMBUS,
.algo = &smbus_algorithm,
.name = "unset",
};
 
enum chiptype { AMD756, AMD766, AMD768, NFORCE };
 
static struct pci_device_id amd756_ids[] = {
{PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 },
{PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 },
{PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 },
{PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ 0, }
};
 
static int __devinit amd756_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int nforce = (id->driver_data == NFORCE), 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_adapter.dev.parent = &pdev->dev;
 
snprintf(amd756_adapter.name, I2C_NAME_SIZE,
"SMBus AMD75x adapter at %04x", amd756_ioport);
 
error = i2c_add_adapter(&amd756_adapter);
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_adapter);
release_region(amd756_ioport, SMB_IOSIZE);
}
 
static struct pci_driver amd756_driver = {
.name = "amd75x smbus",
.id_table = amd756_ids,
.probe = amd756_probe,
.remove = __devexit_p(amd756_remove),
};
 
static int __init amd756_init(void)
{
return pci_module_init(&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/nVidia nForce SMBus driver");
MODULE_LICENSE("GPL");
 
module_init(amd756_init)
module_exit(amd756_exit)
/shark/trunk/drivers/i2c/busses/i2c-velleman.c
0,0 → 1,160
/* ------------------------------------------------------------------------- */
/* i2c-velleman.c i2c-hw access for Velleman K9000 adapters */
/* ------------------------------------------------------------------------- */
/* 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. */
/* ------------------------------------------------------------------------- */
 
/* $Id: i2c-velleman.c,v 1.1 2004-01-28 15:12:07 giacomo Exp $ */
 
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) /* should be reasonable open, close &c. */
#define DEB2(x) /* low level debugging - very slow */
#define DEBE(x) x /* error messages */
 
/* Pin Port Inverted name */
#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */
#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */
 
#define I2C_SDAIN 0x10 /* stat bit 4 */
#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv)(reads own output)*/
 
#define I2C_DMASK 0xfd
#define I2C_CMASK 0xf7
 
 
/* --- Convenience defines for the parallel port: */
#define BASE (unsigned int)(data)
#define DATA BASE /* Centronics data port */
#define STAT (BASE+1) /* Centronics status port */
#define CTRL (BASE+2) /* Centronics control port */
 
#define DEFAULT_BASE 0x378
static int base=0;
 
/* ----- local functions --------------------------------------------------- */
 
static void bit_velle_setscl(void *data, int state)
{
if (state) {
outb(inb(CTRL) & I2C_CMASK, CTRL);
} else {
outb(inb(CTRL) | I2C_SCL, CTRL);
}
}
 
static void bit_velle_setsda(void *data, int state)
{
if (state) {
outb(inb(CTRL) & I2C_DMASK , CTRL);
} else {
outb(inb(CTRL) | I2C_SDA, CTRL);
}
}
 
static int bit_velle_getscl(void *data)
{
return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) );
}
 
static int bit_velle_getsda(void *data)
{
return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) );
}
 
static int bit_velle_init(void)
{
if (!request_region(base, (base == 0x3bc) ? 3 : 8,
"i2c (Vellemann adapter)"))
return -ENODEV;
 
bit_velle_setsda((void*)base,1);
bit_velle_setscl((void*)base,1);
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_bit_data bit_velle_data = {
.setsda = bit_velle_setsda,
.setscl = bit_velle_setscl,
.getsda = bit_velle_getsda,
.getscl = bit_velle_getscl,
.udelay = 10,
.mdelay = 10,
.timeout = HZ
};
 
static struct i2c_adapter bit_velle_ops = {
.owner = THIS_MODULE,
.algo_data = &bit_velle_data,
.name = "Velleman K8000",
};
 
static int __init i2c_bitvelle_init(void)
{
printk(KERN_INFO "i2c-velleman: i2c Velleman K8000 driver\n");
if (base==0) {
/* probe some values */
base=DEFAULT_BASE;
bit_velle_data.data=(void*)DEFAULT_BASE;
if (bit_velle_init()==0) {
if(i2c_bit_add_bus(&bit_velle_ops) < 0)
return -ENODEV;
} else {
return -ENODEV;
}
} else {
bit_velle_data.data=(void*)base;
if (bit_velle_init()==0) {
if(i2c_bit_add_bus(&bit_velle_ops) < 0)
return -ENODEV;
} else {
return -ENODEV;
}
}
printk(KERN_DEBUG "i2c-velleman: found device at %#x.\n",base);
return 0;
}
 
static void __exit i2c_bitvelle_exit(void)
{
i2c_bit_del_bus(&bit_velle_ops);
release_region(base, (base == 0x3bc) ? 3 : 8);
}
 
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter");
MODULE_LICENSE("GPL");
 
MODULE_PARM(base, "i");
 
module_init(i2c_bitvelle_init);
module_exit(i2c_bitvelle_exit);
/shark/trunk/drivers/i2c/busses/i2c-elektor.c
0,0 → 1,286
/* ------------------------------------------------------------------------- */
/* 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ö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/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;
static int i2c_debug;
 
/* 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;
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) if (i2c_debug>=1) x
#define DEB2(x) if (i2c_debug>=2) x
#define DEB3(x) if (i2c_debug>=3) x
#define DEBE(x) x /* error messages */
 
/* ----- 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;
}
 
DEB3(printk(KERN_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, address);
/* fall */
case 1: /* memory mapped I/O */
writeb(val, address);
break;
}
}
 
static int pcf_isa_getbyte(void *data, int ctl)
{
int address = ctl ? (base + 1) : base;
int val = mmapped ? readb(address) : inb(address);
 
DEB3(printk(KERN_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) {
 
int timeout = 2;
 
if (irq > 0) {
cli();
if (pcf_pending == 0) {
interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ );
} else
pcf_pending = 0;
sti();
} else {
udelay(100);
}
}
 
 
static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) {
pcf_pending = 1;
wake_up_interruptible(&pcf_wait);
return IRQ_HANDLED;
}
 
 
static int pcf_isa_init(void)
{
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", 0) < 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,
.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 =
pci_find_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)) {
DEB3(printk(KERN_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");
}
}
}
}
#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, 0);
}
 
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, 0);
}
 
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_PARM(base, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(clock, "i");
MODULE_PARM(own, "i");
MODULE_PARM(mmapped, "i");
MODULE_PARM(i2c_debug, "i");
 
module_init(i2c_pcfisa_init);
module_exit(i2c_pcfisa_exit);
/shark/trunk/drivers/i2c/busses/scx200_acb.c
0,0 → 1,553
/* 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 <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] = { 0x840 };
MODULE_PARM(base, "1-4i");
MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers");
 
#define DEBUG 0
 
#if 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;
}
schedule_timeout(HZ/100+1);
}
 
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);
 
#if DEBUG
printk(KERN_DEBUG NAME ": 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,
};
 
struct scx200_acb_iface *scx200_acb_list;
 
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;
 
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 int __init scx200_acb_init(void)
{
int i;
int rc;
 
printk(KERN_DEBUG NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
 
/* Verify that this really is a SCx200 processor */
if (pci_find_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL) == NULL)
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:
*/
 
/shark/trunk/drivers/i2c/busses/i2c-amd8111.c
0,0 → 1,412
/*
* 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/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
*/
 
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;
}
 
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;
}
 
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;
}
 
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
 
 
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_DATA, data->byte);
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) {
i2c_delay(HZ/100);
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;
}
 
 
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[] = {
{ 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};
 
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_ADAP_CLASS_SMBUS;
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 smbus 2",
.id_table = amd8111_ids,
.probe = amd8111_probe,
.remove = __devexit_p(amd8111_remove),
};
 
static int __init i2c_amd8111_init(void)
{
return pci_module_init(&amd8111_driver);
}
 
 
static void __exit i2c_amd8111_exit(void)
{
pci_unregister_driver(&amd8111_driver);
}
 
module_init(i2c_amd8111_init);
module_exit(i2c_amd8111_exit);
/shark/trunk/drivers/i2c/busses/i2c-i801.c
0,0 → 1,635
/*
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)
 
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 */
 
/* #define DEBUG 1 */
 
#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 <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 int force_addr = 0;
MODULE_PARM(force_addr, "i");
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))
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 {
i2c_delay(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);
i2c_delay(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);
i2c_delay(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_ADAP_CLASS_SMBUS,
.algo = &smbus_algorithm,
.name = "unset",
};
 
static struct pci_device_id i801_ids[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801AA_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801AB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801BA_2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801CA_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801DB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801EB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
 
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);
}
 
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_module_init(&i801_driver);
}
 
static void __exit i2c_i801_exit(void)
{
pci_unregister_driver(&i801_driver);
release_region(i801_smba, (isich4 ? 16 : 8));
}
 
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);
/shark/trunk/drivers/i2c/busses/i2c-ite.c
0,0 → 1,278
/*
-------------------------------------------------------------------------
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östi Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
 
#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 <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 = 0;
static int irq = 0;
static int clock = 0;
static int own = 0;
 
static int i2c_debug=0;
static struct iic_ite gpi;
static wait_queue_head_t iic_wait;
static int iic_pending;
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) if (i2c_debug>=1) x
#define DEB2(x) if (i2c_debug>=2) x
#define DEB3(x) if (i2c_debug>=3) x
#define DEBE(x) x /* error messages */
 
 
/* ----- local functions ---------------------------------------------- */
 
static void iic_ite_setiic(void *data, int ctl, short val)
{
unsigned long j = jiffies + 10;
 
DEB3(printk(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff));
DEB3({while (time_before(jiffies, j)) schedule();})
outw(val,ctl);
}
 
static short iic_ite_getiic(void *data, int ctl)
{
short val;
 
val = inw(ctl);
DEB3(printk("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);
}
 
 
#if 0
static void iic_ite_sleep(unsigned long timeout)
{
schedule_timeout( timeout * HZ);
}
#endif
 
 
/* Put this process to sleep. We will wake up when the
* IIC controller interrupts.
*/
static void iic_ite_waitforpin(void) {
 
int timeout = 2;
 
/* 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) {
cli();
if (iic_pending == 0) {
interruptible_sleep_on_timeout(&iic_wait, timeout*HZ );
} else
iic_pending = 0;
sti();
} else {
udelay(100);
}
}
 
 
static void iic_ite_handler(int this_irq, void *dev_id, struct pt_regs *regs)
{
iic_pending = 1;
 
DEB2(printk("iic_ite_handler: in interrupt handler\n"));
wake_up_interruptible(&iic_wait);
}
 
 
/* 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);
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_PARM(base, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(clock, "i");
MODULE_PARM(own, "i");
MODULE_PARM(i2c_debug,"i");
 
 
/* 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);
/shark/trunk/drivers/i2c/busses/i2c-ali1535.c
0,0 → 1,545
/*
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 */
 
/* #define DEBUG 1 */
 
#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/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;
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 {
i2c_delay(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++) {
i2c_delay(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;
}
 
 
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,
.algo = &smbus_algorithm,
.name = "unset",
};
 
static struct pci_device_id ali1535_ids[] = {
{
.vendor = PCI_VENDOR_ID_AL,
.device = PCI_DEVICE_ID_AL_M7101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ },
};
 
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);
}
 
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_module_init(&ali1535_driver);
}
 
static void __exit i2c_ali1535_exit(void)
{
pci_unregister_driver(&ali1535_driver);
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
}
 
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);
/shark/trunk/drivers/i2c/busses/i2c-elv.c
0,0 → 1,173
/* ------------------------------------------------------------------------- */
/* i2c-elv.c i2c-hw access for philips style parallel port 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 Kyösti Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <asm/io.h>
 
#define DEFAULT_BASE 0x378
static int base=0;
static unsigned char port_data = 0;
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) /* should be reasonable open, close &c. */
#define DEB2(x) /* low level debugging - very slow */
#define DEBE(x) x /* error messages */
#define DEBINIT(x) x /* detection status messages */
 
/* --- Convenience defines for the parallel port: */
#define BASE (unsigned int)(data)
#define DATA BASE /* Centronics data port */
#define STAT (BASE+1) /* Centronics status port */
#define CTRL (BASE+2) /* Centronics control port */
 
 
/* ----- local functions ---------------------------------------------- */
 
 
static void bit_elv_setscl(void *data, int state)
{
if (state) {
port_data &= 0xfe;
} else {
port_data |=1;
}
outb(port_data, DATA);
}
 
static void bit_elv_setsda(void *data, int state)
{
if (state) {
port_data &=0xfd;
} else {
port_data |=2;
}
outb(port_data, DATA);
}
 
static int bit_elv_getscl(void *data)
{
return ( 0 == ( (inb_p(STAT)) & 0x08 ) );
}
 
static int bit_elv_getsda(void *data)
{
return ( 0 == ( (inb_p(STAT)) & 0x40 ) );
}
 
static int bit_elv_init(void)
{
if (!request_region(base, (base == 0x3bc) ? 3 : 8,
"i2c (ELV adapter)"))
return -ENODEV;
 
if (inb(base+1) & 0x80) { /* BUSY should be high */
DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Busy was low.\n"));
goto fail;
}
 
outb(0x0c,base+2); /* SLCT auf low */
udelay(400);
if (!(inb(base+1) && 0x10)) {
outb(0x04,base+2);
DEBINIT(printk(KERN_DEBUG "i2c-elv.o: Select was high.\n"));
goto fail;
}
 
port_data = 0;
bit_elv_setsda((void*)base,1);
bit_elv_setscl((void*)base,1);
return 0;
 
fail:
release_region(base , (base == 0x3bc) ? 3 : 8);
return -ENODEV;
}
 
/* ------------------------------------------------------------------------
* 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 bit_elv_data = {
.setsda = bit_elv_setsda,
.setscl = bit_elv_setscl,
.getsda = bit_elv_getsda,
.getscl = bit_elv_getscl,
.udelay = 80,
.mdelay = 80,
.timeout = HZ
};
 
static struct i2c_adapter bit_elv_ops = {
.owner = THIS_MODULE,
.algo_data = &bit_elv_data,
.name = "ELV Parallel port adaptor",
};
 
static int __init i2c_bitelv_init(void)
{
printk(KERN_INFO "i2c ELV parallel port adapter driver\n");
if (base==0) {
/* probe some values */
base=DEFAULT_BASE;
bit_elv_data.data=(void*)DEFAULT_BASE;
if (bit_elv_init()==0) {
if(i2c_bit_add_bus(&bit_elv_ops) < 0)
return -ENODEV;
} else {
return -ENODEV;
}
} else {
i2c_set_adapdata(&bit_elv_ops, (void *)base);
if (bit_elv_init()==0) {
if(i2c_bit_add_bus(&bit_elv_ops) < 0)
return -ENODEV;
} else {
return -ENODEV;
}
}
printk(KERN_DEBUG "i2c-elv.o: found device at %#x.\n",base);
return 0;
}
 
static void __exit i2c_bitelv_exit(void)
{
i2c_bit_del_bus(&bit_elv_ops);
release_region(base , (base == 0x3bc) ? 3 : 8);
}
 
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter");
MODULE_LICENSE("GPL");
 
MODULE_PARM(base, "i");
 
module_init(i2c_bitelv_init);
module_exit(i2c_bitelv_exit);
/shark/trunk/drivers/i2c/busses/i2c-sis5595.c
0,0 → 1,417
/*
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
*/
 
/* #define DEBUG 1 */
 
#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 <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 int force_addr = 0;
MODULE_PARM(force_addr, "i");
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))
goto error;
if (!pci_read_config_word(SIS5595_dev, ACPI_BASE, &a))
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))
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))
goto error;
if (!pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
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 {
i2c_delay(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("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,
.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, }
};
 
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);
}
 
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_module_init(&sis5595_driver);
}
 
static void __exit i2c_sis5595_exit(void)
{
pci_unregister_driver(&sis5595_driver);
release_region(sis5595_base + SMB_INDEX, 2);
}
 
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);
/shark/trunk/drivers/i2c/busses/i2c-prosavage.c
0,0 → 1,338
/*
* 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/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 *mmvga;
int i2c_reg;
int adap_ok;
struct i2c_adapter adap;
struct i2c_algo_bit_data algo;
};
 
struct s_i2c_chip {
void *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
*/
#ifndef PCI_DEVICE_ID_S3_SAVAGE4
#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25
#endif
#ifndef PCI_DEVICE_ID_S3_PROSAVAGE8
#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04
#endif
 
#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, u8 *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 __devexit 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, },
};
 
static struct pci_driver prosavage_driver = {
.name = "prosavage-smbus",
.id_table = prosavage_pci_tbl,
.probe = prosavage_probe,
.remove = __devexit_p(prosavage_remove),
};
 
static int __init i2c_prosavage_init(void)
{
return pci_module_init(&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);
/shark/trunk/drivers/i2c/busses/i2c-voodoo3.c
0,0 → 1,249
/*
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/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 *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 int 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,
.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,
.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, }
};
 
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_module_init(&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);
/shark/trunk/drivers/i2c/busses/i2c-savage4.c
0,0 → 1,206
/*
i2c-savage4.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 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/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 *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 int 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, }
};
 
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_module_init(&savage4_driver);
}
 
static void __exit i2c_savage4_exit(void)
{
pci_unregister_driver(&savage4_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("Savage4 I2C/SMBus driver");
MODULE_LICENSE("GPL");
 
module_init(i2c_savage4_init);
module_exit(i2c_savage4_exit);
/shark/trunk/drivers/i2c/busses/i2c-ibm_iic.c
0,0 → 1,729
/*
* drivers/i2c/i2c-ibm_iic.c
*
* 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.
*
* 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ö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/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.0"
 
MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION);
MODULE_LICENSE("GPL");
 
static int iic_scan = 0;
MODULE_PARM(iic_scan, "i");
MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus");
 
static int iic_force_poll = 0;
MODULE_PARM(iic_force_poll, "i");
MODULE_PARM_DESC(iic_force_poll, "Force polling mode");
 
static int iic_force_fast = 0;
MODULE_PARM(iic_force_fast, "i");
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(x...) printk(KERN_DEBUG "ibm-iic" ##x)
#else
# define DBG(x...) ((void)0)
#endif
#if DBG_LEVEL > 1
# define DBG2(x...) DBG( ##x )
#else
# define DBG2(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 *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
 
/* 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 *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 *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);
}
 
/*
* 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* 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 *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 *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 *iic = dev->vaddr;
int ret = 0;
if (dev->irq >= 0){
/* Interrupt mode */
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
add_wait_queue(&dev->wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (in_8(&iic->sts) & STS_PT)
schedule_timeout(dev->adap.timeout * HZ);
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->wq, &wait);
if (unlikely(signal_pending(current))){
DBG("%d: wait interrupted\n", dev->idx);
ret = -ERESTARTSYS;
} 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 *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((volatile u8*)&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((volatile u8*)&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 *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 *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)){
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,
.smbus_xfer = NULL,
.slave_send = NULL,
.slave_recv = NULL,
.algo_control = NULL,
.functionality = iic_func
};
 
/*
* Scan bus for valid 7-bit addresses (ie things that ACK on 1 byte read)
* We only scan range [0x08 - 0x77], all other addresses are reserved anyway
*/
static void __devinit iic_scan_bus(struct ibm_iic_private* dev)
{
int found = 0;
char dummy;
struct i2c_msg msg = {
.buf = &dummy,
.len = sizeof(dummy),
.flags = I2C_M_RD
};
printk(KERN_INFO "ibm-iic%d: scanning bus...\n" KERN_INFO, dev->idx);
for (msg.addr = 8; msg.addr < 0x78; ++msg.addr)
if (iic_xfer(&dev->adap, &msg, 1) == 1){
++found;
printk(" 0x%02x", msg.addr);
}
 
printk("%sibm-iic%d: %d device(s) detected\n",
found ? "\n" KERN_INFO : "", dev->idx, found);
}
 
/*
* 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;
int ret;
bd_t* bd = (bd_t*)&__res;
if (!(dev = kmalloc(sizeof(*dev), GFP_KERNEL))){
printk(KERN_CRIT "ibm-iic: failed to allocate device data\n");
return -ENOMEM;
}
 
memset(dev, 0, sizeof(*dev));
dev->idx = ocp->num;
ocp_set_drvdata(ocp, dev);
if (!(dev->vaddr = ioremap(ocp->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->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 */
BUG_ON(dev->idx >= sizeof(bd->bi_iic_fast) / sizeof(bd->bi_iic_fast[0]));
dev->fast_mode = iic_force_fast ? 1 : bd->bi_iic_fast[dev->idx];
/* clckdiv is the same for *all* IIC interfaces,
* but I'd rather make a copy than introduce another global. --ebs
*/
dev->clckdiv = iic_clckdiv(bd->bi_opb_busfreq);
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->dev.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)");
 
/* Scan bus if requested by user */
if (iic_scan)
iic_scan_bus(dev);
 
return 0;
 
fail:
if (dev->irq >= 0){
iic_interrupt_mode(dev, 0);
free_irq(dev->irq, dev);
}
 
iounmap((void*)dev->vaddr);
fail2:
ocp_set_drvdata(ocp, 0);
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((void*)dev->vaddr);
kfree(dev);
}
}
 
static struct ocp_device_id ibm_iic_ids[] __devinitdata =
{
{ .vendor = OCP_VENDOR_IBM, .device = OCP_FUNC_IIC },
{ .vendor = OCP_VENDOR_INVALID }
};
 
MODULE_DEVICE_TABLE(ocp, ibm_iic_ids);
 
static struct ocp_driver ibm_iic_driver =
{
.name = "ocp_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_module_init(&ibm_iic_driver);
}
 
static void __exit iic_exit(void)
{
ocp_unregister_driver(&ibm_iic_driver);
}
 
module_init(iic_init);
module_exit(iic_exit);
/shark/trunk/drivers/i2c/busses/i2c-piix4.c
0,0 → 1,492
/*
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
SMSC Victory66
 
Note: we assume there can only be one device, with one SMBus interface.
*/
 
/* #define DEBUG 1 */
 
#include <linux/module.h>
#include <linux/config.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 <linux/apm_bios.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)
 
/* 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_PARM(force, "i");
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_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!");
 
static int piix4_transaction(void);
 
 
static unsigned short piix4_smba = 0;
static struct i2c_adapter piix4_adapter;
 
/*
* Get DMI information.
*/
static int ibm_dmi_probe(void)
{
#ifdef CONFIG_X86
extern int is_unsafe_smbus;
return is_unsafe_smbus;
#else
return 0;
#endif
}
 
static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id)
{
int error_return = 0;
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));
 
if(ibm_dmi_probe()) {
dev_err(&PIIX4_dev->dev, "IBM Laptop detected; this module "
"may corrupt your serial eeprom! Refusing to load "
"module!\n");
error_return = -EPERM;
goto END;
}
 
/* 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, 8, "piix4-smbus")) {
dev_err(&PIIX4_dev->dev, "SMB region 0x%x already in use!\n",
piix4_smba);
error_return = -ENODEV;
goto END;
}
 
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) {
dev_info(&PIIX4_dev->dev, "Worked around buggy BIOS (I2C)\n");
temp = temp & 0xfd;
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp);
}
/* 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");
error_return = -ENODEV;
goto END;
}
}
 
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);
 
END:
return error_return;
}
 
/* 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 {
i2c_delay(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_ADAP_CLASS_SMBUS,
.algo = &smbus_algorithm,
.name = "unset",
};
 
static struct pci_device_id piix4_ids[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82371AB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 3
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_OSB4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82443MX_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 3,
},
{
.vendor = PCI_VENDOR_ID_EFAR,
.device = PCI_DEVICE_ID_EFAR_SLC90E66_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{ 0, }
};
 
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);
 
retval = i2c_add_adapter(&piix4_adapter);
 
return retval;
}
 
static void __devexit piix4_remove(struct pci_dev *dev)
{
i2c_del_adapter(&piix4_adapter);
}
 
 
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_module_init(&piix4_driver);
}
 
 
static void __exit i2c_piix4_exit(void)
{
pci_unregister_driver(&piix4_driver);
release_region(piix4_smba, 8);
}
 
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);
/shark/trunk/drivers/i2c/busses/i2c-nforce2.c
0,0 → 1,404
/*
SMBus driver for nVidia nForce2 MCP
 
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
 
This driver supports the 2 SMBuses that are included in the MCP2 of the
nForce2 chipset.
*/
 
/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
 
#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");
 
 
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064
#endif
 
 
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_ADAP_CLASS_SMBUS,
.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) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ/100);
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_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0 }
};
 
 
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_module_init(&nforce2_driver);
}
 
static void __exit nforce2_exit(void)
{
pci_unregister_driver(&nforce2_driver);
}
 
module_init(nforce2_init);
module_exit(nforce2_exit);
 
/shark/trunk/drivers/i2c/busses/i2c-viapro.c
0,0 → 1,472
/*
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ö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
Note: we assume there can only be one device, with one SMBus interface.
*/
 
#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/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
 
#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_PARM(force, "i");
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 int force_addr;
MODULE_PARM(force_addr, "i");
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)) != 0x00) {
dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). "
"Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
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 {
i2c_delay(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 (inb_p(SMBHSTSTS) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
 
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
dev_dbg(&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 > 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 = 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);
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_ADAP_CLASS_SMBUS,
.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);
return i2c_add_adapter(&vt596_adapter);
 
release_region:
release_region(vt596_smba, 8);
return error;
}
 
static void __devexit vt596_remove(struct pci_dev *pdev)
{
i2c_del_adapter(&vt596_adapter);
release_region(vt596_smba, 8);
}
 
static struct pci_device_id vt596_ids[] = {
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C596_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C596B_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C686_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8233_0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8233A,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8235,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8231_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{ 0, }
};
 
static struct pci_driver vt596_driver = {
.name = "vt596 smbus",
.id_table = vt596_ids,
.probe = vt596_probe,
.remove = __devexit_p(vt596_remove),
};
 
static int __init i2c_vt596_init(void)
{
return pci_module_init(&vt596_driver);
}
 
 
static void __exit i2c_vt596_exit(void)
{
pci_unregister_driver(&vt596_driver);
}
 
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);
/shark/trunk/drivers/i2c/busses/i2c-ibm_iic.h
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 *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_ */
/shark/trunk/drivers/i2c/busses/i2c-sis96x.c
0,0 → 1,366
/*
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.
*/
 
/* #define DEBUG */
 
#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/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"
 
/* SiS96x SMBus PCI device ID */
#define PCI_DEVICE_ID_SI_SMBUS 0x16
 
/* 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 {
i2c_delay(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_ADAP_CLASS_SMBUS,
.algo = &smbus_algorithm,
.name = "unset",
};
 
static struct pci_device_id sis96x_ids[] = {
 
{
.vendor = PCI_VENDOR_ID_SI,
.device = PCI_DEVICE_ID_SI_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
 
{ 0, }
};
 
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_module_init(&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);
 
/shark/trunk/drivers/i2c/busses/i2c-rpx.c
0,0 → 1,103
/*
* 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/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/stddef.h>
#include <linux/parport.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 = m8xx_cpm_dpalloc(sizeof(cbd_t) * 4);
 
/* 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("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);
/shark/trunk/drivers/i2c/busses/i2c-frodo.c
0,0 → 1,85
 
/*
* 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/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);
 
/shark/trunk/drivers/i2c/busses/i2c-isa.c
0,0 → 1,71
/*
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/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_ADAP_CLASS_SMBUS,
.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);
/shark/trunk/drivers/i2c/busses/i2c-via.c
0,0 → 1,183
/*
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ö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.
*/
 
#define DEBUG
 
#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,
.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, }
};
 
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_module_init(&vt586b_driver);
}
 
static void __exit i2c_vt586b_exit(void)
{
pci_unregister_driver(&vt586b_driver);
}
 
 
MODULE_AUTHOR("Kyö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);
/shark/trunk/drivers/i2c/busses/i2c-i810.c
0,0 → 1,257
/*
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/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 *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, },
};
 
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_module_init(&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);
/shark/trunk/drivers/i2c/i2c-core.c
0,0 → 1,1308
/* i2c-core.c - a device driver for the iic-bus interface */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 1995-99 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östi Mälkki <kmalkki@cc.hut.fi>.
All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> */
 
/* $Id: i2c-core.c,v 1.1 2004-01-28 15:12:01 giacomo Exp $ */
 
/* #define DEBUG 1 */ /* needed to pick up the dev_dbg() calls */
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
 
 
#define DEB(x) if (i2c_debug>=1) x;
#define DEB2(x) if (i2c_debug>=2) x;
 
static LIST_HEAD(adapters);
static LIST_HEAD(drivers);
static DECLARE_MUTEX(core_lists);
 
/**** debug level */
static int i2c_debug;
 
int i2c_device_probe(struct device *dev)
{
return -ENODEV;
}
 
int i2c_device_remove(struct device *dev)
{
return 0;
}
 
static void i2c_adapter_dev_release(struct device *dev)
{
struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
complete(&adap->dev_released);
}
 
static struct device_driver i2c_adapter_driver = {
.name = "i2c_adapter",
.bus = &i2c_bus_type,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
};
 
static void i2c_adapter_class_dev_release(struct class_device *dev)
{
struct i2c_adapter *adap = class_dev_to_i2c_adapter(dev);
complete(&adap->class_dev_released);
}
 
static struct class i2c_adapter_class = {
.name = "i2c-adapter",
.release = &i2c_adapter_class_dev_release,
};
 
static ssize_t show_adapter_name(struct device *dev, char *buf)
{
struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
return sprintf(buf, "%s\n", adap->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
 
 
static void i2c_client_release(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
complete(&client->released);
}
 
static ssize_t show_client_name(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%s\n", client->name);
}
 
/*
* We can't use the DEVICE_ATTR() macro here as we want the same filename for a
* different type of a device. So beware if the DEVICE_ATTR() macro ever
* changes, this definition will also have to change.
*/
static struct device_attribute dev_attr_client_name = {
.attr = {.name = "name", .mode = S_IRUGO, .owner = THIS_MODULE },
.show = &show_client_name,
};
 
 
/* ---------------------------------------------------
* registering functions
* ---------------------------------------------------
*/
 
/* -----
* i2c_add_adapter is called from within the algorithm layer,
* when a new hw adapter registers. A new device is register to be
* available for clients.
*/
int i2c_add_adapter(struct i2c_adapter *adap)
{
static int nr = 0;
struct list_head *item;
struct i2c_driver *driver;
 
down(&core_lists);
 
adap->nr = nr++;
init_MUTEX(&adap->bus_lock);
init_MUTEX(&adap->clist_lock);
list_add_tail(&adap->list,&adapters);
INIT_LIST_HEAD(&adap->clients);
 
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the legacy bus.
*/
if (adap->dev.parent == NULL)
adap->dev.parent = &legacy_bus;
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
device_register(&adap->dev);
device_create_file(&adap->dev, &dev_attr_name);
 
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
class_device_register(&adap->class_dev);
 
/* inform drivers of new adapters */
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->flags & I2C_DF_NOTIFY)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
up(&core_lists);
 
DEB(dev_dbg(&adap->dev, "registered as adapter #%d\n", adap->nr));
return 0;
}
 
 
int i2c_del_adapter(struct i2c_adapter *adap)
{
struct list_head *item, *_n;
struct i2c_driver *driver;
struct i2c_client *client;
int res = 0;
 
down(&core_lists);
 
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->detach_adapter)
if ((res = driver->detach_adapter(adap))) {
dev_warn(&adap->dev, "can't detach adapter"
"while detaching driver %s: driver not "
"detached!", driver->name);
goto out_unlock;
}
}
 
/* detach any active clients. This must be done first, because
* it can fail; in which case we give upp. */
list_for_each_safe(item, _n, &adap->clients) {
client = list_entry(item, struct i2c_client, list);
 
/* detaching devices is unconditional of the set notify
* flag, as _all_ clients that reside on the adapter
* must be deleted, as this would cause invalid states.
*/
if ((res=client->driver->detach_client(client))) {
dev_err(&adap->dev, "adapter not "
"unregistered, because client at "
"address %02x can't be detached. ",
client->addr);
goto out_unlock;
}
}
 
/* clean up the sysfs representation */
init_completion(&adap->dev_released);
init_completion(&adap->class_dev_released);
class_device_unregister(&adap->class_dev);
device_remove_file(&adap->dev, &dev_attr_name);
device_unregister(&adap->dev);
list_del(&adap->list);
 
/* wait for sysfs to drop all references */
wait_for_completion(&adap->dev_released);
wait_for_completion(&adap->class_dev_released);
 
DEB(dev_dbg(&adap->dev, "adapter unregistered\n"));
 
out_unlock:
up(&core_lists);
return res;
}
 
 
/* -----
* What follows is the "upwards" interface: commands for talking to clients,
* which implement the functions to access the physical information of the
* chips.
*/
 
int i2c_add_driver(struct i2c_driver *driver)
{
struct list_head *item;
struct i2c_adapter *adapter;
int res = 0;
 
down(&core_lists);
 
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.name = driver->name;
driver->driver.bus = &i2c_bus_type;
driver->driver.probe = i2c_device_probe;
driver->driver.remove = i2c_device_remove;
 
res = driver_register(&driver->driver);
if (res)
goto out_unlock;
list_add_tail(&driver->list,&drivers);
DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name));
 
/* now look for instances of driver on our adapters */
if (driver->flags & I2C_DF_NOTIFY) {
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
driver->attach_adapter(adapter);
}
}
 
out_unlock:
up(&core_lists);
return res;
}
 
int i2c_del_driver(struct i2c_driver *driver)
{
struct list_head *item1, *item2, *_n;
struct i2c_client *client;
struct i2c_adapter *adap;
int res = 0;
 
down(&core_lists);
 
/* Have a look at each adapter, if clients of this driver are still
* attached. If so, detach them to be able to kill the driver
* afterwards.
*/
DEB2(printk(KERN_DEBUG "i2c-core.o: unregister_driver - looking for clients.\n"));
/* removing clients does not depend on the notify flag, else
* invalid operation might (will!) result, when using stale client
* pointers.
*/
list_for_each(item1,&adapters) {
adap = list_entry(item1, struct i2c_adapter, list);
DEB2(dev_dbg(&adap->dev, "examining adapter\n"));
if (driver->detach_adapter) {
if ((res = driver->detach_adapter(adap))) {
dev_warn(&adap->dev, "while unregistering "
"dummy driver %s, adapter could "
"not be detached properly; driver "
"not unloaded!",driver->name);
goto out_unlock;
}
} else {
list_for_each_safe(item2, _n, &adap->clients) {
client = list_entry(item2, struct i2c_client, list);
if (client->driver != driver)
continue;
DEB2(printk(KERN_DEBUG "i2c-core.o: "
"detaching client %s:\n",
client->name));
if ((res = driver->detach_client(client))) {
dev_err(&adap->dev, "while "
"unregistering driver "
"`%s', the client at "
"address %02x of "
"adapter could not "
"be detached; driver "
"not unloaded!",
driver->name,
client->addr);
goto out_unlock;
}
}
}
}
 
driver_unregister(&driver->driver);
list_del(&driver->list);
DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name));
 
out_unlock:
up(&core_lists);
return 0;
}
 
static int __i2c_check_addr(struct i2c_adapter *adapter, unsigned int addr)
{
struct list_head *item;
struct i2c_client *client;
 
list_for_each(item,&adapter->clients) {
client = list_entry(item, struct i2c_client, list);
if (client->addr == addr)
return -EBUSY;
}
return 0;
}
 
int i2c_check_addr(struct i2c_adapter *adapter, int addr)
{
int rval;
 
down(&adapter->clist_lock);
rval = __i2c_check_addr(adapter, addr);
up(&adapter->clist_lock);
 
return rval;
}
 
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
 
down(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
up(&adapter->clist_lock);
return -EBUSY;
}
list_add_tail(&client->list,&adapter->clients);
up(&adapter->clist_lock);
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_warn(&adapter->dev, "warning: client_register "
"seems to have failed for client %02x\n",
client->addr);
}
}
 
DEB(dev_dbg(&adapter->dev, "client [%s] registered to adapter\n",
client->dev.name));
 
if (client->flags & I2C_CLIENT_ALLOW_USE)
client->usage_count = 0;
 
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
printk("registering %s\n", client->dev.bus_id);
device_register(&client->dev);
device_create_file(&client->dev, &dev_attr_client_name);
return 0;
}
 
 
int i2c_detach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
if ((client->flags & I2C_CLIENT_ALLOW_USE) && (client->usage_count > 0))
return -EBUSY;
 
if (adapter->client_unregister) {
res = adapter->client_unregister(client);
if (res) {
printk(KERN_ERR
"i2c-core.o: client_unregister [%s] failed, "
"client not detached", client->name);
goto out;
}
}
 
down(&adapter->clist_lock);
list_del(&client->list);
init_completion(&client->released);
device_remove_file(&client->dev, &dev_attr_client_name);
device_unregister(&client->dev);
up(&adapter->clist_lock);
wait_for_completion(&client->released);
 
out:
return res;
}
 
static int i2c_inc_use_client(struct i2c_client *client)
{
 
if (!try_module_get(client->driver->owner))
return -ENODEV;
if (!try_module_get(client->adapter->owner)) {
module_put(client->driver->owner);
return -ENODEV;
}
 
return 0;
}
 
static void i2c_dec_use_client(struct i2c_client *client)
{
module_put(client->driver->owner);
module_put(client->adapter->owner);
}
 
int i2c_use_client(struct i2c_client *client)
{
if (!i2c_inc_use_client(client))
return -ENODEV;
 
if (client->flags & I2C_CLIENT_ALLOW_USE) {
if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE)
client->usage_count++;
else if (client->usage_count > 0)
goto busy;
else
client->usage_count++;
}
 
return 0;
busy:
i2c_dec_use_client(client);
return -EBUSY;
}
 
int i2c_release_client(struct i2c_client *client)
{
if(client->flags & I2C_CLIENT_ALLOW_USE) {
if(client->usage_count>0)
client->usage_count--;
else
{
printk(KERN_WARNING " i2c-core.o: dec_use_client used one too many times\n");
return -EPERM;
}
}
i2c_dec_use_client(client);
return 0;
}
 
void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg)
{
struct list_head *item;
struct i2c_client *client;
 
down(&adap->clist_lock);
list_for_each(item,&adap->clients) {
client = list_entry(item, struct i2c_client, list);
if (!try_module_get(client->driver->owner))
continue;
if (NULL != client->driver->command) {
up(&adap->clist_lock);
client->driver->command(client,cmd,arg);
down(&adap->clist_lock);
}
module_put(client->driver->owner);
}
up(&adap->clist_lock);
}
 
 
/* match always succeeds, as we want the probe() to tell if we really accept this match */
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
 
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
};
 
static int __init i2c_init(void)
{
int retval;
 
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
retval = driver_register(&i2c_adapter_driver);
if (retval)
return retval;
return class_register(&i2c_adapter_class);
}
 
static void __exit i2c_exit(void)
{
class_unregister(&i2c_adapter_class);
driver_unregister(&i2c_adapter_driver);
bus_unregister(&i2c_bus_type);
}
 
subsys_initcall(i2c_init);
module_exit(i2c_exit);
 
/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
 
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num)
{
int ret;
 
if (adap->algo->master_xfer) {
DEB2(dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num));
 
down(&adap->bus_lock);
ret = adap->algo->master_xfer(adap,msgs,num);
up(&adap->bus_lock);
 
return ret;
} else {
DEB2(dev_dbg(&adap->dev, "I2C level transfers not supported\n"));
return -ENOSYS;
}
}
 
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
 
if (client->adapter->algo->master_xfer) {
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
DEB2(dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n",
count));
down(&adap->bus_lock);
ret = adap->algo->master_xfer(adap,&msg,1);
up(&adap->bus_lock);
 
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 )? count : ret;
} else {
dev_err(&client->adapter->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
 
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
int ret;
if (client->adapter->algo->master_xfer) {
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
 
DEB2(dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n",
count));
down(&adap->bus_lock);
ret = adap->algo->master_xfer(adap,&msg,1);
up(&adap->bus_lock);
DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
ret, count, client->addr));
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 )? count : ret;
} else {
dev_err(&client->adapter->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
 
 
int i2c_control(struct i2c_client *client,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct i2c_adapter *adap = client->adapter;
 
DEB2(printk(KERN_DEBUG "i2c-core.o: i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg));
switch ( cmd ) {
case I2C_RETRIES:
adap->retries = arg;
break;
case I2C_TIMEOUT:
adap->timeout = arg;
break;
default:
if (adap->algo->algo_control!=NULL)
ret = adap->algo->algo_control(adap,cmd,arg);
}
return ret;
}
 
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
* ----------------------------------------------------
*/
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int addr,i,found,err;
int adap_id = i2c_adapter_id(adapter);
 
/* Forget it if we can't probe using SMBUS_QUICK */
if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK))
return -1;
 
for (addr = 0x00; addr <= 0x7f; addr++) {
 
/* Skip if already in use */
if (i2c_check_addr(adapter,addr))
continue;
 
/* If it is in one of the force entries, we don't do any detection
at all */
found = 0;
 
for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) {
if (((adap_id == address_data->force[i]) ||
(address_data->force[i] == ANY_I2C_BUS)) &&
(addr == address_data->force[i+1])) {
DEB2(printk(KERN_DEBUG "i2c-core.o: found force parameter for adapter %d, addr %04x\n",
adap_id,addr));
if ((err = found_proc(adapter,addr,0)))
return err;
found = 1;
}
}
if (found)
continue;
 
/* If this address is in one of the ignores, we can forget about
it right now */
for (i = 0;
!found && (address_data->ignore[i] != I2C_CLIENT_END);
i += 2) {
if (((adap_id == address_data->ignore[i]) ||
((address_data->ignore[i] == ANY_I2C_BUS))) &&
(addr == address_data->ignore[i+1])) {
DEB2(printk(KERN_DEBUG "i2c-core.o: found ignore parameter for adapter %d, "
"addr %04x\n", adap_id ,addr));
found = 1;
}
}
for (i = 0;
!found && (address_data->ignore_range[i] != I2C_CLIENT_END);
i += 3) {
if (((adap_id == address_data->ignore_range[i]) ||
((address_data->ignore_range[i]==ANY_I2C_BUS))) &&
(addr >= address_data->ignore_range[i+1]) &&
(addr <= address_data->ignore_range[i+2])) {
DEB2(printk(KERN_DEBUG "i2c-core.o: found ignore_range parameter for adapter %d, "
"addr %04x\n", adap_id,addr));
found = 1;
}
}
if (found)
continue;
 
/* Now, we will do a detection, but only if it is in the normal or
probe entries */
for (i = 0;
!found && (address_data->normal_i2c[i] != I2C_CLIENT_END);
i += 1) {
if (addr == address_data->normal_i2c[i]) {
found = 1;
DEB2(printk(KERN_DEBUG "i2c-core.o: found normal i2c entry for adapter %d, "
"addr %02x", adap_id,addr));
}
}
 
for (i = 0;
!found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END);
i += 2) {
if ((addr >= address_data->normal_i2c_range[i]) &&
(addr <= address_data->normal_i2c_range[i+1])) {
found = 1;
DEB2(printk(KERN_DEBUG "i2c-core.o: found normal i2c_range entry for adapter %d, "
"addr %04x\n", adap_id,addr));
}
}
 
for (i = 0;
!found && (address_data->probe[i] != I2C_CLIENT_END);
i += 2) {
if (((adap_id == address_data->probe[i]) ||
((address_data->probe[i] == ANY_I2C_BUS))) &&
(addr == address_data->probe[i+1])) {
found = 1;
DEB2(printk(KERN_DEBUG "i2c-core.o: found probe parameter for adapter %d, "
"addr %04x\n", adap_id,addr));
}
}
for (i = 0;
!found && (address_data->probe_range[i] != I2C_CLIENT_END);
i += 3) {
if (((adap_id == address_data->probe_range[i]) ||
(address_data->probe_range[i] == ANY_I2C_BUS)) &&
(addr >= address_data->probe_range[i+1]) &&
(addr <= address_data->probe_range[i+2])) {
found = 1;
DEB2(printk(KERN_DEBUG "i2c-core.o: found probe_range parameter for adapter %d, "
"addr %04x\n", adap_id,addr));
}
}
if (!found)
continue;
 
/* OK, so we really should examine this address. First check
whether there is some client here at all! */
if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
if ((err = found_proc(adapter,addr,-1)))
return err;
}
return 0;
}
 
/*
* return id number for a specific adapter
*/
int i2c_adapter_id(struct i2c_adapter *adap)
{
return adap->nr;
}
 
struct i2c_adapter* i2c_get_adapter(int id)
{
struct list_head *item;
struct i2c_adapter *adapter;
down(&core_lists);
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
if (id == adapter->nr &&
try_module_get(adapter->owner)) {
up(&core_lists);
return adapter;
}
}
up(&core_lists);
return NULL;
}
 
void i2c_put_adapter(struct i2c_adapter *adap)
{
module_put(adap->owner);
}
 
/* The SMBus parts */
 
#define POLY (0x1070U << 3)
static u8
crc8(u16 data)
{
int i;
for(i = 0; i < 8; i++) {
if (data & 0x8000)
data = data ^ POLY;
data = data << 1;
}
return (u8)(data >> 8);
}
 
/* CRC over count bytes in the first array plus the bytes in the rest
array if it is non-null. rest[0] is the (length of rest) - 1
and is included. */
u8 i2c_smbus_partial_pec(u8 crc, int count, u8 *first, u8 *rest)
{
int i;
 
for(i = 0; i < count; i++)
crc = crc8((crc ^ first[i]) << 8);
if(rest != NULL)
for(i = 0; i <= rest[0]; i++)
crc = crc8((crc ^ rest[i]) << 8);
return crc;
}
 
u8 i2c_smbus_pec(int count, u8 *first, u8 *rest)
{
return i2c_smbus_partial_pec(0, count, first, rest);
}
 
/* Returns new "size" (transaction type)
Note that we convert byte to byte_data and byte_data to word_data
rather than invent new xxx_PEC transactions. */
int i2c_smbus_add_pec(u16 addr, u8 command, int size,
union i2c_smbus_data *data)
{
u8 buf[3];
 
buf[0] = addr << 1;
buf[1] = command;
switch(size) {
case I2C_SMBUS_BYTE:
data->byte = i2c_smbus_pec(2, buf, NULL);
size = I2C_SMBUS_BYTE_DATA;
break;
case I2C_SMBUS_BYTE_DATA:
buf[2] = data->byte;
data->word = buf[2] ||
(i2c_smbus_pec(3, buf, NULL) << 8);
size = I2C_SMBUS_WORD_DATA;
break;
case I2C_SMBUS_WORD_DATA:
/* unsupported */
break;
case I2C_SMBUS_BLOCK_DATA:
data->block[data->block[0] + 1] =
i2c_smbus_pec(2, buf, data->block);
size = I2C_SMBUS_BLOCK_DATA_PEC;
break;
}
return size;
}
 
int i2c_smbus_check_pec(u16 addr, u8 command, int size, u8 partial,
union i2c_smbus_data *data)
{
u8 buf[3], rpec, cpec;
 
buf[1] = command;
switch(size) {
case I2C_SMBUS_BYTE_DATA:
buf[0] = (addr << 1) | 1;
cpec = i2c_smbus_pec(2, buf, NULL);
rpec = data->byte;
break;
case I2C_SMBUS_WORD_DATA:
buf[0] = (addr << 1) | 1;
buf[2] = data->word & 0xff;
cpec = i2c_smbus_pec(3, buf, NULL);
rpec = data->word >> 8;
break;
case I2C_SMBUS_WORD_DATA_PEC:
/* unsupported */
cpec = rpec = 0;
break;
case I2C_SMBUS_PROC_CALL_PEC:
/* unsupported */
cpec = rpec = 0;
break;
case I2C_SMBUS_BLOCK_DATA_PEC:
buf[0] = (addr << 1);
buf[2] = (addr << 1) | 1;
cpec = i2c_smbus_pec(3, buf, data->block);
rpec = data->block[data->block[0] + 1];
break;
case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
buf[0] = (addr << 1) | 1;
rpec = i2c_smbus_partial_pec(partial, 1,
buf, data->block);
cpec = data->block[data->block[0] + 1];
break;
default:
cpec = rpec = 0;
break;
}
if(rpec != cpec) {
DEB(printk(KERN_DEBUG "i2c-core.o: Bad PEC 0x%02x vs. 0x%02x\n",
rpec, cpec));
return -1;
}
return 0;
}
 
s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
value,0,I2C_SMBUS_QUICK,NULL);
}
 
s32 i2c_smbus_read_byte(struct i2c_client *client)
{
union i2c_smbus_data data;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data))
return -1;
else
return 0x0FF & data.byte;
}
 
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
{
union i2c_smbus_data data; /* only for PEC */
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,&data);
}
 
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data))
return -1;
else
return 0x0FF & data.byte;
}
 
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)
{
union i2c_smbus_data data;
data.byte = value;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_BYTE_DATA,&data);
}
 
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data))
return -1;
else
return 0x0FFFF & data.word;
}
 
s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
{
union i2c_smbus_data data;
data.word = value;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_WORD_DATA,&data);
}
 
s32 i2c_smbus_process_call(struct i2c_client *client, u8 command, u16 value)
{
union i2c_smbus_data data;
data.word = value;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_PROC_CALL, &data))
return -1;
else
return 0x0FFFF & data.word;
}
 
/* Returns the number of read bytes */
s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values)
{
union i2c_smbus_data data;
int i;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,command,
I2C_SMBUS_BLOCK_DATA,&data))
return -1;
else {
for (i = 1; i <= data.block[0]; i++)
values[i-1] = data.block[i];
return data.block[0];
}
}
 
s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command, u8 length, u8 *values)
{
union i2c_smbus_data data;
int i;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_BLOCK_DATA,&data);
}
 
/* Returns the number of read bytes */
s32 i2c_smbus_block_process_call(struct i2c_client *client, u8 command, u8 length, u8 *values)
{
union i2c_smbus_data data;
int i;
if (length > I2C_SMBUS_BLOCK_MAX - 1)
return -1;
data.block[0] = length;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
if(i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_BLOCK_PROC_CALL, &data))
return -1;
for (i = 1; i <= data.block[0]; i++)
values[i-1] = data.block[i];
return data.block[0];
}
 
/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values)
{
union i2c_smbus_data data;
int i;
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_READ,command,
I2C_SMBUS_I2C_BLOCK_DATA,&data))
return -1;
else {
for (i = 1; i <= data.block[0]; i++)
values[i-1] = data.block[i];
return data.block[0];
}
}
 
s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command, u8 length, u8 *values)
{
union i2c_smbus_data data;
int i;
if (length > I2C_SMBUS_I2C_BLOCK_MAX)
length = I2C_SMBUS_I2C_BLOCK_MAX;
for (i = 1; i <= length; i++)
data.block[i] = values[i-1];
data.block[0] = length;
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
I2C_SMBUS_WRITE,command,
I2C_SMBUS_I2C_BLOCK_DATA,&data);
}
 
/* Simulate a SMBus command using the i2c protocol
No checking of parameters is done! */
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
/* So we need to generate a series of msgs. In the case of writing, we
need to use only one message; when reading, we need two. We initialize
most things with sane defaults, to keep the code below somewhat
simpler. */
unsigned char msgbuf0[34];
unsigned char msgbuf1[34];
int num = read_write == I2C_SMBUS_READ?2:1;
struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
{ addr, flags | I2C_M_RD, 0, msgbuf1 }
};
int i;
 
msgbuf0[0] = command;
switch(size) {
case I2C_SMBUS_QUICK:
msg[0].len = 0;
/* Special case: The read/write field is used as data */
msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
num = 1;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ) {
/* Special case: only a read! */
msg[0].flags = I2C_M_RD | flags;
num = 1;
}
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 1;
else {
msg[0].len = 2;
msgbuf0[1] = data->byte;
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ)
msg[1].len = 2;
else {
msg[0].len=3;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = (data->word >> 8) & 0xff;
}
break;
case I2C_SMBUS_PROC_CALL:
num = 2; /* Special case */
read_write = I2C_SMBUS_READ;
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
msgbuf0[2] = (data->word >> 8) & 0xff;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_DATA_PEC:
if (read_write == I2C_SMBUS_READ) {
printk(KERN_ERR "i2c-core.o: Block read not supported "
"under I2C emulation!\n");
return -1;
} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
printk(KERN_ERR "i2c-core.o: smbus_access called with "
"invalid block write size (%d)\n",
data->block[0]);
return -1;
}
if(size == I2C_SMBUS_BLOCK_DATA_PEC)
(msg[0].len)++;
for (i = 1; i <= msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
printk(KERN_ERR "i2c-core.o: Block process call not supported "
"under I2C emulation!\n");
return -1;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX;
} else {
msg[0].len = data->block[0] + 1;
if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) {
printk("i2c-core.o: i2c_smbus_xfer_emulated called with "
"invalid block write size (%d)\n",
data->block[0]);
return -1;
}
for (i = 1; i <= data->block[0]; i++)
msgbuf0[i] = data->block[i];
}
break;
default:
printk(KERN_ERR "i2c-core.o: smbus_access called with invalid size (%d)\n",
size);
return -1;
}
 
if (i2c_transfer(adapter, msg, num) < 0)
return -1;
 
if (read_write == I2C_SMBUS_READ)
switch(size) {
case I2C_SMBUS_BYTE:
data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
/* fixed at 32 for now */
data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
data->block[i+1] = msgbuf1[i];
break;
}
return 0;
}
 
 
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
s32 res;
int swpec = 0;
u8 partial = 0;
 
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if((flags & I2C_CLIENT_PEC) &&
!(i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HWPEC_CALC))) {
swpec = 1;
if(read_write == I2C_SMBUS_READ &&
size == I2C_SMBUS_BLOCK_DATA)
size = I2C_SMBUS_BLOCK_DATA_PEC;
else if(size == I2C_SMBUS_PROC_CALL)
size = I2C_SMBUS_PROC_CALL_PEC;
else if(size == I2C_SMBUS_BLOCK_PROC_CALL) {
i2c_smbus_add_pec(addr, command,
I2C_SMBUS_BLOCK_DATA, data);
partial = data->block[data->block[0] + 1];
size = I2C_SMBUS_BLOCK_PROC_CALL_PEC;
} else if(read_write == I2C_SMBUS_WRITE &&
size != I2C_SMBUS_QUICK &&
size != I2C_SMBUS_I2C_BLOCK_DATA)
size = i2c_smbus_add_pec(addr, command, size, data);
}
 
if (adapter->algo->smbus_xfer) {
down(&adapter->bus_lock);
res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
command,size,data);
up(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command,size,data);
 
if(res >= 0 && swpec &&
size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA &&
(read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC ||
size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) {
if(i2c_smbus_check_pec(addr, command, size, partial, data))
return -1;
}
return res;
}
 
 
/* You should always define `functionality'; the 'else' is just for
backward compatibility. */
u32 i2c_get_functionality (struct i2c_adapter *adap)
{
if (adap->algo->functionality)
return adap->algo->functionality(adap);
else
return 0xffffffff;
}
 
int i2c_check_functionality (struct i2c_adapter *adap, u32 func)
{
u32 adap_func = i2c_get_functionality (adap);
return (func & adap_func) == func;
}
 
EXPORT_SYMBOL(i2c_add_adapter);
EXPORT_SYMBOL(i2c_del_adapter);
EXPORT_SYMBOL(i2c_add_driver);
EXPORT_SYMBOL(i2c_del_driver);
EXPORT_SYMBOL(i2c_attach_client);
EXPORT_SYMBOL(i2c_detach_client);
EXPORT_SYMBOL(i2c_use_client);
EXPORT_SYMBOL(i2c_release_client);
EXPORT_SYMBOL(i2c_clients_command);
EXPORT_SYMBOL(i2c_check_addr);
 
EXPORT_SYMBOL(i2c_master_send);
EXPORT_SYMBOL(i2c_master_recv);
EXPORT_SYMBOL(i2c_control);
EXPORT_SYMBOL(i2c_transfer);
EXPORT_SYMBOL(i2c_adapter_id);
EXPORT_SYMBOL(i2c_get_adapter);
EXPORT_SYMBOL(i2c_put_adapter);
EXPORT_SYMBOL(i2c_probe);
 
EXPORT_SYMBOL(i2c_smbus_xfer);
EXPORT_SYMBOL(i2c_smbus_write_quick);
EXPORT_SYMBOL(i2c_smbus_read_byte);
EXPORT_SYMBOL(i2c_smbus_write_byte);
EXPORT_SYMBOL(i2c_smbus_read_byte_data);
EXPORT_SYMBOL(i2c_smbus_write_byte_data);
EXPORT_SYMBOL(i2c_smbus_read_word_data);
EXPORT_SYMBOL(i2c_smbus_write_word_data);
EXPORT_SYMBOL(i2c_smbus_process_call);
EXPORT_SYMBOL(i2c_smbus_read_block_data);
EXPORT_SYMBOL(i2c_smbus_write_block_data);
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data);
 
EXPORT_SYMBOL(i2c_get_functionality);
EXPORT_SYMBOL(i2c_check_functionality);
 
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus main module");
MODULE_LICENSE("GPL");
 
MODULE_PARM(i2c_debug, "i");
MODULE_PARM_DESC(i2c_debug,"debug level");
/shark/trunk/drivers/i2c/algos/i2c-algo-ite.h
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 */
/shark/trunk/drivers/i2c/algos/i2c-algo-pcf.c
0,0 → 1,479
/* ------------------------------------------------------------------------- */
/* 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ö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 */
 
/* #define DEBUG 1 */ /* to pick up dev_dbg calls */
 
#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"
 
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) if (i2c_debug>=1) x
#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=0;
 
/* --- 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 inline void pcf_sleep(unsigned long timeout)
{
schedule_timeout( timeout * HZ);
}
 
 
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);
}
#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;
for (i=0;i<retries;i++) {
i2c_outb(adap, addr);
i2c_start(adap);
status = get_pcf(adap, 1);
if (wait_for_pin(adap, &status) >= 0) {
if ((status & I2C_PCF_LRB) == 0) {
i2c_stop(adap);
break; /* success! */
}
}
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) {
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;
 
/* increment number of bytes to read by one -- read dummy byte */
for (i = 0; i <= count; i++) {
 
if (wait_for_pin(adap, &status)) {
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) {
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_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_PARM(i2c_debug,"i");
MODULE_PARM_DESC(i2c_debug,
"debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol");
/shark/trunk/drivers/i2c/algos/i2c-algo-pcf.h
0,0 → 1,78
/* -------------------------------------------------------------------- */
/* 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> */
 
/* $Id: i2c-algo-pcf.h,v 1.1 2004-01-28 15:12:03 giacomo Exp $ */
 
#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 */
/shark/trunk/drivers/i2c/algos/i2c-algo-bit.c
0,0 → 1,567
/* ------------------------------------------------------------------------- */
/* 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 Kyösti Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
 
/* $Id: i2c-algo-bit.c,v 1.1 2004-01-28 15:12:02 giacomo Exp $ */
 
/* #define DEBUG 1 */
 
#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 )
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;
sda=getsda(adap);
if (adap->getscl==NULL) {
printk(KERN_WARNING "i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n");
return 0;
}
scl=getscl(adap);
printk(KERN_INFO "i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n",
name,getscl(adap),getsda(adap));
if (!scl || !sda ) {
printk(KERN_INFO " i2c-algo-bit.o: %s seems to be busy.\n",name);
goto bailout;
}
sdalo(adap);
printk(KERN_DEBUG "i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 != getsda(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck high!\n",name);
sdahi(adap);
goto bailout;
}
if ( 0 == getscl(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n",
name);
goto bailout;
}
sdahi(adap);
printk(KERN_DEBUG "i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 == getsda(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck low!\n",name);
sdahi(adap);
goto bailout;
}
if ( 0 == getscl(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",
name);
goto bailout;
}
scllo(adap);
printk(KERN_DEBUG "i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 != getscl(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck high!\n",name);
sclhi(adap);
goto bailout;
}
if ( 0 == getsda(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n",
name);
goto bailout;
}
sclhi(adap);
printk(KERN_DEBUG "i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap),
getsda(adap));
if ( 0 == getscl(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck low!\n",name);
sclhi(adap);
goto bailout;
}
if ( 0 == getsda(adap) ) {
printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n",
name);
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;
}
 
if ( count > 1 ) { /* 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);
temp++;
count--;
}
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_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_PARM(bit_test, "i");
MODULE_PARM(i2c_debug,"i");
 
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");
/shark/trunk/drivers/i2c/algos/i2c-algo-ite.c
0,0 → 1,865
/*
-------------------------------------------------------------------------
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ö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
 
/* ----- global defines ----------------------------------------------- */
#define DEB(x) if (i2c_debug>=1) x
#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
 
/* debugging - slow down transfer to have a look at the data .. */
/* I use this with two leds&resistors, each one connected to sda,scl */
/* respectively. This makes sure that the algorithm works. Some chips */
/* might not like this, as they have an internal timeout of some mils */
/*
#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\
cond_resched();
*/
 
 
/* ----- global variables --------------------------------------------- */
 
#ifdef SLO_IO
int jif;
#endif
 
/* module parameters:
*/
static int i2c_debug=1;
static int iic_test=0; /* see if the line-setting functions work */
static int iic_scan=0; /* have a look at what's hanging 'round */
 
/* --- 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);
}
 
/*
* Puts this process to sleep for a period equal to timeout
*/
static inline void iic_sleep(unsigned long timeout)
{
schedule_timeout( timeout * HZ);
}
 
/* 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 = {
"ITE IIC algorithm",
I2C_ALGO_IIC,
iic_xfer, /* master_xfer */
NULL, /* smbus_xfer */
NULL, /* slave_xmit */
NULL, /* slave_recv */
algo_control, /* ioctl */
iic_func, /* functionality */
};
 
 
/*
* registering functions to load algorithms at runtime
*/
int i2c_iic_add_bus(struct i2c_adapter *adap)
{
int i;
short status;
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);
 
/* scan bus */
/* By default scanning the bus is turned off. */
if (iic_scan) {
printk(KERN_INFO " i2c-algo-ite: scanning bus %s.\n",
adap->name);
for (i = 0x00; i < 0xff; i+=2) {
iic_outw(iic_adap, ITE_I2CSAR, i);
iic_start(iic_adap);
if ( (wait_for_pin(iic_adap, &status) == 0) &&
((status & ITE_I2CHSR_DNE) == 0) ) {
printk(KERN_INFO "\n(%02x)\n",i>>1);
} else {
printk(KERN_INFO ".");
iic_reset(iic_adap);
}
udelay(iic_adap->udelay);
}
}
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_PARM(iic_test, "i");
MODULE_PARM(iic_scan, "i");
MODULE_PARM(i2c_debug,"i");
 
MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available");
MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus");
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);
/shark/trunk/drivers/i2c/i2c-sensor.c
0,0 → 1,168
/*
i2c-sensor.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl> 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.
*/
 
/* #define DEBUG 1 */
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/uaccess.h>
 
 
/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */
int i2c_detect(struct i2c_adapter *adapter,
struct i2c_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int addr, i, found, j, err;
struct i2c_force_data *this_force;
int is_isa = i2c_is_isa_adapter(adapter);
int adapter_id =
is_isa ? ANY_I2C_ISA_BUS : i2c_adapter_id(adapter);
 
/* Forget it if we can't probe using SMBUS_QUICK */
if ((!is_isa) &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK))
return -1;
 
for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
if (!is_isa && i2c_check_addr(adapter, addr))
continue;
 
/* If it is in one of the force entries, we don't do any
detection at all */
found = 0;
for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) {
for (j = 0; !found && (this_force->force[j] != I2C_CLIENT_END); j += 2) {
if ( ((adapter_id == this_force->force[j]) ||
((this_force->force[j] == ANY_I2C_BUS) && !is_isa)) &&
(addr == this_force->force[j + 1]) ) {
dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr);
if ((err = found_proc(adapter, addr, this_force->kind)))
return err;
found = 1;
}
}
}
if (found)
continue;
 
/* If this address is in one of the ignores, we can forget about it
right now */
for (i = 0; !found && (address_data->ignore[i] != I2C_CLIENT_END); i += 2) {
if ( ((adapter_id == address_data->ignore[i]) ||
((address_data->ignore[i] == ANY_I2C_BUS) &&
!is_isa)) &&
(addr == address_data->ignore[i + 1])) {
dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
for (i = 0; !found && (address_data->ignore_range[i] != I2C_CLIENT_END); i += 3) {
if ( ((adapter_id == address_data->ignore_range[i]) ||
((address_data-> ignore_range[i] == ANY_I2C_BUS) &
!is_isa)) &&
(addr >= address_data->ignore_range[i + 1]) &&
(addr <= address_data->ignore_range[i + 2])) {
dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
if (found)
continue;
 
/* Now, we will do a detection, but only if it is in the normal or
probe entries */
if (is_isa) {
for (i = 0; !found && (address_data->normal_isa[i] != I2C_CLIENT_ISA_END); i += 1) {
if (addr == address_data->normal_isa[i]) {
dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
for (i = 0; !found && (address_data->normal_isa_range[i] != I2C_CLIENT_ISA_END); i += 3) {
if ((addr >= address_data->normal_isa_range[i]) &&
(addr <= address_data->normal_isa_range[i + 1]) &&
((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) {
dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr);
found = 1;
}
}
} else {
for (i = 0; !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); i += 1) {
if (addr == address_data->normal_i2c[i]) {
found = 1;
dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr);
}
}
for (i = 0; !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); i += 2) {
if ((addr >= address_data->normal_i2c_range[i]) &&
(addr <= address_data->normal_i2c_range[i + 1])) {
dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
}
 
for (i = 0;
!found && (address_data->probe[i] != I2C_CLIENT_END);
i += 2) {
if (((adapter_id == address_data->probe[i]) ||
((address_data->
probe[i] == ANY_I2C_BUS) && !is_isa))
&& (addr == address_data->probe[i + 1])) {
dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr);
found = 1;
}
}
for (i = 0; !found && (address_data->probe_range[i] != I2C_CLIENT_END); i += 3) {
if ( ((adapter_id == address_data->probe_range[i]) ||
((address_data->probe_range[i] == ANY_I2C_BUS) && !is_isa)) &&
(addr >= address_data->probe_range[i + 1]) &&
(addr <= address_data->probe_range[i + 2])) {
found = 1;
dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
}
}
if (!found)
continue;
 
/* OK, so we really should examine this address. First check
whether there is some client here at all! */
if (is_isa ||
(i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0))
if ((err = found_proc(adapter, addr, -1)))
return err;
}
return 0;
}
 
EXPORT_SYMBOL(i2c_detect);
 
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("i2c-sensor driver");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/i2c/chips/via686a.c
0,0 → 1,984
/*
via686a.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Kyö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.
See doc/chips/via686a for details.
Warning - only supports a single device.
*/
 
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.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 int force_addr = 0;
MODULE_PARM(force_addr, "i");
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 short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { 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. Rounding and 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>)
These get us close, but they don't completely agree with what my BIOS
says- they are all a bit low. But, it all we have to go on... */
static inline u8 IN_TO_REG(long val, int inNum)
{
/* to avoid floating point, we multiply everything by 100.
val is guaranteed to be positive, so we can achieve the effect of
rounding by (...*10+5)/10. Note that the *10 is hidden in the
/250 (which should really be /2500).
At the end, we need to /100 because we *100 everything and we need
to /10 because of the rounding thing, so we /1000. */
if (inNum <= 1)
return (u8)
SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000,
0, 255);
else if (inNum == 2)
return (u8)
SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000,
0, 255);
else if (inNum == 3)
return (u8)
SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000,
0, 255);
else
return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5)
/ 1000, 0, 255);
}
 
static inline long IN_FROM_REG(u8 val, int inNum)
{
/* to avoid floating point, we multiply everything by 100.
val is guaranteed to be positive, so we can achieve the effect of
rounding by adding 0.5. Or, to avoid fp math, we do (...*10+5)/10.
We need to scale with *100 anyway, so no need to /100 at the end. */
if (inNum <= 1)
return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10);
else if (inNum == 2)
return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10);
else if (inNum == 3)
return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10);
else
return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10);
}
 
/********* 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. Just check the limits and go.
The +5 effectively rounds off properly and the +50 is because
the temps start at -50 */
static inline u8 TEMP_TO_REG(long val)
{
return (u8)
SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 :
((val + 5) / 10 + 50))], 0, 255);
}
 
/* for 8-bit temperature hyst and over registers
The temp values are already *10, so we don't need to do that.
But we _will_ round these off to the nearest degree with (...*10+5)/10 */
#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10)
 
/* for 10-bit temperature readings
You might _think_ this is too long to inline, but's it's really only
called once... */
static inline long TEMP_FROM_REG10(u16 val)
{
/* the temp values are already *10, so we don't need to do that. */
long temp;
u16 eightBits = val >> 2;
u16 twoBits = val & 3;
 
/* handle the extremes first (they won't interpolate well! ;-) */
if (val == 0)
return (long) tempLUT[0];
if (val == 1023)
return (long) tempLUT[255];
 
if (twoBits == 0)
return (long) tempLUT[eightBits];
else {
/* do some interpolation by multipying the lower and upper
bounds by 25, 50 or 75, then /100. */
temp = ((25 * (4 - twoBits)) * tempLUT[eightBits]
+ (25 * twoBits) * tempLUT[eightBits + 1]);
/* increase the magnitude by 50 to achieve rounding. */
if (temp > 0)
temp += 50;
else
temp -= 50;
return (temp / 100);
}
}
 
#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)
 
/* Initial limits */
#define VIA686A_INIT_IN_0 200
#define VIA686A_INIT_IN_1 250
#define VIA686A_INIT_IN_2 330
#define VIA686A_INIT_IN_3 500
#define VIA686A_INIT_IN_4 1200
 
#define VIA686A_INIT_IN_PERCENTAGE 10
 
#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \
* VIA686A_INIT_IN_PERCENTAGE / 100)
 
#define VIA686A_INIT_FAN_MIN 3000
 
#define VIA686A_INIT_TEMP_OVER 600
#define VIA686A_INIT_TEMP_HYST 500
 
/* For the VIA686A, we need to keep some data in memory. That
data is pointed to by via686a_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new via686a client is
allocated. */
struct via686a_data {
int sysctl_id;
 
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 void via686a_update_client(struct i2c_client *client);
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 i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr)*10 );
}
 
static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr)*10 );
}
 
static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr)*10 );
}
 
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)/10;
data->in_min[nr] = IN_TO_REG(val,nr);
via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
data->in_min[nr]);
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)/10;
data->in_max[nr] = IN_TO_REG(val,nr);
via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
data->in_max[nr]);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, 0x##offset); \
} \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, 0x##offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, 0x##offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, 0x##offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in##offset, NULL) \
static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min) \
static DEVICE_ATTR(in_max##offset, 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 i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])*100 );
}
/* more like overshoot temperature */
static ssize_t show_temp_max(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])*100);
}
/* more like hysteresis temperature */
static ssize_t show_temp_min(struct device *dev, char *buf, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])*100);
}
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 via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10)/100;
data->temp_over[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_OVER(nr), data->temp_over[nr]);
return count;
}
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 via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10)/100;
data->temp_hyst[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_HYST(nr), data->temp_hyst[nr]);
return count;
}
#define show_temp_offset(offset) \
static ssize_t show_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
show_temp_##offset##_max (struct device *dev, char *buf) \
{ \
return show_temp_max(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
show_temp_##offset##_min (struct device *dev, char *buf) \
{ \
return show_temp_min(dev, buf, 0x##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, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_max, set_temp_##offset##_max) \
static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min)
 
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 i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
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);
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]);
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 = 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);
return count;
}
 
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 0x##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, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \
static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min) \
static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div)
 
show_fan_offset(1);
show_fan_offset(2);
 
/* Alarm */
static ssize_t show_alarm(struct device *dev, char *buf) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
via686a_update_client(client);
return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
}
static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, 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_ADAP_CLASS_SMBUS))
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-sensor")) {
dev_err(&adapter->dev,"region 0x%x already in use!\n",
address);
return -ENODEV;
}
 
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct via686a_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
 
memset(new_client,0x00, sizeof(struct i2c_client) +
sizeof(struct via686a_data));
data = (struct via686a_data *) (new_client + 1);
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_in_input0);
device_create_file(&new_client->dev, &dev_attr_in_input1);
device_create_file(&new_client->dev, &dev_attr_in_input2);
device_create_file(&new_client->dev, &dev_attr_in_input3);
device_create_file(&new_client->dev, &dev_attr_in_input4);
device_create_file(&new_client->dev, &dev_attr_in_min0);
device_create_file(&new_client->dev, &dev_attr_in_min1);
device_create_file(&new_client->dev, &dev_attr_in_min2);
device_create_file(&new_client->dev, &dev_attr_in_min3);
device_create_file(&new_client->dev, &dev_attr_in_min4);
device_create_file(&new_client->dev, &dev_attr_in_max0);
device_create_file(&new_client->dev, &dev_attr_in_max1);
device_create_file(&new_client->dev, &dev_attr_in_max2);
device_create_file(&new_client->dev, &dev_attr_in_max3);
device_create_file(&new_client->dev, &dev_attr_in_max4);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_input2);
device_create_file(&new_client->dev, &dev_attr_temp_input3);
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_max2);
device_create_file(&new_client->dev, &dev_attr_temp_max3);
device_create_file(&new_client->dev, &dev_attr_temp_min1);
device_create_file(&new_client->dev, &dev_attr_temp_min2);
device_create_file(&new_client->dev, &dev_attr_temp_min3);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
device_create_file(&new_client->dev, &dev_attr_alarm);
 
return 0;
 
ERROR3:
release_region(address, VIA686A_EXTENT);
kfree(new_client);
ERROR0:
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(client);
 
return 0;
}
 
/* Called when we have found a new VIA686A. Set limits, etc. */
static void via686a_init_client(struct i2c_client *client)
{
int i;
 
/* Reset the device */
via686a_write_value(client, VIA686A_REG_CONFIG, 0x80);
 
/* Have to wait for reset to complete or else the following
initializations won't work reliably. The delay was arrived at
empirically, the datasheet doesn't tell you.
Waiting for the reset bit to clear doesn't work, it
clears in about 2-4 udelays and that isn't nearly enough. */
udelay(50);
 
via686a_write_value(client, VIA686A_REG_IN_MIN(0),
IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0));
via686a_write_value(client, VIA686A_REG_IN_MAX(0),
IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0));
via686a_write_value(client, VIA686A_REG_IN_MIN(1),
IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1));
via686a_write_value(client, VIA686A_REG_IN_MAX(1),
IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1));
via686a_write_value(client, VIA686A_REG_IN_MIN(2),
IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2));
via686a_write_value(client, VIA686A_REG_IN_MAX(2),
IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2));
via686a_write_value(client, VIA686A_REG_IN_MIN(3),
IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3));
via686a_write_value(client, VIA686A_REG_IN_MAX(3),
IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3));
via686a_write_value(client, VIA686A_REG_IN_MIN(4),
IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4));
via686a_write_value(client, VIA686A_REG_IN_MAX(4),
IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4));
via686a_write_value(client, VIA686A_REG_FAN_MIN(1),
FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
via686a_write_value(client, VIA686A_REG_FAN_MIN(2),
FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
for (i = 0; i <= 2; i++) {
via686a_write_value(client, VIA686A_REG_TEMP_OVER(i),
TEMP_TO_REG(VIA686A_INIT_TEMP_OVER));
via686a_write_value(client, VIA686A_REG_TEMP_HYST(i),
TEMP_TO_REG(VIA686A_INIT_TEMP_HYST));
}
 
/* Start monitoring */
via686a_write_value(client, VIA686A_REG_CONFIG, 0x01);
 
/* Cofigure 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 void via686a_update_client(struct i2c_client *client)
{
struct via686a_data *data = i2c_get_clientdata(client);
int i;
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !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);
}
 
static struct pci_device_id via686a_pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C686_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
 
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 = dev;
return i2c_add_driver(&via686a_driver);
}
 
static void __devexit via686a_pci_remove(struct pci_dev *dev)
{
i2c_del_driver(&via686a_driver);
}
 
static struct pci_driver via686a_pci_driver = {
.name = "via686a",
.id_table = via686a_pci_ids,
.probe = via686a_pci_probe,
.remove = __devexit_p(via686a_pci_remove),
};
 
static int __init sm_via686a_init(void)
{
return pci_module_init(&via686a_pci_driver);
}
 
static void __exit sm_via686a_exit(void)
{
pci_unregister_driver(&via686a_pci_driver);
}
 
MODULE_AUTHOR("Kyö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);
/shark/trunk/drivers/i2c/chips/lm75.c
0,0 → 1,303
/*
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.
*/
 
/* #define DEBUG 1 */
 
#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[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x48, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_1(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
 
/* 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 TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0))
#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff))
 
/* Initial values */
#define LM75_INIT_TEMP_OS 600
#define LM75_INIT_TEMP_HYST 500
 
/* Each client has this additional data */
struct lm75_data {
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 void lm75_update_client(struct i2c_client *client);
 
 
/* 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,
};
 
static int lm75_id = 0;
 
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm75_data *data = i2c_get_clientdata(client); \
int temp; \
\
lm75_update_client(client); \
temp = TEMP_FROM_REG(data->value); \
return sprintf(buf, "%d\n", temp * 100); \
}
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) / 100; \
\
data->value = TEMP_TO_REG(temp); \
lm75_write_value(client, reg, data->value); \
return count; \
}
set(temp_max, LM75_REG_TEMP_OS);
set(temp_hyst, LM75_REG_TEMP_HYST);
 
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
 
static int lm75_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
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, cur, conf, hyst, os;
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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct lm75_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct lm75_data));
 
data = (struct lm75_data *) (new_client + 1);
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. It is lousy. */
if (kind < 0) {
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);
os = i2c_smbus_read_word_data(new_client, 3);
for (i = 0; i <= 0x1f; i++)
if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) ||
(i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) ||
(i2c_smbus_read_word_data(new_client, i * 8 + 3) != os))
goto exit_free;
}
 
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = lm75;
 
if (kind == lm75) {
name = "lm75";
} else {
dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?",
kind);
goto exit_free;
}
 
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
 
new_client->id = lm75_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
 
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
 
/* Initialize the LM75 chip */
lm75_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp_input);
 
return 0;
 
exit_free:
kfree(new_client);
exit:
return err;
}
 
static int lm75_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(client);
return 0;
}
 
static u16 swap_bytes(u16 val)
{
return (val >> 8) | (val << 8);
}
 
/* 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 swap_bytes(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,
swap_bytes(value));
}
 
static void lm75_init_client(struct i2c_client *client)
{
/* Initialize the LM75 chip */
lm75_write_value(client, LM75_REG_TEMP_OS,
TEMP_TO_REG(LM75_INIT_TEMP_OS));
lm75_write_value(client, LM75_REG_TEMP_HYST,
TEMP_TO_REG(LM75_INIT_TEMP_HYST));
lm75_write_value(client, LM75_REG_CONF, 0);
}
 
static void lm75_update_client(struct i2c_client *client)
{
struct lm75_data *data = i2c_get_clientdata(client);
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !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);
}
 
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);
/shark/trunk/drivers/i2c/chips/lm85.c
0,0 → 1,1236
/*
lm85.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
 
Chip details at <http://www.national.com/ds/LM/LM85.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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/*
#include <asm/io.h>
*/
 
#undef LM85EXTENDEDFUNC /* Extended functionality */
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_4(lm85b, lm85c, adm1027, adt7463);
 
/* Enable debug if true */
static int lm85debug = 0;
 
/* The LM85 registers */
 
#define LM85_REG_IN(nr) (0x20 + (nr))
#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2)
#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2)
 
#define LM85_REG_TEMP(nr) (0x25 + (nr))
#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2)
#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2)
 
/* Fan speeds are LSB, MSB (2 bytes) */
#define LM85_REG_FAN(nr) (0x28 + (nr) *2)
#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2)
 
#define LM85_REG_PWM(nr) (0x30 + (nr))
 
#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr))
 
#define ADT7463_REG_TMIN_CTL1 0x36
#define ADT7463_REG_TMIN_CTL2 0x37
 
#define LM85_REG_DEVICE 0x3d
#define LM85_REG_COMPANY 0x3e
#define LM85_REG_VERSTEP 0x3f
/* These are the recognized values for the above regs */
#define LM85_DEVICE_ADX 0x27
#define LM85_COMPANY_NATIONAL 0x01
#define LM85_COMPANY_ANALOG_DEV 0x41
#define LM85_VERSTEP_GENERIC 0x60
#define LM85_VERSTEP_LM85C 0x60
#define LM85_VERSTEP_LM85B 0x62
#define LM85_VERSTEP_ADM1027 0x60
#define LM85_VERSTEP_ADT7463 0x62
 
#define LM85_REG_CONFIG 0x40
 
#define LM85_REG_ALARM1 0x41
#define LM85_REG_ALARM2 0x42
 
#define LM85_REG_VID 0x43
 
/* Automated FAN control */
#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr))
#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr))
#define LM85_REG_AFAN_SPIKE1 0x62
#define LM85_REG_AFAN_SPIKE2 0x63
#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr))
#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr))
#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr))
#define LM85_REG_AFAN_HYST1 0x6d
#define LM85_REG_AFAN_HYST2 0x6e
 
#define LM85_REG_TACH_MODE 0x74
#define LM85_REG_SPINUP_CTL 0x75
 
#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr))
#define ADM1027_REG_CONFIG2 0x73
#define ADM1027_REG_INTMASK1 0x74
#define ADM1027_REG_INTMASK2 0x75
#define ADM1027_REG_EXTEND_ADC1 0x76
#define ADM1027_REG_EXTEND_ADC2 0x77
#define ADM1027_REG_CONFIG3 0x78
#define ADM1027_REG_FAN_PPR 0x7b
 
#define ADT7463_REG_THERM 0x79
#define ADT7463_REG_THERM_LIMIT 0x7A
 
#define LM85_ALARM_IN0 0x0001
#define LM85_ALARM_IN1 0x0002
#define LM85_ALARM_IN2 0x0004
#define LM85_ALARM_IN3 0x0008
#define LM85_ALARM_TEMP1 0x0010
#define LM85_ALARM_TEMP2 0x0020
#define LM85_ALARM_TEMP3 0x0040
#define LM85_ALARM_ALARM2 0x0080
#define LM85_ALARM_IN4 0x0100
#define LM85_ALARM_RESERVED 0x0200
#define LM85_ALARM_FAN1 0x0400
#define LM85_ALARM_FAN2 0x0800
#define LM85_ALARM_FAN3 0x1000
#define LM85_ALARM_FAN4 0x2000
#define LM85_ALARM_TEMP1_FAULT 0x4000
#define LM85_ALARM_TEMP3_FAULT 0x8000
 
 
/* 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.
*/
 
/* IN are scaled 1.000 == 0xc0, mag = 3 */
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255))
#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0)
#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0))
 
/* IN are scaled acording to built-in resistors */
static int lm85_scaling[] = { /* .001 Volts */
2500, 2250, 3300, 5000, 12000
};
#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from))
#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255))
#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,lm85_scaling[n]))
#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0))
 
/* FAN speed is measured using 90kHz clock */
#define FAN_TO_REG(val) (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534))
#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val))
 
/* Temperature is reported in .001 degC increments */
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+500)/1000,-127,127))
#define TEMPEXT_FROM_REG(val,ext) ((val)*1000 + (ext)*250)
#define TEMP_FROM_REG(val) (TEMPEXT_FROM_REG(val,0))
#define EXTTEMP_TO_REG(val) (SENSORS_LIMIT((val)/250,-127,127))
 
#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255))
#define PWM_FROM_REG(val) (val)
 
#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03)
 
#ifdef LM85EXTENDEDFUNC /* Extended functionality */
 
/* ZONEs have the following parameters:
* Limit (low) temp, 1. degC
* Hysteresis (below limit), 1. degC (0-15)
* Range of speed control, .1 degC (2-80)
* Critical (high) temp, 1. degC
*
* FAN PWMs have the following parameters:
* Reference Zone, 1, 2, 3, etc.
* Spinup time, .05 sec
* PWM value at limit/low temp, 1 count
* PWM Frequency, 1. Hz
* PWM is Min or OFF below limit, flag
* Invert PWM output, flag
*
* Some chips filter the temp, others the fan.
* Filter constant (or disabled) .1 seconds
*/
 
/* These are the zone temperature range encodings */
static int lm85_range_map[] = { /* .1 degC */
20, 25, 33, 40, 50, 66,
80, 100, 133, 160, 200, 266,
320, 400, 533, 800
};
static int RANGE_TO_REG( int range )
{
int i;
 
if( range >= lm85_range_map[15] ) { return 15 ; }
for( i = 0 ; i < 15 ; ++i )
if( range <= lm85_range_map[i] )
break ;
return( i & 0x0f );
}
#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f])
 
/* These are the Acoustic Enhancement, or Temperature smoothing encodings
* NOTE: The enable/disable bit is INCLUDED in these encodings as the
* MSB (bit 3, value 8). If the enable bit is 0, the encoded value
* is ignored, or set to 0.
*/
static int lm85_smooth_map[] = { /* .1 sec */
350, 176, 118, 70, 44, 30, 16, 8
/* 35.4 * 1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48 */
};
static int SMOOTH_TO_REG( int smooth )
{
int i;
 
if( smooth <= 0 ) { return 0 ; } /* Disabled */
for( i = 0 ; i < 7 ; ++i )
if( smooth >= lm85_smooth_map[i] )
break ;
return( (i & 0x07) | 0x08 );
}
#define SMOOTH_FROM_REG(val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0)
 
/* These are the fan spinup delay time encodings */
static int lm85_spinup_map[] = { /* .1 sec */
0, 1, 2, 4, 7, 10, 20, 40
};
static int SPINUP_TO_REG( int spinup )
{
int i;
 
if( spinup >= lm85_spinup_map[7] ) { return 7 ; }
for( i = 0 ; i < 7 ; ++i )
if( spinup <= lm85_spinup_map[i] )
break ;
return( i & 0x07 );
}
#define SPINUP_FROM_REG(val) (lm85_spinup_map[(val)&0x07])
 
/* These are the PWM frequency encodings */
static int lm85_freq_map[] = { /* .1 Hz */
100, 150, 230, 300, 380, 470, 620, 980
};
static int FREQ_TO_REG( int freq )
{
int i;
 
if( freq >= lm85_freq_map[7] ) { return 7 ; }
for( i = 0 ; i < 7 ; ++i )
if( freq <= lm85_freq_map[i] )
break ;
return( i & 0x07 );
}
#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07])
 
/* Since we can't use strings, I'm abusing these numbers
* to stand in for the following meanings:
* 1 -- PWM responds to Zone 1
* 2 -- PWM responds to Zone 2
* 3 -- PWM responds to Zone 3
* 23 -- PWM responds to the higher temp of Zone 2 or 3
* 123 -- PWM responds to highest of Zone 1, 2, or 3
* 0 -- PWM is always at 0% (ie, off)
* -1 -- PWM is always at 100%
* -2 -- PWM responds to manual control
*/
 
#endif /* Extended functionality */
 
static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 };
#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07])
 
#ifdef LM85EXTENDEDFUNC /* Extended functionality */
 
static int ZONE_TO_REG( int zone )
{
int i;
 
for( i = 0 ; i <= 7 ; ++i )
if( zone == lm85_zone_map[i] )
break ;
if( i > 7 ) /* Not found. */
i = 3; /* Always 100% */
return( (i & 0x07)<<5 );
}
 
#endif /* Extended functionality */
 
#define HYST_TO_REG(val) (SENSORS_LIMIT((-(val)+5)/10,0,15))
#define HYST_FROM_REG(val) (-(val)*10)
 
#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127))
#define OFFSET_FROM_REG(val) ((val)*25)
 
#define PPR_MASK(fan) (0x03<<(fan *2))
#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2))
#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1)
 
/* i2c-vid.h defines vid_from_reg() */
#define VID_FROM_REG(val,vrm) (vid_from_reg((val),(vrm)))
 
#define ALARMS_FROM_REG(val) (val)
 
/* Unlike some other drivers we DO NOT set initial limits. Use
* the config file to set limits. Some users have reported
* motherboards shutting down when we set limits in a previous
* version of the driver.
*/
 
/* Typically used with Pentium 4 systems v9.1 VRM spec */
#define LM85_INIT_VRM 91
 
/* Chip sampling rates
*
* Some sensors are not updated more frequently than once per second
* so it doesn't make sense to read them more often than that.
* We cache the results and return the saved data if the driver
* is called again before a second has elapsed.
*
* Also, there is significant configuration data for this chip
* given the automatic PWM fan control that is possible. There
* are about 47 bytes of config data to only 22 bytes of actual
* readings. So, we keep the config data up to date in the cache
* when it is written and only sample it once every 1 *minute*
*/
#define LM85_DATA_INTERVAL (HZ + HZ / 2)
#define LM85_CONFIG_INTERVAL (1 * 60 * HZ)
 
/* For each registered LM85, we need to keep some data in memory. That
data is pointed to by lm85_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm85 client is
allocated. */
 
/* LM85 can automatically adjust fan speeds based on temperature
* This structure encapsulates an entire Zone config. There are
* three zones (one for each temperature input) on the lm85
*/
struct lm85_zone {
s8 limit; /* Low temp limit */
u8 hyst; /* Low limit hysteresis. (0-15) */
u8 range; /* Temp range, encoded */
s8 critical; /* "All fans ON" temp limit */
};
 
struct lm85_autofan {
u8 config; /* Register value */
u8 freq; /* PWM frequency, encoded */
u8 min_pwm; /* Minimum PWM value, encoded */
u8 min_off; /* Min PWM or OFF below "limit", flag */
};
 
struct lm85_data {
struct semaphore lock;
enum chips type;
 
struct semaphore update_lock;
int valid; /* !=0 if following fields are valid */
unsigned long last_reading; /* In jiffies */
unsigned long last_config; /* In jiffies */
 
u8 in[5]; /* Register value */
u8 in_max[5]; /* Register value */
u8 in_min[5]; /* Register value */
s8 temp[3]; /* Register value */
s8 temp_min[3]; /* Register value */
s8 temp_max[3]; /* Register value */
s8 temp_offset[3]; /* Register value */
u16 fan[4]; /* Register value */
u16 fan_min[4]; /* Register value */
u8 pwm[3]; /* Register value */
u8 spinup_ctl; /* Register encoding, combined */
u8 tach_mode; /* Register encoding, combined */
u16 extend_adc; /* Register value */
u8 fan_ppr; /* Register value */
u8 smooth[3]; /* Register encoding */
u8 vid; /* Register value */
u8 vrm; /* VRM version */
u8 syncpwm3; /* Saved PWM3 for TACH 2,3,4 config */
u8 oppoint[3]; /* Register value */
u16 tmin_ctl; /* Register value */
u32 therm_total; /* Cummulative therm count */
u8 therm_limit; /* Register value */
u16 alarms; /* Register encoding, combined */
struct lm85_autofan autofan[3];
struct lm85_zone zone[3];
};
 
static int lm85_attach_adapter(struct i2c_adapter *adapter);
static int lm85_detect(struct i2c_adapter *adapter, int address,
int kind);
static int lm85_detach_client(struct i2c_client *client);
 
static int lm85_read_value(struct i2c_client *client, u8 register);
static int lm85_write_value(struct i2c_client *client, u8 register, int value);
static void lm85_update_client(struct i2c_client *client);
static void lm85_init_client(struct i2c_client *client);
 
 
static struct i2c_driver lm85_driver = {
.owner = THIS_MODULE,
.name = "lm85",
.id = I2C_DRIVERID_LM85,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm85_attach_adapter,
.detach_client = lm85_detach_client,
};
 
/* Unique ID assigned to each LM85 detected */
static int lm85_id = 0;
 
 
/* 4 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr]) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->fan_min[nr] = FAN_TO_REG(val);
lm85_write_value(client, LM85_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, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \
static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min)
 
show_fan_offset(1);
show_fan_offset(2);
show_fan_offset(3);
show_fan_offset(4);
 
/* vid, vrm, alarms */
 
static ssize_t show_vid_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
 
static DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL)
 
static ssize_t show_vrm_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf, "%ld\n", (long) data->vrm);
}
 
static ssize_t store_vrm_reg(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
u32 val;
 
val = simple_strtoul(buf, NULL, 10);
data->vrm = val;
return count;
}
 
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg)
 
static ssize_t show_alarms_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf, "%ld\n", (long) ALARMS_FROM_REG(data->alarms));
}
 
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL)
 
/* pwm */
 
static ssize_t show_pwm(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->pwm[nr] = PWM_TO_REG(val);
lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm_enable(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
int pwm_zone;
 
lm85_update_client(client);
pwm_zone = ZONE_FROM_REG(data->autofan[nr].config);
return sprintf(buf,"%d\n", (pwm_zone != 0 && pwm_zone != -1) );
}
 
#define show_pwm_reg(offset) \
static ssize_t show_pwm_##offset (struct device *dev, char *buf) \
{ \
return show_pwm(dev, buf, 0x##offset - 1); \
} \
static ssize_t set_pwm_##offset (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_pwm(dev, buf, count, 0x##offset - 1); \
} \
static ssize_t show_pwm_enable##offset (struct device *dev, char *buf) \
{ \
return show_pwm_enable(dev, buf, 0x##offset - 1); \
} \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm_##offset, set_pwm_##offset) \
static DEVICE_ATTR(pwm_enable##offset, S_IRUGO, show_pwm_enable##offset, NULL)
 
show_pwm_reg(1);
show_pwm_reg(2);
show_pwm_reg(3);
 
/* Voltages */
 
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr]) );
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->in_min[nr] = INS_TO_REG(nr, val);
lm85_write_value(client, LM85_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr]) );
}
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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->in_max[nr] = INS_TO_REG(nr, val);
lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_reg(offset) \
static ssize_t show_in_##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, 0x##offset); \
} \
static ssize_t show_in_##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, 0x##offset); \
} \
static ssize_t show_in_##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, 0x##offset); \
} \
static ssize_t set_in_##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, 0x##offset); \
} \
static ssize_t set_in_##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in_##offset, NULL) \
static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, \
show_in_##offset##_min, set_in_##offset##_min) \
static DEVICE_ATTR(in_max##offset, S_IRUGO | S_IWUSR, \
show_in_##offset##_max, set_in_##offset##_max)
 
show_in_reg(0);
show_in_reg(1);
show_in_reg(2);
show_in_reg(3);
show_in_reg(4);
 
/* Temps */
 
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr]) );
}
static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->temp_min[nr] = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm85_data *data = i2c_get_clientdata(client);
 
lm85_update_client(client);
return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr]) );
}
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 lm85_data *data = i2c_get_clientdata(client);
int val;
 
down(&data->update_lock);
val = simple_strtol(buf, NULL, 10);
data->temp_max[nr] = TEMP_TO_REG(val);
lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]);
up(&data->update_lock);
return count;
}
#define show_temp_reg(offset) \
static ssize_t show_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \
{ \
return show_temp_min(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \
{ \
return show_temp_max(dev, buf, 0x##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, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min) \
static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_max, set_temp_##offset##_max)
 
show_temp_reg(1);
show_temp_reg(2);
show_temp_reg(3);
 
 
int lm85_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, lm85_detect);
}
 
int lm85_detect(struct i2c_adapter *adapter, int address,
int kind)
{
int company, verstep ;
struct i2c_client *new_client = NULL;
struct lm85_data *data;
int err = 0;
const char *type_name = "";
 
if (i2c_is_isa_adapter(adapter)) {
/* This chip has no ISA interface */
goto ERROR0 ;
};
 
if (!i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
/* We need to be able to do byte I/O */
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 lm85_{read,write}_value. */
 
if (!(new_client = kmalloc((sizeof(struct i2c_client)) +
sizeof(struct lm85_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
 
memset(new_client, 0, sizeof(struct i2c_client) +
sizeof(struct lm85_data));
data = (struct lm85_data *) (new_client + 1);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm85_driver;
new_client->flags = 0;
 
/* Now, we do the remaining detection. */
 
company = lm85_read_value(new_client, LM85_REG_COMPANY);
verstep = lm85_read_value(new_client, LM85_REG_VERSTEP);
 
if (lm85debug) {
printk("lm85: Detecting device at %d,0x%02x with"
" COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
i2c_adapter_id(new_client->adapter), new_client->addr,
company, verstep);
}
 
/* If auto-detecting, Determine the chip type. */
if (kind <= 0) {
if (lm85debug) {
printk("lm85: Autodetecting device at %d,0x%02x ...\n",
i2c_adapter_id(adapter), address );
}
if( company == LM85_COMPANY_NATIONAL
&& verstep == LM85_VERSTEP_LM85C ) {
kind = lm85c ;
} else if( company == LM85_COMPANY_NATIONAL
&& verstep == LM85_VERSTEP_LM85B ) {
kind = lm85b ;
} else if( company == LM85_COMPANY_NATIONAL
&& (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) {
printk("lm85: Unrecgonized version/stepping 0x%02x"
" Defaulting to LM85.\n", verstep );
kind = any_chip ;
} else if( company == LM85_COMPANY_ANALOG_DEV
&& verstep == LM85_VERSTEP_ADM1027 ) {
kind = adm1027 ;
} else if( company == LM85_COMPANY_ANALOG_DEV
&& verstep == LM85_VERSTEP_ADT7463 ) {
kind = adt7463 ;
} else if( company == LM85_COMPANY_ANALOG_DEV
&& (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) {
printk("lm85: Unrecgonized version/stepping 0x%02x"
" Defaulting to ADM1027.\n", verstep );
kind = adm1027 ;
} else if( kind == 0 && (verstep & 0xf0) == 0x60) {
printk("lm85: Generic LM85 Version 6 detected\n");
/* Leave kind as "any_chip" */
} else {
if (lm85debug) {
printk("lm85: Autodetection failed\n");
}
/* Not an LM85 ... */
if( kind == 0 ) { /* User used force=x,y */
printk("lm85: Generic LM85 Version 6 not"
" found at %d,0x%02x. Try force_lm85c.\n",
i2c_adapter_id(adapter), address );
}
err = 0 ;
goto ERROR1;
}
}
 
/* Fill in the chip specific driver values */
if ( kind == any_chip ) {
type_name = "lm85";
} else if ( kind == lm85b ) {
type_name = "lm85b";
} else if ( kind == lm85c ) {
type_name = "lm85c";
} else if ( kind == adm1027 ) {
type_name = "adm1027";
} else if ( kind == adt7463 ) {
type_name = "adt7463";
} else {
dev_dbg(&adapter->dev, "Internal error, invalid kind (%d)!", kind);
err = -EFAULT ;
goto ERROR1;
}
strlcpy(new_client->name, type_name, I2C_NAME_SIZE);
 
/* Fill in the remaining client fields */
new_client->id = lm85_id++;
data->type = kind;
data->valid = 0;
init_MUTEX(&data->update_lock);
 
if (lm85debug) {
printk("lm85: Assigning ID %d to %s at %d,0x%02x\n",
new_client->id, new_client->name,
i2c_adapter_id(new_client->adapter),
new_client->addr);
}
 
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR1;
 
/* Set the VRM version */
data->vrm = LM85_INIT_VRM ;
 
/* Initialize the LM85 chip */
lm85_init_client(new_client);
 
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_input3);
device_create_file(&new_client->dev, &dev_attr_fan_input4);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_min3);
device_create_file(&new_client->dev, &dev_attr_fan_min4);
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev, &dev_attr_pwm3);
device_create_file(&new_client->dev, &dev_attr_pwm_enable1);
device_create_file(&new_client->dev, &dev_attr_pwm_enable2);
device_create_file(&new_client->dev, &dev_attr_pwm_enable3);
device_create_file(&new_client->dev, &dev_attr_in_input0);
device_create_file(&new_client->dev, &dev_attr_in_input1);
device_create_file(&new_client->dev, &dev_attr_in_input2);
device_create_file(&new_client->dev, &dev_attr_in_input3);
device_create_file(&new_client->dev, &dev_attr_in_input4);
device_create_file(&new_client->dev, &dev_attr_in_min0);
device_create_file(&new_client->dev, &dev_attr_in_min1);
device_create_file(&new_client->dev, &dev_attr_in_min2);
device_create_file(&new_client->dev, &dev_attr_in_min3);
device_create_file(&new_client->dev, &dev_attr_in_min4);
device_create_file(&new_client->dev, &dev_attr_in_max0);
device_create_file(&new_client->dev, &dev_attr_in_max1);
device_create_file(&new_client->dev, &dev_attr_in_max2);
device_create_file(&new_client->dev, &dev_attr_in_max3);
device_create_file(&new_client->dev, &dev_attr_in_max4);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_input2);
device_create_file(&new_client->dev, &dev_attr_temp_input3);
device_create_file(&new_client->dev, &dev_attr_temp_min1);
device_create_file(&new_client->dev, &dev_attr_temp_min2);
device_create_file(&new_client->dev, &dev_attr_temp_min3);
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_max2);
device_create_file(&new_client->dev, &dev_attr_temp_max3);
device_create_file(&new_client->dev, &dev_attr_vrm);
device_create_file(&new_client->dev, &dev_attr_vid);
device_create_file(&new_client->dev, &dev_attr_alarms);
 
return 0;
 
/* Error out and cleanup code */
ERROR1:
kfree(new_client);
ERROR0:
return err;
}
 
int lm85_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(client);
return 0;
}
 
 
int lm85_read_value(struct i2c_client *client, u8 reg)
{
int res;
 
/* What size location is it? */
switch( reg ) {
case LM85_REG_FAN(0) : /* Read WORD data */
case LM85_REG_FAN(1) :
case LM85_REG_FAN(2) :
case LM85_REG_FAN(3) :
case LM85_REG_FAN_MIN(0) :
case LM85_REG_FAN_MIN(1) :
case LM85_REG_FAN_MIN(2) :
case LM85_REG_FAN_MIN(3) :
case LM85_REG_ALARM1 : /* Read both bytes at once */
case ADM1027_REG_EXTEND_ADC1 : /* Read two bytes at once */
res = i2c_smbus_read_byte_data(client, reg) & 0xff ;
res |= i2c_smbus_read_byte_data(client, reg+1) << 8 ;
break ;
case ADT7463_REG_TMIN_CTL1 : /* Read WORD MSB, LSB */
res = i2c_smbus_read_byte_data(client, reg) << 8 ;
res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ;
break ;
default: /* Read BYTE data */
res = i2c_smbus_read_byte_data(client, reg);
break ;
}
 
return res ;
}
 
int lm85_write_value(struct i2c_client *client, u8 reg, int value)
{
int res ;
 
switch( reg ) {
case LM85_REG_FAN(0) : /* Write WORD data */
case LM85_REG_FAN(1) :
case LM85_REG_FAN(2) :
case LM85_REG_FAN(3) :
case LM85_REG_FAN_MIN(0) :
case LM85_REG_FAN_MIN(1) :
case LM85_REG_FAN_MIN(2) :
case LM85_REG_FAN_MIN(3) :
/* NOTE: ALARM is read only, so not included here */
res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ;
res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ;
break ;
case ADT7463_REG_TMIN_CTL1 : /* Write WORD MSB, LSB */
res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff);
res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ;
break ;
default: /* Write BYTE data */
res = i2c_smbus_write_byte_data(client, reg, value);
break ;
}
 
return res ;
}
 
void lm85_init_client(struct i2c_client *client)
{
int value;
struct lm85_data *data = i2c_get_clientdata(client);
 
if (lm85debug) {
printk("lm85(%d): Initializing device\n", client->id);
}
 
/* Warn if part was not "READY" */
value = lm85_read_value(client, LM85_REG_CONFIG);
if (lm85debug) {
printk("lm85(%d): LM85_REG_CONFIG is: 0x%02x\n", client->id, value );
}
if( value & 0x02 ) {
printk("lm85(%d): Client (%d,0x%02x) config is locked.\n",
client->id,
i2c_adapter_id(client->adapter), client->addr );
};
if( ! (value & 0x04) ) {
printk("lm85(%d): Client (%d,0x%02x) is not ready.\n",
client->id,
i2c_adapter_id(client->adapter), client->addr );
};
if( value & 0x10
&& ( data->type == adm1027
|| data->type == adt7463 ) ) {
printk("lm85(%d): Client (%d,0x%02x) VxI mode is set. "
"Please report this to the lm85 maintainer.\n",
client->id,
i2c_adapter_id(client->adapter), client->addr );
};
 
/* WE INTENTIONALLY make no changes to the limits,
* offsets, pwms, fans and zones. If they were
* configured, we don't want to mess with them.
* If they weren't, the default is 100% PWM, no
* control and will suffice until 'sensors -s'
* can be run by the user.
*/
 
/* Start monitoring */
value = lm85_read_value(client, LM85_REG_CONFIG);
/* Try to clear LOCK, Set START, save everything else */
value = (value & ~ 0x02) | 0x01 ;
if (lm85debug) {
printk("lm85(%d): Setting CONFIG to: 0x%02x\n", client->id, value );
}
lm85_write_value(client, LM85_REG_CONFIG, value);
 
}
 
void lm85_update_client(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
int i;
 
down(&data->update_lock);
 
if ( !data->valid ||
(jiffies - data->last_reading > LM85_DATA_INTERVAL ) ) {
/* Things that change quickly */
 
if (lm85debug) {
printk("lm85(%d): Reading sensor values\n", client->id);
}
/* Have to read extended bits first to "freeze" the
* more significant bits that are read later.
*/
if ( (data->type == adm1027) || (data->type == adt7463) ) {
data->extend_adc =
lm85_read_value(client, ADM1027_REG_EXTEND_ADC1);
}
 
for (i = 0; i <= 4; ++i) {
data->in[i] =
lm85_read_value(client, LM85_REG_IN(i));
}
 
for (i = 0; i <= 3; ++i) {
data->fan[i] =
lm85_read_value(client, LM85_REG_FAN(i));
}
 
for (i = 0; i <= 2; ++i) {
data->temp[i] =
lm85_read_value(client, LM85_REG_TEMP(i));
}
 
for (i = 0; i <= 2; ++i) {
data->pwm[i] =
lm85_read_value(client, LM85_REG_PWM(i));
}
 
if ( data->type == adt7463 ) {
if( data->therm_total < ULONG_MAX - 256 ) {
data->therm_total +=
lm85_read_value(client, ADT7463_REG_THERM );
}
}
 
data->alarms = lm85_read_value(client, LM85_REG_ALARM1);
 
data->last_reading = jiffies ;
}; /* last_reading */
 
if ( !data->valid ||
(jiffies - data->last_config > LM85_CONFIG_INTERVAL) ) {
/* Things that don't change often */
 
if (lm85debug) {
printk("lm85(%d): Reading config values\n", client->id);
}
for (i = 0; i <= 4; ++i) {
data->in_min[i] =
lm85_read_value(client, LM85_REG_IN_MIN(i));
data->in_max[i] =
lm85_read_value(client, LM85_REG_IN_MAX(i));
}
 
for (i = 0; i <= 3; ++i) {
data->fan_min[i] =
lm85_read_value(client, LM85_REG_FAN_MIN(i));
}
 
for (i = 0; i <= 2; ++i) {
data->temp_min[i] =
lm85_read_value(client, LM85_REG_TEMP_MIN(i));
data->temp_max[i] =
lm85_read_value(client, LM85_REG_TEMP_MAX(i));
}
 
data->vid = lm85_read_value(client, LM85_REG_VID);
 
for (i = 0; i <= 2; ++i) {
int val ;
data->autofan[i].config =
lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
data->autofan[i].freq = val & 0x07 ;
data->zone[i].range = (val >> 4) & 0x0f ;
data->autofan[i].min_pwm =
lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
data->zone[i].limit =
lm85_read_value(client, LM85_REG_AFAN_LIMIT(i));
data->zone[i].critical =
lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i));
}
 
i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
data->smooth[0] = i & 0x0f ;
data->syncpwm3 = i & 0x10 ; /* Save PWM3 config */
data->autofan[0].min_off = (i & 0x20) != 0 ;
data->autofan[1].min_off = (i & 0x40) != 0 ;
data->autofan[2].min_off = (i & 0x80) != 0 ;
i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2);
data->smooth[1] = (i>>4) & 0x0f ;
data->smooth[2] = i & 0x0f ;
 
i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
data->zone[0].hyst = (i>>4) & 0x0f ;
data->zone[1].hyst = i & 0x0f ;
 
i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
data->zone[2].hyst = (i>>4) & 0x0f ;
 
if ( (data->type == lm85b) || (data->type == lm85c) ) {
data->tach_mode = lm85_read_value(client,
LM85_REG_TACH_MODE );
data->spinup_ctl = lm85_read_value(client,
LM85_REG_SPINUP_CTL );
} else if ( (data->type == adt7463) || (data->type == adm1027) ) {
if ( data->type == adt7463 ) {
for (i = 0; i <= 2; ++i) {
data->oppoint[i] = lm85_read_value(client,
ADT7463_REG_OPPOINT(i) );
}
data->tmin_ctl = lm85_read_value(client,
ADT7463_REG_TMIN_CTL1 );
data->therm_limit = lm85_read_value(client,
ADT7463_REG_THERM_LIMIT );
}
for (i = 0; i <= 2; ++i) {
data->temp_offset[i] = lm85_read_value(client,
ADM1027_REG_TEMP_OFFSET(i) );
}
data->tach_mode = lm85_read_value(client,
ADM1027_REG_CONFIG3 );
data->fan_ppr = lm85_read_value(client,
ADM1027_REG_FAN_PPR );
}
data->last_config = jiffies;
}; /* last_config */
 
data->valid = 1;
 
up(&data->update_lock);
}
 
 
static int __init sm_lm85_init(void)
{
return i2c_add_driver(&lm85_driver);
}
 
static void __exit sm_lm85_exit(void)
{
i2c_del_driver(&lm85_driver);
}
 
/* Thanks to Richard Barrington for adding the LM85 to sensors-detect.
* Thanks to Margit Schubert-While <margitsw@t-online.de> for help with
* post 2.7.0 CVS changes
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, Margit Schubert-While <margitsw@t-online.de>");
MODULE_DESCRIPTION("LM85-B, LM85-C driver");
MODULE_PARM(lm85debug, "i");
MODULE_PARM_DESC(lm85debug, "Enable debugging statements");
 
module_init(sm_lm85_init);
module_exit(sm_lm85_exit);
/shark/trunk/drivers/i2c/chips/w83781d.c
0,0 → 1,1954
/*
w83781d.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
and Mark 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 following chips:
 
Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
as99127f 7 3 1? 3 0x30 0x12c3 yes no
asb100 "bach" (type_name = as99127f) 0x30 0x0694 yes no
w83781d 7 3 0 3 0x10 0x5ca3 yes yes
w83627hf 9 3 2 3 0x20 0x5ca3 yes yes(LPC)
w83627thf 9 3 2 3 0x90 0x5ca3 no yes(LPC)
w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes
w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no
w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC)
 
*/
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
#include <asm/io.h>
 
/* RT Table support #defined so we can take it out if it gets bothersome */
#define W83781D_RT 1
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf);
I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: "
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
 
static int init = 1;
MODULE_PARM(init, "i");
MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
 
/* Constants specified below */
 
/* Length of ISA address segment */
#define W83781D_EXTENT 8
 
/* Where are the ISA address/data registers relative to the base address */
#define W83781D_ADDR_REG_OFFSET 5
#define W83781D_DATA_REG_OFFSET 6
 
/* The W83781D registers */
/* The W83782D registers for nr=7,8 are in bank 5 */
#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
(0x554 + (((nr) - 7) * 2)))
#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
(0x555 + (((nr) - 7) * 2)))
#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
(0x550 + (nr) - 7))
 
#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr))
#define W83781D_REG_FAN(nr) (0x27 + (nr))
 
#define W83781D_REG_BANK 0x4E
#define W83781D_REG_TEMP2_CONFIG 0x152
#define W83781D_REG_TEMP3_CONFIG 0x252
#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \
((nr == 2) ? (0x0150) : \
(0x27)))
#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \
((nr == 2) ? (0x153) : \
(0x3A)))
#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \
((nr == 2) ? (0x155) : \
(0x39)))
 
#define W83781D_REG_CONFIG 0x40
#define W83781D_REG_ALARM1 0x41
#define W83781D_REG_ALARM2 0x42
#define W83781D_REG_ALARM3 0x450 /* not on W83781D */
 
#define W83781D_REG_IRQ 0x4C
#define W83781D_REG_BEEP_CONFIG 0x4D
#define W83781D_REG_BEEP_INTS1 0x56
#define W83781D_REG_BEEP_INTS2 0x57
#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */
 
#define W83781D_REG_VID_FANDIV 0x47
 
#define W83781D_REG_CHIPID 0x49
#define W83781D_REG_WCHIPID 0x58
#define W83781D_REG_CHIPMAN 0x4F
#define W83781D_REG_PIN 0x4B
 
/* 782D/783S only */
#define W83781D_REG_VBAT 0x5D
 
/* PWM 782D (1-4) and 783S (1-2) only */
#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */
/* on which is which; */
#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */
/* However 782d is probably wrong. */
#define W83781D_REG_PWM3 0x5E
#define W83781D_REG_PWM4 0x5F
#define W83781D_REG_PWMCLK12 0x5C
#define W83781D_REG_PWMCLK34 0x45C
static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2,
W83781D_REG_PWM3, W83781D_REG_PWM4
};
 
#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1])
 
#define W83781D_REG_I2C_ADDR 0x48
#define W83781D_REG_I2C_SUBADDR 0x4A
 
/* The following are undocumented in the data sheets however we
received the information in an email from Winbond tech support */
/* Sensor selection - not on 781d */
#define W83781D_REG_SCFG1 0x5D
static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
 
#define W83781D_REG_SCFG2 0x59
static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
 
#define W83781D_DEFAULT_BETA 3435
 
/* RT Table registers */
#define W83781D_REG_RT_IDX 0x50
#define W83781D_REG_RT_VAL 0x51
 
/* 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) * 10 + 8)/16),0,255))
#define IN_FROM_REG(val) (((val) * 16) / 10)
 
static inline u8
FAN_TO_REG(long rpm, int 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 / ((val) * (div))))
 
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val / 10) < 0 ? (((val / 10) - 5) / 10) : \
((val / 10) + 5) / 10), 0, 255))
#define TEMP_FROM_REG(val) ((((val ) > 0x80 ? (val) - 0x100 : (val)) * 10) * 10)
 
#define TEMP_ADD_TO_REG(val) (SENSORS_LIMIT(((((val / 10) + 2) / 5) << 7),\
0, 0xffff))
#define TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 5) * 10)
 
#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val / 10) + 2)*4)/10) \
<< 7), 0, 0xffff))
#define AS99127_TEMP_ADD_FROM_REG(val) (((((val) >> 7) * 10) / 4) * 10)
 
#define ALARMS_FROM_REG(val) (val)
#define PWM_FROM_REG(val) (val)
#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
#define BEEP_MASK_FROM_REG(val) (val)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
 
#define BEEP_ENABLE_TO_REG(val) ((val) ? 1 : 0)
#define BEEP_ENABLE_FROM_REG(val) ((val) ? 1 : 0)
 
#define DIV_FROM_REG(val) (1 << (val))
 
static inline u8
DIV_TO_REG(long val, enum chips type)
{
int i;
val = SENSORS_LIMIT(val, 1,
((type == w83781d
|| type == as99127f) ? 8 : 128)) >> 1;
for (i = 0; i < 6; i++) {
if (val == 0)
break;
val >>= 1;
}
return ((u8) i);
}
 
/* Initial limits */
#define W83781D_INIT_IN_0 (vid == 3500 ? 280 : vid / 10)
#define W83781D_INIT_IN_1 (vid == 3500 ? 280 : vid / 10)
#define W83781D_INIT_IN_2 330
#define W83781D_INIT_IN_3 (((500) * 100) / 168)
#define W83781D_INIT_IN_4 (((1200) * 10) / 38)
#define W83781D_INIT_IN_5 (((-1200) * -604) / 2100)
#define W83781D_INIT_IN_6 (((-500) * -604) / 909)
#define W83781D_INIT_IN_7 (((500) * 100) / 168)
#define W83781D_INIT_IN_8 300
/* Initial limits for 782d/783s negative voltages */
/* Note level shift. Change min/max below if you change these. */
#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514)
#define W83782D_INIT_IN_6 ((( (-500) + 771) * 100)/314)
 
#define W83781D_INIT_IN_PERCENTAGE 10
#define W83781D_INIT_IN_MIN(val) (val - val * W83781D_INIT_IN_PERCENTAGE / 100)
#define W83781D_INIT_IN_MAX(val) (val + val * W83781D_INIT_IN_PERCENTAGE / 100)
 
#define W83781D_INIT_IN_MIN_0 W83781D_INIT_IN_MIN(W83781D_INIT_IN_0)
#define W83781D_INIT_IN_MAX_0 W83781D_INIT_IN_MAX(W83781D_INIT_IN_0)
#define W83781D_INIT_IN_MIN_1 W83781D_INIT_IN_MIN(W83781D_INIT_IN_1)
#define W83781D_INIT_IN_MAX_1 W83781D_INIT_IN_MAX(W83781D_INIT_IN_1)
#define W83781D_INIT_IN_MIN_2 W83781D_INIT_IN_MIN(W83781D_INIT_IN_2)
#define W83781D_INIT_IN_MAX_2 W83781D_INIT_IN_MAX(W83781D_INIT_IN_2)
#define W83781D_INIT_IN_MIN_3 W83781D_INIT_IN_MIN(W83781D_INIT_IN_3)
#define W83781D_INIT_IN_MAX_3 W83781D_INIT_IN_MAX(W83781D_INIT_IN_3)
#define W83781D_INIT_IN_MIN_4 W83781D_INIT_IN_MIN(W83781D_INIT_IN_4)
#define W83781D_INIT_IN_MAX_4 W83781D_INIT_IN_MAX(W83781D_INIT_IN_4)
#define W83781D_INIT_IN_MIN_5 W83781D_INIT_IN_MIN(W83781D_INIT_IN_5)
#define W83781D_INIT_IN_MAX_5 W83781D_INIT_IN_MAX(W83781D_INIT_IN_5)
#define W83781D_INIT_IN_MIN_6 W83781D_INIT_IN_MIN(W83781D_INIT_IN_6)
#define W83781D_INIT_IN_MAX_6 W83781D_INIT_IN_MAX(W83781D_INIT_IN_6)
#define W83781D_INIT_IN_MIN_7 W83781D_INIT_IN_MIN(W83781D_INIT_IN_7)
#define W83781D_INIT_IN_MAX_7 W83781D_INIT_IN_MAX(W83781D_INIT_IN_7)
#define W83781D_INIT_IN_MIN_8 W83781D_INIT_IN_MIN(W83781D_INIT_IN_8)
#define W83781D_INIT_IN_MAX_8 W83781D_INIT_IN_MAX(W83781D_INIT_IN_8)
 
/* Initial limits for 782d/783s negative voltages */
/* These aren't direct multiples because of level shift */
/* Beware going negative - check */
#define W83782D_INIT_IN_MIN_5_TMP \
(((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
#define W83782D_INIT_IN_MIN_5 \
((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0)
#define W83782D_INIT_IN_MAX_5 \
(((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
#define W83782D_INIT_IN_MIN_6_TMP \
((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314)
#define W83782D_INIT_IN_MIN_6 \
((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0)
#define W83782D_INIT_IN_MAX_6 \
((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314)
 
#define W83781D_INIT_FAN_MIN_1 3000
#define W83781D_INIT_FAN_MIN_2 3000
#define W83781D_INIT_FAN_MIN_3 3000
 
/* temp = value / 100 */
#define W83781D_INIT_TEMP_OVER 6000
#define W83781D_INIT_TEMP_HYST 12700 /* must be 127 for ALARM to work */
#define W83781D_INIT_TEMP2_OVER 6000
#define W83781D_INIT_TEMP2_HYST 5000
#define W83781D_INIT_TEMP3_OVER 6000
#define W83781D_INIT_TEMP3_HYST 5000
 
/* There are some complications in a module like this. First off, W83781D 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
W83781D 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 W83781D 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 W83781D, we need to keep some data in memory. That
data is pointed to by w83781d_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new w83781d client is
allocated. */
struct w83781d_data {
struct semaphore lock;
enum chips type;
 
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
 
struct i2c_client *lm75[2]; /* for secondary I2C addresses */
/* array of 2 pointers to subclients */
 
u8 in[9]; /* Register value - 8 & 9 for 782D only */
u8 in_max[9]; /* Register value - 8 & 9 for 782D only */
u8 in_min[9]; /* Register value - 8 & 9 for 782D only */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp;
u8 temp_min; /* Register value */
u8 temp_max; /* Register value */
u16 temp_add[2]; /* Register value */
u16 temp_max_add[2]; /* Register value */
u16 temp_min_add[2]; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u32 alarms; /* Register encoding, combined */
u32 beep_mask; /* Register encoding, combined */
u8 beep_enable; /* Boolean */
u8 pwm[4]; /* Register value */
u8 pwmenable[4]; /* Boolean */
u16 sens[3]; /* 782D/783S only.
1 = pentium diode; 2 = 3904 diode;
3000-5000 = thermistor beta.
Default = 3435.
Other Betas unimplemented */
#ifdef W83781D_RT
u8 rt[3][32]; /* Register value */
#endif
u8 vrm;
};
 
static int w83781d_attach_adapter(struct i2c_adapter *adapter);
static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind);
static int w83781d_detach_client(struct i2c_client *client);
 
static int w83781d_read_value(struct i2c_client *client, u16 register);
static int w83781d_write_value(struct i2c_client *client, u16 register,
u16 value);
static void w83781d_update_client(struct i2c_client *client);
static void w83781d_init_client(struct i2c_client *client);
 
static inline u16 swap_bytes(u16 val)
{
return (val >> 8) | (val << 8);
}
 
static struct i2c_driver w83781d_driver = {
.owner = THIS_MODULE,
.name = "w83781d",
.id = I2C_DRIVERID_W83781D,
.flags = I2C_DF_NOTIFY,
.attach_adapter = w83781d_attach_adapter,
.detach_client = w83781d_detach_client,
};
 
/* following are the sysfs callback functions */
#define show_in_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
\
w83781d_update_client(client); \
\
return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr] * 10)); \
}
show_in_reg(in);
show_in_reg(in_min);
show_in_reg(in_max);
 
#define store_in_reg(REG, reg) \
static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
u32 val; \
\
val = simple_strtoul(buf, NULL, 10) / 10; \
data->in_##reg[nr] = IN_TO_REG(val); \
w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \
\
return count; \
}
store_in_reg(MIN, min);
store_in_reg(MAX, max);
 
#define sysfs_in_offset(offset) \
static ssize_t \
show_regs_in_##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, 0x##offset); \
} \
static DEVICE_ATTR(in_input##offset, S_IRUGO, show_regs_in_##offset, NULL)
 
#define sysfs_in_reg_offset(reg, offset) \
static ssize_t show_regs_in_##reg##offset (struct device *dev, char *buf) \
{ \
return show_in_##reg (dev, buf, 0x##offset); \
} \
static ssize_t store_regs_in_##reg##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_in_##reg (dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(in_##reg##offset, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset)
 
#define sysfs_in_offsets(offset) \
sysfs_in_offset(offset); \
sysfs_in_reg_offset(min, offset); \
sysfs_in_reg_offset(max, offset);
 
sysfs_in_offsets(0);
sysfs_in_offsets(1);
sysfs_in_offsets(2);
sysfs_in_offsets(3);
sysfs_in_offsets(4);
sysfs_in_offsets(5);
sysfs_in_offsets(6);
sysfs_in_offsets(7);
sysfs_in_offsets(8);
 
#define device_create_file_in(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_in_input##offset); \
device_create_file(&client->dev, &dev_attr_in_min##offset); \
device_create_file(&client->dev, &dev_attr_in_max##offset); \
} while (0)
 
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
\
w83781d_update_client(client); \
\
return sprintf(buf,"%ld\n", \
FAN_FROM_REG(data->reg[nr-1], (long)DIV_FROM_REG(data->fan_div[nr-1]))); \
}
show_fan_reg(fan);
show_fan_reg(fan_min);
 
static ssize_t
store_fan_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val;
 
val = simple_strtoul(buf, NULL, 10);
data->fan_min[nr - 1] =
FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1]));
w83781d_write_value(client, W83781D_REG_FAN_MIN(nr),
data->fan_min[nr - 1]);
 
return count;
}
 
#define sysfs_fan_offset(offset) \
static ssize_t show_regs_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, 0x##offset); \
} \
static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_regs_fan_##offset, NULL)
 
#define sysfs_fan_min_offset(offset) \
static ssize_t show_regs_fan_min##offset (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, 0x##offset); \
} \
static ssize_t store_regs_fan_min##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_fan_min(dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, show_regs_fan_min##offset, store_regs_fan_min##offset)
 
sysfs_fan_offset(1);
sysfs_fan_min_offset(1);
sysfs_fan_offset(2);
sysfs_fan_min_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
 
#define device_create_file_fan(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_fan_input##offset); \
device_create_file(&client->dev, &dev_attr_fan_min##offset); \
} while (0)
 
#define show_temp_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
\
w83781d_update_client(client); \
\
if (nr >= 2) { /* TEMP2 and TEMP3 */ \
if (data->type == as99127f) { \
return sprintf(buf,"%ld\n", \
(long)AS99127_TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \
} else { \
return sprintf(buf,"%ld\n", \
(long)TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \
} \
} else { /* TEMP1 */ \
return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \
} \
}
show_temp_reg(temp);
show_temp_reg(temp_min);
show_temp_reg(temp_max);
 
#define store_temp_reg(REG, reg) \
static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
u32 val; \
\
val = simple_strtoul(buf, NULL, 10); \
\
if (nr >= 2) { /* TEMP2 and TEMP3 */ \
if (data->type == as99127f) \
data->temp_##reg##_add[nr-2] = AS99127_TEMP_ADD_TO_REG(val); \
else \
data->temp_##reg##_add[nr-2] = TEMP_ADD_TO_REG(val); \
\
w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \
data->temp_##reg##_add[nr-2]); \
} else { /* TEMP1 */ \
data->temp_##reg = TEMP_TO_REG(val); \
w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \
data->temp_##reg); \
} \
\
return count; \
}
store_temp_reg(OVER, min);
store_temp_reg(HYST, max);
 
#define sysfs_temp_offset(offset) \
static ssize_t \
show_regs_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, 0x##offset); \
} \
static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_regs_temp_##offset, NULL)
 
#define sysfs_temp_reg_offset(reg, offset) \
static ssize_t show_regs_temp_##reg##offset (struct device *dev, char *buf) \
{ \
return show_temp_##reg (dev, buf, 0x##offset); \
} \
static ssize_t store_regs_temp_##reg##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_temp_##reg (dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(temp_##reg##offset, S_IRUGO| S_IWUSR, show_regs_temp_##reg##offset, store_regs_temp_##reg##offset)
 
#define sysfs_temp_offsets(offset) \
sysfs_temp_offset(offset); \
sysfs_temp_reg_offset(min, offset); \
sysfs_temp_reg_offset(max, offset);
 
sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
sysfs_temp_offsets(3);
 
#define device_create_file_temp(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_temp_input##offset); \
device_create_file(&client->dev, &dev_attr_temp_max##offset); \
device_create_file(&client->dev, &dev_attr_temp_min##offset); \
} while (0)
 
static ssize_t
show_vid_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
 
static
DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL)
#define device_create_file_vid(client) \
device_create_file(&client->dev, &dev_attr_vid);
static ssize_t
show_vrm_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) data->vrm);
}
 
static ssize_t
store_vrm_reg(struct device *dev, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val;
 
val = simple_strtoul(buf, NULL, 10);
data->vrm = val;
 
return count;
}
 
static
DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg)
#define device_create_file_vrm(client) \
device_create_file(&client->dev, &dev_attr_vrm);
static ssize_t
show_alarms_reg(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) ALARMS_FROM_REG(data->alarms));
}
 
static
DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL)
#define device_create_file_alarms(client) \
device_create_file(&client->dev, &dev_attr_alarms);
#define show_beep_reg(REG, reg) \
static ssize_t show_beep_##reg (struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83781d_data *data = i2c_get_clientdata(client); \
\
w83781d_update_client(client); \
\
return sprintf(buf,"%ld\n", (long)BEEP_##REG##_FROM_REG(data->beep_##reg)); \
}
show_beep_reg(ENABLE, enable);
show_beep_reg(MASK, mask);
 
#define BEEP_ENABLE 0 /* Store beep_enable */
#define BEEP_MASK 1 /* Store beep_mask */
 
static ssize_t
store_beep_reg(struct device *dev, const char *buf, size_t count,
int update_mask)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val, val2;
 
val = simple_strtoul(buf, NULL, 10);
 
if (update_mask == BEEP_MASK) { /* We are storing beep_mask */
data->beep_mask = BEEP_MASK_TO_REG(val);
w83781d_write_value(client, W83781D_REG_BEEP_INTS1,
data->beep_mask & 0xff);
 
if ((data->type != w83781d) && (data->type != as99127f)) {
w83781d_write_value(client, W83781D_REG_BEEP_INTS3,
((data->beep_mask) >> 16) & 0xff);
}
 
val2 = (data->beep_mask >> 8) & 0x7f;
} else { /* We are storing beep_enable */
val2 = w83781d_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f;
data->beep_enable = BEEP_ENABLE_TO_REG(val);
}
 
w83781d_write_value(client, W83781D_REG_BEEP_INTS2,
val2 | data->beep_enable << 7);
 
return count;
}
 
#define sysfs_beep(REG, reg) \
static ssize_t show_regs_beep_##reg (struct device *dev, char *buf) \
{ \
return show_beep_##reg(dev, buf); \
} \
static ssize_t store_regs_beep_##reg (struct device *dev, const char *buf, size_t count) \
{ \
return store_beep_reg(dev, buf, count, BEEP_##REG); \
} \
static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_regs_beep_##reg)
 
sysfs_beep(ENABLE, enable);
sysfs_beep(MASK, mask);
 
#define device_create_file_beep(client) \
do { \
device_create_file(&client->dev, &dev_attr_beep_enable); \
device_create_file(&client->dev, &dev_attr_beep_mask); \
} while (0)
 
/* w83697hf only has two fans */
static ssize_t
show_fan_div_reg(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n",
(long) DIV_FROM_REG(data->fan_div[nr - 1]));
}
 
/* w83697hf only has two fans */
static ssize_t
store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val, old, old2, old3 = 0;
 
val = simple_strtoul(buf, NULL, 10);
old = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
 
data->fan_div[nr - 1] = DIV_TO_REG(val, data->type);
 
/* w83781d and as99127f don't have extended divisor bits */
if ((data->type != w83781d) && data->type != as99127f) {
old3 = w83781d_read_value(client, W83781D_REG_VBAT);
}
if (nr >= 3 && data->type != w83697hf) {
old2 = w83781d_read_value(client, W83781D_REG_PIN);
old2 = (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
w83781d_write_value(client, W83781D_REG_PIN, old2);
 
if ((data->type != w83781d) && (data->type != as99127f)) {
old3 = (old3 & 0x7f) | ((data->fan_div[2] & 0x04) << 5);
}
}
if (nr >= 2) {
old = (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
 
if ((data->type != w83781d) && (data->type != as99127f)) {
old3 = (old3 & 0xbf) | ((data->fan_div[1] & 0x04) << 4);
}
}
if (nr >= 1) {
old = (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
w83781d_write_value(client, W83781D_REG_VID_FANDIV, old);
 
if ((data->type != w83781d) && (data->type != as99127f)) {
old3 = (old3 & 0xdf) | ((data->fan_div[0] & 0x04) << 3);
w83781d_write_value(client, W83781D_REG_VBAT, old3);
}
}
 
return count;
}
 
#define sysfs_fan_div(offset) \
static ssize_t show_regs_fan_div_##offset (struct device *dev, char *buf) \
{ \
return show_fan_div_reg(dev, buf, offset); \
} \
static ssize_t store_regs_fan_div_##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_fan_div_reg(dev, buf, count, offset); \
} \
static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, show_regs_fan_div_##offset, store_regs_fan_div_##offset)
 
sysfs_fan_div(1);
sysfs_fan_div(2);
sysfs_fan_div(3);
 
#define device_create_file_fan_div(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_fan_div##offset); \
} while (0)
 
/* w83697hf only has two fans */
static ssize_t
show_pwm_reg(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr - 1]));
}
 
/* w83697hf only has two fans */
static ssize_t
show_pwmenable_reg(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) data->pwmenable[nr - 1]);
}
 
static ssize_t
store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val;
 
val = simple_strtoul(buf, NULL, 10);
 
data->pwm[nr - 1] = PWM_TO_REG(val);
w83781d_write_value(client, W83781D_REG_PWM(nr), data->pwm[nr - 1]);
 
return count;
}
 
static ssize_t
store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val, j, k;
 
val = simple_strtoul(buf, NULL, 10);
 
/* only PWM2 can be enabled/disabled */
if (nr == 2) {
j = w83781d_read_value(client, W83781D_REG_PWMCLK12);
k = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
 
if (val > 0) {
if (!(j & 0x08))
w83781d_write_value(client,
W83781D_REG_PWMCLK12,
j | 0x08);
if (k & 0x10)
w83781d_write_value(client,
W83781D_REG_BEEP_CONFIG,
k & 0xef);
 
data->pwmenable[1] = 1;
} else {
if (j & 0x08)
w83781d_write_value(client,
W83781D_REG_PWMCLK12,
j & 0xf7);
if (!(k & 0x10))
w83781d_write_value(client,
W83781D_REG_BEEP_CONFIG,
j | 0x10);
 
data->pwmenable[1] = 0;
}
}
 
return count;
}
 
#define sysfs_pwm(offset) \
static ssize_t show_regs_pwm_##offset (struct device *dev, char *buf) \
{ \
return show_pwm_reg(dev, buf, offset); \
} \
static ssize_t store_regs_pwm_##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_pwm_reg(dev, buf, count, offset); \
} \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, show_regs_pwm_##offset, store_regs_pwm_##offset)
 
#define sysfs_pwmenable(offset) \
static ssize_t show_regs_pwmenable_##offset (struct device *dev, char *buf) \
{ \
return show_pwmenable_reg(dev, buf, offset); \
} \
static ssize_t store_regs_pwmenable_##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_pwmenable_reg(dev, buf, count, offset); \
} \
static DEVICE_ATTR(pwm_enable##offset, S_IRUGO | S_IWUSR, show_regs_pwmenable_##offset, store_regs_pwmenable_##offset)
 
sysfs_pwm(1);
sysfs_pwm(2);
sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */
sysfs_pwm(3);
sysfs_pwm(4);
 
#define device_create_file_pwm(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_pwm##offset); \
} while (0)
 
#define device_create_file_pwmenable(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_pwm_enable##offset); \
} while (0)
 
static ssize_t
show_sensor_reg(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
 
w83781d_update_client(client);
 
return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]);
}
 
static ssize_t
store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val, tmp;
 
val = simple_strtoul(buf, NULL, 10);
 
switch (val) {
case 1: /* PII/Celeron diode */
tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
w83781d_write_value(client, W83781D_REG_SCFG1,
tmp | BIT_SCFG1[nr - 1]);
tmp = w83781d_read_value(client, W83781D_REG_SCFG2);
w83781d_write_value(client, W83781D_REG_SCFG2,
tmp | BIT_SCFG2[nr - 1]);
data->sens[nr - 1] = val;
break;
case 2: /* 3904 */
tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
w83781d_write_value(client, W83781D_REG_SCFG1,
tmp | BIT_SCFG1[nr - 1]);
tmp = w83781d_read_value(client, W83781D_REG_SCFG2);
w83781d_write_value(client, W83781D_REG_SCFG2,
tmp & ~BIT_SCFG2[nr - 1]);
data->sens[nr - 1] = val;
break;
case W83781D_DEFAULT_BETA: /* thermistor */
tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
w83781d_write_value(client, W83781D_REG_SCFG1,
tmp & ~BIT_SCFG1[nr - 1]);
data->sens[nr - 1] = val;
break;
default:
dev_err(&client->dev,
"Invalid sensor type %ld; must be 1, 2, or %d\n",
(long) val, W83781D_DEFAULT_BETA);
break;
}
 
return count;
}
 
#define sysfs_sensor(offset) \
static ssize_t show_regs_sensor_##offset (struct device *dev, char *buf) \
{ \
return show_sensor_reg(dev, buf, offset); \
} \
static ssize_t store_regs_sensor_##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_sensor_reg(dev, buf, count, offset); \
} \
static DEVICE_ATTR(sensor##offset, S_IRUGO | S_IWUSR, show_regs_sensor_##offset, store_regs_sensor_##offset)
 
sysfs_sensor(1);
sysfs_sensor(2);
sysfs_sensor(3);
 
#define device_create_file_sensor(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_sensor##offset); \
} while (0)
 
#ifdef W83781D_RT
static ssize_t
show_rt_reg(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
int i, j = 0;
 
w83781d_update_client(client);
 
for (i = 0; i < 32; i++) {
if (i > 0)
j += sprintf(buf, " %ld", (long) data->rt[nr - 1][i]);
else
j += sprintf(buf, "%ld", (long) data->rt[nr - 1][i]);
}
j += sprintf(buf, "\n");
 
return j;
}
 
static ssize_t
store_rt_reg(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83781d_data *data = i2c_get_clientdata(client);
u32 val, i;
 
for (i = 0; i < count; i++) {
val = simple_strtoul(buf + count, NULL, 10);
 
/* fixme: no bounds checking 0-255 */
data->rt[nr - 1][i] = val & 0xff;
w83781d_write_value(client, W83781D_REG_RT_IDX, i);
w83781d_write_value(client, W83781D_REG_RT_VAL,
data->rt[nr - 1][i]);
}
 
return count;
}
 
#define sysfs_rt(offset) \
static ssize_t show_regs_rt_##offset (struct device *dev, char *buf) \
{ \
return show_rt_reg(dev, buf, offset); \
} \
static ssize_t store_regs_rt_##offset (struct device *dev, const char *buf, size_t count) \
{ \
return store_rt_reg(dev, buf, count, offset); \
} \
static DEVICE_ATTR(rt##offset, S_IRUGO | S_IWUSR, show_regs_rt_##offset, store_regs_rt_##offset)
 
sysfs_rt(1);
sysfs_rt(2);
sysfs_rt(3);
 
#define device_create_file_rt(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_rt##offset); \
} while (0)
 
#endif /* ifdef W83781D_RT */
 
/* This function is called when:
* w83781d_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and w83781d_driver is still present) */
static int
w83781d_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
return 0;
return i2c_detect(adapter, &addr_data, w83781d_detect);
}
 
/* Assumes that adapter is of I2C, not ISA variety.
* OTHERWISE DON'T CALL THIS
*/
static int
w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
struct i2c_client *new_client)
{
int i, val1 = 0, id;
int err;
const char *client_name;
struct w83781d_data *data = i2c_get_clientdata(new_client);
 
data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!(data->lm75[0])) {
err = -ENOMEM;
goto ERROR_SC_0;
}
memset(data->lm75[0], 0x00, sizeof (struct i2c_client));
 
id = i2c_adapter_id(adapter);
 
if (force_subclients[0] == id && force_subclients[1] == address) {
for (i = 2; i <= 3; i++) {
if (force_subclients[i] < 0x48 ||
force_subclients[i] > 0x4f) {
dev_err(&new_client->dev, "Invalid subclient "
"address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -EINVAL;
goto ERROR_SC_1;
}
}
w83781d_write_value(new_client, W83781D_REG_I2C_SUBADDR,
(force_subclients[2] & 0x07) |
((force_subclients[3] & 0x07) << 4));
data->lm75[0]->addr = force_subclients[2];
} else {
val1 = w83781d_read_value(new_client, W83781D_REG_I2C_SUBADDR);
data->lm75[0]->addr = 0x48 + (val1 & 0x07);
}
 
if (kind != w83783s) {
 
data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!(data->lm75[1])) {
err = -ENOMEM;
goto ERROR_SC_1;
}
memset(data->lm75[1], 0x0, sizeof(struct i2c_client));
 
if (force_subclients[0] == id &&
force_subclients[1] == address) {
data->lm75[1]->addr = force_subclients[3];
} else {
data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07);
}
if (data->lm75[0]->addr == data->lm75[1]->addr) {
dev_err(&new_client->dev,
"Duplicate addresses 0x%x for subclients.\n",
data->lm75[0]->addr);
err = -EBUSY;
goto ERROR_SC_2;
}
}
 
if (kind == w83781d)
client_name = "w83781d subclient";
else if (kind == w83782d)
client_name = "w83782d subclient";
else if (kind == w83783s)
client_name = "w83783s subclient";
else if (kind == w83627hf)
client_name = "w83627hf subclient";
else if (kind == as99127f)
client_name = "as99127f subclient";
else
client_name = "unknown subclient?";
 
for (i = 0; i <= 1; i++) {
/* store all data in w83781d */
i2c_set_clientdata(data->lm75[i], NULL);
data->lm75[i]->adapter = adapter;
data->lm75[i]->driver = &w83781d_driver;
data->lm75[i]->flags = 0;
strlcpy(data->lm75[i]->name, client_name,
I2C_NAME_SIZE);
if ((err = i2c_attach_client(data->lm75[i]))) {
dev_err(&new_client->dev, "Subclient %d "
"registration at address 0x%x "
"failed.\n", i, data->lm75[i]->addr);
if (i == 1)
goto ERROR_SC_3;
goto ERROR_SC_2;
}
if (kind == w83783s)
break;
}
 
return 0;
 
/* Undo inits in case of errors */
ERROR_SC_3:
i2c_detach_client(data->lm75[0]);
ERROR_SC_2:
if (NULL != data->lm75[1])
kfree(data->lm75[1]);
ERROR_SC_1:
if (NULL != data->lm75[0])
kfree(data->lm75[0]);
ERROR_SC_0:
return err;
}
 
static int
w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i = 0, val1 = 0, val2;
struct i2c_client *new_client;
struct w83781d_data *data;
int err;
const char *client_name = "";
int is_isa = i2c_is_isa_adapter(adapter);
enum vendor { winbond, asus } vendid;
 
if (!is_isa
&& !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
err = -EINVAL;
goto ERROR0;
}
 
if (is_isa)
if (!request_region(address, W83781D_EXTENT, "w83781d")) {
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 w83781d_{read,write}_value. */
 
if (!(new_client = kmalloc(sizeof (struct i2c_client) +
sizeof (struct w83781d_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
 
memset(new_client, 0x00, sizeof (struct i2c_client) +
sizeof (struct w83781d_data));
 
data = (struct w83781d_data *) (new_client + 1);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
init_MUTEX(&data->lock);
new_client->adapter = adapter;
new_client->driver = &w83781d_driver;
new_client->flags = 0;
 
/* Now, we do the remaining detection. */
 
/* The w8378?d may be stuck in some other bank than bank 0. This may
make reading other information impossible. Specify a force=... or
force_*=... parameter, and the Winbond will be reset to the right
bank. */
if (kind < 0) {
if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80){
err = -ENODEV;
goto ERROR2;
}
val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
/* Check for Winbond or Asus ID if in bank 0 */
if ((!(val1 & 0x07)) &&
(((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)
&& (val2 != 0x94))
|| ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)
&& (val2 != 0x06)))) {
err = -ENODEV;
goto ERROR2;
}
/* If Winbond SMBus, check address at 0x48.
Asus doesn't support */
if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
((val1 & 0x80) && (val2 == 0x5c)))) {
if (w83781d_read_value
(new_client, W83781D_REG_I2C_ADDR) != address) {
err = -ENODEV;
goto ERROR2;
}
}
}
 
/* We have either had a force parameter, or we have already detected the
Winbond. Put it now into bank 0 and Vendor ID High Byte */
w83781d_write_value(new_client, W83781D_REG_BANK,
(w83781d_read_value(new_client,
W83781D_REG_BANK) & 0x78) |
0x80);
 
/* Determine the chip type. */
if (kind <= 0) {
/* get vendor ID */
val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
if (val2 == 0x5c)
vendid = winbond;
else if ((val2 == 0x12) || (val2 == 0x06))
vendid = asus;
else {
err = -ENODEV;
goto ERROR2;
}
 
/* mask off lower bit, not reliable */
val1 =
w83781d_read_value(new_client, W83781D_REG_WCHIPID) & 0xfe;
if (val1 == 0x10 && vendid == winbond)
kind = w83781d;
else if (val1 == 0x30 && vendid == winbond)
kind = w83782d;
else if (val1 == 0x40 && vendid == winbond && !is_isa)
kind = w83783s;
else if ((val1 == 0x20 || val1 == 0x90) && vendid == winbond)
kind = w83627hf;
else if (val1 == 0x30 && vendid == asus && !is_isa)
kind = as99127f;
else if (val1 == 0x60 && vendid == winbond && is_isa)
kind = w83697hf;
else {
if (kind == 0)
dev_warn(&new_client->dev,
"Ignoring 'force' parameter for unknown chip at"
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
err = -EINVAL;
goto ERROR2;
}
}
 
if (kind == w83781d) {
client_name = "w83781d";
} else if (kind == w83782d) {
client_name = "w83782d";
} else if (kind == w83783s) {
client_name = "w83783s";
} else if (kind == w83627hf) {
if (val1 == 0x90)
client_name = "w83627thf";
else
client_name = "w83627hf";
} else if (kind == as99127f) {
client_name = "as99127f";
} else if (kind == w83697hf) {
client_name = "w83697hf";
} else {
dev_err(&new_client->dev, "Internal error: unknown "
"kind (%d)?!?", kind);
err = -ENODEV;
goto ERROR2;
}
 
/* 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;
 
/* attach secondary i2c lm75-like clients */
if (!is_isa) {
if ((err = w83781d_detect_subclients(adapter, address,
kind, new_client)))
goto ERROR3;
} else {
data->lm75[0] = NULL;
data->lm75[1] = NULL;
}
 
/* Initialize the chip */
w83781d_init_client(new_client);
 
/* Register sysfs hooks */
device_create_file_in(new_client, 0);
if (kind != w83783s && kind != w83697hf)
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_in(new_client, 3);
device_create_file_in(new_client, 4);
device_create_file_in(new_client, 5);
device_create_file_in(new_client, 6);
if (kind != as99127f && kind != w83781d && kind != w83783s) {
device_create_file_in(new_client, 7);
device_create_file_in(new_client, 8);
}
 
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
if (kind != w83697hf)
device_create_file_fan(new_client, 3);
 
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
if (kind != w83783s && kind != w83697hf)
device_create_file_temp(new_client, 3);
 
if (kind != w83697hf)
device_create_file_vid(new_client);
 
if (kind != w83697hf)
device_create_file_vrm(new_client);
 
device_create_file_fan_div(new_client, 1);
device_create_file_fan_div(new_client, 2);
if (kind != w83697hf)
device_create_file_fan_div(new_client, 3);
 
device_create_file_alarms(new_client);
 
device_create_file_beep(new_client);
 
if (kind != w83781d) {
device_create_file_pwm(new_client, 1);
device_create_file_pwm(new_client, 2);
device_create_file_pwmenable(new_client, 2);
}
if (kind == w83782d && !is_isa) {
device_create_file_pwm(new_client, 3);
device_create_file_pwm(new_client, 4);
}
 
if (kind != as99127f && kind != w83781d) {
device_create_file_sensor(new_client, 1);
device_create_file_sensor(new_client, 2);
if (kind != w83783s && kind != w83697hf)
device_create_file_sensor(new_client, 3);
}
#ifdef W83781D_RT
if (kind == w83781d) {
device_create_file_rt(new_client, 1);
device_create_file_rt(new_client, 2);
device_create_file_rt(new_client, 3);
}
#endif
 
return 0;
 
ERROR3:
i2c_detach_client(new_client);
ERROR2:
kfree(new_client);
ERROR1:
if (is_isa)
release_region(address, W83781D_EXTENT);
ERROR0:
return err;
}
 
static int
w83781d_detach_client(struct i2c_client *client)
{
int err;
 
if (i2c_is_isa_client(client))
release_region(client->addr, W83781D_EXTENT);
 
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev,
"Client deregistration failed, client not detached.\n");
return err;
}
 
kfree(client);
 
return 0;
}
 
/* The SMBus locks itself, usually, but nothing may access the Winbond between
bank switches. ISA access must always be locked explicitly!
We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
would slow down the W83781D access and should not be necessary.
There are some ugly typecasts here, but the good news is - they should
nowhere else be necessary! */
static int
w83781d_read_value(struct i2c_client *client, u16 reg)
{
struct w83781d_data *data = i2c_get_clientdata(client);
int res, word_sized, bank;
struct i2c_client *cl;
 
down(&data->lock);
if (i2c_is_isa_client(client)) {
word_sized = (((reg & 0xff00) == 0x100)
|| ((reg & 0xff00) == 0x200))
&& (((reg & 0x00ff) == 0x50)
|| ((reg & 0x00ff) == 0x53)
|| ((reg & 0x00ff) == 0x55));
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
if (word_sized) {
outb_p((reg & 0xff) + 1,
client->addr + W83781D_ADDR_REG_OFFSET);
res =
(res << 8) + inb_p(client->addr +
W83781D_DATA_REG_OFFSET);
}
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
}
} else {
bank = (reg >> 8) & 0x0f;
if (bank > 2)
/* switch banks */
i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
bank);
if (bank == 0 || bank > 2) {
res = i2c_smbus_read_byte_data(client, reg & 0xff);
} else {
/* switch to subclient */
cl = data->lm75[bank - 1];
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x50: /* TEMP */
res =
swap_bytes(i2c_smbus_read_word_data(cl, 0));
break;
case 0x52: /* CONFIG */
res = i2c_smbus_read_byte_data(cl, 1);
break;
case 0x53: /* HYST */
res =
swap_bytes(i2c_smbus_read_word_data(cl, 2));
break;
case 0x55: /* OVER */
default:
res =
swap_bytes(i2c_smbus_read_word_data(cl, 3));
break;
}
}
if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
}
up(&data->lock);
return res;
}
 
static int
w83781d_write_value(struct i2c_client *client, u16 reg, u16 value)
{
struct w83781d_data *data = i2c_get_clientdata(client);
int word_sized, bank;
struct i2c_client *cl;
 
down(&data->lock);
if (i2c_is_isa_client(client)) {
word_sized = (((reg & 0xff00) == 0x100)
|| ((reg & 0xff00) == 0x200))
&& (((reg & 0x00ff) == 0x53)
|| ((reg & 0x00ff) == 0x55));
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(reg >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
}
outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
if (word_sized) {
outb_p(value >> 8,
client->addr + W83781D_DATA_REG_OFFSET);
outb_p((reg & 0xff) + 1,
client->addr + W83781D_ADDR_REG_OFFSET);
}
outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET);
if (reg & 0xff00) {
outb_p(W83781D_REG_BANK,
client->addr + W83781D_ADDR_REG_OFFSET);
outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
}
} else {
bank = (reg >> 8) & 0x0f;
if (bank > 2)
/* switch banks */
i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
bank);
if (bank == 0 || bank > 2) {
i2c_smbus_write_byte_data(client, reg & 0xff,
value & 0xff);
} else {
/* switch to subclient */
cl = data->lm75[bank - 1];
/* convert from ISA to LM75 I2C addresses */
switch (reg & 0xff) {
case 0x52: /* CONFIG */
i2c_smbus_write_byte_data(cl, 1, value & 0xff);
break;
case 0x53: /* HYST */
i2c_smbus_write_word_data(cl, 2,
swap_bytes(value));
break;
case 0x55: /* OVER */
i2c_smbus_write_word_data(cl, 3,
swap_bytes(value));
break;
}
}
if (bank > 2)
i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
}
up(&data->lock);
return 0;
}
 
/* Called when we have found a new W83781D. It should set limits, etc. */
static void
w83781d_init_client(struct i2c_client *client)
{
struct w83781d_data *data = i2c_get_clientdata(client);
int vid = 0, i, p;
int type = data->type;
u8 tmp;
 
if (init && type != as99127f) { /* this resets registers we don't have
documentation for on the as99127f */
/* save these registers */
i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
p = w83781d_read_value(client, W83781D_REG_PWMCLK12);
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
w83781d_write_value(client, W83781D_REG_CONFIG, 0x80);
/* Restore the registers and disable power-on abnormal beep.
This saves FAN 1/2/3 input/output values set by BIOS. */
w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80);
w83781d_write_value(client, W83781D_REG_PWMCLK12, p);
/* Disable master beep-enable (reset turns it on).
Individual beep_mask should be reset to off but for some reason
disabling this bit helps some people not get beeped */
w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0);
}
 
if (type != w83697hf) {
vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f;
vid |=
(w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) <<
4;
data->vrm = DEFAULT_VRM;
vid = vid_from_reg(vid, data->vrm);
}
 
if ((type != w83781d) && (type != as99127f)) {
tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
for (i = 1; i <= 3; i++) {
if (!(tmp & BIT_SCFG1[i - 1])) {
data->sens[i - 1] = W83781D_DEFAULT_BETA;
} else {
if (w83781d_read_value
(client,
W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
data->sens[i - 1] = 1;
else
data->sens[i - 1] = 2;
}
if ((type == w83783s || type == w83697hf) && (i == 2))
break;
}
}
#ifdef W83781D_RT
/*
Fill up the RT Tables.
We assume that they are 32 bytes long, in order for temp 1-3.
Data sheet documentation is sparse.
We also assume that it is only for the 781D although I suspect
that the others support it as well....
*/
 
if (init && type == w83781d) {
u16 k = 0;
/*
Auto-indexing doesn't seem to work...
w83781d_write_value(client,W83781D_REG_RT_IDX,0);
*/
for (i = 0; i < 3; i++) {
int j;
for (j = 0; j < 32; j++) {
w83781d_write_value(client,
W83781D_REG_RT_IDX, k++);
data->rt[i][j] =
w83781d_read_value(client,
W83781D_REG_RT_VAL);
}
}
}
#endif /* W83781D_RT */
 
if (init) {
w83781d_write_value(client, W83781D_REG_IN_MIN(0),
IN_TO_REG(W83781D_INIT_IN_MIN_0));
w83781d_write_value(client, W83781D_REG_IN_MAX(0),
IN_TO_REG(W83781D_INIT_IN_MAX_0));
if (type != w83783s && type != w83697hf) {
w83781d_write_value(client, W83781D_REG_IN_MIN(1),
IN_TO_REG(W83781D_INIT_IN_MIN_1));
w83781d_write_value(client, W83781D_REG_IN_MAX(1),
IN_TO_REG(W83781D_INIT_IN_MAX_1));
}
 
w83781d_write_value(client, W83781D_REG_IN_MIN(2),
IN_TO_REG(W83781D_INIT_IN_MIN_2));
w83781d_write_value(client, W83781D_REG_IN_MAX(2),
IN_TO_REG(W83781D_INIT_IN_MAX_2));
w83781d_write_value(client, W83781D_REG_IN_MIN(3),
IN_TO_REG(W83781D_INIT_IN_MIN_3));
w83781d_write_value(client, W83781D_REG_IN_MAX(3),
IN_TO_REG(W83781D_INIT_IN_MAX_3));
w83781d_write_value(client, W83781D_REG_IN_MIN(4),
IN_TO_REG(W83781D_INIT_IN_MIN_4));
w83781d_write_value(client, W83781D_REG_IN_MAX(4),
IN_TO_REG(W83781D_INIT_IN_MAX_4));
if (type == w83781d || type == as99127f) {
w83781d_write_value(client, W83781D_REG_IN_MIN(5),
IN_TO_REG(W83781D_INIT_IN_MIN_5));
w83781d_write_value(client, W83781D_REG_IN_MAX(5),
IN_TO_REG(W83781D_INIT_IN_MAX_5));
} else {
w83781d_write_value(client, W83781D_REG_IN_MIN(5),
IN_TO_REG(W83782D_INIT_IN_MIN_5));
w83781d_write_value(client, W83781D_REG_IN_MAX(5),
IN_TO_REG(W83782D_INIT_IN_MAX_5));
}
if (type == w83781d || type == as99127f) {
w83781d_write_value(client, W83781D_REG_IN_MIN(6),
IN_TO_REG(W83781D_INIT_IN_MIN_6));
w83781d_write_value(client, W83781D_REG_IN_MAX(6),
IN_TO_REG(W83781D_INIT_IN_MAX_6));
} else {
w83781d_write_value(client, W83781D_REG_IN_MIN(6),
IN_TO_REG(W83782D_INIT_IN_MIN_6));
w83781d_write_value(client, W83781D_REG_IN_MAX(6),
IN_TO_REG(W83782D_INIT_IN_MAX_6));
}
if ((type == w83782d) || (type == w83627hf) ||
(type == w83697hf)) {
w83781d_write_value(client, W83781D_REG_IN_MIN(7),
IN_TO_REG(W83781D_INIT_IN_MIN_7));
w83781d_write_value(client, W83781D_REG_IN_MAX(7),
IN_TO_REG(W83781D_INIT_IN_MAX_7));
w83781d_write_value(client, W83781D_REG_IN_MIN(8),
IN_TO_REG(W83781D_INIT_IN_MIN_8));
w83781d_write_value(client, W83781D_REG_IN_MAX(8),
IN_TO_REG(W83781D_INIT_IN_MAX_8));
w83781d_write_value(client, W83781D_REG_VBAT,
(w83781d_read_value
(client,
W83781D_REG_VBAT) | 0x01));
}
w83781d_write_value(client, W83781D_REG_FAN_MIN(1),
FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2));
w83781d_write_value(client, W83781D_REG_FAN_MIN(2),
FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2));
if (type != w83697hf) {
w83781d_write_value(client, W83781D_REG_FAN_MIN(3),
FAN_TO_REG(W83781D_INIT_FAN_MIN_3,
2));
}
 
w83781d_write_value(client, W83781D_REG_TEMP_OVER(1),
TEMP_TO_REG(W83781D_INIT_TEMP_OVER));
w83781d_write_value(client, W83781D_REG_TEMP_HYST(1),
TEMP_TO_REG(W83781D_INIT_TEMP_HYST));
 
if (type == as99127f) {
w83781d_write_value(client, W83781D_REG_TEMP_OVER(2),
AS99127_TEMP_ADD_TO_REG
(W83781D_INIT_TEMP2_OVER));
w83781d_write_value(client, W83781D_REG_TEMP_HYST(2),
AS99127_TEMP_ADD_TO_REG
(W83781D_INIT_TEMP2_HYST));
} else {
w83781d_write_value(client, W83781D_REG_TEMP_OVER(2),
TEMP_ADD_TO_REG
(W83781D_INIT_TEMP2_OVER));
w83781d_write_value(client, W83781D_REG_TEMP_HYST(2),
TEMP_ADD_TO_REG
(W83781D_INIT_TEMP2_HYST));
}
w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00);
 
if (type == as99127f) {
w83781d_write_value(client, W83781D_REG_TEMP_OVER(3),
AS99127_TEMP_ADD_TO_REG
(W83781D_INIT_TEMP3_OVER));
w83781d_write_value(client, W83781D_REG_TEMP_HYST(3),
AS99127_TEMP_ADD_TO_REG
(W83781D_INIT_TEMP3_HYST));
} else if (type != w83783s && type != w83697hf) {
w83781d_write_value(client, W83781D_REG_TEMP_OVER(3),
TEMP_ADD_TO_REG
(W83781D_INIT_TEMP3_OVER));
w83781d_write_value(client, W83781D_REG_TEMP_HYST(3),
TEMP_ADD_TO_REG
(W83781D_INIT_TEMP3_HYST));
}
if (type != w83783s && type != w83697hf) {
w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG,
0x00);
}
if (type != w83781d) {
/* enable comparator mode for temp2 and temp3 so
alarm indication will work correctly */
w83781d_write_value(client, W83781D_REG_IRQ, 0x41);
for (i = 0; i < 3; i++)
data->pwmenable[i] = 1;
}
}
 
/* Start monitoring */
w83781d_write_value(client, W83781D_REG_CONFIG,
(w83781d_read_value(client,
W83781D_REG_CONFIG) & 0xf7)
| 0x01);
}
 
static void
w83781d_update_client(struct i2c_client *client)
{
struct w83781d_data *data = i2c_get_clientdata(client);
int i;
 
down(&data->update_lock);
 
if (time_after
(jiffies - data->last_updated, (unsigned long) (HZ + HZ / 2))
|| time_before(jiffies, data->last_updated) || !data->valid) {
pr_debug(KERN_DEBUG "Starting device update\n");
 
for (i = 0; i <= 8; i++) {
if ((data->type == w83783s || data->type == w83697hf)
&& (i == 1))
continue; /* 783S has no in1 */
data->in[i] =
w83781d_read_value(client, W83781D_REG_IN(i));
data->in_min[i] =
w83781d_read_value(client, W83781D_REG_IN_MIN(i));
data->in_max[i] =
w83781d_read_value(client, W83781D_REG_IN_MAX(i));
if ((data->type != w83782d) && (data->type != w83697hf)
&& (data->type != w83627hf) && (i == 6))
break;
}
for (i = 1; i <= 3; i++) {
data->fan[i - 1] =
w83781d_read_value(client, W83781D_REG_FAN(i));
data->fan_min[i - 1] =
w83781d_read_value(client, W83781D_REG_FAN_MIN(i));
}
if (data->type != w83781d) {
for (i = 1; i <= 4; i++) {
data->pwm[i - 1] =
w83781d_read_value(client,
W83781D_REG_PWM(i));
if (((data->type == w83783s)
|| (data->type == w83627hf)
|| (data->type == as99127f)
|| (data->type == w83697hf)
|| ((data->type == w83782d)
&& i2c_is_isa_client(client)))
&& i == 2)
break;
}
}
 
data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1));
data->temp_min =
w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
data->temp_max =
w83781d_read_value(client, W83781D_REG_TEMP_HYST(1));
data->temp_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP(2));
data->temp_max_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP_OVER(2));
data->temp_min_add[0] =
w83781d_read_value(client, W83781D_REG_TEMP_HYST(2));
if (data->type != w83783s && data->type != w83697hf) {
data->temp_add[1] =
w83781d_read_value(client, W83781D_REG_TEMP(3));
data->temp_max_add[1] =
w83781d_read_value(client,
W83781D_REG_TEMP_OVER(3));
data->temp_min_add[1] =
w83781d_read_value(client,
W83781D_REG_TEMP_HYST(3));
}
i = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
if (data->type != w83697hf) {
data->vid = i & 0x0f;
data->vid |=
(w83781d_read_value(client, W83781D_REG_CHIPID) &
0x01)
<< 4;
}
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = (i >> 6) & 0x03;
if (data->type != w83697hf) {
data->fan_div[2] = (w83781d_read_value(client,
W83781D_REG_PIN)
>> 6) & 0x03;
}
if ((data->type != w83781d) && (data->type != as99127f)) {
i = w83781d_read_value(client, W83781D_REG_VBAT);
data->fan_div[0] |= (i >> 3) & 0x04;
data->fan_div[1] |= (i >> 4) & 0x04;
if (data->type != w83697hf)
data->fan_div[2] |= (i >> 5) & 0x04;
}
data->alarms =
w83781d_read_value(client,
W83781D_REG_ALARM1) +
(w83781d_read_value(client, W83781D_REG_ALARM2) << 8);
if ((data->type == w83782d) || (data->type == w83627hf)) {
data->alarms |=
w83781d_read_value(client,
W83781D_REG_ALARM3) << 16;
}
i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2);
data->beep_enable = i >> 7;
data->beep_mask = ((i & 0x7f) << 8) +
w83781d_read_value(client, W83781D_REG_BEEP_INTS1);
if ((data->type != w83781d) && (data->type != as99127f)) {
data->beep_mask |=
w83781d_read_value(client,
W83781D_REG_BEEP_INTS3) << 16;
}
data->last_updated = jiffies;
data->valid = 1;
}
 
up(&data->update_lock);
}
 
static int __init
sensors_w83781d_init(void)
{
return i2c_add_driver(&w83781d_driver);
}
 
static void __exit
sensors_w83781d_exit(void)
{
i2c_del_driver(&w83781d_driver);
}
 
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Mark Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("W83781D driver");
MODULE_LICENSE("GPL");
 
module_init(sensors_w83781d_init);
module_exit(sensors_w83781d_exit);
/shark/trunk/drivers/i2c/chips/adm1021.c
0,0 → 1,443
/*
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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
 
 
/* Registers */
#define ADM1021_SYSCTL_TEMP 1200
#define ADM1021_SYSCTL_REMOTE_TEMP 1201
#define ADM1021_SYSCTL_DIE_CODE 1202
#define ADM1021_SYSCTL_ALARMS 1203
 
#define ADM1021_ALARM_TEMP_HIGH 0x40
#define ADM1021_ALARM_TEMP_LOW 0x20
#define ADM1021_ALARM_RTEMP_HIGH 0x10
#define ADM1021_ALARM_RTEMP_LOW 0x08
#define ADM1021_ALARM_RTEMP_NA 0x04
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
0x4c, 0x4e, I2C_CLIENT_END
};
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_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 */
#define adm1021_INIT_TOS 60
#define adm1021_INIT_THYST 20
#define adm1021_INIT_REMOTE_TOS 60
#define adm1021_INIT_REMOTE_THYST 20
 
/* Each client has this additional data */
struct adm1021_data {
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 void adm1021_update_client(struct i2c_client *client);
 
/* (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-MAX1617",
.id = I2C_DRIVERID_ADM1021,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1021_attach_adapter,
.detach_client = adm1021_detach_client,
};
 
/* I choose here for semi-static allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructure takes now. */
static int adm1021_id = 0;
 
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm1021_data *data = i2c_get_clientdata(client); \
int temp; \
\
adm1021_update_client(client); \
temp = TEMP_FROM_REG(data->value); \
return sprintf(buf, "%d\n", temp); \
}
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 i2c_client *client = to_i2c_client(dev); \
struct adm1021_data *data = i2c_get_clientdata(client); \
\
adm1021_update_client(client); \
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); \
\
data->value = TEMP_TO_REG(temp); \
adm1021_write_value(client, reg, data->value); \
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(temp_max1, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp_min1, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input, NULL);
static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max);
static DEVICE_ATTR(temp_min2, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst);
static DEVICE_ATTR(temp_input2, 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_ADAP_CLASS_SMBUS))
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 (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct adm1021_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto error0;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct adm1021_data));
 
data = (struct adm1021_data *) (new_client + 1);
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)
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;
/* LM84 Mfr ID in a different place */
else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
kind = lm84;
else if (i == 0x54)
kind = mc1066;
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";
} else {
dev_err(&adapter->dev, "Internal error: unknown kind (%d)?!?",
kind);
goto error1;
}
 
/* 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;
 
new_client->id = adm1021_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
 
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto error3;
 
/* Initialize the ADM1021 chip */
adm1021_init_client(new_client);
 
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_min1);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_max2);
device_create_file(&new_client->dev, &dev_attr_temp_min2);
device_create_file(&new_client->dev, &dev_attr_temp_input2);
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;
 
error3:
error1:
kfree(new_client);
error0:
return err;
}
 
static void adm1021_init_client(struct i2c_client *client)
{
/* Initialize the adm1021 chip */
adm1021_write_value(client, ADM1021_REG_TOS_W,
adm1021_INIT_TOS);
adm1021_write_value(client, ADM1021_REG_THYST_W,
adm1021_INIT_THYST);
adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W,
adm1021_INIT_REMOTE_TOS);
adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W,
adm1021_INIT_REMOTE_THYST);
/* Enable ADC and disable suspend mode */
adm1021_write_value(client, ADM1021_REG_CONFIG_W, 0);
/* 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(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 void adm1021_update_client(struct i2c_client *client)
{
struct adm1021_data *data = i2c_get_clientdata(client);
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !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) & 0xec;
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);
}
 
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_PARM(read_only, "i");
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
 
module_init(sensors_adm1021_init)
module_exit(sensors_adm1021_exit)
/shark/trunk/drivers/i2c/chips/eeprom.c
0,0 → 1,276
/*
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.
 
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 */
 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x50, 0x57, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_1(eeprom);
 
static int checksum = 0;
MODULE_PARM(checksum, "i");
MODULE_PARM_DESC(checksum, "Only accept eeproms whose checksum is correct");
 
 
/* EEPROM registers */
#define EEPROM_REG_CHECKSUM 0x3f
 
/* 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 semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 data[EEPROM_SIZE]; /* Register values */
};
 
 
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 int eeprom_id = 0;
 
static void eeprom_update_client(struct i2c_client *client)
{
struct eeprom_data *data = i2c_get_clientdata(client);
int i, j;
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > 300 * HZ) |
(jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting eeprom update\n");
 
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
for (i=0; i < EEPROM_SIZE; 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, 0)) {
dev_dbg(&client->dev, "eeprom read start has failed!\n");
goto exit;
}
for (i = 0; i < EEPROM_SIZE; i++) {
j = i2c_smbus_read_byte(client);
if (j < 0)
goto exit;
data->data[i] = (u8) j;
}
}
data->last_updated = jiffies;
data->valid = 1;
}
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);
 
eeprom_update_client(client);
 
if (off > EEPROM_SIZE)
return 0;
if (off + count > EEPROM_SIZE)
count = EEPROM_SIZE - off;
 
memcpy(buf, &data->data[off], count);
return count;
}
 
static struct bin_attribute eeprom_attr = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
},
.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)
{
int i, cs;
struct i2c_client *new_client;
struct eeprom_data *data;
enum eeprom_nature nature = UNKNOWN;
int err = 0;
 
/* 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, " eeprom_detect called for an ISA bus adapter?!?\n");
return 0;
}
#endif
 
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 eeprom_{read,write}_value. */
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct eeprom_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct eeprom_data));
 
data = (struct eeprom_data *) (new_client + 1);
memset(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;
 
/* Now, we do the remaining detection. It is not there, unless you force
the checksum to work out. */
if (checksum) {
/* prevent 24RF08 corruption */
i2c_smbus_write_quick(new_client, 0);
cs = 0;
for (i = 0; i <= 0x3e; i++)
cs += i2c_smbus_read_byte_data(new_client, i);
cs &= 0xff;
if (i2c_smbus_read_byte_data (new_client, EEPROM_REG_CHECKSUM) != cs)
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_data(new_client, 0x81) == 'C' &&
i2c_smbus_read_byte_data(new_client, 0x82) == 'G' &&
i2c_smbus_read_byte_data(new_client, 0x83) == '-')
nature = VAIO;
}
 
/* If this is a VIAO, then we only allow root to read from this file,
as BIOS passwords can be present here in plaintext */
switch (nature) {
case VAIO:
eeprom_attr.attr.mode = S_IRUSR;
break;
default:
eeprom_attr.attr.mode = S_IRUGO;
}
 
/* Fill in the remaining client fields */
strncpy(new_client->name, "eeprom", I2C_NAME_SIZE);
new_client->id = eeprom_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
 
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
 
/* create the sysfs eeprom file */
sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
 
return 0;
 
exit_kfree:
kfree(new_client);
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(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);
/shark/trunk/drivers/i2c/chips/lm78.c
0,0 → 1,896
/*
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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/io.h>
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { 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;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
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 u8 TEMP_TO_REG(int val)
{
int nval = SENSORS_LIMIT(val, -128000, 127000) ;
return nval<0 ? (nval-500)/1000+0x100 : (nval+500)/1000;
}
 
static inline int TEMP_FROM_REG(u8 val)
{
return (val>=0x80 ? val-0x100 : 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;
}
 
/* ALARMS: chip-specific bitmask
REG: (same) */
#define ALARMS_FROM_REG(val) (val)
 
/* 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))
 
/* Initial limits. To keep them sane, we use the 'standard' translation as
specified in the LM78 sheet. Use the config file to set better limits. */
#define LM78_INIT_IN_0(vid) ((vid)==3500 ? 2800 : (vid))
#define LM78_INIT_IN_1(vid) ((vid)==3500 ? 2800 : (vid))
#define LM78_INIT_IN_2 3300
#define LM78_INIT_IN_3 (((5000) * 100)/168)
#define LM78_INIT_IN_4 (((12000) * 10)/38)
#define LM78_INIT_IN_5 (((-12000) * -604)/2100)
#define LM78_INIT_IN_6 (((-5000) * -604)/909)
 
#define LM78_INIT_IN_PERCENTAGE 10
 
#define LM78_INIT_IN_MIN_0(vid) (LM78_INIT_IN_0(vid) - \
LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_0(vid) (LM78_INIT_IN_0(vid) + \
LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_1(vid) (LM78_INIT_IN_1(vid) - \
LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_1(vid) (LM78_INIT_IN_1(vid) + \
LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100)
 
#define LM78_INIT_IN_MIN_2 \
(LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_2 \
(LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_3 \
(LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_3 \
(LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_4 \
(LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_4 \
(LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_5 \
(LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_5 \
(LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_6 \
(LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_6 \
(LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
 
#define LM78_INIT_FAN_MIN_1 3000
#define LM78_INIT_FAN_MIN_2 3000
#define LM78_INIT_FAN_MIN_3 3000
 
#define LM78_INIT_TEMP_OVER 60000
#define LM78_INIT_TEMP_HYST 50000
 
/* 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 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 */
u8 temp; /* Register value */
u8 temp_over; /* Register value */
u8 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 void lm78_update_client(struct i2c_client *client);
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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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);
data->in_min[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]);
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);
data->in_max[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, 0x##offset); \
} \
static DEVICE_ATTR(in_input##offset, S_IRUGO, \
show_in##offset, NULL) \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, 0x##offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, 0x##offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, 0x##offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min) \
static DEVICE_ATTR(in_max##offset, 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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
 
static ssize_t show_temp_over(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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);
data->temp_over = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over);
return count;
}
 
static ssize_t show_temp_hyst(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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);
data->temp_hyst = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst);
return count;
}
 
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL)
static DEVICE_ATTR(temp_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over)
static DEVICE_ATTR(temp_min, 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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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);
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]);
return count;
}
 
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
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 min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
unsigned long val = simple_strtoul(buf, NULL, 10);
int reg = lm78_read_value(client, LM78_REG_VID_FANDIV);
data->fan_div[nr] = DIV_TO_REG(val);
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]);
return count;
}
 
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \
static DEVICE_ATTR(fan_min##offset, 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(fan_div1, S_IRUGO | S_IWUSR,
show_fan_1_div, set_fan_1_div)
static DEVICE_ATTR(fan_div2, S_IRUGO | S_IWUSR,
show_fan_2_div, set_fan_2_div)
static DEVICE_ATTR(fan_div3, S_IRUGO, show_fan_3_div, NULL)
 
/* VID */
static ssize_t show_vid(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
return sprintf(buf, "%d\n", VID_FROM_REG(data->vid));
}
static DEVICE_ATTR(vid, S_IRUGO, show_vid, NULL);
 
/* Alarms */
static ssize_t show_alarms(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
lm78_update_client(client);
return sprintf(buf, "%d\n", ALARMS_FROM_REG(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_ADAP_CLASS_SMBUS))
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")) {
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 (!(new_client = kmalloc((sizeof(struct i2c_client)) +
sizeof(struct lm78_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
memset(new_client, 0, sizeof(struct i2c_client) +
sizeof(struct lm78_data));
 
data = (struct lm78_data *) (new_client + 1);
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)
printk(KERN_WARNING "lm78.o: 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";
} else {
dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?",
kind);
err = -ENODEV;
goto ERROR2;
}
 
/* 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);
 
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in_input0);
device_create_file(&new_client->dev, &dev_attr_in_min0);
device_create_file(&new_client->dev, &dev_attr_in_max0);
device_create_file(&new_client->dev, &dev_attr_in_input1);
device_create_file(&new_client->dev, &dev_attr_in_min1);
device_create_file(&new_client->dev, &dev_attr_in_max1);
device_create_file(&new_client->dev, &dev_attr_in_input2);
device_create_file(&new_client->dev, &dev_attr_in_min2);
device_create_file(&new_client->dev, &dev_attr_in_max2);
device_create_file(&new_client->dev, &dev_attr_in_input3);
device_create_file(&new_client->dev, &dev_attr_in_min3);
device_create_file(&new_client->dev, &dev_attr_in_max3);
device_create_file(&new_client->dev, &dev_attr_in_input4);
device_create_file(&new_client->dev, &dev_attr_in_min4);
device_create_file(&new_client->dev, &dev_attr_in_max4);
device_create_file(&new_client->dev, &dev_attr_in_input5);
device_create_file(&new_client->dev, &dev_attr_in_min5);
device_create_file(&new_client->dev, &dev_attr_in_max5);
device_create_file(&new_client->dev, &dev_attr_in_input6);
device_create_file(&new_client->dev, &dev_attr_in_min6);
device_create_file(&new_client->dev, &dev_attr_in_max6);
device_create_file(&new_client->dev, &dev_attr_temp_input);
device_create_file(&new_client->dev, &dev_attr_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp_max);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
device_create_file(&new_client->dev, &dev_attr_fan_input3);
device_create_file(&new_client->dev, &dev_attr_fan_min3);
device_create_file(&new_client->dev, &dev_attr_fan_div3);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_vid);
 
return 0;
 
ERROR2:
kfree(new_client);
ERROR1:
if (is_isa)
release_region(address, LM78_EXTENT);
ERROR0:
return err;
}
 
static int lm78_detach_client(struct i2c_client *client)
{
int err;
 
/* release ISA region first */
if(i2c_is_isa_client(client))
release_region(client->addr, LM78_EXTENT);
 
/* now it's safe to scrap the rest */
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev,
"Client deregistration failed, client not detached.\n");
return err;
}
 
kfree(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.
There are some ugly typecasts here, but the good new is - they should
nowhere else 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)
{
struct lm78_data *data = i2c_get_clientdata(client);
int vid;
 
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
lm78_write_value(client, LM78_REG_CONFIG, 0x80);
 
vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f;
if (data->type == lm79)
vid |=
(lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4;
else
vid |= 0x10;
vid = VID_FROM_REG(vid);
 
lm78_write_value(client, LM78_REG_IN_MIN(0),
IN_TO_REG(LM78_INIT_IN_MIN_0(vid)));
lm78_write_value(client, LM78_REG_IN_MAX(0),
IN_TO_REG(LM78_INIT_IN_MAX_0(vid)));
lm78_write_value(client, LM78_REG_IN_MIN(1),
IN_TO_REG(LM78_INIT_IN_MIN_1(vid)));
lm78_write_value(client, LM78_REG_IN_MAX(1),
IN_TO_REG(LM78_INIT_IN_MAX_1(vid)));
lm78_write_value(client, LM78_REG_IN_MIN(2),
IN_TO_REG(LM78_INIT_IN_MIN_2));
lm78_write_value(client, LM78_REG_IN_MAX(2),
IN_TO_REG(LM78_INIT_IN_MAX_2));
lm78_write_value(client, LM78_REG_IN_MIN(3),
IN_TO_REG(LM78_INIT_IN_MIN_3));
lm78_write_value(client, LM78_REG_IN_MAX(3),
IN_TO_REG(LM78_INIT_IN_MAX_3));
lm78_write_value(client, LM78_REG_IN_MIN(4),
IN_TO_REG(LM78_INIT_IN_MIN_4));
lm78_write_value(client, LM78_REG_IN_MAX(4),
IN_TO_REG(LM78_INIT_IN_MAX_4));
lm78_write_value(client, LM78_REG_IN_MIN(5),
IN_TO_REG(LM78_INIT_IN_MIN_5));
lm78_write_value(client, LM78_REG_IN_MAX(5),
IN_TO_REG(LM78_INIT_IN_MAX_5));
lm78_write_value(client, LM78_REG_IN_MIN(6),
IN_TO_REG(LM78_INIT_IN_MIN_6));
lm78_write_value(client, LM78_REG_IN_MAX(6),
IN_TO_REG(LM78_INIT_IN_MAX_6));
lm78_write_value(client, LM78_REG_FAN_MIN(0),
FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2));
lm78_write_value(client, LM78_REG_FAN_MIN(1),
FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2));
lm78_write_value(client, LM78_REG_FAN_MIN(2),
FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2));
lm78_write_value(client, LM78_REG_TEMP_OVER,
TEMP_TO_REG(LM78_INIT_TEMP_OVER));
lm78_write_value(client, LM78_REG_TEMP_HYST,
TEMP_TO_REG(LM78_INIT_TEMP_HYST));
 
/* Start monitoring */
lm78_write_value(client, LM78_REG_CONFIG,
(lm78_read_value(client, LM78_REG_CONFIG) & 0xf7)
| 0x01);
 
}
 
static void lm78_update_client(struct i2c_client *client)
{
struct lm78_data *data = i2c_get_clientdata(client);
int i;
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !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);
}
 
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);
/shark/trunk/drivers/i2c/chips/it87.c
0,0 → 1,996
/*
it87.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring.
 
Supports: IT8705F Super I/O chip w/LPC interface
IT8712F Super I/O chip w/LPC interface & SMbus
Sis950 A clone of the IT8705F
 
Copyright (c) 2001 Chris Gauthron <chrisg@0-in.com>
Largely inspired by lm78.c of the same package
 
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.
*/
 
/*
djg@pdp8.net David Gesswein 7/18/01
Modified to fix bug with not all alarms enabled.
Added ability to read battery voltage and select temperature sensor
type at module load time.
*/
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/io.h>
 
 
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
 
/* Insmod parameters */
SENSORS_INSMOD_4(it87, it8705, it8712, sis950);
 
 
/* Update battery voltage after every reading if true */
static int update_vbat = 0;
 
 
/* Enable Temp1 as thermal resistor */
/* Enable Temp2 as thermal diode */
/* Enable Temp3 as thermal resistor */
static int temp_type = 0x2a;
 
/* Many IT87 constants specified below */
 
/* Length of ISA address segment */
#define IT87_EXTENT 8
 
/* Where are the ISA address/data registers relative to the base address */
#define IT87_ADDR_REG_OFFSET 5
#define IT87_DATA_REG_OFFSET 6
 
/*----- The IT87 registers -----*/
 
#define IT87_REG_CONFIG 0x00
 
#define IT87_REG_ALARM1 0x01
#define IT87_REG_ALARM2 0x02
#define IT87_REG_ALARM3 0x03
 
#define IT87_REG_VID 0x0a
#define IT87_REG_FAN_DIV 0x0b
 
/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
 
#define IT87_REG_FAN(nr) (0x0d + (nr))
#define IT87_REG_FAN_MIN(nr) (0x10 + (nr))
#define IT87_REG_FAN_CTRL 0x13
 
#define IT87_REG_VIN(nr) (0x20 + (nr))
#define IT87_REG_TEMP(nr) (0x29 + (nr))
 
#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2)
#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2)
#define IT87_REG_TEMP_HIGH(nr) (0x40 + ((nr) * 2))
#define IT87_REG_TEMP_LOW(nr) (0x41 + ((nr) * 2))
 
#define IT87_REG_I2C_ADDR 0x48
 
#define IT87_REG_VIN_ENABLE 0x50
#define IT87_REG_TEMP_ENABLE 0x51
 
#define IT87_REG_CHIPID 0x58
 
#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
#define IN_FROM_REG(val) (((val) * 16) / 10)
 
static inline u8 FAN_TO_REG(long rpm, int 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/((val)*(div)))
 
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
((val)+5)/10),0,255))
#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
 
#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
205-(val)*5)
#define ALARMS_FROM_REG(val) (val)
 
static int log2(int val)
{
int answer = 0;
while ((val >>= 1))
answer++;
return answer;
}
#define DIV_TO_REG(val) log2(val)
#define DIV_FROM_REG(val) (1 << (val))
 
/* Initial limits. Use the config file to set better limits. */
#define IT87_INIT_IN_0 170
#define IT87_INIT_IN_1 250
#define IT87_INIT_IN_2 (330 / 2)
#define IT87_INIT_IN_3 (((500) * 100)/168)
#define IT87_INIT_IN_4 (((1200) * 10)/38)
#define IT87_INIT_IN_5 (((1200) * 10)/72)
#define IT87_INIT_IN_6 (((500) * 10)/56)
#define IT87_INIT_IN_7 (((500) * 100)/168)
 
#define IT87_INIT_IN_PERCENTAGE 10
 
#define IT87_INIT_IN_MIN_0 \
(IT87_INIT_IN_0 - IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_0 \
(IT87_INIT_IN_0 + IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_1 \
(IT87_INIT_IN_1 - IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_1 \
(IT87_INIT_IN_1 + IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_2 \
(IT87_INIT_IN_2 - IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_2 \
(IT87_INIT_IN_2 + IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_3 \
(IT87_INIT_IN_3 - IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_3 \
(IT87_INIT_IN_3 + IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_4 \
(IT87_INIT_IN_4 - IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_4 \
(IT87_INIT_IN_4 + IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_5 \
(IT87_INIT_IN_5 - IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_5 \
(IT87_INIT_IN_5 + IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_6 \
(IT87_INIT_IN_6 - IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_6 \
(IT87_INIT_IN_6 + IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_7 \
(IT87_INIT_IN_7 - IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_7 \
(IT87_INIT_IN_7 + IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100)
 
#define IT87_INIT_FAN_MIN_1 3000
#define IT87_INIT_FAN_MIN_2 3000
#define IT87_INIT_FAN_MIN_3 3000
 
#define IT87_INIT_TEMP_HIGH_1 600
#define IT87_INIT_TEMP_LOW_1 200
#define IT87_INIT_TEMP_HIGH_2 600
#define IT87_INIT_TEMP_LOW_2 200
#define IT87_INIT_TEMP_HIGH_3 600
#define IT87_INIT_TEMP_LOW_3 200
 
/* For each registered IT87, we need to keep some data in memory. That
data is pointed to by it87_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new it87 client is
allocated. */
struct it87_data {
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[9]; /* Register value */
u8 in_max[9]; /* Register value */
u8 in_min[9]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp[3]; /* Register value */
u8 temp_high[3]; /* Register value */
u8 temp_low[3]; /* Register value */
u8 sensor; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u32 alarms; /* Register encoding, combined */
};
 
 
static int it87_attach_adapter(struct i2c_adapter *adapter);
static int it87_detect(struct i2c_adapter *adapter, int address, int kind);
static int it87_detach_client(struct i2c_client *client);
 
static int it87_read_value(struct i2c_client *client, u8 register);
static int it87_write_value(struct i2c_client *client, u8 register,
u8 value);
static void it87_update_client(struct i2c_client *client);
static void it87_init_client(struct i2c_client *client, struct it87_data *data);
 
 
static struct i2c_driver it87_driver = {
.owner = THIS_MODULE,
.name = "IT87xx",
.id = I2C_DRIVERID_IT87,
.flags = I2C_DF_NOTIFY,
.attach_adapter = it87_attach_adapter,
.detach_client = it87_detach_client,
};
 
static int it87_id = 0;
 
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])*10 );
}
 
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])*10 );
}
 
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])*10 );
}
 
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 it87_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10)/10;
data->in_min[nr] = IN_TO_REG(val);
it87_write_value(client, IT87_REG_VIN_MIN(nr),
data->in_min[nr]);
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 it87_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10)/10;
data->in_max[nr] = IN_TO_REG(val);
it87_write_value(client, IT87_REG_VIN_MAX(nr),
data->in_max[nr]);
return count;
}
 
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, char *buf) \
{ \
return show_in(dev, buf, 0x##offset); \
} \
static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in##offset, NULL)
 
#define limit_in_offset(offset) \
static ssize_t \
show_in##offset##_min (struct device *dev, char *buf) \
{ \
return show_in_min(dev, buf, 0x##offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, char *buf) \
{ \
return show_in_max(dev, buf, 0x##offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, 0x##offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, 0x##offset); \
} \
static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min) \
static DEVICE_ATTR(in_max##offset, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max)
 
show_in_offset(0);
limit_in_offset(0);
show_in_offset(1);
limit_in_offset(1);
show_in_offset(2);
limit_in_offset(2);
show_in_offset(3);
limit_in_offset(3);
show_in_offset(4);
limit_in_offset(4);
show_in_offset(5);
limit_in_offset(5);
show_in_offset(6);
limit_in_offset(6);
show_in_offset(7);
limit_in_offset(7);
show_in_offset(8);
 
/* 3 temperatures */
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*100 );
}
/* more like overshoot temperature */
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*100);
}
/* more like hysteresis temperature */
static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])*100);
}
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 it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10)/100;
data->temp_high[nr] = TEMP_TO_REG(val);
it87_write_value(client, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]);
return count;
}
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 it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10)/100;
data->temp_low[nr] = TEMP_TO_REG(val);
it87_write_value(client, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]);
return count;
}
#define show_temp_offset(offset) \
static ssize_t show_temp_##offset (struct device *dev, char *buf) \
{ \
return show_temp(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
show_temp_##offset##_max (struct device *dev, char *buf) \
{ \
return show_temp_max(dev, buf, 0x##offset - 1); \
} \
static ssize_t \
show_temp_##offset##_min (struct device *dev, char *buf) \
{ \
return show_temp_min(dev, buf, 0x##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, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_max, set_temp_##offset##_max) \
static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min)
 
show_temp_offset(1);
show_temp_offset(2);
show_temp_offset(3);
 
/* more like overshoot temperature */
static ssize_t show_sensor(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
if (data->sensor & (1 << nr))
return sprintf(buf, "1\n");
if (data->sensor & (8 << nr))
return sprintf(buf, "2\n");
return sprintf(buf, "0\n");
}
static ssize_t set_sensor(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
 
data->sensor &= ~(1 << nr);
data->sensor &= ~(8 << nr);
if (val == 1)
data->sensor |= 1 << nr;
else if (val == 2)
data->sensor |= 8 << nr;
it87_write_value(client, IT87_REG_TEMP_ENABLE, data->sensor);
return count;
}
#define show_sensor_offset(offset) \
static ssize_t show_sensor_##offset (struct device *dev, char *buf) \
{ \
return show_sensor(dev, buf, 0x##offset - 1); \
} \
static ssize_t set_sensor_##offset (struct device *dev, \
const char *buf, size_t count) \
{ \
return set_sensor(dev, buf, count, 0x##offset - 1); \
} \
static DEVICE_ATTR(sensor##offset, S_IRUGO | S_IWUSR, \
show_sensor_##offset, set_sensor_##offset)
 
show_sensor_offset(1);
show_sensor_offset(2);
show_sensor_offset(3);
 
/* 3 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
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 i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
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 it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]);
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 it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
int i, min[3];
u8 old = it87_read_value(client, IT87_REG_FAN_DIV);
 
for (i = 0; i < 3; i++)
min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i]));
 
switch (nr) {
case 0:
case 1:
data->fan_div[nr] = DIV_TO_REG(val);
break;
case 2:
if (val < 8)
data->fan_div[nr] = 1;
else
data->fan_div[nr] = 3;
}
val = old & 0x100;
val |= (data->fan_div[0] & 0x07);
val |= (data->fan_div[1] & 0x07) << 3;
if (data->fan_div[2] == 3)
val |= 0x1 << 6;
it87_write_value(client, IT87_REG_FAN_DIV, val);
 
for (i = 0; i < 3; i++) {
data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i]));
it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]);
}
return count;
}
 
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, char *buf) \
{ \
return show_fan(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
{ \
return show_fan_min(dev, buf, 0x##offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
{ \
return show_fan_div(dev, buf, 0x##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, 0x##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, 0x##offset - 1); \
} \
static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \
static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min) \
static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div)
 
show_fan_offset(1);
show_fan_offset(2);
show_fan_offset(3);
 
/* Alarm */
static ssize_t show_alarm(struct device *dev, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
it87_update_client(client);
return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
}
static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
 
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and it87_driver is still present) */
static int it87_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
return 0;
return i2c_detect(adapter, &addr_data, it87_detect);
}
 
/* This function is called by i2c_detect */
int it87_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client = NULL;
struct it87_data *data;
int err = 0;
const char *name = "";
int is_isa = i2c_is_isa_adapter(adapter);
 
if (!is_isa &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto ERROR0;
 
/* Reserve the ISA region */
if (is_isa)
if (!request_region(address, IT87_EXTENT, name))
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 IT87-like chips. But only
if we read 'undefined' registers. */
i = inb_p(address + 1);
if (inb_p(address + 2) != i)
goto ERROR1;
if (inb_p(address + 3) != i)
goto ERROR1;
if (inb_p(address + 7) != i)
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);
return 0;
}
}
}
 
/* 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 it87_{read,write}_value. */
 
if (!(new_client = kmalloc((sizeof(struct i2c_client)) +
sizeof(struct it87_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct it87_data));
 
data = (struct it87_data *) (new_client + 1);
if (is_isa)
init_MUTEX(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &it87_driver;
new_client->flags = 0;
 
/* Now, we do the remaining detection. */
 
if (kind < 0) {
if (it87_read_value(new_client, IT87_REG_CONFIG) & 0x80)
goto ERROR1;
if (!is_isa
&& (it87_read_value(new_client, IT87_REG_I2C_ADDR) !=
address)) goto ERROR1;
}
 
/* Determine the chip type. */
if (kind <= 0) {
i = it87_read_value(new_client, IT87_REG_CHIPID);
if (i == 0x90) {
kind = it87;
}
else {
if (kind == 0)
printk
("it87.o: Ignoring 'force' parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto ERROR1;
}
}
 
if (kind == it87) {
name = "it87";
} /* else if (kind == it8712) {
name = "it8712";
} */ else {
dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?",
kind);
goto ERROR1;
}
 
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
 
data->type = kind;
 
new_client->id = it87_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
 
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR1;
 
/* Initialize the IT87 chip */
it87_init_client(new_client, data);
 
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in_input0);
device_create_file(&new_client->dev, &dev_attr_in_input1);
device_create_file(&new_client->dev, &dev_attr_in_input2);
device_create_file(&new_client->dev, &dev_attr_in_input3);
device_create_file(&new_client->dev, &dev_attr_in_input4);
device_create_file(&new_client->dev, &dev_attr_in_input5);
device_create_file(&new_client->dev, &dev_attr_in_input6);
device_create_file(&new_client->dev, &dev_attr_in_input7);
device_create_file(&new_client->dev, &dev_attr_in_input8);
device_create_file(&new_client->dev, &dev_attr_in_min0);
device_create_file(&new_client->dev, &dev_attr_in_min1);
device_create_file(&new_client->dev, &dev_attr_in_min2);
device_create_file(&new_client->dev, &dev_attr_in_min3);
device_create_file(&new_client->dev, &dev_attr_in_min4);
device_create_file(&new_client->dev, &dev_attr_in_min5);
device_create_file(&new_client->dev, &dev_attr_in_min6);
device_create_file(&new_client->dev, &dev_attr_in_min7);
device_create_file(&new_client->dev, &dev_attr_in_max0);
device_create_file(&new_client->dev, &dev_attr_in_max1);
device_create_file(&new_client->dev, &dev_attr_in_max2);
device_create_file(&new_client->dev, &dev_attr_in_max3);
device_create_file(&new_client->dev, &dev_attr_in_max4);
device_create_file(&new_client->dev, &dev_attr_in_max5);
device_create_file(&new_client->dev, &dev_attr_in_max6);
device_create_file(&new_client->dev, &dev_attr_in_max7);
device_create_file(&new_client->dev, &dev_attr_temp_input1);
device_create_file(&new_client->dev, &dev_attr_temp_input2);
device_create_file(&new_client->dev, &dev_attr_temp_input3);
device_create_file(&new_client->dev, &dev_attr_temp_max1);
device_create_file(&new_client->dev, &dev_attr_temp_max2);
device_create_file(&new_client->dev, &dev_attr_temp_max3);
device_create_file(&new_client->dev, &dev_attr_temp_min1);
device_create_file(&new_client->dev, &dev_attr_temp_min2);
device_create_file(&new_client->dev, &dev_attr_temp_min3);
device_create_file(&new_client->dev, &dev_attr_sensor1);
device_create_file(&new_client->dev, &dev_attr_sensor2);
device_create_file(&new_client->dev, &dev_attr_sensor3);
device_create_file(&new_client->dev, &dev_attr_fan_input1);
device_create_file(&new_client->dev, &dev_attr_fan_input2);
device_create_file(&new_client->dev, &dev_attr_fan_input3);
device_create_file(&new_client->dev, &dev_attr_fan_min1);
device_create_file(&new_client->dev, &dev_attr_fan_min2);
device_create_file(&new_client->dev, &dev_attr_fan_min3);
device_create_file(&new_client->dev, &dev_attr_fan_div1);
device_create_file(&new_client->dev, &dev_attr_fan_div2);
device_create_file(&new_client->dev, &dev_attr_fan_div3);
device_create_file(&new_client->dev, &dev_attr_alarm);
 
return 0;
 
ERROR1:
kfree(new_client);
 
if (is_isa)
release_region(address, IT87_EXTENT);
ERROR0:
return err;
}
 
static int it87_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, IT87_EXTENT);
kfree(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 IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 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 it87_read_value(struct i2c_client *client, u8 reg)
{
struct it87_data *data = i2c_get_clientdata(client);
 
int res;
if (i2c_is_isa_client(client)) {
down(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
res = inb_p(client->addr + IT87_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 IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 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 it87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
struct it87_data *data = i2c_get_clientdata(client);
 
if (i2c_is_isa_client(client)) {
down(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_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 IT87. It should set limits, etc. */
static void it87_init_client(struct i2c_client *client, struct it87_data *data)
{
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
it87_write_value(client, IT87_REG_CONFIG, 0x80);
it87_write_value(client, IT87_REG_VIN_MIN(0),
IN_TO_REG(IT87_INIT_IN_MIN_0));
it87_write_value(client, IT87_REG_VIN_MAX(0),
IN_TO_REG(IT87_INIT_IN_MAX_0));
it87_write_value(client, IT87_REG_VIN_MIN(1),
IN_TO_REG(IT87_INIT_IN_MIN_1));
it87_write_value(client, IT87_REG_VIN_MAX(1),
IN_TO_REG(IT87_INIT_IN_MAX_1));
it87_write_value(client, IT87_REG_VIN_MIN(2),
IN_TO_REG(IT87_INIT_IN_MIN_2));
it87_write_value(client, IT87_REG_VIN_MAX(2),
IN_TO_REG(IT87_INIT_IN_MAX_2));
it87_write_value(client, IT87_REG_VIN_MIN(3),
IN_TO_REG(IT87_INIT_IN_MIN_3));
it87_write_value(client, IT87_REG_VIN_MAX(3),
IN_TO_REG(IT87_INIT_IN_MAX_3));
it87_write_value(client, IT87_REG_VIN_MIN(4),
IN_TO_REG(IT87_INIT_IN_MIN_4));
it87_write_value(client, IT87_REG_VIN_MAX(4),
IN_TO_REG(IT87_INIT_IN_MAX_4));
it87_write_value(client, IT87_REG_VIN_MIN(5),
IN_TO_REG(IT87_INIT_IN_MIN_5));
it87_write_value(client, IT87_REG_VIN_MAX(5),
IN_TO_REG(IT87_INIT_IN_MAX_5));
it87_write_value(client, IT87_REG_VIN_MIN(6),
IN_TO_REG(IT87_INIT_IN_MIN_6));
it87_write_value(client, IT87_REG_VIN_MAX(6),
IN_TO_REG(IT87_INIT_IN_MAX_6));
it87_write_value(client, IT87_REG_VIN_MIN(7),
IN_TO_REG(IT87_INIT_IN_MIN_7));
it87_write_value(client, IT87_REG_VIN_MAX(7),
IN_TO_REG(IT87_INIT_IN_MAX_7));
/* Note: Battery voltage does not have limit registers */
it87_write_value(client, IT87_REG_FAN_MIN(0),
FAN_TO_REG(IT87_INIT_FAN_MIN_1, 2));
it87_write_value(client, IT87_REG_FAN_MIN(1),
FAN_TO_REG(IT87_INIT_FAN_MIN_2, 2));
it87_write_value(client, IT87_REG_FAN_MIN(2),
FAN_TO_REG(IT87_INIT_FAN_MIN_3, 2));
it87_write_value(client, IT87_REG_TEMP_HIGH(0),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_1));
it87_write_value(client, IT87_REG_TEMP_LOW(0),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_1));
it87_write_value(client, IT87_REG_TEMP_HIGH(1),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_2));
it87_write_value(client, IT87_REG_TEMP_LOW(1),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_2));
it87_write_value(client, IT87_REG_TEMP_HIGH(2),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_3));
it87_write_value(client, IT87_REG_TEMP_LOW(2),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_3));
 
/* Enable voltage monitors */
it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff);
 
/* Enable Temp1-Temp3 */
data->sensor = (it87_read_value(client, IT87_REG_TEMP_ENABLE) & 0xc0);
data->sensor |= temp_type & 0x3f;
it87_write_value(client, IT87_REG_TEMP_ENABLE, data->sensor);
 
/* Enable fans */
it87_write_value(client, IT87_REG_FAN_CTRL,
(it87_read_value(client, IT87_REG_FAN_CTRL) & 0x8f)
| 0x70);
 
/* Start monitoring */
it87_write_value(client, IT87_REG_CONFIG,
(it87_read_value(client, IT87_REG_CONFIG) & 0xb7)
| (update_vbat ? 0x41 : 0x01));
}
 
static void it87_update_client(struct i2c_client *client)
{
struct it87_data *data = i2c_get_clientdata(client);
int i;
 
down(&data->update_lock);
 
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
 
if (update_vbat) {
/* Cleared after each update, so reenable. Value
returned by this read will be previous value */
it87_write_value(client, IT87_REG_CONFIG,
it87_read_value(client, IT87_REG_CONFIG) | 0x40);
}
for (i = 0; i <= 7; i++) {
data->in[i] =
it87_read_value(client, IT87_REG_VIN(i));
data->in_min[i] =
it87_read_value(client, IT87_REG_VIN_MIN(i));
data->in_max[i] =
it87_read_value(client, IT87_REG_VIN_MAX(i));
}
data->in[8] =
it87_read_value(client, IT87_REG_VIN(8));
/* Temperature sensor doesn't have limit registers, set
to min and max value */
data->in_min[8] = 0;
data->in_max[8] = 255;
 
for (i = 0; i < 3; i++) {
data->fan[i] =
it87_read_value(client, IT87_REG_FAN(i));
data->fan_min[i] =
it87_read_value(client, IT87_REG_FAN_MIN(i));
}
for (i = 0; i < 3; i++) {
data->temp[i] =
it87_read_value(client, IT87_REG_TEMP(i));
data->temp_high[i] =
it87_read_value(client, IT87_REG_TEMP_HIGH(i));
data->temp_low[i] =
it87_read_value(client, IT87_REG_TEMP_LOW(i));
}
 
/* The 8705 does not have VID capability */
/*if (data->type == it8712) {
data->vid = it87_read_value(client, IT87_REG_VID);
data->vid &= 0x1f;
}
else */ {
data->vid = 0x1f;
}
 
i = it87_read_value(client, IT87_REG_FAN_DIV);
data->fan_div[0] = i & 0x07;
data->fan_div[1] = (i >> 3) & 0x07;
data->fan_div[2] = (i & 0x40) ? 3 : 1;
 
data->alarms =
it87_read_value(client, IT87_REG_ALARM1) |
(it87_read_value(client, IT87_REG_ALARM2) << 8) |
(it87_read_value(client, IT87_REG_ALARM3) << 16);
 
data->last_updated = jiffies;
data->valid = 1;
}
 
up(&data->update_lock);
}
 
static int __init sm_it87_init(void)
{
return i2c_add_driver(&it87_driver);
}
 
static void __exit sm_it87_exit(void)
{
i2c_del_driver(&it87_driver);
}
 
 
MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver");
MODULE_PARM(update_vbat, "i");
MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
MODULE_PARM(temp_type, "i");
MODULE_PARM_DESC(temp_type, "Temperature sensor type, normally leave unset");
MODULE_LICENSE("GPL");
 
module_init(sm_it87_init);
module_exit(sm_it87_exit);
/shark/trunk/drivers/i2c/i2c-dev.c
0,0 → 1,551
/*
i2c-dev.c - i2c-bus driver, char device interface
 
Copyright (C) 1995-97 Simon G. Vogl
Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.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 that this is a complete rewrite of Simon Vogl's i2c-dev module.
But I have used so much of his original code and ideas that it seems
only fair to recognize him as co-author -- Frodo */
 
/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
 
/* The devfs code is contributed by Philipp Matthias Hahn
<pmhahn@titan.lahn.de> */
 
/* If you want debugging uncomment: */
/* #define DEBUG 1 */
 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <asm/uaccess.h>
 
static struct i2c_client i2cdev_client_template;
 
struct i2c_dev {
int minor;
struct i2c_adapter *adap;
struct class_device class_dev;
struct completion released; /* FIXME, we need a class_device_unregister() */
};
#define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev)
 
#define I2C_MINORS 256
static struct i2c_dev *i2c_dev_array[I2C_MINORS];
static spinlock_t i2c_dev_array_lock = SPIN_LOCK_UNLOCKED;
 
struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
 
spin_lock(&i2c_dev_array_lock);
i2c_dev = i2c_dev_array[index];
spin_unlock(&i2c_dev_array_lock);
return i2c_dev;
}
 
struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev = NULL;
int i;
 
spin_lock(&i2c_dev_array_lock);
for (i = 0; i < I2C_MINORS; ++i) {
if ((i2c_dev_array[i]) &&
(i2c_dev_array[i]->adap == adap)) {
i2c_dev = i2c_dev_array[i];
break;
}
}
spin_unlock(&i2c_dev_array_lock);
return i2c_dev;
}
 
static struct i2c_dev *get_free_i2c_dev(void)
{
struct i2c_dev *i2c_dev;
unsigned int i;
 
i2c_dev = kmalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
memset(i2c_dev, 0x00, sizeof(*i2c_dev));
 
spin_lock(&i2c_dev_array_lock);
for (i = 0; i < I2C_MINORS; ++i) {
if (i2c_dev_array[i])
continue;
i2c_dev->minor = i;
i2c_dev_array[i] = i2c_dev;
spin_unlock(&i2c_dev_array_lock);
return i2c_dev;
}
spin_unlock(&i2c_dev_array_lock);
kfree(i2c_dev);
return ERR_PTR(-ENODEV);
}
 
static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
spin_lock(&i2c_dev_array_lock);
i2c_dev_array[i2c_dev->minor] = NULL;
spin_unlock(&i2c_dev_array_lock);
}
 
static ssize_t show_dev(struct class_device *class_dev, char *buf)
{
struct i2c_dev *i2c_dev = to_i2c_dev(class_dev);
return print_dev_t(buf, MKDEV(I2C_MAJOR, i2c_dev->minor));
}
static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
 
static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
 
struct i2c_client *client = (struct i2c_client *)file->private_data;
 
if (count > 8192)
count = 8192;
 
tmp = kmalloc(count,GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
 
pr_debug("i2c-dev.o: i2c-%d reading %d bytes.\n",
iminor(file->f_dentry->d_inode), count);
 
ret = i2c_master_recv(client,tmp,count);
if (ret >= 0)
ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
kfree(tmp);
return ret;
}
 
static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count,
loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = (struct i2c_client *)file->private_data;
 
if (count > 8192)
count = 8192;
 
tmp = kmalloc(count,GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
if (copy_from_user(tmp,buf,count)) {
kfree(tmp);
return -EFAULT;
}
 
pr_debug("i2c-dev.o: i2c-%d writing %d bytes.\n",
iminor(file->f_dentry->d_inode), count);
 
ret = i2c_master_send(client,tmp,count);
kfree(tmp);
return ret;
}
 
int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
struct i2c_msg *rdwr_pa;
u8 **data_ptrs;
int i,datasize,res;
unsigned long funcs;
 
dev_dbg(&client->dev, "i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n",
iminor(inode),cmd, arg);
 
switch ( cmd ) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg))
return -EBUSY;
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return (copy_to_user((unsigned long __user *)arg, &funcs,
sizeof(unsigned long)))?-EFAULT:0;
 
case I2C_RDWR:
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
 
/* Put an arbritrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > 42)
return -EINVAL;
rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
 
if (rdwr_pa == NULL) return -ENOMEM;
 
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
 
data_ptrs = (u8 **) kmalloc(rdwr_arg.nmsgs * sizeof(u8 *),
GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
 
res = 0;
for( i=0; i<rdwr_arg.nmsgs; i++ ) {
/* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
res = -EINVAL;
break;
}
data_ptrs[i] = rdwr_pa[i].buf;
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) {
res = -ENOMEM;
break;
}
if(copy_from_user(rdwr_pa[i].buf,
data_ptrs[i],
rdwr_pa[i].len)) {
++i; /* Needs to be kfreed too */
res = -EFAULT;
break;
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
 
res = i2c_transfer(client->adapter,
rdwr_pa,
rdwr_arg.nmsgs);
while(i-- > 0) {
if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user(
data_ptrs[i],
rdwr_pa[i].buf,
rdwr_pa[i].len)) {
res = -EFAULT;
}
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
 
case I2C_SMBUS:
if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
if ((data_arg.size != I2C_SMBUS_BYTE) &&
(data_arg.size != I2C_SMBUS_QUICK) &&
(data_arg.size != I2C_SMBUS_BYTE_DATA) &&
(data_arg.size != I2C_SMBUS_WORD_DATA) &&
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
dev_dbg(&client->dev,
"size out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.size);
return -EINVAL;
}
/* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
so the check is valid if size==I2C_SMBUS_QUICK too. */
if ((data_arg.read_write != I2C_SMBUS_READ) &&
(data_arg.read_write != I2C_SMBUS_WRITE)) {
dev_dbg(&client->dev,
"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
data_arg.read_write);
return -EINVAL;
}
 
/* Note that command values are always valid! */
 
if ((data_arg.size == I2C_SMBUS_QUICK) ||
((data_arg.size == I2C_SMBUS_BYTE) &&
(data_arg.read_write == I2C_SMBUS_WRITE)))
/* These are special: we do not use data */
return i2c_smbus_xfer(client->adapter, client->addr,
client->flags,
data_arg.read_write,
data_arg.command,
data_arg.size, NULL);
 
if (data_arg.data == NULL) {
dev_dbg(&client->dev,
"data is NULL pointer in ioctl I2C_SMBUS.\n");
return -EINVAL;
}
 
if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
(data_arg.size == I2C_SMBUS_BYTE))
datasize = sizeof(data_arg.data->byte);
else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
(data_arg.size == I2C_SMBUS_PROC_CALL))
datasize = sizeof(data_arg.data->word);
else /* size == smbus block, i2c block, or block proc. call */
datasize = sizeof(data_arg.data->block);
 
if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
}
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
data_arg.read_write,
data_arg.command,data_arg.size,&temp);
if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
 
default:
return i2c_control(client,cmd,arg);
}
return 0;
}
 
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
 
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev)
return -ENODEV;
 
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
 
client = kmalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
memcpy(client, &i2cdev_client_template, sizeof(*client));
 
/* registered with adapter, passed as client to user */
client->adapter = adap;
file->private_data = client;
 
return 0;
}
 
static int i2cdev_release(struct inode *inode, struct file *file)
{
struct i2c_client *client = file->private_data;
 
i2c_put_adapter(client->adapter);
kfree(client);
file->private_data = NULL;
 
return 0;
}
 
static struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
 
static void release_i2c_dev(struct class_device *dev)
{
struct i2c_dev *i2c_dev = to_i2c_dev(dev);
complete(&i2c_dev->released);
}
 
static struct class i2c_dev_class = {
.name = "i2c-dev",
.release = &release_i2c_dev,
};
 
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int retval;
 
i2c_dev = get_free_i2c_dev();
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
 
devfs_mk_cdev(MKDEV(I2C_MAJOR, i2c_dev->minor),
S_IFCHR|S_IRUSR|S_IWUSR, "i2c/%d", i2c_dev->minor);
dev_dbg(&adap->dev, "Registered as minor %d\n", i2c_dev->minor);
 
/* register this i2c device with the driver core */
i2c_dev->adap = adap;
if (adap->dev.parent == &legacy_bus)
i2c_dev->class_dev.dev = &adap->dev;
else
i2c_dev->class_dev.dev = adap->dev.parent;
i2c_dev->class_dev.class = &i2c_dev_class;
snprintf(i2c_dev->class_dev.class_id, BUS_ID_SIZE, "i2c-%d", i2c_dev->minor);
retval = class_device_register(&i2c_dev->class_dev);
if (retval)
goto error;
class_device_create_file(&i2c_dev->class_dev, &class_device_attr_dev);
return 0;
error:
return_i2c_dev(i2c_dev);
kfree(i2c_dev);
return retval;
}
 
static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
 
i2c_dev = i2c_dev_get_by_adapter(adap);
if (!i2c_dev)
return -ENODEV;
 
init_completion(&i2c_dev->released);
devfs_remove("i2c/%d", i2c_dev->minor);
return_i2c_dev(i2c_dev);
class_device_unregister(&i2c_dev->class_dev);
wait_for_completion(&i2c_dev->released);
kfree(i2c_dev);
 
dev_dbg(&adap->dev, "Adapter unregistered\n");
return 0;
}
 
static int i2cdev_detach_client(struct i2c_client *client)
{
return 0;
}
 
static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
return -1;
}
 
static struct i2c_driver i2cdev_driver = {
.owner = THIS_MODULE,
.name = "dev_driver",
.id = I2C_DRIVERID_I2CDEV,
.flags = I2C_DF_NOTIFY,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
.command = i2cdev_command,
};
 
static struct i2c_client i2cdev_client_template = {
.name = "I2C /dev entry",
.id = 1,
.addr = -1,
.driver = &i2cdev_driver,
};
 
static int __init i2c_dev_init(void)
{
int res;
 
printk(KERN_INFO "i2c /dev entries driver\n");
 
if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n",
I2C_MAJOR);
return -EIO;
}
devfs_mk_dir("i2c");
class_register(&i2c_dev_class);
if ((res = i2c_add_driver(&i2cdev_driver))) {
printk(KERN_ERR "i2c-dev.o: Driver registration failed, module not inserted.\n");
devfs_remove("i2c");
unregister_chrdev(I2C_MAJOR,"i2c");
return res;
}
return 0;
}
 
static void __exit i2c_dev_exit(void)
{
i2c_del_driver(&i2cdev_driver);
class_unregister(&i2c_dev_class);
devfs_remove("i2c");
unregister_chrdev(I2C_MAJOR,"i2c");
}
 
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C /dev entries driver");
MODULE_LICENSE("GPL");
 
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
/shark/trunk/drivers/i2c/makefile
0,0 → 1,23
# I2C Driver
 
ifndef BASE
BASE=../..
endif
 
include $(BASE)/config/config.mk
 
LIBRARY = i2c
 
OBJS_PATH = $(BASE)/drivers/i2c
 
OBJS = i2c-core.o
 
OTHERINCL += -I$(BASE)/drivers/linuxc26/include
 
C_OPT += -D__KERNEL__
 
include $(BASE)/config/lib.mk
 
clean::
rm -f $(OBJS)