/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) |