mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
Merge branch 'i2c-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
* 'i2c-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6: (26 commits) i2c-rpx: Remove i2c-mpc: work around missing-9th-clock-pulse bug i2c: New PMC MSP71xx TWI bus driver i2c-savage4: Delete many unused defines i2c/tsl2550: Speed up initialization i2c: New bus driver for the TAOS evaluation modules i2c-i801: Use the internal 32-byte buffer on ICH4+ i2c-i801: Various cleanups i2c: Add support for the TSL2550 i2c-pxa: Support new-style I2C drivers i2c-gpio: Make some internal functions static i2c-gpio: Add support for new-style clients i2c-iop3xx: Switch to static adapter numbering i2c-sis5595: Resolve resource conflict with sis5595 matroxfb: Clean-up i2c header inclusions i2c-nforce2: Add support for SMBus block transactions i2c-mpc: Use i2c_add_numbered_adapter i2c-mv64xxx: Use i2c_add_numbered_adapter i2c-piix4: Add support for the ATI SB700 i2c: New DS1682 chip driver ...
This commit is contained in:
commit
068345f4a8
@ -643,6 +643,60 @@ X!Idrivers/video/console/fonts.c
|
||||
!Edrivers/spi/spi.c
|
||||
</chapter>
|
||||
|
||||
<chapter id="i2c">
|
||||
<title>I<superscript>2</superscript>C and SMBus Subsystem</title>
|
||||
|
||||
<para>
|
||||
I<superscript>2</superscript>C (or without fancy typography, "I2C")
|
||||
is an acronym for the "Inter-IC" bus, a simple bus protocol which is
|
||||
widely used where low data rate communications suffice.
|
||||
Since it's also a licensed trademark, some vendors use another
|
||||
name (such as "Two-Wire Interface", TWI) for the same bus.
|
||||
I2C only needs two signals (SCL for clock, SDA for data), conserving
|
||||
board real estate and minimizing signal quality issues.
|
||||
Most I2C devices use seven bit addresses, and bus speeds of up
|
||||
to 400 kHz; there's a high speed extension (3.4 MHz) that's not yet
|
||||
found wide use.
|
||||
I2C is a multi-master bus; open drain signaling is used to
|
||||
arbitrate between masters, as well as to handshake and to
|
||||
synchronize clocks from slower clients.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The Linux I2C programming interfaces support only the master
|
||||
side of bus interactions, not the slave side.
|
||||
The programming interface is structured around two kinds of driver,
|
||||
and two kinds of device.
|
||||
An I2C "Adapter Driver" abstracts the controller hardware; it binds
|
||||
to a physical device (perhaps a PCI device or platform_device) and
|
||||
exposes a <structname>struct i2c_adapter</structname> representing
|
||||
each I2C bus segment it manages.
|
||||
On each I2C bus segment will be I2C devices represented by a
|
||||
<structname>struct i2c_client</structname>. Those devices will
|
||||
be bound to a <structname>struct i2c_driver</structname>,
|
||||
which should follow the standard Linux driver model.
|
||||
(At this writing, a legacy model is more widely used.)
|
||||
There are functions to perform various I2C protocol operations; at
|
||||
this writing all such functions are usable only from task context.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The System Management Bus (SMBus) is a sibling protocol. Most SMBus
|
||||
systems are also I2C conformant. The electrical constraints are
|
||||
tighter for SMBus, and it standardizes particular protocol messages
|
||||
and idioms. Controllers that support I2C can also support most
|
||||
SMBus operations, but SMBus controllers don't support all the protocol
|
||||
options that an I2C controller will.
|
||||
There are functions to perform various SMBus protocol operations,
|
||||
either using I2C primitives or by issuing SMBus commands to
|
||||
i2c_adapter devices which don't support those I2C operations.
|
||||
</para>
|
||||
|
||||
!Iinclude/linux/i2c.h
|
||||
!Fdrivers/i2c/i2c-boardinfo.c i2c_register_board_info
|
||||
!Edrivers/i2c/i2c-core.c
|
||||
</chapter>
|
||||
|
||||
<chapter id="splice">
|
||||
<title>splice API</title>
|
||||
<para>)
|
||||
@ -654,4 +708,5 @@ X!Idrivers/video/console/fonts.c
|
||||
!Ffs/splice.c
|
||||
</chapter>
|
||||
|
||||
|
||||
</book>
|
||||
|
@ -330,3 +330,10 @@ Who: Tejun Heo <htejun@gmail.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
What: Legacy RTC drivers (under drivers/i2c/chips)
|
||||
When: November 2007
|
||||
Why: Obsolete. We have a RTC subsystem with better drivers.
|
||||
Who: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
---------------------------
|
||||
|
||||
|
@ -5,8 +5,8 @@ Supported adapters:
|
||||
'810' and '810E' chipsets)
|
||||
* Intel 82801BA (ICH2 - part of the '815E' chipset)
|
||||
* Intel 82801CA/CAM (ICH3)
|
||||
* Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported)
|
||||
* Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported)
|
||||
* Intel 82801DB (ICH4) (HW PEC supported)
|
||||
* Intel 82801EB/ER (ICH5) (HW PEC supported)
|
||||
* Intel 6300ESB
|
||||
* Intel 82801FB/FR/FW/FRW (ICH6)
|
||||
* Intel 82801G (ICH7)
|
||||
|
@ -6,7 +6,7 @@ Supported adapters:
|
||||
Datasheet: Publicly available at the Intel website
|
||||
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
|
||||
Datasheet: Only available via NDA from ServerWorks
|
||||
* ATI IXP200, IXP300, IXP400 and SB600 southbridges
|
||||
* ATI IXP200, IXP300, IXP400, SB600 and SB700 southbridges
|
||||
Datasheet: Not publicly available
|
||||
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
||||
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
||||
|
46
Documentation/i2c/busses/i2c-taos-evm
Normal file
46
Documentation/i2c/busses/i2c-taos-evm
Normal file
@ -0,0 +1,46 @@
|
||||
Kernel driver i2c-taos-evm
|
||||
|
||||
Author: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
This is a driver for the evaluation modules for TAOS I2C/SMBus chips.
|
||||
The modules include an SMBus master with limited capabilities, which can
|
||||
be controlled over the serial port. Virtually all evaluation modules
|
||||
are supported, but a few lines of code need to be added for each new
|
||||
module to instantiate the right I2C chip on the bus. Obviously, a driver
|
||||
for the chip in question is also needed.
|
||||
|
||||
Currently supported devices are:
|
||||
|
||||
* TAOS TSL2550 EVM
|
||||
|
||||
For addtional information on TAOS products, please see
|
||||
http://www.taosinc.com/
|
||||
|
||||
|
||||
Using this driver
|
||||
-----------------
|
||||
|
||||
In order to use this driver, you'll need the serport driver, and the
|
||||
inputattach tool, which is part of the input-utils package. The following
|
||||
commands will tell the kernel that you have a TAOS EVM on the first
|
||||
serial port:
|
||||
|
||||
# modprobe serport
|
||||
# inputattach --taos-evm /dev/ttyS0
|
||||
|
||||
|
||||
Technical details
|
||||
-----------------
|
||||
|
||||
Only 4 SMBus transaction types are supported by the TAOS evaluation
|
||||
modules:
|
||||
* Receive Byte
|
||||
* Send Byte
|
||||
* Read Byte
|
||||
* Write Byte
|
||||
|
||||
The communication protocol is text-based and pretty simple. It is
|
||||
described in a PDF document on the CD which comes with the evaluation
|
||||
module. The communication is rather slow, because the serial port has
|
||||
to operate at 1200 bps. However, I don't think this is a big concern in
|
||||
practice, as these modules are meant for evaluation and testing only.
|
@ -99,7 +99,7 @@ And then read the data
|
||||
|
||||
or
|
||||
|
||||
count = i2c_smbus_read_i2c_block_data(fd, 0x84, buffer);
|
||||
count = i2c_smbus_read_i2c_block_data(fd, 0x84, 16, buffer);
|
||||
|
||||
The block read should read 16 bytes.
|
||||
0x84 is the block read command.
|
||||
|
@ -1,38 +0,0 @@
|
||||
Kernel driver x1205
|
||||
===================
|
||||
|
||||
Supported chips:
|
||||
* Xicor X1205 RTC
|
||||
Prefix: 'x1205'
|
||||
Addresses scanned: none
|
||||
Datasheet: http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html
|
||||
|
||||
Authors:
|
||||
Karen Spearel <kas11@tampabay.rr.com>,
|
||||
Alessandro Zummo <a.zummo@towertech.it>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This module aims to provide complete access to the Xicor X1205 RTC.
|
||||
Recently Xicor has merged with Intersil, but the chip is
|
||||
still sold under the Xicor brand.
|
||||
|
||||
This chip is located at address 0x6f and uses a 2-byte register addressing.
|
||||
Two bytes need to be written to read a single register, while most
|
||||
other chips just require one and take the second one as the data
|
||||
to be written. To prevent corrupting unknown chips, the user must
|
||||
explicitely set the probe parameter.
|
||||
|
||||
example:
|
||||
|
||||
modprobe x1205 probe=0,0x6f
|
||||
|
||||
The module supports one more option, hctosys, which is used to set the
|
||||
software clock from the x1205. On systems where the x1205 is the
|
||||
only hardware rtc, this parameter could be used to achieve a correct
|
||||
date/time earlier in the system boot sequence.
|
||||
|
||||
example:
|
||||
|
||||
modprobe x1205 probe=0,0x6f hctosys=1
|
@ -67,7 +67,6 @@ i2c-proc: The /proc/sys/dev/sensors interface for device (client) drivers
|
||||
Algorithm drivers
|
||||
-----------------
|
||||
|
||||
i2c-algo-8xx: An algorithm for CPM's I2C device in Motorola 8xx processors (NOT BUILT BY DEFAULT)
|
||||
i2c-algo-bit: A bit-banging algorithm
|
||||
i2c-algo-pcf: A PCF 8584 style algorithm
|
||||
i2c-algo-ibm_ocp: An algorithm for the I2C device in IBM 4xx processors (NOT BUILT BY DEFAULT)
|
||||
@ -81,6 +80,5 @@ i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (NOT mkpatch
|
||||
i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit)
|
||||
i2c-adap-ibm_ocp: IBM 4xx processor I2C device (uses i2c-algo-ibm_ocp) (NOT BUILT BY DEFAULT)
|
||||
i2c-pport: Primitive parallel port adapter (uses i2c-algo-bit)
|
||||
i2c-rpx: RPX board Motorola 8xx I2C device (uses i2c-algo-8xx) (NOT BUILT BY DEFAULT)
|
||||
i2c-velleman: Velleman K8000 parallel port adapter (uses i2c-algo-bit)
|
||||
|
||||
|
@ -571,7 +571,7 @@ SMBus communication
|
||||
u8 command, u8 length,
|
||||
u8 *values);
|
||||
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
|
||||
u8 command, u8 *values);
|
||||
u8 command, u8 length, u8 *values);
|
||||
|
||||
These ones were removed in Linux 2.6.10 because they had no users, but could
|
||||
be added back later if needed:
|
||||
|
@ -34,10 +34,6 @@ config I2C_ALGOPCA
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-algo-pca.
|
||||
|
||||
config I2C_ALGO8XX
|
||||
tristate "MPC8xx CPM I2C interface"
|
||||
depends on 8xx
|
||||
|
||||
config I2C_ALGO_SGI
|
||||
tristate "I2C SGI interfaces"
|
||||
depends on SGI_IP22 || SGI_IP32 || X86_VISWS
|
||||
|
@ -207,6 +207,7 @@ config I2C_PIIX4
|
||||
ATI IXP300
|
||||
ATI IXP400
|
||||
ATI SB600
|
||||
ATI SB700
|
||||
Serverworks OSB4
|
||||
Serverworks CSB5
|
||||
Serverworks CSB6
|
||||
@ -390,11 +391,6 @@ config I2C_PROSAVAGE
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-prosavage.
|
||||
|
||||
config I2C_RPXLITE
|
||||
tristate "Embedded Planet RPX Lite/Classic support"
|
||||
depends on RPXLITE || RPXCLASSIC
|
||||
select I2C_ALGO8XX
|
||||
|
||||
config I2C_S3C2410
|
||||
tristate "S3C2410 I2C Driver"
|
||||
depends on ARCH_S3C2410
|
||||
@ -512,6 +508,22 @@ config I2C_SIS96X
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-sis96x.
|
||||
|
||||
config I2C_TAOS_EVM
|
||||
tristate "TAOS evaluation module"
|
||||
depends on EXPERIMENTAL
|
||||
select SERIO
|
||||
select SERIO_SERPORT
|
||||
default n
|
||||
help
|
||||
This supports TAOS evaluation modules on serial port. In order to
|
||||
use this driver, you will need the inputattach tool, which is part
|
||||
of the input-utils package.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
This support is also available as a module. If so, the module
|
||||
will be called i2c-taos-evm.
|
||||
|
||||
config I2C_STUB
|
||||
tristate "I2C/SMBus Test Stub"
|
||||
depends on EXPERIMENTAL && m
|
||||
@ -635,4 +647,13 @@ config I2C_PNX
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-pnx.
|
||||
|
||||
config I2C_PMCMSP
|
||||
tristate "PMC MSP I2C TWI Controller"
|
||||
depends on PMC_MSP
|
||||
help
|
||||
This driver supports the PMC TWI controller on MSP devices.
|
||||
|
||||
This driver can also be built as module. If so, the module
|
||||
will be called i2c-pmcmsp.
|
||||
|
||||
endmenu
|
||||
|
@ -32,10 +32,10 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
||||
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
|
||||
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
|
||||
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
|
||||
obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
|
||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||
obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o
|
||||
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
|
||||
obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o
|
||||
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
||||
@ -44,6 +44,7 @@ obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
|
||||
obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
|
||||
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
|
||||
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
|
||||
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
|
||||
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
|
||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
|
||||
|
@ -63,14 +63,14 @@ static void i2c_gpio_setscl_val(void *data, int state)
|
||||
gpio_set_value(pdata->scl_pin, state);
|
||||
}
|
||||
|
||||
int i2c_gpio_getsda(void *data)
|
||||
static int i2c_gpio_getsda(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
return gpio_get_value(pdata->sda_pin);
|
||||
}
|
||||
|
||||
int i2c_gpio_getscl(void *data)
|
||||
static int i2c_gpio_getscl(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
@ -142,7 +142,13 @@ static int __init i2c_gpio_probe(struct platform_device *pdev)
|
||||
adap->algo_data = bit_data;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
||||
ret = i2c_bit_add_bus(adap);
|
||||
/*
|
||||
* If "dev->id" is negative we consider it as zero.
|
||||
* The reason to do so is to avoid sysfs names that only make
|
||||
* sense when there are multiple adapters.
|
||||
*/
|
||||
adap->nr = pdev->id >= 0 ? pdev->id : 0;
|
||||
ret = i2c_bit_add_numbered_bus(adap);
|
||||
if (ret)
|
||||
goto err_add_bus;
|
||||
|
||||
|
@ -22,12 +22,12 @@
|
||||
|
||||
/*
|
||||
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)
|
||||
82801AA 2413
|
||||
82801AB 2423
|
||||
82801BA 2443
|
||||
82801CA/CAM 2483
|
||||
82801DB 24C3 (HW PEC supported)
|
||||
82801EB 24D3 (HW PEC supported)
|
||||
6300ESB 25A4
|
||||
ICH6 266A
|
||||
ICH7 27DA
|
||||
@ -74,6 +74,13 @@
|
||||
#define SMBHSTCFG_SMB_SMI_EN 2
|
||||
#define SMBHSTCFG_I2C_EN 4
|
||||
|
||||
/* Auxillary control register bits, ICH4+ only */
|
||||
#define SMBAUXCTL_CRC 1
|
||||
#define SMBAUXCTL_E32B 2
|
||||
|
||||
/* kill bit for SMBHSTCNT */
|
||||
#define SMBHSTCNT_KILL 2
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_TIMEOUT 100
|
||||
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
|
||||
@ -91,10 +98,15 @@
|
||||
#define I801_START 0x40
|
||||
#define I801_PEC_EN 0x80 /* ICH4 only */
|
||||
|
||||
|
||||
static int i801_transaction(void);
|
||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
int command, int hwpec);
|
||||
/* I801 Hosts Status register bits */
|
||||
#define SMBHSTSTS_BYTE_DONE 0x80
|
||||
#define SMBHSTSTS_INUSE_STS 0x40
|
||||
#define SMBHSTSTS_SMBALERT_STS 0x20
|
||||
#define SMBHSTSTS_FAILED 0x10
|
||||
#define SMBHSTSTS_BUS_ERR 0x08
|
||||
#define SMBHSTSTS_DEV_ERR 0x04
|
||||
#define SMBHSTSTS_INTR 0x02
|
||||
#define SMBHSTSTS_HOST_BUSY 0x01
|
||||
|
||||
static unsigned long i801_smba;
|
||||
static unsigned char i801_original_hstcfg;
|
||||
@ -102,7 +114,7 @@ static struct pci_driver i801_driver;
|
||||
static struct pci_dev *I801_dev;
|
||||
static int isich4;
|
||||
|
||||
static int i801_transaction(void)
|
||||
static int i801_transaction(int xact)
|
||||
{
|
||||
int temp;
|
||||
int result = 0;
|
||||
@ -127,33 +139,40 @@ static int i801_transaction(void)
|
||||
}
|
||||
}
|
||||
|
||||
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
|
||||
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
|
||||
* INTREN, SMBSCMD are passed in xact */
|
||||
outb_p(xact | I801_START, SMBHSTCNT);
|
||||
|
||||
/* We will always wait for a fraction of a second! */
|
||||
do {
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
|
||||
} while ((temp & SMBHSTSTS_HOST_BUSY) && (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;
|
||||
/* try to stop the current command */
|
||||
dev_dbg(&I801_dev->dev, "Terminating the current operation\n");
|
||||
outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT);
|
||||
msleep(1);
|
||||
outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL), SMBHSTCNT);
|
||||
}
|
||||
|
||||
if (temp & 0x10) {
|
||||
if (temp & SMBHSTSTS_FAILED) {
|
||||
result = -1;
|
||||
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
|
||||
}
|
||||
|
||||
if (temp & 0x08) {
|
||||
if (temp & SMBHSTSTS_BUS_ERR) {
|
||||
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) {
|
||||
if (temp & SMBHSTSTS_DEV_ERR) {
|
||||
result = -1;
|
||||
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
||||
}
|
||||
@ -172,44 +191,70 @@ static int i801_transaction(void)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* All-inclusive block transaction function */
|
||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
int command, int hwpec)
|
||||
/* wait for INTR bit as advised by Intel */
|
||||
static void i801_wait_hwpec(void)
|
||||
{
|
||||
int timeout = 0;
|
||||
int temp;
|
||||
|
||||
do {
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
} while ((!(temp & SMBHSTSTS_INTR))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
if (timeout >= MAX_TIMEOUT) {
|
||||
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
|
||||
}
|
||||
outb_p(temp, SMBHSTSTS);
|
||||
}
|
||||
|
||||
static int i801_block_transaction_by_block(union i2c_smbus_data *data,
|
||||
char read_write, int hwpec)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
inb_p(SMBHSTCNT); /* reset the data buffer index */
|
||||
|
||||
/* Use 32-byte buffer to process this transaction */
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
len = data->block[0];
|
||||
outb_p(len, SMBHSTDAT0);
|
||||
for (i = 0; i < len; i++)
|
||||
outb_p(data->block[i+1], SMBBLKDAT);
|
||||
}
|
||||
|
||||
if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
|
||||
I801_PEC_EN * hwpec))
|
||||
return -1;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
len = inb_p(SMBHSTDAT0);
|
||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
return -1;
|
||||
|
||||
data->block[0] = len;
|
||||
for (i = 0; i < len; i++)
|
||||
data->block[i + 1] = inb_p(SMBBLKDAT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
||||
char read_write, int hwpec)
|
||||
{
|
||||
int i, len;
|
||||
int smbcmd;
|
||||
int temp;
|
||||
int result = 0;
|
||||
int timeout;
|
||||
unsigned char hostc, errmask;
|
||||
unsigned char 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;
|
||||
}
|
||||
}
|
||||
len = data->block[0];
|
||||
|
||||
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++) {
|
||||
@ -227,13 +272,13 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
/* Make sure the SMBus host is ready to start transmitting */
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
if (i == 1) {
|
||||
/* Erronenous conditions before transaction:
|
||||
/* Erronenous conditions before transaction:
|
||||
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
|
||||
errmask=0x9f;
|
||||
errmask = 0x9f;
|
||||
} else {
|
||||
/* Erronenous conditions during transaction:
|
||||
/* Erronenous conditions during transaction:
|
||||
* Failed, Bus_Err, Dev_Err, Intr */
|
||||
errmask=0x1e;
|
||||
errmask = 0x1e;
|
||||
}
|
||||
if (temp & errmask) {
|
||||
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
|
||||
@ -242,14 +287,11 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
|
||||
dev_err(&I801_dev->dev,
|
||||
"Reset failed! (%02x)\n", temp);
|
||||
result = -1;
|
||||
goto END;
|
||||
return -1;
|
||||
}
|
||||
if (i != 1) {
|
||||
if (i != 1)
|
||||
/* if die in middle of block transaction, fail */
|
||||
result = -1;
|
||||
goto END;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
@ -261,33 +303,38 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
}
|
||||
while ((!(temp & 0x80))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
while ((!(temp & SMBHSTSTS_BYTE_DONE))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (timeout >= MAX_TIMEOUT) {
|
||||
/* try to stop the current command */
|
||||
dev_dbg(&I801_dev->dev, "Terminating the current "
|
||||
"operation\n");
|
||||
outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT);
|
||||
msleep(1);
|
||||
outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL),
|
||||
SMBHSTCNT);
|
||||
result = -1;
|
||||
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
|
||||
}
|
||||
|
||||
if (temp & 0x10) {
|
||||
if (temp & SMBHSTSTS_FAILED) {
|
||||
result = -1;
|
||||
dev_dbg(&I801_dev->dev,
|
||||
"Error: Failed bus transaction\n");
|
||||
} else if (temp & 0x08) {
|
||||
} else if (temp & SMBHSTSTS_BUS_ERR) {
|
||||
result = -1;
|
||||
dev_err(&I801_dev->dev, "Bus collision!\n");
|
||||
} else if (temp & 0x04) {
|
||||
} else if (temp & SMBHSTSTS_DEV_ERR) {
|
||||
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;
|
||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
return -1;
|
||||
data->block[0] = len;
|
||||
}
|
||||
|
||||
@ -310,25 +357,58 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
|
||||
|
||||
if (result < 0)
|
||||
goto END;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (hwpec) {
|
||||
/* wait for INTR bit as advised by Intel */
|
||||
timeout = 0;
|
||||
do {
|
||||
msleep(1);
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
} while ((!(temp & 0x02))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
static int i801_set_block_buffer_mode(void)
|
||||
{
|
||||
outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_E32B, SMBAUXCTL);
|
||||
if ((inb_p(SMBAUXCTL) & SMBAUXCTL_E32B) == 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout >= MAX_TIMEOUT) {
|
||||
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
|
||||
/* Block transaction function */
|
||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
int command, int hwpec)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned char hostc;
|
||||
|
||||
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;
|
||||
}
|
||||
outb_p(temp, SMBHSTSTS);
|
||||
}
|
||||
result = 0;
|
||||
END:
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
if (data->block[0] < 1)
|
||||
data->block[0] = 1;
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
} else {
|
||||
data->block[0] = 32; /* max for reads */
|
||||
}
|
||||
|
||||
if (isich4 && i801_set_block_buffer_mode() == 0 )
|
||||
result = i801_block_transaction_by_block(data, read_write,
|
||||
hwpec);
|
||||
else
|
||||
result = i801_block_transaction_byte_by_byte(data, read_write,
|
||||
hwpec);
|
||||
|
||||
if (result == 0 && hwpec)
|
||||
i801_wait_hwpec();
|
||||
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
/* restore saved configuration register value */
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
|
||||
@ -393,19 +473,22 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb_p(hwpec, SMBAUXCTL); /* enable/disable hardware PEC */
|
||||
if (hwpec) /* enable/disable hardware PEC */
|
||||
outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_CRC, SMBAUXCTL);
|
||||
else
|
||||
outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL);
|
||||
|
||||
if(block)
|
||||
ret = i801_block_transaction(data, read_write, size, hwpec);
|
||||
else {
|
||||
outb_p(xact | ENABLE_INT9, SMBHSTCNT);
|
||||
ret = i801_transaction();
|
||||
}
|
||||
else
|
||||
ret = i801_transaction(xact | ENABLE_INT9);
|
||||
|
||||
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
|
||||
time, so we forcibly disable it after every transaction. */
|
||||
time, so we forcibly disable it after every transaction. Turn off
|
||||
E32B for the same reason. */
|
||||
if (hwpec)
|
||||
outb_p(0, SMBAUXCTL);
|
||||
outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B),
|
||||
SMBAUXCTL);
|
||||
|
||||
if(block)
|
||||
return ret;
|
||||
|
@ -491,6 +491,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
||||
new_adapter->id = I2C_HW_IOP3XX;
|
||||
new_adapter->owner = THIS_MODULE;
|
||||
new_adapter->dev.parent = &pdev->dev;
|
||||
new_adapter->nr = pdev->id;
|
||||
|
||||
/*
|
||||
* Default values...should these come in from board code?
|
||||
@ -508,7 +509,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, new_adapter);
|
||||
new_adapter->algo_data = adapter_data;
|
||||
|
||||
i2c_add_adapter(new_adapter);
|
||||
i2c_add_numbered_adapter(new_adapter);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -74,6 +74,25 @@ static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
|
||||
* the bus, because it wants to send ACK.
|
||||
* Following sequence of enabling/disabling and sending start/stop generates
|
||||
* the pulse, so it's all OK.
|
||||
*/
|
||||
static void mpc_i2c_fixup(struct mpc_i2c *i2c)
|
||||
{
|
||||
writeccr(i2c, 0);
|
||||
udelay(30);
|
||||
writeccr(i2c, CCR_MEN);
|
||||
udelay(30);
|
||||
writeccr(i2c, CCR_MSTA | CCR_MTX);
|
||||
udelay(30);
|
||||
writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
|
||||
udelay(30);
|
||||
writeccr(i2c, CCR_MEN);
|
||||
udelay(30);
|
||||
}
|
||||
|
||||
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
@ -153,6 +172,7 @@ static void mpc_i2c_start(struct mpc_i2c *i2c)
|
||||
static void mpc_i2c_stop(struct mpc_i2c *i2c)
|
||||
{
|
||||
writeccr(i2c, CCR_MEN);
|
||||
writeccr(i2c, 0);
|
||||
}
|
||||
|
||||
static int mpc_write(struct mpc_i2c *i2c, int target,
|
||||
@ -245,6 +265,9 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
}
|
||||
if (time_after(jiffies, orig_jiffies + HZ)) {
|
||||
pr_debug("I2C: timeout\n");
|
||||
if (readb(i2c->base + MPC_I2C_SR) ==
|
||||
(CSR_MCF | CSR_MBB | CSR_RXAK))
|
||||
mpc_i2c_fixup(i2c);
|
||||
return -EIO;
|
||||
}
|
||||
schedule();
|
||||
@ -327,9 +350,10 @@ static int fsl_i2c_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
i2c->adap = mpc_ops;
|
||||
i2c->adap.nr = pdev->id;
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
if ((result = i2c_add_adapter(&i2c->adap)) < 0) {
|
||||
if ((result = i2c_add_numbered_adapter(&i2c->adap)) < 0) {
|
||||
printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
|
||||
goto fail_add;
|
||||
}
|
||||
|
@ -527,6 +527,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
drv_data->adapter.class = I2C_CLASS_HWMON;
|
||||
drv_data->adapter.timeout = pdata->timeout;
|
||||
drv_data->adapter.retries = pdata->retries;
|
||||
drv_data->adapter.nr = pd->id;
|
||||
platform_set_drvdata(pd, drv_data);
|
||||
i2c_set_adapdata(&drv_data->adapter, drv_data);
|
||||
|
||||
@ -539,7 +540,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
drv_data->irq);
|
||||
rc = -EINVAL;
|
||||
goto exit_unmap_regs;
|
||||
} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
|
||||
} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
|
||||
dev_err(&drv_data->adapter.dev,
|
||||
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
|
||||
goto exit_free_irq;
|
||||
|
@ -61,6 +61,7 @@ struct nforce2_smbus {
|
||||
struct i2c_adapter adapter;
|
||||
int base;
|
||||
int size;
|
||||
int blockops;
|
||||
};
|
||||
|
||||
|
||||
@ -80,6 +81,8 @@ struct nforce2_smbus {
|
||||
#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_STS_DONE 0x80
|
||||
#define NVIDIA_SMB_STS_ALRM 0x40
|
||||
@ -92,6 +95,7 @@ struct nforce2_smbus {
|
||||
#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_PEC 0x80
|
||||
|
||||
static struct pci_driver nforce2_driver;
|
||||
@ -103,6 +107,8 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
|
||||
{
|
||||
struct nforce2_smbus *smbus = adap->algo_data;
|
||||
unsigned char protocol, pec, temp;
|
||||
u8 len;
|
||||
int i;
|
||||
|
||||
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
|
||||
NVIDIA_SMB_PRTCL_WRITE;
|
||||
@ -137,6 +143,25 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
|
||||
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 = data->block[0];
|
||||
if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) {
|
||||
dev_err(&adap->dev,
|
||||
"Transaction failed "
|
||||
"(requested block size: %d)\n",
|
||||
len);
|
||||
return -1;
|
||||
}
|
||||
outb_p(len, NVIDIA_SMB_BCNT);
|
||||
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
|
||||
outb_p(data->block[i + 1],
|
||||
NVIDIA_SMB_DATA+i);
|
||||
}
|
||||
protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
|
||||
return -1;
|
||||
@ -174,6 +199,14 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
len = inb_p(NVIDIA_SMB_BCNT);
|
||||
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
||||
for (i = 0; i < len; i++)
|
||||
data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
|
||||
data->block[0] = len;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -184,7 +217,9 @@ 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_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
(((struct nforce2_smbus*)adapter->algo_data)->blockops ?
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA : 0);
|
||||
}
|
||||
|
||||
static struct i2c_algorithm smbus_algorithm = {
|
||||
@ -268,6 +303,13 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_
|
||||
return -ENOMEM;
|
||||
pci_set_drvdata(dev, smbuses);
|
||||
|
||||
switch(dev->device) {
|
||||
case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS:
|
||||
case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS:
|
||||
smbuses[0].blockops = 1;
|
||||
smbuses[1].blockops = 1;
|
||||
}
|
||||
|
||||
/* SMBus adapter 1 */
|
||||
res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
|
||||
if (res1 < 0) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
Supports:
|
||||
Intel PIIX4, 440MX
|
||||
Serverworks OSB4, CSB5, CSB6, HT-1000
|
||||
ATI IXP200, IXP300, IXP400, SB600
|
||||
ATI IXP200, IXP300, IXP400, SB600, SB700
|
||||
SMSC Victory66
|
||||
|
||||
Note: we assume there can only be one device, with one SMBus interface.
|
||||
@ -399,6 +399,8 @@ static struct pci_device_id piix4_ids[] = {
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SMBUS),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SMBUS),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
|
||||
|
653
drivers/i2c/busses/i2c-pmcmsp.c
Normal file
653
drivers/i2c/busses/i2c-pmcmsp.c
Normal file
@ -0,0 +1,653 @@
|
||||
/*
|
||||
* Specific bus support for PMC-TWI compliant implementation on MSP71xx.
|
||||
*
|
||||
* Copyright 2005-2007 PMC-Sierra, 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "pmcmsptwi"
|
||||
|
||||
#define MSP_TWI_SF_CLK_REG_OFFSET 0x00
|
||||
#define MSP_TWI_HS_CLK_REG_OFFSET 0x04
|
||||
#define MSP_TWI_CFG_REG_OFFSET 0x08
|
||||
#define MSP_TWI_CMD_REG_OFFSET 0x0c
|
||||
#define MSP_TWI_ADD_REG_OFFSET 0x10
|
||||
#define MSP_TWI_DAT_0_REG_OFFSET 0x14
|
||||
#define MSP_TWI_DAT_1_REG_OFFSET 0x18
|
||||
#define MSP_TWI_INT_STS_REG_OFFSET 0x1c
|
||||
#define MSP_TWI_INT_MSK_REG_OFFSET 0x20
|
||||
#define MSP_TWI_BUSY_REG_OFFSET 0x24
|
||||
|
||||
#define MSP_TWI_INT_STS_DONE (1 << 0)
|
||||
#define MSP_TWI_INT_STS_LOST_ARBITRATION (1 << 1)
|
||||
#define MSP_TWI_INT_STS_NO_RESPONSE (1 << 2)
|
||||
#define MSP_TWI_INT_STS_DATA_COLLISION (1 << 3)
|
||||
#define MSP_TWI_INT_STS_BUSY (1 << 4)
|
||||
#define MSP_TWI_INT_STS_ALL 0x1f
|
||||
|
||||
#define MSP_MAX_BYTES_PER_RW 8
|
||||
#define MSP_MAX_POLL 5
|
||||
#define MSP_POLL_DELAY 10
|
||||
#define MSP_IRQ_TIMEOUT (MSP_MAX_POLL * MSP_POLL_DELAY)
|
||||
|
||||
/* IO Operation macros */
|
||||
#define pmcmsptwi_readl __raw_readl
|
||||
#define pmcmsptwi_writel __raw_writel
|
||||
|
||||
/* TWI command type */
|
||||
enum pmcmsptwi_cmd_type {
|
||||
MSP_TWI_CMD_WRITE = 0, /* Write only */
|
||||
MSP_TWI_CMD_READ = 1, /* Read only */
|
||||
MSP_TWI_CMD_WRITE_READ = 2, /* Write then Read */
|
||||
};
|
||||
|
||||
/* The possible results of the xferCmd */
|
||||
enum pmcmsptwi_xfer_result {
|
||||
MSP_TWI_XFER_OK = 0,
|
||||
MSP_TWI_XFER_TIMEOUT,
|
||||
MSP_TWI_XFER_BUSY,
|
||||
MSP_TWI_XFER_DATA_COLLISION,
|
||||
MSP_TWI_XFER_NO_RESPONSE,
|
||||
MSP_TWI_XFER_LOST_ARBITRATION,
|
||||
};
|
||||
|
||||
/* Corresponds to a PMCTWI clock configuration register */
|
||||
struct pmcmsptwi_clock {
|
||||
u8 filter; /* Bits 15:12, default = 0x03 */
|
||||
u16 clock; /* Bits 9:0, default = 0x001f */
|
||||
};
|
||||
|
||||
struct pmcmsptwi_clockcfg {
|
||||
struct pmcmsptwi_clock standard; /* The standard/fast clock config */
|
||||
struct pmcmsptwi_clock highspeed; /* The highspeed clock config */
|
||||
};
|
||||
|
||||
/* Corresponds to the main TWI configuration register */
|
||||
struct pmcmsptwi_cfg {
|
||||
u8 arbf; /* Bits 15:12, default=0x03 */
|
||||
u8 nak; /* Bits 11:8, default=0x03 */
|
||||
u8 add10; /* Bit 7, default=0x00 */
|
||||
u8 mst_code; /* Bits 6:4, default=0x00 */
|
||||
u8 arb; /* Bit 1, default=0x01 */
|
||||
u8 highspeed; /* Bit 0, default=0x00 */
|
||||
};
|
||||
|
||||
/* A single pmctwi command to issue */
|
||||
struct pmcmsptwi_cmd {
|
||||
u16 addr; /* The slave address (7 or 10 bits) */
|
||||
enum pmcmsptwi_cmd_type type; /* The command type */
|
||||
u8 write_len; /* Number of bytes in the write buffer */
|
||||
u8 read_len; /* Number of bytes in the read buffer */
|
||||
u8 *write_data; /* Buffer of characters to send */
|
||||
u8 *read_data; /* Buffer to fill with incoming data */
|
||||
};
|
||||
|
||||
/* The private data */
|
||||
struct pmcmsptwi_data {
|
||||
void __iomem *iobase; /* iomapped base for IO */
|
||||
int irq; /* IRQ to use (0 disables) */
|
||||
struct completion wait; /* Completion for xfer */
|
||||
struct mutex lock; /* Used for threadsafeness */
|
||||
enum pmcmsptwi_xfer_result last_result; /* result of last xfer */
|
||||
};
|
||||
|
||||
/* The default settings */
|
||||
const static struct pmcmsptwi_clockcfg pmcmsptwi_defclockcfg = {
|
||||
.standard = {
|
||||
.filter = 0x3,
|
||||
.clock = 0x1f,
|
||||
},
|
||||
.highspeed = {
|
||||
.filter = 0x3,
|
||||
.clock = 0x1f,
|
||||
},
|
||||
};
|
||||
|
||||
const static struct pmcmsptwi_cfg pmcmsptwi_defcfg = {
|
||||
.arbf = 0x03,
|
||||
.nak = 0x03,
|
||||
.add10 = 0x00,
|
||||
.mst_code = 0x00,
|
||||
.arb = 0x01,
|
||||
.highspeed = 0x00,
|
||||
};
|
||||
|
||||
static struct pmcmsptwi_data pmcmsptwi_data;
|
||||
|
||||
static struct i2c_adapter pmcmsptwi_adapter;
|
||||
|
||||
/* inline helper functions */
|
||||
static inline u32 pmcmsptwi_clock_to_reg(
|
||||
const struct pmcmsptwi_clock *clock)
|
||||
{
|
||||
return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff);
|
||||
}
|
||||
|
||||
static inline void pmcmsptwi_reg_to_clock(
|
||||
u32 reg, struct pmcmsptwi_clock *clock)
|
||||
{
|
||||
clock->filter = (reg >> 12) & 0xf;
|
||||
clock->clock = reg & 0x03ff;
|
||||
}
|
||||
|
||||
static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg)
|
||||
{
|
||||
return ((cfg->arbf & 0xf) << 12) |
|
||||
((cfg->nak & 0xf) << 8) |
|
||||
((cfg->add10 & 0x1) << 7) |
|
||||
((cfg->mst_code & 0x7) << 4) |
|
||||
((cfg->arb & 0x1) << 1) |
|
||||
(cfg->highspeed & 0x1);
|
||||
}
|
||||
|
||||
static inline void pmcmsptwi_reg_to_cfg(u32 reg, struct pmcmsptwi_cfg *cfg)
|
||||
{
|
||||
cfg->arbf = (reg >> 12) & 0xf;
|
||||
cfg->nak = (reg >> 8) & 0xf;
|
||||
cfg->add10 = (reg >> 7) & 0x1;
|
||||
cfg->mst_code = (reg >> 4) & 0x7;
|
||||
cfg->arb = (reg >> 1) & 0x1;
|
||||
cfg->highspeed = reg & 0x1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current clock configuration
|
||||
*/
|
||||
static void pmcmsptwi_set_clock_config(const struct pmcmsptwi_clockcfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->standard),
|
||||
data->iobase + MSP_TWI_SF_CLK_REG_OFFSET);
|
||||
pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->highspeed),
|
||||
data->iobase + MSP_TWI_HS_CLK_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the current TWI bus configuration
|
||||
*/
|
||||
static void pmcmsptwi_get_twi_config(struct pmcmsptwi_cfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_reg_to_cfg(pmcmsptwi_readl(
|
||||
data->iobase + MSP_TWI_CFG_REG_OFFSET), cfg);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current TWI bus configuration
|
||||
*/
|
||||
static void pmcmsptwi_set_twi_config(const struct pmcmsptwi_cfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_writel(pmcmsptwi_cfg_to_reg(cfg),
|
||||
data->iobase + MSP_TWI_CFG_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the 'int_sts' register and returns a well-defined error code
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_get_result(u32 reg)
|
||||
{
|
||||
if (reg & MSP_TWI_INT_STS_LOST_ARBITRATION) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Lost arbitration\n");
|
||||
return MSP_TWI_XFER_LOST_ARBITRATION;
|
||||
} else if (reg & MSP_TWI_INT_STS_NO_RESPONSE) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: No response\n");
|
||||
return MSP_TWI_XFER_NO_RESPONSE;
|
||||
} else if (reg & MSP_TWI_INT_STS_DATA_COLLISION) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Data collision\n");
|
||||
return MSP_TWI_XFER_DATA_COLLISION;
|
||||
} else if (reg & MSP_TWI_INT_STS_BUSY) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Bus busy\n");
|
||||
return MSP_TWI_XFER_BUSY;
|
||||
}
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Result: Operation succeeded\n");
|
||||
return MSP_TWI_XFER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* In interrupt mode, handle the interrupt.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static irqreturn_t pmcmsptwi_interrupt(int irq, void *ptr)
|
||||
{
|
||||
struct pmcmsptwi_data *data = ptr;
|
||||
|
||||
u32 reason = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
pmcmsptwi_writel(reason, data->iobase + MSP_TWI_INT_STS_REG_OFFSET);
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Got interrupt 0x%08x\n", reason);
|
||||
if (!(reason & MSP_TWI_INT_STS_DONE))
|
||||
return IRQ_NONE;
|
||||
|
||||
data->last_result = pmcmsptwi_get_result(reason);
|
||||
complete(&data->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for and register the device and return 0 if there is one.
|
||||
*/
|
||||
static int __devinit pmcmsptwi_probe(struct platform_device *pldev)
|
||||
{
|
||||
struct resource *res;
|
||||
int rc = -ENODEV;
|
||||
|
||||
/* get the static platform resources */
|
||||
res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pldev->dev, "IOMEM resource not found\n");
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* reserve the memory region */
|
||||
if (!request_mem_region(res->start, res->end - res->start + 1,
|
||||
pldev->name)) {
|
||||
dev_err(&pldev->dev,
|
||||
"Unable to get memory/io address region 0x%08x\n",
|
||||
res->start);
|
||||
rc = -EBUSY;
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* remap the memory */
|
||||
pmcmsptwi_data.iobase = ioremap_nocache(res->start,
|
||||
res->end - res->start + 1);
|
||||
if (!pmcmsptwi_data.iobase) {
|
||||
dev_err(&pldev->dev,
|
||||
"Unable to ioremap address 0x%08x\n", res->start);
|
||||
rc = -EIO;
|
||||
goto ret_unreserve;
|
||||
}
|
||||
|
||||
/* request the irq */
|
||||
pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
|
||||
if (pmcmsptwi_data.irq) {
|
||||
rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
|
||||
IRQF_SHARED | IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
|
||||
pldev->name, &pmcmsptwi_data);
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* Enable 'DONE' interrupt only.
|
||||
*
|
||||
* If you enable all interrupts, you will get one on
|
||||
* error and another when the operation completes.
|
||||
* This way you only have to handle one interrupt,
|
||||
* but you can still check all result flags.
|
||||
*/
|
||||
pmcmsptwi_writel(MSP_TWI_INT_STS_DONE,
|
||||
pmcmsptwi_data.iobase +
|
||||
MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
} else {
|
||||
dev_warn(&pldev->dev,
|
||||
"Could not assign TWI IRQ handler "
|
||||
"to irq %d (continuing with poll)\n",
|
||||
pmcmsptwi_data.irq);
|
||||
pmcmsptwi_data.irq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
init_completion(&pmcmsptwi_data.wait);
|
||||
mutex_init(&pmcmsptwi_data.lock);
|
||||
|
||||
pmcmsptwi_set_clock_config(&pmcmsptwi_defclockcfg, &pmcmsptwi_data);
|
||||
pmcmsptwi_set_twi_config(&pmcmsptwi_defcfg, &pmcmsptwi_data);
|
||||
|
||||
printk(KERN_INFO DRV_NAME ": Registering MSP71xx I2C adapter\n");
|
||||
|
||||
pmcmsptwi_adapter.dev.parent = &pldev->dev;
|
||||
platform_set_drvdata(pldev, &pmcmsptwi_adapter);
|
||||
i2c_set_adapdata(&pmcmsptwi_adapter, &pmcmsptwi_data);
|
||||
|
||||
rc = i2c_add_adapter(&pmcmsptwi_adapter);
|
||||
if (rc) {
|
||||
dev_err(&pldev->dev, "Unable to register I2C adapter\n");
|
||||
goto ret_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ret_unmap:
|
||||
platform_set_drvdata(pldev, NULL);
|
||||
if (pmcmsptwi_data.irq) {
|
||||
pmcmsptwi_writel(0,
|
||||
pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
|
||||
}
|
||||
|
||||
iounmap(pmcmsptwi_data.iobase);
|
||||
|
||||
ret_unreserve:
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
|
||||
ret_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the device and return 0 if there is one.
|
||||
*/
|
||||
static int __devexit pmcmsptwi_remove(struct platform_device *pldev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
i2c_del_adapter(&pmcmsptwi_adapter);
|
||||
|
||||
platform_set_drvdata(pldev, NULL);
|
||||
if (pmcmsptwi_data.irq) {
|
||||
pmcmsptwi_writel(0,
|
||||
pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
|
||||
}
|
||||
|
||||
iounmap(pmcmsptwi_data.iobase);
|
||||
|
||||
res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, res->end - res->start + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Polls the 'busy' register until the command is complete.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static void pmcmsptwi_poll_complete(struct pmcmsptwi_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MSP_MAX_POLL; i++) {
|
||||
u32 val = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_BUSY_REG_OFFSET);
|
||||
if (val == 0) {
|
||||
u32 reason = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
pmcmsptwi_writel(reason, data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
data->last_result = pmcmsptwi_get_result(reason);
|
||||
return;
|
||||
}
|
||||
udelay(MSP_POLL_DELAY);
|
||||
}
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Result: Poll timeout\n");
|
||||
data->last_result = MSP_TWI_XFER_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the transfer (low level):
|
||||
* May use interrupt-driven or polling, depending on if an IRQ is
|
||||
* presently registered.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_do_xfer(
|
||||
u32 reg, struct pmcmsptwi_data *data)
|
||||
{
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Writing cmd reg 0x%08x\n", reg);
|
||||
pmcmsptwi_writel(reg, data->iobase + MSP_TWI_CMD_REG_OFFSET);
|
||||
if (data->irq) {
|
||||
unsigned long timeleft = wait_for_completion_timeout(
|
||||
&data->wait, MSP_IRQ_TIMEOUT);
|
||||
if (timeleft == 0) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: IRQ timeout\n");
|
||||
complete(&data->wait);
|
||||
data->last_result = MSP_TWI_XFER_TIMEOUT;
|
||||
}
|
||||
} else
|
||||
pmcmsptwi_poll_complete(data);
|
||||
|
||||
return data->last_result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routine, converts 'pmctwi_cmd' struct to register format
|
||||
*/
|
||||
static inline u32 pmcmsptwi_cmd_to_reg(const struct pmcmsptwi_cmd *cmd)
|
||||
{
|
||||
return ((cmd->type & 0x3) << 8) |
|
||||
(((cmd->write_len - 1) & 0x7) << 4) |
|
||||
((cmd->read_len - 1) & 0x7);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the transfer (high level)
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd(
|
||||
struct pmcmsptwi_cmd *cmd,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
enum pmcmsptwi_xfer_result retval;
|
||||
|
||||
if ((cmd->type == MSP_TWI_CMD_WRITE && cmd->write_len == 0) ||
|
||||
(cmd->type == MSP_TWI_CMD_READ && cmd->read_len == 0) ||
|
||||
(cmd->type == MSP_TWI_CMD_WRITE_READ &&
|
||||
(cmd->read_len == 0 || cmd->write_len == 0))) {
|
||||
dev_err(&pmcmsptwi_adapter.dev,
|
||||
"%s: Cannot transfer less than 1 byte\n",
|
||||
__FUNCTION__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cmd->read_len > MSP_MAX_BYTES_PER_RW ||
|
||||
cmd->write_len > MSP_MAX_BYTES_PER_RW) {
|
||||
dev_err(&pmcmsptwi_adapter.dev,
|
||||
"%s: Cannot transfer more than %d bytes\n",
|
||||
__FUNCTION__, MSP_MAX_BYTES_PER_RW);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Setting address to 0x%04x\n", cmd->addr);
|
||||
pmcmsptwi_writel(cmd->addr, data->iobase + MSP_TWI_ADD_REG_OFFSET);
|
||||
|
||||
if (cmd->type == MSP_TWI_CMD_WRITE ||
|
||||
cmd->type == MSP_TWI_CMD_WRITE_READ) {
|
||||
__be64 tmp = cpu_to_be64p((u64 *)cmd->write_data);
|
||||
tmp >>= (MSP_MAX_BYTES_PER_RW - cmd->write_len) * 8;
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Writing 0x%016llx\n", tmp);
|
||||
pmcmsptwi_writel(tmp & 0x00000000ffffffffLL,
|
||||
data->iobase + MSP_TWI_DAT_0_REG_OFFSET);
|
||||
if (cmd->write_len > 4)
|
||||
pmcmsptwi_writel(tmp >> 32,
|
||||
data->iobase + MSP_TWI_DAT_1_REG_OFFSET);
|
||||
}
|
||||
|
||||
retval = pmcmsptwi_do_xfer(pmcmsptwi_cmd_to_reg(cmd), data);
|
||||
if (retval != MSP_TWI_XFER_OK)
|
||||
goto xfer_err;
|
||||
|
||||
if (cmd->type == MSP_TWI_CMD_READ ||
|
||||
cmd->type == MSP_TWI_CMD_WRITE_READ) {
|
||||
int i;
|
||||
u64 rmsk = ~(0xffffffffffffffffLL << (cmd->read_len * 8));
|
||||
u64 tmp = (u64)pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_DAT_0_REG_OFFSET);
|
||||
if (cmd->read_len > 4)
|
||||
tmp |= (u64)pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_DAT_1_REG_OFFSET) << 32;
|
||||
tmp &= rmsk;
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Read 0x%016llx\n", tmp);
|
||||
|
||||
for (i = 0; i < cmd->read_len; i++)
|
||||
cmd->read_data[i] = tmp >> i;
|
||||
}
|
||||
|
||||
xfer_err:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* -- Algorithm functions -- */
|
||||
|
||||
/*
|
||||
* Sends an i2c command out on the adapter
|
||||
*/
|
||||
static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, int num)
|
||||
{
|
||||
struct pmcmsptwi_data *data = i2c_get_adapdata(adap);
|
||||
struct pmcmsptwi_cmd cmd;
|
||||
struct pmcmsptwi_cfg oldcfg, newcfg;
|
||||
int ret;
|
||||
|
||||
if (num > 2) {
|
||||
dev_dbg(&adap->dev, "%d messages unsupported\n", num);
|
||||
return -EINVAL;
|
||||
} else if (num == 2) {
|
||||
/* Check for a dual write-then-read command */
|
||||
struct i2c_msg *nextmsg = msg + 1;
|
||||
if (!(msg->flags & I2C_M_RD) &&
|
||||
(nextmsg->flags & I2C_M_RD) &&
|
||||
msg->addr == nextmsg->addr) {
|
||||
cmd.type = MSP_TWI_CMD_WRITE_READ;
|
||||
cmd.write_len = msg->len;
|
||||
cmd.write_data = msg->buf;
|
||||
cmd.read_len = nextmsg->len;
|
||||
cmd.read_data = nextmsg->buf;
|
||||
} else {
|
||||
dev_dbg(&adap->dev,
|
||||
"Non write-read dual messages unsupported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (msg->flags & I2C_M_RD) {
|
||||
cmd.type = MSP_TWI_CMD_READ;
|
||||
cmd.read_len = msg->len;
|
||||
cmd.read_data = msg->buf;
|
||||
cmd.write_len = 0;
|
||||
cmd.write_data = NULL;
|
||||
} else {
|
||||
cmd.type = MSP_TWI_CMD_WRITE;
|
||||
cmd.read_len = 0;
|
||||
cmd.read_data = NULL;
|
||||
cmd.write_len = msg->len;
|
||||
cmd.write_data = msg->buf;
|
||||
}
|
||||
|
||||
if (msg->len == 0) {
|
||||
dev_err(&adap->dev, "Zero-byte messages unsupported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cmd.addr = msg->addr;
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
pmcmsptwi_get_twi_config(&newcfg, data);
|
||||
memcpy(&oldcfg, &newcfg, sizeof(oldcfg));
|
||||
|
||||
/* Set the special 10-bit address flag */
|
||||
newcfg.add10 = 1;
|
||||
|
||||
pmcmsptwi_set_twi_config(&newcfg, data);
|
||||
}
|
||||
|
||||
/* Execute the command */
|
||||
ret = pmcmsptwi_xfer_cmd(&cmd, data);
|
||||
|
||||
if (msg->flags & I2C_M_TEN)
|
||||
pmcmsptwi_set_twi_config(&oldcfg, data);
|
||||
|
||||
dev_dbg(&adap->dev, "I2C %s of %d bytes ",
|
||||
(msg->flags & I2C_M_RD) ? "read" : "write", msg->len);
|
||||
if (ret != MSP_TWI_XFER_OK) {
|
||||
/*
|
||||
* TODO: We could potentially loop and retry in the case
|
||||
* of MSP_TWI_XFER_TIMEOUT.
|
||||
*/
|
||||
dev_dbg(&adap->dev, "failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev_dbg(&adap->dev, "succeeded\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
|
||||
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL;
|
||||
}
|
||||
|
||||
/* -- Initialization -- */
|
||||
|
||||
static struct i2c_algorithm pmcmsptwi_algo = {
|
||||
.master_xfer = pmcmsptwi_master_xfer,
|
||||
.functionality = pmcmsptwi_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter pmcmsptwi_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.algo = &pmcmsptwi_algo,
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static struct platform_driver pmcmsptwi_driver = {
|
||||
.probe = pmcmsptwi_probe,
|
||||
.remove = __devexit_p(pmcmsptwi_remove),
|
||||
.driver {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pmcmsptwi_init(void)
|
||||
{
|
||||
return platform_driver_register(&pmcmsptwi_driver);
|
||||
}
|
||||
|
||||
static void __exit pmcmsptwi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pmcmsptwi_driver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("PMC MSP TWI/SMBus/I2C driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(pmcmsptwi_init);
|
||||
module_exit(pmcmsptwi_exit);
|
@ -121,8 +121,7 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
|
||||
if (rc)
|
||||
goto bail;
|
||||
rc = pmac_i2c_xfer(bus, addrdir, 1, command,
|
||||
read ? data->block : &data->block[1],
|
||||
data->block[0]);
|
||||
&data->block[1], data->block[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -921,7 +921,14 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
||||
i2c->adap.class = plat->class;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&i2c->adap);
|
||||
/*
|
||||
* If "dev->id" is negative we consider it as zero.
|
||||
* The reason to do so is to avoid sysfs names that only make
|
||||
* sense when there are multiple adapters.
|
||||
*/
|
||||
i2c->adap.nr = dev->id >= 0 ? dev->id : 0;
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "I2C: Failed to add bus\n");
|
||||
goto eadapt;
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-8xx.h>
|
||||
#include <asm/mpc8xx.h>
|
||||
#include <asm/commproc.h>
|
||||
|
||||
|
||||
static void
|
||||
rpx_iic_init(struct i2c_algo_8xx_data *data)
|
||||
{
|
||||
volatile cpm8xx_t *cp;
|
||||
volatile immap_t *immap;
|
||||
|
||||
cp = cpmp; /* Get pointer to Communication Processor */
|
||||
immap = (immap_t *)IMAP_ADDR; /* and to internal registers */
|
||||
|
||||
data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
|
||||
|
||||
/* Check for and use a microcode relocation patch.
|
||||
*/
|
||||
if ((data->reloc = data->iip->iic_rpbase))
|
||||
data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
|
||||
|
||||
data->i2c = (i2c8xx_t *)&(immap->im_i2c);
|
||||
data->cp = cp;
|
||||
|
||||
/* Initialize Port B IIC pins.
|
||||
*/
|
||||
cp->cp_pbpar |= 0x00000030;
|
||||
cp->cp_pbdir |= 0x00000030;
|
||||
cp->cp_pbodr |= 0x00000030;
|
||||
|
||||
/* Allocate space for two transmit and two receive buffer
|
||||
* descriptors in the DP ram.
|
||||
*/
|
||||
data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 4, 8);
|
||||
|
||||
/* ptr to i2c area */
|
||||
data->i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
|
||||
}
|
||||
|
||||
static int rpx_install_isr(int irq, void (*func)(void *), void *data)
|
||||
{
|
||||
/* install interrupt handler */
|
||||
cpm_install_handler(irq, func, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_algo_8xx_data rpx_data = {
|
||||
.setisr = rpx_install_isr
|
||||
};
|
||||
|
||||
static struct i2c_adapter rpx_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "m8xx",
|
||||
.id = I2C_HW_MPC8XX_EPON,
|
||||
.algo_data = &rpx_data,
|
||||
};
|
||||
|
||||
int __init i2c_rpx_init(void)
|
||||
{
|
||||
printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
|
||||
|
||||
/* reset hardware to sane state */
|
||||
rpx_iic_init(&rpx_data);
|
||||
|
||||
if (i2c_8xx_add_bus(&rpx_ops) < 0) {
|
||||
printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit i2c_rpx_exit(void)
|
||||
{
|
||||
i2c_8xx_del_bus(&rpx_ops);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
|
||||
MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
|
||||
|
||||
module_init(i2c_rpx_init);
|
||||
module_exit(i2c_rpx_exit);
|
@ -25,8 +25,6 @@
|
||||
/* 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>
|
||||
@ -37,36 +35,19 @@
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* 3DFX defines */
|
||||
#define PCI_CHIP_SAVAGE3D 0x8A20
|
||||
#define PCI_CHIP_SAVAGE3D_MV 0x8A21
|
||||
/* device IDs */
|
||||
#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)
|
||||
|
@ -129,6 +129,7 @@ MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller"
|
||||
|
||||
static struct pci_driver sis5595_driver;
|
||||
static unsigned short sis5595_base;
|
||||
static struct pci_dev *sis5595_pdev;
|
||||
|
||||
static u8 sis5595_read(u8 reg)
|
||||
{
|
||||
@ -379,6 +380,8 @@ MODULE_DEVICE_TABLE (pci, sis5595_ids);
|
||||
|
||||
static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (sis5595_setup(dev)) {
|
||||
dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
|
||||
return -ENODEV;
|
||||
@ -389,20 +392,24 @@ static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_
|
||||
|
||||
sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
|
||||
sis5595_base + SMB_INDEX);
|
||||
return i2c_add_adapter(&sis5595_adapter);
|
||||
}
|
||||
err = i2c_add_adapter(&sis5595_adapter);
|
||||
if (err) {
|
||||
release_region(sis5595_base + SMB_INDEX, 2);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit sis5595_remove(struct pci_dev *dev)
|
||||
{
|
||||
i2c_del_adapter(&sis5595_adapter);
|
||||
release_region(sis5595_base + SMB_INDEX, 2);
|
||||
/* Always return failure here. This is to allow other drivers to bind
|
||||
* to this pci device. We don't really want to have control over the
|
||||
* pci device, we only wanted to read as few register values from it.
|
||||
*/
|
||||
sis5595_pdev = pci_dev_get(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -413,6 +420,12 @@ static int __init i2c_sis5595_init(void)
|
||||
static void __exit i2c_sis5595_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&sis5595_driver);
|
||||
if (sis5595_pdev) {
|
||||
i2c_del_adapter(&sis5595_adapter);
|
||||
release_region(sis5595_base + SMB_INDEX, 2);
|
||||
pci_dev_put(sis5595_pdev);
|
||||
sis5595_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
|
330
drivers/i2c/busses/i2c-taos-evm.c
Normal file
330
drivers/i2c/busses/i2c-taos-evm.c
Normal file
@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Driver for the TAOS evaluation modules
|
||||
* These devices include an I2C master which can be controlled over the
|
||||
* serial port.
|
||||
*
|
||||
* Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#define TAOS_BUFFER_SIZE 63
|
||||
|
||||
#define TAOS_STATE_INIT 0
|
||||
#define TAOS_STATE_IDLE 1
|
||||
#define TAOS_STATE_SEND 2
|
||||
#define TAOS_STATE_RECV 3
|
||||
|
||||
#define TAOS_CMD_RESET 0x12
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(wq);
|
||||
|
||||
struct taos_data {
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_client *client;
|
||||
int state;
|
||||
u8 addr; /* last used address */
|
||||
unsigned char buffer[TAOS_BUFFER_SIZE];
|
||||
unsigned int pos; /* position inside the buffer */
|
||||
};
|
||||
|
||||
/* TAOS TSL2550 EVM */
|
||||
static struct i2c_board_info tsl2550_info = {
|
||||
I2C_BOARD_INFO("tsl2550", 0x39),
|
||||
.type = "tsl2550",
|
||||
};
|
||||
|
||||
/* Instantiate i2c devices based on the adapter name */
|
||||
static struct i2c_client *taos_instantiate_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
if (!strncmp(adapter->name, "TAOS TSL2550 EVM", 16)) {
|
||||
dev_info(&adapter->dev, "Instantiating device %s at 0x%02x\n",
|
||||
tsl2550_info.driver_name, tsl2550_info.addr);
|
||||
return i2c_new_device(adapter, &tsl2550_info);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||
unsigned short flags, char read_write, u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct serio *serio = adapter->algo_data;
|
||||
struct taos_data *taos = serio_get_drvdata(serio);
|
||||
char *p;
|
||||
|
||||
/* Encode our transaction. "@" is for the device address, "$" for the
|
||||
SMBus command and "#" for the data. */
|
||||
p = taos->buffer;
|
||||
|
||||
/* The device remembers the last used address, no need to send it
|
||||
again if it's the same */
|
||||
if (addr != taos->addr)
|
||||
p += sprintf(p, "@%02X", addr);
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
sprintf(p, "$#%02X", command);
|
||||
else
|
||||
sprintf(p, "$");
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
sprintf(p, "$%02X#%02X", command, data->byte);
|
||||
else
|
||||
sprintf(p, "$%02X", command);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&adapter->dev, "Unsupported transaction size %d\n",
|
||||
size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Send the transaction to the TAOS EVM */
|
||||
dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer);
|
||||
taos->pos = 0;
|
||||
taos->state = TAOS_STATE_SEND;
|
||||
serio_write(serio, taos->buffer[0]);
|
||||
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
||||
msecs_to_jiffies(250));
|
||||
if (taos->state != TAOS_STATE_IDLE) {
|
||||
dev_err(&adapter->dev, "Transaction failed "
|
||||
"(state=%d, pos=%d)\n", taos->state, taos->pos);
|
||||
taos->addr = 0;
|
||||
return -EIO;
|
||||
}
|
||||
taos->addr = addr;
|
||||
|
||||
/* Start the transaction and read the answer */
|
||||
taos->pos = 0;
|
||||
taos->state = TAOS_STATE_RECV;
|
||||
serio_write(serio, read_write == I2C_SMBUS_WRITE ? '>' : '<');
|
||||
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
||||
msecs_to_jiffies(150));
|
||||
if (taos->state != TAOS_STATE_IDLE
|
||||
|| taos->pos != 6) {
|
||||
dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n",
|
||||
taos->pos);
|
||||
return -EIO;
|
||||
}
|
||||
dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer);
|
||||
|
||||
/* Interpret the returned string */
|
||||
p = taos->buffer + 2;
|
||||
p[3] = '\0';
|
||||
if (!strcmp(p, "NAK"))
|
||||
return -ENODEV;
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
if (!strcmp(p, "ACK"))
|
||||
return 0;
|
||||
} else {
|
||||
if (p[0] == 'x') {
|
||||
data->byte = simple_strtol(p + 1, NULL, 16);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static u32 taos_smbus_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm taos_algorithm = {
|
||||
.smbus_xfer = taos_smbus_xfer,
|
||||
.functionality = taos_smbus_func,
|
||||
};
|
||||
|
||||
static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct taos_data *taos = serio_get_drvdata(serio);
|
||||
|
||||
switch (taos->state) {
|
||||
case TAOS_STATE_INIT:
|
||||
taos->buffer[taos->pos++] = data;
|
||||
if (data == ':'
|
||||
|| taos->pos == TAOS_BUFFER_SIZE - 1) {
|
||||
taos->buffer[taos->pos] = '\0';
|
||||
taos->state = TAOS_STATE_IDLE;
|
||||
wake_up_interruptible(&wq);
|
||||
}
|
||||
break;
|
||||
case TAOS_STATE_SEND:
|
||||
if (taos->buffer[++taos->pos])
|
||||
serio_write(serio, taos->buffer[taos->pos]);
|
||||
else {
|
||||
taos->state = TAOS_STATE_IDLE;
|
||||
wake_up_interruptible(&wq);
|
||||
}
|
||||
break;
|
||||
case TAOS_STATE_RECV:
|
||||
taos->buffer[taos->pos++] = data;
|
||||
if (data == ']') {
|
||||
taos->buffer[taos->pos] = '\0';
|
||||
taos->state = TAOS_STATE_IDLE;
|
||||
wake_up_interruptible(&wq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Extract the adapter name from the buffer received after reset.
|
||||
The buffer is modified and a pointer inside the buffer is returned. */
|
||||
static char *taos_adapter_name(char *buffer)
|
||||
{
|
||||
char *start, *end;
|
||||
|
||||
start = strstr(buffer, "TAOS ");
|
||||
if (!start)
|
||||
return NULL;
|
||||
|
||||
end = strchr(start, '\r');
|
||||
if (!end)
|
||||
return NULL;
|
||||
*end = '\0';
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static int taos_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct taos_data *taos;
|
||||
struct i2c_adapter *adapter;
|
||||
char *name;
|
||||
int err;
|
||||
|
||||
taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL);
|
||||
if (!taos) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
taos->state = TAOS_STATE_INIT;
|
||||
serio_set_drvdata(serio, taos);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto exit_kfree;
|
||||
|
||||
adapter = &taos->adapter;
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->algo = &taos_algorithm;
|
||||
adapter->algo_data = serio;
|
||||
adapter->dev.parent = &serio->dev;
|
||||
|
||||
/* Reset the TAOS evaluation module to identify it */
|
||||
serio_write(serio, TAOS_CMD_RESET);
|
||||
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
||||
msecs_to_jiffies(2000));
|
||||
|
||||
if (taos->state != TAOS_STATE_IDLE) {
|
||||
err = -ENODEV;
|
||||
dev_dbg(&serio->dev, "TAOS EVM reset failed (state=%d, "
|
||||
"pos=%d)\n", taos->state, taos->pos);
|
||||
goto exit_close;
|
||||
}
|
||||
|
||||
name = taos_adapter_name(taos->buffer);
|
||||
if (!name) {
|
||||
err = -ENODEV;
|
||||
dev_err(&serio->dev, "TAOS EVM identification failed\n");
|
||||
goto exit_close;
|
||||
}
|
||||
strlcpy(adapter->name, name, sizeof(adapter->name));
|
||||
|
||||
err = i2c_add_adapter(adapter);
|
||||
if (err)
|
||||
goto exit_close;
|
||||
dev_dbg(&serio->dev, "Connected to TAOS EVM\n");
|
||||
|
||||
taos->client = taos_instantiate_device(adapter);
|
||||
return 0;
|
||||
|
||||
exit_close:
|
||||
serio_close(serio);
|
||||
exit_kfree:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
kfree(taos);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void taos_disconnect(struct serio *serio)
|
||||
{
|
||||
struct taos_data *taos = serio_get_drvdata(serio);
|
||||
|
||||
if (taos->client)
|
||||
i2c_unregister_device(taos->client);
|
||||
i2c_del_adapter(&taos->adapter);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
kfree(taos);
|
||||
|
||||
dev_dbg(&serio->dev, "Disconnected from TAOS EVM\n");
|
||||
}
|
||||
|
||||
static struct serio_device_id taos_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_TAOSEVM,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(serio, taos_serio_ids);
|
||||
|
||||
static struct serio_driver taos_drv = {
|
||||
.driver = {
|
||||
.name = "taos-evm",
|
||||
},
|
||||
.description = "TAOS evaluation module driver",
|
||||
.id_table = taos_serio_ids,
|
||||
.connect = taos_connect,
|
||||
.disconnect = taos_disconnect,
|
||||
.interrupt = taos_interrupt,
|
||||
};
|
||||
|
||||
static int __init taos_init(void)
|
||||
{
|
||||
return serio_register_driver(&taos_drv);
|
||||
}
|
||||
|
||||
static void __exit taos_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&taos_drv);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
|
||||
MODULE_DESCRIPTION("TAOS evaluation module driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(taos_init);
|
||||
module_exit(taos_exit);
|
@ -235,7 +235,7 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
|
||||
if (!(vt596_features & FEATURE_I2CBLOCK))
|
||||
goto exit_unsupported;
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
outb_p(I2C_SMBUS_BLOCK_MAX, SMBHSTDAT0);
|
||||
outb_p(data->block[0], SMBHSTDAT0);
|
||||
/* Fall through */
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
outb_p(command, SMBHSTCMD);
|
||||
|
@ -310,8 +310,6 @@ static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
if (rw == I2C_SMBUS_READ)
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX; /* For now */
|
||||
len = data->block[0];
|
||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EINVAL;
|
||||
@ -388,7 +386,7 @@ static const struct i2c_algorithm scx200_acb_algorithm = {
|
||||
};
|
||||
|
||||
static struct scx200_acb_iface *scx200_acb_list;
|
||||
static DECLARE_MUTEX(scx200_acb_list_mutex);
|
||||
static DEFINE_MUTEX(scx200_acb_list_mutex);
|
||||
|
||||
static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
{
|
||||
@ -472,10 +470,10 @@ static int __init scx200_acb_create(struct scx200_acb_iface *iface)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
down(&scx200_acb_list_mutex);
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
iface->next = scx200_acb_list;
|
||||
scx200_acb_list = iface;
|
||||
up(&scx200_acb_list_mutex);
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -633,10 +631,10 @@ static void __exit scx200_acb_cleanup(void)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
|
||||
down(&scx200_acb_list_mutex);
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
while ((iface = scx200_acb_list) != NULL) {
|
||||
scx200_acb_list = iface->next;
|
||||
up(&scx200_acb_list_mutex);
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
|
||||
i2c_del_adapter(&iface->adapter);
|
||||
|
||||
@ -648,9 +646,9 @@ static void __exit scx200_acb_cleanup(void)
|
||||
release_region(iface->base, 8);
|
||||
|
||||
kfree(iface);
|
||||
down(&scx200_acb_list_mutex);
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
}
|
||||
up(&scx200_acb_list_mutex);
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
}
|
||||
|
||||
module_init(scx200_acb_init);
|
||||
|
@ -5,7 +5,7 @@
|
||||
menu "Miscellaneous I2C Chip support"
|
||||
|
||||
config SENSORS_DS1337
|
||||
tristate "Dallas Semiconductor DS1337 and DS1339 Real Time Clock"
|
||||
tristate "Dallas DS1337 and DS1339 Real Time Clock (DEPRECATED)"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
@ -14,8 +14,11 @@ config SENSORS_DS1337
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ds1337.
|
||||
|
||||
This driver is deprecated and will be dropped soon. Use
|
||||
rtc-ds1307 instead.
|
||||
|
||||
config SENSORS_DS1374
|
||||
tristate "Maxim/Dallas Semiconductor DS1374 Real Time Clock"
|
||||
tristate "Dallas DS1374 Real Time Clock (DEPRECATED)"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
@ -24,6 +27,19 @@ config SENSORS_DS1374
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ds1374.
|
||||
|
||||
This driver is deprecated and will be dropped soon. Use
|
||||
rtc-ds1374 instead.
|
||||
|
||||
config DS1682
|
||||
tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
DS1682 Total Elapsed Time Recorder.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ds1682.
|
||||
|
||||
config SENSORS_EEPROM
|
||||
tristate "EEPROM reader"
|
||||
depends on EXPERIMENTAL
|
||||
@ -101,7 +117,7 @@ config TPS65010
|
||||
will be called tps65010.
|
||||
|
||||
config SENSORS_M41T00
|
||||
tristate "ST M41T00 RTC chip"
|
||||
tristate "ST M41T00 RTC chip (DEPRECATED)"
|
||||
depends on PPC32
|
||||
help
|
||||
If you say yes here you get support for the ST M41T00 RTC chip.
|
||||
@ -109,6 +125,9 @@ config SENSORS_M41T00
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called m41t00.
|
||||
|
||||
This driver is deprecated and will be dropped soon. Use
|
||||
rtc-ds1307 or rtc-m41t80 instead.
|
||||
|
||||
config SENSORS_MAX6875
|
||||
tristate "Maxim MAX6875 Power supply supervisor"
|
||||
depends on EXPERIMENTAL
|
||||
@ -124,4 +143,14 @@ config SENSORS_MAX6875
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max6875.
|
||||
|
||||
config SENSORS_TSL2550
|
||||
tristate "Taos TSL2550 ambient light sensor"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the Taos TSL2550
|
||||
ambient light sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2550.
|
||||
|
||||
endmenu
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
obj-$(CONFIG_SENSORS_DS1337) += ds1337.o
|
||||
obj-$(CONFIG_SENSORS_DS1374) += ds1374.o
|
||||
obj-$(CONFIG_DS1682) += ds1682.o
|
||||
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
|
||||
obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
|
||||
obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
|
||||
@ -12,6 +13,7 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
|
||||
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
|
||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
|
||||
|
||||
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
259
drivers/i2c/chips/ds1682.c
Normal file
259
drivers/i2c/chips/ds1682.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Dallas Semiconductor DS1682 Elapsed Time Recorder device driver
|
||||
*
|
||||
* Written by: Grant Likely <grant.likely@secretlab.ca>
|
||||
*
|
||||
* Copyright (C) 2007 Secret Lab Technologies Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The DS1682 elapsed timer recorder is a simple device that implements
|
||||
* one elapsed time counter, one event counter, an alarm signal and 10
|
||||
* bytes of general purpose EEPROM.
|
||||
*
|
||||
* This driver provides access to the DS1682 counters and user data via
|
||||
* the sysfs. The following attributes are added to the device node:
|
||||
* elapsed_time (u32): Total elapsed event time in ms resolution
|
||||
* alarm_time (u32): When elapsed time exceeds the value in alarm_time,
|
||||
* then the alarm pin is asserted.
|
||||
* event_count (u16): number of times the event pin has gone low.
|
||||
* eeprom (u8[10]): general purpose EEPROM
|
||||
*
|
||||
* Counter registers and user data are both read/write unless the device
|
||||
* has been write protected. This driver does not support turning off write
|
||||
* protection. Once write protection is turned on, it is impossible to
|
||||
* turn it off again, so I have left the feature out of this driver to avoid
|
||||
* accidental enabling, but it is trivial to add write protect support.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
/* Device registers */
|
||||
#define DS1682_REG_CONFIG 0x00
|
||||
#define DS1682_REG_ALARM 0x01
|
||||
#define DS1682_REG_ELAPSED 0x05
|
||||
#define DS1682_REG_EVT_CNTR 0x09
|
||||
#define DS1682_REG_EEPROM 0x0b
|
||||
#define DS1682_REG_RESET 0x1d
|
||||
#define DS1682_REG_WRITE_DISABLE 0x1e
|
||||
#define DS1682_REG_WRITE_MEM_DISABLE 0x1f
|
||||
|
||||
#define DS1682_EEPROM_SIZE 10
|
||||
|
||||
/*
|
||||
* Generic counter attributes
|
||||
*/
|
||||
static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
__le32 val = 0;
|
||||
int rc;
|
||||
|
||||
dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name);
|
||||
|
||||
/* Read the register */
|
||||
rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,
|
||||
(u8 *) & val);
|
||||
if (rc < 0)
|
||||
return -EIO;
|
||||
|
||||
/* Special case: the 32 bit regs are time values with 1/4s
|
||||
* resolution, scale them up to milliseconds */
|
||||
if (sattr->nr == 4)
|
||||
return sprintf(buf, "%llu\n", ((u64) le32_to_cpu(val)) * 250);
|
||||
|
||||
/* Format the output string and return # of bytes */
|
||||
return sprintf(buf, "%li\n", (long)le32_to_cpu(val));
|
||||
}
|
||||
|
||||
static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
char *endp;
|
||||
u64 val;
|
||||
__le32 val_le;
|
||||
int rc;
|
||||
|
||||
dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
|
||||
|
||||
/* Decode input */
|
||||
val = simple_strtoull(buf, &endp, 0);
|
||||
if (buf == endp) {
|
||||
dev_dbg(dev, "input string not a number\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Special case: the 32 bit regs are time values with 1/4s
|
||||
* resolution, scale input down to quarter-seconds */
|
||||
if (sattr->nr == 4)
|
||||
do_div(val, 250);
|
||||
|
||||
/* write out the value */
|
||||
val_le = cpu_to_le32(val);
|
||||
rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr,
|
||||
(u8 *) & val_le);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "register write failed; reg=0x%x, size=%i\n",
|
||||
sattr->index, sattr->nr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple register attributes
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show,
|
||||
ds1682_store, 4, DS1682_REG_ELAPSED);
|
||||
static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show,
|
||||
ds1682_store, 4, DS1682_REG_ALARM);
|
||||
static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show,
|
||||
ds1682_store, 2, DS1682_REG_EVT_CNTR);
|
||||
|
||||
static const struct attribute_group ds1682_group = {
|
||||
.attrs = (struct attribute *[]) {
|
||||
&sensor_dev_attr_elapsed_time.dev_attr.attr,
|
||||
&sensor_dev_attr_alarm_time.dev_attr.attr,
|
||||
&sensor_dev_attr_event_count.dev_attr.attr,
|
||||
NULL,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* User data attribute
|
||||
*/
|
||||
static ssize_t ds1682_eeprom_read(struct kobject *kobj, char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
struct i2c_client *client = kobj_to_i2c_client(kobj);
|
||||
int rc;
|
||||
|
||||
dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n",
|
||||
buf, off, count);
|
||||
|
||||
if (off >= DS1682_EEPROM_SIZE)
|
||||
return 0;
|
||||
|
||||
if (off + count > DS1682_EEPROM_SIZE)
|
||||
count = DS1682_EEPROM_SIZE - off;
|
||||
|
||||
rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,
|
||||
count, buf);
|
||||
if (rc < 0)
|
||||
return -EIO;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ds1682_eeprom_write(struct kobject *kobj, char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
struct i2c_client *client = kobj_to_i2c_client(kobj);
|
||||
|
||||
dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n",
|
||||
buf, off, count);
|
||||
|
||||
if (off >= DS1682_EEPROM_SIZE)
|
||||
return -ENOSPC;
|
||||
|
||||
if (off + count > DS1682_EEPROM_SIZE)
|
||||
count = DS1682_EEPROM_SIZE - off;
|
||||
|
||||
/* Write out to the device */
|
||||
if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,
|
||||
count, buf) < 0)
|
||||
return -EIO;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute ds1682_eeprom_attr = {
|
||||
.attr = {
|
||||
.name = "eeprom",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.size = DS1682_EEPROM_SIZE,
|
||||
.read = ds1682_eeprom_read,
|
||||
.write = ds1682_eeprom_write,
|
||||
};
|
||||
|
||||
/*
|
||||
* Called when a ds1682 device is matched with this driver
|
||||
*/
|
||||
static int ds1682_probe(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
dev_err(&client->dev, "i2c bus does not support the ds1682\n");
|
||||
rc = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);
|
||||
if (rc)
|
||||
goto exit_bin_attr;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_bin_attr:
|
||||
sysfs_remove_group(&client->dev.kobj, &ds1682_group);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ds1682_remove(struct i2c_client *client)
|
||||
{
|
||||
sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);
|
||||
sysfs_remove_group(&client->dev.kobj, &ds1682_group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver ds1682_driver = {
|
||||
.driver = {
|
||||
.name = "ds1682",
|
||||
},
|
||||
.probe = ds1682_probe,
|
||||
.remove = ds1682_remove,
|
||||
};
|
||||
|
||||
static int __init ds1682_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ds1682_driver);
|
||||
}
|
||||
|
||||
static void __exit ds1682_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ds1682_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
|
||||
MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ds1682_init);
|
||||
module_exit(ds1682_exit);
|
@ -88,8 +88,10 @@ static void eeprom_update_client(struct i2c_client *client, u8 slice)
|
||||
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
|
||||
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
for (i = slice << 5; i < (slice + 1) << 5; i += I2C_SMBUS_BLOCK_MAX)
|
||||
if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_BLOCK_MAX)
|
||||
for (i = slice << 5; i < (slice + 1) << 5; i += 32)
|
||||
if (i2c_smbus_read_i2c_block_data(client, i,
|
||||
32, data->data + i)
|
||||
!= 32)
|
||||
goto exit;
|
||||
} else {
|
||||
if (i2c_smbus_write_byte(client, slice << 5)) {
|
||||
|
@ -106,6 +106,7 @@ static void max6875_update_slice(struct i2c_client *client, int slice)
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
if (i2c_smbus_read_i2c_block_data(client,
|
||||
MAX6875_CMD_BLK_READ,
|
||||
SLICE_SIZE,
|
||||
buf) != SLICE_SIZE) {
|
||||
goto exit_up;
|
||||
}
|
||||
|
460
drivers/i2c/chips/tsl2550.c
Normal file
460
drivers/i2c/chips/tsl2550.c
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
* tsl2550.c - Linux kernel modules for ambient light sensor
|
||||
*
|
||||
* Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
|
||||
* Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
|
||||
*
|
||||
* 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/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define TSL2550_DRV_NAME "tsl2550"
|
||||
#define DRIVER_VERSION "1.1.1"
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
||||
#define TSL2550_POWER_DOWN 0x00
|
||||
#define TSL2550_POWER_UP 0x03
|
||||
#define TSL2550_STANDARD_RANGE 0x18
|
||||
#define TSL2550_EXTENDED_RANGE 0x1d
|
||||
#define TSL2550_READ_ADC0 0x43
|
||||
#define TSL2550_READ_ADC1 0x83
|
||||
|
||||
/*
|
||||
* Structs
|
||||
*/
|
||||
|
||||
struct tsl2550_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
|
||||
unsigned int power_state : 1;
|
||||
unsigned int operating_mode : 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Global data
|
||||
*/
|
||||
|
||||
static const u8 TSL2550_MODE_RANGE[2] = {
|
||||
TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Management functions
|
||||
*/
|
||||
|
||||
static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
|
||||
{
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
|
||||
int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
|
||||
|
||||
data->operating_mode = mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2550_set_power_state(struct i2c_client *client, int state)
|
||||
{
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
if (state == 0)
|
||||
ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
|
||||
else {
|
||||
ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
|
||||
|
||||
/* On power up we should reset operating mode also... */
|
||||
tsl2550_set_operating_mode(client, data->operating_mode);
|
||||
}
|
||||
|
||||
data->power_state = state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
|
||||
{
|
||||
unsigned long end;
|
||||
int loop = 0, ret = 0;
|
||||
|
||||
/*
|
||||
* Read ADC channel waiting at most 400ms (see data sheet for further
|
||||
* info).
|
||||
* To avoid long busy wait we spin for few milliseconds then
|
||||
* start sleeping.
|
||||
*/
|
||||
end = jiffies + msecs_to_jiffies(400);
|
||||
while (time_before(jiffies, end)) {
|
||||
i2c_smbus_write_byte(client, cmd);
|
||||
|
||||
if (loop++ < 5)
|
||||
mdelay(1);
|
||||
else
|
||||
msleep(1);
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret & 0x0080)
|
||||
break;
|
||||
}
|
||||
if (!(ret & 0x80))
|
||||
return -EIO;
|
||||
return ret & 0x7f; /* remove the "valid" bit */
|
||||
}
|
||||
|
||||
/*
|
||||
* LUX calculation
|
||||
*/
|
||||
|
||||
#define TSL2550_MAX_LUX 1846
|
||||
|
||||
static const u8 ratio_lut[] = {
|
||||
100, 100, 100, 100, 100, 100, 100, 100,
|
||||
100, 100, 100, 100, 100, 100, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 98, 98, 98, 98, 98,
|
||||
98, 98, 97, 97, 97, 97, 97, 96,
|
||||
96, 96, 96, 95, 95, 95, 94, 94,
|
||||
93, 93, 93, 92, 92, 91, 91, 90,
|
||||
89, 89, 88, 87, 87, 86, 85, 84,
|
||||
83, 82, 81, 80, 79, 78, 77, 75,
|
||||
74, 73, 71, 69, 68, 66, 64, 62,
|
||||
60, 58, 56, 54, 52, 49, 47, 44,
|
||||
42, 41, 40, 40, 39, 39, 38, 38,
|
||||
37, 37, 37, 36, 36, 36, 35, 35,
|
||||
35, 35, 34, 34, 34, 34, 33, 33,
|
||||
33, 33, 32, 32, 32, 32, 32, 31,
|
||||
31, 31, 31, 31, 30, 30, 30, 30,
|
||||
30,
|
||||
};
|
||||
|
||||
static const u16 count_lut[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46,
|
||||
49, 53, 57, 61, 65, 69, 73, 77,
|
||||
81, 85, 89, 93, 97, 101, 105, 109,
|
||||
115, 123, 131, 139, 147, 155, 163, 171,
|
||||
179, 187, 195, 203, 211, 219, 227, 235,
|
||||
247, 263, 279, 295, 311, 327, 343, 359,
|
||||
375, 391, 407, 423, 439, 455, 471, 487,
|
||||
511, 543, 575, 607, 639, 671, 703, 735,
|
||||
767, 799, 831, 863, 895, 927, 959, 991,
|
||||
1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
|
||||
1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
|
||||
2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
|
||||
3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is described into Taos TSL2550 Designer's Notebook
|
||||
* pages 2, 3.
|
||||
*/
|
||||
static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
|
||||
{
|
||||
unsigned int lux;
|
||||
|
||||
/* Look up count from channel values */
|
||||
u16 c0 = count_lut[ch0];
|
||||
u16 c1 = count_lut[ch1];
|
||||
|
||||
/*
|
||||
* Calculate ratio.
|
||||
* Note: the "128" is a scaling factor
|
||||
*/
|
||||
u8 r = 128;
|
||||
|
||||
/* Avoid division by 0 and count 1 cannot be greater than count 0 */
|
||||
if (c0 && (c1 <= c0))
|
||||
r = c1 * 128 / c0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* Calculate LUX */
|
||||
lux = ((c0 - c1) * ratio_lut[r]) / 256;
|
||||
|
||||
/* LUX range check */
|
||||
return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
|
||||
}
|
||||
|
||||
/*
|
||||
* SysFS support
|
||||
*/
|
||||
|
||||
static ssize_t tsl2550_show_power_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
return sprintf(buf, "%u\n", data->power_state);
|
||||
}
|
||||
|
||||
static ssize_t tsl2550_store_power_state(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = tsl2550_set_power_state(client, val);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
|
||||
tsl2550_show_power_state, tsl2550_store_power_state);
|
||||
|
||||
static ssize_t tsl2550_show_operating_mode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
return sprintf(buf, "%u\n", data->operating_mode);
|
||||
}
|
||||
|
||||
static ssize_t tsl2550_store_operating_mode(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->power_state == 0)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = tsl2550_set_operating_mode(client, val);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
|
||||
tsl2550_show_operating_mode, tsl2550_store_operating_mode);
|
||||
|
||||
static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
|
||||
{
|
||||
u8 ch0, ch1;
|
||||
int ret;
|
||||
|
||||
ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ch0 = ret;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ch1 = ret;
|
||||
|
||||
/* Do the job */
|
||||
ret = tsl2550_calculate_lux(ch0, ch1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
static ssize_t tsl2550_show_lux1_input(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
/* No LUX data if not operational */
|
||||
if (!data->power_state)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = __tsl2550_show_lux(client, buf);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lux1_input, S_IRUGO,
|
||||
tsl2550_show_lux1_input, NULL);
|
||||
|
||||
static struct attribute *tsl2550_attributes[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_operating_mode.attr,
|
||||
&dev_attr_lux1_input.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tsl2550_attr_group = {
|
||||
.attrs = tsl2550_attributes,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialization function
|
||||
*/
|
||||
|
||||
static int tsl2550_init_client(struct i2c_client *client)
|
||||
{
|
||||
struct tsl2550_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Probe the chip. To do so we try to power up the device and then to
|
||||
* read back the 0x03 code
|
||||
*/
|
||||
err = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mdelay(1);
|
||||
if (i2c_smbus_read_byte(client) != TSL2550_POWER_UP)
|
||||
return -ENODEV;
|
||||
data->power_state = 1;
|
||||
|
||||
/* Set the default operating mode */
|
||||
err = i2c_smbus_write_byte(client,
|
||||
TSL2550_MODE_RANGE[data->operating_mode]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init/probing/exit functions
|
||||
*/
|
||||
|
||||
static struct i2c_driver tsl2550_driver;
|
||||
static int __devinit tsl2550_probe(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct tsl2550_data *data;
|
||||
int *opmode, err = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
|
||||
err = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
/* Check platform data */
|
||||
opmode = client->dev.platform_data;
|
||||
if (opmode) {
|
||||
if (*opmode < 0 || *opmode > 1) {
|
||||
dev_err(&client->dev, "invalid operating_mode (%d)\n",
|
||||
*opmode);
|
||||
err = -EINVAL;
|
||||
goto exit_kfree;
|
||||
}
|
||||
data->operating_mode = *opmode;
|
||||
} else
|
||||
data->operating_mode = 0; /* default mode is standard */
|
||||
dev_info(&client->dev, "%s operating mode\n",
|
||||
data->operating_mode ? "extended" : "standard");
|
||||
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* Initialize the TSL2550 chip */
|
||||
err = tsl2550_init_client(client);
|
||||
if (err)
|
||||
goto exit_kfree;
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
|
||||
if (err)
|
||||
goto exit_kfree;
|
||||
|
||||
dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_kfree:
|
||||
kfree(data);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit tsl2550_remove(struct i2c_client *client)
|
||||
{
|
||||
sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
|
||||
|
||||
/* Power down the device */
|
||||
tsl2550_set_power_state(client, 0);
|
||||
|
||||
kfree(i2c_get_clientdata(client));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver tsl2550_driver = {
|
||||
.driver = {
|
||||
.name = TSL2550_DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tsl2550_probe,
|
||||
.remove = __devexit_p(tsl2550_remove),
|
||||
};
|
||||
|
||||
static int __init tsl2550_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tsl2550_driver);
|
||||
}
|
||||
|
||||
static void __exit tsl2550_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tsl2550_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
|
||||
MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
|
||||
module_init(tsl2550_init);
|
||||
module_exit(tsl2550_exit);
|
@ -207,6 +207,7 @@ EXPORT_SYMBOL_GPL(i2c_bus_type);
|
||||
* i2c_new_device - instantiate an i2c device for use with a new style driver
|
||||
* @adap: the adapter managing the device
|
||||
* @info: describes one I2C device; bus_num is ignored
|
||||
* Context: can sleep
|
||||
*
|
||||
* Create a device to work with a new style i2c driver, where binding is
|
||||
* handled through driver model probe()/remove() methods. This call is not
|
||||
@ -255,6 +256,7 @@ EXPORT_SYMBOL_GPL(i2c_new_device);
|
||||
/**
|
||||
* i2c_unregister_device - reverse effect of i2c_new_device()
|
||||
* @client: value returned from i2c_new_device()
|
||||
* Context: can sleep
|
||||
*/
|
||||
void i2c_unregister_device(struct i2c_client *client)
|
||||
{
|
||||
@ -379,6 +381,7 @@ out_list:
|
||||
/**
|
||||
* i2c_add_adapter - declare i2c adapter, use dynamic bus number
|
||||
* @adapter: the adapter to add
|
||||
* Context: can sleep
|
||||
*
|
||||
* This routine is used to declare an I2C adapter when its bus number
|
||||
* doesn't matter. Examples: for I2C adapters dynamically added by
|
||||
@ -416,6 +419,7 @@ EXPORT_SYMBOL(i2c_add_adapter);
|
||||
/**
|
||||
* i2c_add_numbered_adapter - declare i2c adapter, use static bus number
|
||||
* @adap: the adapter to register (with adap->nr initialized)
|
||||
* Context: can sleep
|
||||
*
|
||||
* This routine is used to declare an I2C adapter when its bus number
|
||||
* matters. Example: for I2C adapters from system-on-chip CPUs, or
|
||||
@ -463,6 +467,14 @@ retry:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
|
||||
|
||||
/**
|
||||
* i2c_del_adapter - unregister I2C adapter
|
||||
* @adap: the adapter being unregistered
|
||||
* Context: can sleep
|
||||
*
|
||||
* This unregisters an I2C adapter which was previously registered
|
||||
* by @i2c_add_adapter or @i2c_add_numbered_adapter.
|
||||
*/
|
||||
int i2c_del_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
struct list_head *item, *_n;
|
||||
@ -598,6 +610,7 @@ EXPORT_SYMBOL(i2c_register_driver);
|
||||
/**
|
||||
* i2c_del_driver - unregister I2C driver
|
||||
* @driver: the driver being unregistered
|
||||
* Context: can sleep
|
||||
*/
|
||||
void i2c_del_driver(struct i2c_driver *driver)
|
||||
{
|
||||
@ -1331,10 +1344,14 @@ s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command,
|
||||
EXPORT_SYMBOL(i2c_smbus_write_block_data);
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values)
|
||||
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command,
|
||||
u8 length, u8 *values)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
|
||||
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||
length = I2C_SMBUS_BLOCK_MAX;
|
||||
data.block[0] = length;
|
||||
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
|
||||
I2C_SMBUS_READ,command,
|
||||
I2C_SMBUS_I2C_BLOCK_DATA,&data))
|
||||
@ -1455,7 +1472,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
msg[1].len = I2C_SMBUS_BLOCK_MAX;
|
||||
msg[1].len = data->block[0];
|
||||
} else {
|
||||
msg[0].len = data->block[0] + 1;
|
||||
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
|
||||
@ -1511,9 +1528,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
|
||||
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
/* fixed at 32 for now */
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
|
||||
for (i = 0; i < data->block[0]; i++)
|
||||
data->block[i+1] = msgbuf1[i];
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
|
@ -283,6 +283,7 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
|
||||
(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_BROKEN) &&
|
||||
(data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
|
||||
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
|
||||
dev_dbg(&client->adapter->dev,
|
||||
@ -329,10 +330,18 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file,
|
||||
|
||||
if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
|
||||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
|
||||
(data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||
|
||||
(data_arg.read_write == I2C_SMBUS_WRITE)) {
|
||||
if (copy_from_user(&temp, data_arg.data, datasize))
|
||||
return -EFAULT;
|
||||
}
|
||||
if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
|
||||
/* Convert old I2C block commands to the new
|
||||
convention. This preserves binary compatibility. */
|
||||
data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;
|
||||
if (data_arg.read_write == I2C_SMBUS_READ)
|
||||
temp.block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
|
||||
data_arg.read_write,
|
||||
data_arg.command,data_arg.size,&temp);
|
||||
|
@ -67,26 +67,6 @@ static struct i2c_driver wf_sat_driver = {
|
||||
.detach_client = wf_sat_detach,
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX i2c_smbus_read_i2c_block_data doesn't pass the requested
|
||||
* length down to the low-level driver, so we use this, which
|
||||
* works well enough with the SMU i2c driver code...
|
||||
*/
|
||||
static int sat_read_block(struct i2c_client *client, u8 command,
|
||||
u8 *values, int len)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
int err;
|
||||
|
||||
data.block[0] = len;
|
||||
err = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
|
||||
I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA,
|
||||
&data);
|
||||
if (!err)
|
||||
memcpy(values, data.block, len);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
|
||||
unsigned int *size)
|
||||
{
|
||||
@ -124,8 +104,8 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
err = sat_read_block(&sat->i2c, 0xa, data, 4);
|
||||
if (err) {
|
||||
err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0xa, 4, data);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
|
||||
err);
|
||||
goto fail;
|
||||
@ -157,8 +137,8 @@ static int wf_sat_read_cache(struct wf_sat *sat)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sat_read_block(&sat->i2c, 0x3f, sat->cache, 16);
|
||||
if (err)
|
||||
err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0x3f, 16, sat->cache);
|
||||
if (err < 0)
|
||||
return err;
|
||||
sat->last_read = jiffies;
|
||||
#ifdef LOTSA_DEBUG
|
||||
|
@ -9,6 +9,9 @@
|
||||
*
|
||||
* based on a lot of other RTC drivers.
|
||||
*
|
||||
* Information and datasheet:
|
||||
* http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
@ -26,7 +29,7 @@
|
||||
* Two bytes need to be written to read a single register,
|
||||
* while most other chips just require one and take the second
|
||||
* one as the data to be written. To prevent corrupting
|
||||
* unknown chips, the user must explicitely set the probe parameter.
|
||||
* unknown chips, the user must explicitly set the probe parameter.
|
||||
*/
|
||||
|
||||
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||
|
@ -2,8 +2,6 @@
|
||||
#define __MATROXFB_CRTC2_H__
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include "matroxfb_base.h"
|
||||
|
||||
struct matroxfb_dh_fb_info {
|
||||
|
@ -90,7 +90,7 @@ extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
|
||||
const u8 *values);
|
||||
/* Returns the number of read bytes */
|
||||
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
|
||||
u8 command, u8 *values);
|
||||
u8 command, u8 length, u8 *values);
|
||||
extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
|
||||
u8 command, u8 length,
|
||||
const u8 *values);
|
||||
@ -150,15 +150,20 @@ struct i2c_driver {
|
||||
|
||||
/**
|
||||
* struct i2c_client - represent an I2C slave device
|
||||
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
|
||||
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
|
||||
* @addr: Address used on the I2C bus connected to the parent adapter.
|
||||
* @name: Indicates the type of the device, usually a chip name that's
|
||||
* generic enough to hide second-sourcing and compatible revisions.
|
||||
* @adapter: manages the bus segment hosting this I2C device
|
||||
* @dev: Driver model device node for the slave.
|
||||
* @irq: indicates the IRQ generated by this device (if any)
|
||||
* @driver_name: Identifies new-style driver used with this device; also
|
||||
* used as the module name for hotplug/coldplug modprobe support.
|
||||
*
|
||||
* An i2c_client identifies a single device (i.e. chip) connected to an
|
||||
* i2c bus. The behaviour is defined by the routines of the driver.
|
||||
* i2c bus. The behaviour exposed to Linux is defined by the driver
|
||||
* managing the device.
|
||||
*/
|
||||
struct i2c_client {
|
||||
unsigned short flags; /* div., see below */
|
||||
@ -180,7 +185,8 @@ struct i2c_client {
|
||||
|
||||
static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj)
|
||||
{
|
||||
return to_i2c_client(container_of(kobj, struct device, kobj));
|
||||
struct device * const dev = container_of(kobj, struct device, kobj);
|
||||
return to_i2c_client(dev);
|
||||
}
|
||||
|
||||
static inline void *i2c_get_clientdata (struct i2c_client *dev)
|
||||
@ -201,7 +207,7 @@ static inline void i2c_set_clientdata (struct i2c_client *dev, void *data)
|
||||
* @addr: stored in i2c_client.addr
|
||||
* @platform_data: stored in i2c_client.dev.platform_data
|
||||
* @irq: stored in i2c_client.irq
|
||||
|
||||
*
|
||||
* I2C doesn't actually support hardware probing, although controllers and
|
||||
* devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
|
||||
* a device at a given address. Drivers commonly need more information than
|
||||
@ -210,7 +216,7 @@ static inline void i2c_set_clientdata (struct i2c_client *dev, void *data)
|
||||
* i2c_board_info is used to build tables of information listing I2C devices
|
||||
* that are present. This information is used to grow the driver model tree
|
||||
* for "new style" I2C drivers. For mainboards this is done statically using
|
||||
* i2c_register_board_info(), where @bus_num represents an adapter that isn't
|
||||
* i2c_register_board_info(); bus numbers identify adapters that aren't
|
||||
* yet available. For add-on boards, i2c_new_device() does this dynamically
|
||||
* with the adapter already known.
|
||||
*/
|
||||
@ -518,8 +524,9 @@ union i2c_smbus_data {
|
||||
#define I2C_SMBUS_WORD_DATA 3
|
||||
#define I2C_SMBUS_PROC_CALL 4
|
||||
#define I2C_SMBUS_BLOCK_DATA 5
|
||||
#define I2C_SMBUS_I2C_BLOCK_DATA 6
|
||||
#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
|
||||
#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
|
||||
#define I2C_SMBUS_I2C_BLOCK_DATA 8
|
||||
|
||||
|
||||
/* ----- commands for the ioctl like i2c_command call:
|
||||
|
@ -371,6 +371,7 @@
|
||||
#define PCI_DEVICE_ID_ATI_IXP600_SMBUS 0x4385
|
||||
#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c
|
||||
#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390
|
||||
#define PCI_DEVICE_ID_ATI_IXP700_SMBUS 0x4395
|
||||
#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c
|
||||
|
||||
#define PCI_VENDOR_ID_VLSI 0x1004
|
||||
|
@ -209,5 +209,6 @@ static inline void serio_unpin_driver(struct serio *serio)
|
||||
#define SERIO_PENMOUNT 0x31
|
||||
#define SERIO_TOUCHRIGHT 0x32
|
||||
#define SERIO_TOUCHWIN 0x33
|
||||
#define SERIO_TAOSEVM 0x34
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user