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:
Коммит
068345f4a8
|
@ -643,6 +643,60 @@ X!Idrivers/video/console/fonts.c
|
||||||
!Edrivers/spi/spi.c
|
!Edrivers/spi/spi.c
|
||||||
</chapter>
|
</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">
|
<chapter id="splice">
|
||||||
<title>splice API</title>
|
<title>splice API</title>
|
||||||
<para>)
|
<para>)
|
||||||
|
@ -654,4 +708,5 @@ X!Idrivers/video/console/fonts.c
|
||||||
!Ffs/splice.c
|
!Ffs/splice.c
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
|
|
||||||
</book>
|
</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)
|
'810' and '810E' chipsets)
|
||||||
* Intel 82801BA (ICH2 - part of the '815E' chipset)
|
* Intel 82801BA (ICH2 - part of the '815E' chipset)
|
||||||
* Intel 82801CA/CAM (ICH3)
|
* Intel 82801CA/CAM (ICH3)
|
||||||
* Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported)
|
* Intel 82801DB (ICH4) (HW PEC supported)
|
||||||
* Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported)
|
* Intel 82801EB/ER (ICH5) (HW PEC supported)
|
||||||
* Intel 6300ESB
|
* Intel 6300ESB
|
||||||
* Intel 82801FB/FR/FW/FRW (ICH6)
|
* Intel 82801FB/FR/FW/FRW (ICH6)
|
||||||
* Intel 82801G (ICH7)
|
* Intel 82801G (ICH7)
|
||||||
|
|
|
@ -6,7 +6,7 @@ Supported adapters:
|
||||||
Datasheet: Publicly available at the Intel website
|
Datasheet: Publicly available at the Intel website
|
||||||
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
|
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
|
||||||
Datasheet: Only available via NDA from ServerWorks
|
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
|
Datasheet: Not publicly available
|
||||||
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
||||||
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
||||||
|
|
|
@ -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
|
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.
|
The block read should read 16 bytes.
|
||||||
0x84 is the block read command.
|
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
|
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-bit: A bit-banging algorithm
|
||||||
i2c-algo-pcf: A PCF 8584 style 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)
|
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-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-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-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)
|
i2c-velleman: Velleman K8000 parallel port adapter (uses i2c-algo-bit)
|
||||||
|
|
||||||
|
|
|
@ -571,7 +571,7 @@ SMBus communication
|
||||||
u8 command, u8 length,
|
u8 command, u8 length,
|
||||||
u8 *values);
|
u8 *values);
|
||||||
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
|
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
|
These ones were removed in Linux 2.6.10 because they had no users, but could
|
||||||
be added back later if needed:
|
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
|
This support is also available as a module. If so, the module
|
||||||
will be called i2c-algo-pca.
|
will be called i2c-algo-pca.
|
||||||
|
|
||||||
config I2C_ALGO8XX
|
|
||||||
tristate "MPC8xx CPM I2C interface"
|
|
||||||
depends on 8xx
|
|
||||||
|
|
||||||
config I2C_ALGO_SGI
|
config I2C_ALGO_SGI
|
||||||
tristate "I2C SGI interfaces"
|
tristate "I2C SGI interfaces"
|
||||||
depends on SGI_IP22 || SGI_IP32 || X86_VISWS
|
depends on SGI_IP22 || SGI_IP32 || X86_VISWS
|
||||||
|
|
|
@ -207,6 +207,7 @@ config I2C_PIIX4
|
||||||
ATI IXP300
|
ATI IXP300
|
||||||
ATI IXP400
|
ATI IXP400
|
||||||
ATI SB600
|
ATI SB600
|
||||||
|
ATI SB700
|
||||||
Serverworks OSB4
|
Serverworks OSB4
|
||||||
Serverworks CSB5
|
Serverworks CSB5
|
||||||
Serverworks CSB6
|
Serverworks CSB6
|
||||||
|
@ -390,11 +391,6 @@ config I2C_PROSAVAGE
|
||||||
This support is also available as a module. If so, the module
|
This support is also available as a module. If so, the module
|
||||||
will be called i2c-prosavage.
|
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
|
config I2C_S3C2410
|
||||||
tristate "S3C2410 I2C Driver"
|
tristate "S3C2410 I2C Driver"
|
||||||
depends on ARCH_S3C2410
|
depends on ARCH_S3C2410
|
||||||
|
@ -512,6 +508,22 @@ config I2C_SIS96X
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-sis96x.
|
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
|
config I2C_STUB
|
||||||
tristate "I2C/SMBus Test Stub"
|
tristate "I2C/SMBus Test Stub"
|
||||||
depends on EXPERIMENTAL && m
|
depends on EXPERIMENTAL && m
|
||||||
|
@ -635,4 +647,13 @@ config I2C_PNX
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-pnx.
|
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
|
endmenu
|
||||||
|
|
|
@ -32,10 +32,10 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
||||||
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
||||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||||
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.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_PNX) += i2c-pnx.o
|
||||||
obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
|
obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
|
||||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.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_S3C2410) += i2c-s3c2410.o
|
||||||
obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o
|
obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o
|
||||||
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.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_SIS630) += i2c-sis630.o
|
||||||
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
|
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
|
||||||
obj-$(CONFIG_I2C_STUB) += i2c-stub.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_TINY_USB) += i2c-tiny-usb.o
|
||||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||||
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.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);
|
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;
|
struct i2c_gpio_platform_data *pdata = data;
|
||||||
|
|
||||||
return gpio_get_value(pdata->sda_pin);
|
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;
|
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->algo_data = bit_data;
|
||||||
adap->dev.parent = &pdev->dev;
|
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)
|
if (ret)
|
||||||
goto err_add_bus;
|
goto err_add_bus;
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SUPPORTED DEVICES PCI ID
|
SUPPORTED DEVICES PCI ID
|
||||||
82801AA 2413
|
82801AA 2413
|
||||||
82801AB 2423
|
82801AB 2423
|
||||||
82801BA 2443
|
82801BA 2443
|
||||||
82801CA/CAM 2483
|
82801CA/CAM 2483
|
||||||
82801DB 24C3 (HW PEC supported, 32 byte buffer not supported)
|
82801DB 24C3 (HW PEC supported)
|
||||||
82801EB 24D3 (HW PEC supported, 32 byte buffer not supported)
|
82801EB 24D3 (HW PEC supported)
|
||||||
6300ESB 25A4
|
6300ESB 25A4
|
||||||
ICH6 266A
|
ICH6 266A
|
||||||
ICH7 27DA
|
ICH7 27DA
|
||||||
|
@ -74,6 +74,13 @@
|
||||||
#define SMBHSTCFG_SMB_SMI_EN 2
|
#define SMBHSTCFG_SMB_SMI_EN 2
|
||||||
#define SMBHSTCFG_I2C_EN 4
|
#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 */
|
/* Other settings */
|
||||||
#define MAX_TIMEOUT 100
|
#define MAX_TIMEOUT 100
|
||||||
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
|
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
|
||||||
|
@ -91,10 +98,15 @@
|
||||||
#define I801_START 0x40
|
#define I801_START 0x40
|
||||||
#define I801_PEC_EN 0x80 /* ICH4 only */
|
#define I801_PEC_EN 0x80 /* ICH4 only */
|
||||||
|
|
||||||
|
/* I801 Hosts Status register bits */
|
||||||
static int i801_transaction(void);
|
#define SMBHSTSTS_BYTE_DONE 0x80
|
||||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
#define SMBHSTSTS_INUSE_STS 0x40
|
||||||
int command, int hwpec);
|
#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 long i801_smba;
|
||||||
static unsigned char i801_original_hstcfg;
|
static unsigned char i801_original_hstcfg;
|
||||||
|
@ -102,7 +114,7 @@ static struct pci_driver i801_driver;
|
||||||
static struct pci_dev *I801_dev;
|
static struct pci_dev *I801_dev;
|
||||||
static int isich4;
|
static int isich4;
|
||||||
|
|
||||||
static int i801_transaction(void)
|
static int i801_transaction(int xact)
|
||||||
{
|
{
|
||||||
int temp;
|
int temp;
|
||||||
int result = 0;
|
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! */
|
/* We will always wait for a fraction of a second! */
|
||||||
do {
|
do {
|
||||||
msleep(1);
|
msleep(1);
|
||||||
temp = inb_p(SMBHSTSTS);
|
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 the SMBus is still busy, we give up */
|
||||||
if (timeout >= MAX_TIMEOUT) {
|
if (timeout >= MAX_TIMEOUT) {
|
||||||
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
|
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
|
||||||
result = -1;
|
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;
|
result = -1;
|
||||||
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
|
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp & 0x08) {
|
if (temp & SMBHSTSTS_BUS_ERR) {
|
||||||
result = -1;
|
result = -1;
|
||||||
dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
|
dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
|
||||||
"until next hard reset. (sorry!)\n");
|
"until next hard reset. (sorry!)\n");
|
||||||
/* Clock stops and slave is stuck in mid-transmission */
|
/* Clock stops and slave is stuck in mid-transmission */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp & 0x04) {
|
if (temp & SMBHSTSTS_DEV_ERR) {
|
||||||
result = -1;
|
result = -1;
|
||||||
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
||||||
}
|
}
|
||||||
|
@ -172,44 +191,70 @@ static int i801_transaction(void)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All-inclusive block transaction function */
|
/* wait for INTR bit as advised by Intel */
|
||||||
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
static void i801_wait_hwpec(void)
|
||||||
int command, int hwpec)
|
{
|
||||||
|
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 i, len;
|
||||||
int smbcmd;
|
int smbcmd;
|
||||||
int temp;
|
int temp;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int timeout;
|
int timeout;
|
||||||
unsigned char hostc, errmask;
|
unsigned char errmask;
|
||||||
|
|
||||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
len = data->block[0];
|
||||||
if (read_write == I2C_SMBUS_WRITE) {
|
|
||||||
/* set I2C_EN bit in configuration register */
|
|
||||||
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
|
|
||||||
pci_write_config_byte(I801_dev, SMBHSTCFG,
|
|
||||||
hostc | SMBHSTCFG_I2C_EN);
|
|
||||||
} else {
|
|
||||||
dev_err(&I801_dev->dev,
|
|
||||||
"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read_write == I2C_SMBUS_WRITE) {
|
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(len, SMBHSTDAT0);
|
||||||
outb_p(data->block[1], SMBBLKDAT);
|
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++) {
|
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 */
|
/* Make sure the SMBus host is ready to start transmitting */
|
||||||
temp = inb_p(SMBHSTSTS);
|
temp = inb_p(SMBHSTSTS);
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
/* Erronenous conditions before transaction:
|
/* Erronenous conditions before transaction:
|
||||||
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
|
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
|
||||||
errmask=0x9f;
|
errmask = 0x9f;
|
||||||
} else {
|
} else {
|
||||||
/* Erronenous conditions during transaction:
|
/* Erronenous conditions during transaction:
|
||||||
* Failed, Bus_Err, Dev_Err, Intr */
|
* Failed, Bus_Err, Dev_Err, Intr */
|
||||||
errmask=0x1e;
|
errmask = 0x1e;
|
||||||
}
|
}
|
||||||
if (temp & errmask) {
|
if (temp & errmask) {
|
||||||
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
|
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) {
|
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
|
||||||
dev_err(&I801_dev->dev,
|
dev_err(&I801_dev->dev,
|
||||||
"Reset failed! (%02x)\n", temp);
|
"Reset failed! (%02x)\n", temp);
|
||||||
result = -1;
|
return -1;
|
||||||
goto END;
|
|
||||||
}
|
}
|
||||||
if (i != 1) {
|
if (i != 1)
|
||||||
/* if die in middle of block transaction, fail */
|
/* if die in middle of block transaction, fail */
|
||||||
result = -1;
|
return -1;
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 1)
|
if (i == 1)
|
||||||
|
@ -261,33 +303,38 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||||
msleep(1);
|
msleep(1);
|
||||||
temp = inb_p(SMBHSTSTS);
|
temp = inb_p(SMBHSTSTS);
|
||||||
}
|
}
|
||||||
while ((!(temp & 0x80))
|
while ((!(temp & SMBHSTSTS_BYTE_DONE))
|
||||||
&& (timeout++ < MAX_TIMEOUT));
|
&& (timeout++ < MAX_TIMEOUT));
|
||||||
|
|
||||||
/* If the SMBus is still busy, we give up */
|
/* If the SMBus is still busy, we give up */
|
||||||
if (timeout >= MAX_TIMEOUT) {
|
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;
|
result = -1;
|
||||||
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
|
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp & 0x10) {
|
if (temp & SMBHSTSTS_FAILED) {
|
||||||
result = -1;
|
result = -1;
|
||||||
dev_dbg(&I801_dev->dev,
|
dev_dbg(&I801_dev->dev,
|
||||||
"Error: Failed bus transaction\n");
|
"Error: Failed bus transaction\n");
|
||||||
} else if (temp & 0x08) {
|
} else if (temp & SMBHSTSTS_BUS_ERR) {
|
||||||
result = -1;
|
result = -1;
|
||||||
dev_err(&I801_dev->dev, "Bus collision!\n");
|
dev_err(&I801_dev->dev, "Bus collision!\n");
|
||||||
} else if (temp & 0x04) {
|
} else if (temp & SMBHSTSTS_DEV_ERR) {
|
||||||
result = -1;
|
result = -1;
|
||||||
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 1 && read_write == I2C_SMBUS_READ) {
|
if (i == 1 && read_write == I2C_SMBUS_READ) {
|
||||||
len = inb_p(SMBHSTDAT0);
|
len = inb_p(SMBHSTDAT0);
|
||||||
if (len < 1)
|
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
len = 1;
|
return -1;
|
||||||
if (len > 32)
|
|
||||||
len = 32;
|
|
||||||
data->block[0] = len;
|
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));
|
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
goto END;
|
return result;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (hwpec) {
|
static int i801_set_block_buffer_mode(void)
|
||||||
/* wait for INTR bit as advised by Intel */
|
{
|
||||||
timeout = 0;
|
outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_E32B, SMBAUXCTL);
|
||||||
do {
|
if ((inb_p(SMBAUXCTL) & SMBAUXCTL_E32B) == 0)
|
||||||
msleep(1);
|
return -1;
|
||||||
temp = inb_p(SMBHSTSTS);
|
return 0;
|
||||||
} while ((!(temp & 0x02))
|
}
|
||||||
&& (timeout++ < MAX_TIMEOUT));
|
|
||||||
|
|
||||||
if (timeout >= MAX_TIMEOUT) {
|
/* Block transaction function */
|
||||||
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
|
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) {
|
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||||
/* restore saved configuration register value */
|
/* restore saved configuration register value */
|
||||||
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
|
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
|
||||||
|
@ -393,19 +473,22 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
|
||||||
return -1;
|
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)
|
if(block)
|
||||||
ret = i801_block_transaction(data, read_write, size, hwpec);
|
ret = i801_block_transaction(data, read_write, size, hwpec);
|
||||||
else {
|
else
|
||||||
outb_p(xact | ENABLE_INT9, SMBHSTCNT);
|
ret = i801_transaction(xact | ENABLE_INT9);
|
||||||
ret = i801_transaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
|
/* 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)
|
if (hwpec)
|
||||||
outb_p(0, SMBAUXCTL);
|
outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B),
|
||||||
|
SMBAUXCTL);
|
||||||
|
|
||||||
if(block)
|
if(block)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -491,6 +491,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
||||||
new_adapter->id = I2C_HW_IOP3XX;
|
new_adapter->id = I2C_HW_IOP3XX;
|
||||||
new_adapter->owner = THIS_MODULE;
|
new_adapter->owner = THIS_MODULE;
|
||||||
new_adapter->dev.parent = &pdev->dev;
|
new_adapter->dev.parent = &pdev->dev;
|
||||||
|
new_adapter->nr = pdev->id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default values...should these come in from board code?
|
* 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);
|
platform_set_drvdata(pdev, new_adapter);
|
||||||
new_adapter->algo_data = adapter_data;
|
new_adapter->algo_data = adapter_data;
|
||||||
|
|
||||||
i2c_add_adapter(new_adapter);
|
i2c_add_numbered_adapter(new_adapter);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,25 @@ static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
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)
|
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
|
||||||
{
|
{
|
||||||
unsigned long orig_jiffies = jiffies;
|
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)
|
static void mpc_i2c_stop(struct mpc_i2c *i2c)
|
||||||
{
|
{
|
||||||
writeccr(i2c, CCR_MEN);
|
writeccr(i2c, CCR_MEN);
|
||||||
|
writeccr(i2c, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mpc_write(struct mpc_i2c *i2c, int target,
|
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)) {
|
if (time_after(jiffies, orig_jiffies + HZ)) {
|
||||||
pr_debug("I2C: timeout\n");
|
pr_debug("I2C: timeout\n");
|
||||||
|
if (readb(i2c->base + MPC_I2C_SR) ==
|
||||||
|
(CSR_MCF | CSR_MBB | CSR_RXAK))
|
||||||
|
mpc_i2c_fixup(i2c);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
schedule();
|
schedule();
|
||||||
|
@ -327,9 +350,10 @@ static int fsl_i2c_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, i2c);
|
platform_set_drvdata(pdev, i2c);
|
||||||
|
|
||||||
i2c->adap = mpc_ops;
|
i2c->adap = mpc_ops;
|
||||||
|
i2c->adap.nr = pdev->id;
|
||||||
i2c_set_adapdata(&i2c->adap, i2c);
|
i2c_set_adapdata(&i2c->adap, i2c);
|
||||||
i2c->adap.dev.parent = &pdev->dev;
|
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");
|
printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
|
||||||
goto fail_add;
|
goto fail_add;
|
||||||
}
|
}
|
||||||
|
|
|
@ -527,6 +527,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||||
drv_data->adapter.class = I2C_CLASS_HWMON;
|
drv_data->adapter.class = I2C_CLASS_HWMON;
|
||||||
drv_data->adapter.timeout = pdata->timeout;
|
drv_data->adapter.timeout = pdata->timeout;
|
||||||
drv_data->adapter.retries = pdata->retries;
|
drv_data->adapter.retries = pdata->retries;
|
||||||
|
drv_data->adapter.nr = pd->id;
|
||||||
platform_set_drvdata(pd, drv_data);
|
platform_set_drvdata(pd, drv_data);
|
||||||
i2c_set_adapdata(&drv_data->adapter, drv_data);
|
i2c_set_adapdata(&drv_data->adapter, drv_data);
|
||||||
|
|
||||||
|
@ -539,7 +540,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||||
drv_data->irq);
|
drv_data->irq);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto exit_unmap_regs;
|
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,
|
dev_err(&drv_data->adapter.dev,
|
||||||
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
|
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
|
||||||
goto exit_free_irq;
|
goto exit_free_irq;
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct nforce2_smbus {
|
||||||
struct i2c_adapter adapter;
|
struct i2c_adapter adapter;
|
||||||
int base;
|
int base;
|
||||||
int size;
|
int size;
|
||||||
|
int blockops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +81,8 @@ struct nforce2_smbus {
|
||||||
#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */
|
#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */
|
||||||
#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */
|
#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */
|
||||||
#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */
|
#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_DONE 0x80
|
||||||
#define NVIDIA_SMB_STS_ALRM 0x40
|
#define NVIDIA_SMB_STS_ALRM 0x40
|
||||||
|
@ -92,6 +95,7 @@ struct nforce2_smbus {
|
||||||
#define NVIDIA_SMB_PRTCL_BYTE 0x04
|
#define NVIDIA_SMB_PRTCL_BYTE 0x04
|
||||||
#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06
|
#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06
|
||||||
#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08
|
#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08
|
||||||
|
#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a
|
||||||
#define NVIDIA_SMB_PRTCL_PEC 0x80
|
#define NVIDIA_SMB_PRTCL_PEC 0x80
|
||||||
|
|
||||||
static struct pci_driver nforce2_driver;
|
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;
|
struct nforce2_smbus *smbus = adap->algo_data;
|
||||||
unsigned char protocol, pec, temp;
|
unsigned char protocol, pec, temp;
|
||||||
|
u8 len;
|
||||||
|
int i;
|
||||||
|
|
||||||
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
|
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
|
||||||
NVIDIA_SMB_PRTCL_WRITE;
|
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;
|
protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
|
||||||
break;
|
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:
|
default:
|
||||||
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
|
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -174,6 +199,14 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
|
||||||
case I2C_SMBUS_WORD_DATA:
|
case I2C_SMBUS_WORD_DATA:
|
||||||
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
|
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
|
||||||
break;
|
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;
|
return 0;
|
||||||
|
@ -184,7 +217,9 @@ static u32 nforce2_func(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
/* other functionality might be possible, but is not tested */
|
/* other functionality might be possible, but is not tested */
|
||||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
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 = {
|
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;
|
return -ENOMEM;
|
||||||
pci_set_drvdata(dev, smbuses);
|
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 */
|
/* SMBus adapter 1 */
|
||||||
res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
|
res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
|
||||||
if (res1 < 0) {
|
if (res1 < 0) {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
Supports:
|
Supports:
|
||||||
Intel PIIX4, 440MX
|
Intel PIIX4, 440MX
|
||||||
Serverworks OSB4, CSB5, CSB6, HT-1000
|
Serverworks OSB4, CSB5, CSB6, HT-1000
|
||||||
ATI IXP200, IXP300, IXP400, SB600
|
ATI IXP200, IXP300, IXP400, SB600, SB700
|
||||||
SMSC Victory66
|
SMSC Victory66
|
||||||
|
|
||||||
Note: we assume there can only be one device, with one SMBus interface.
|
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 },
|
.driver_data = 0 },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SMBUS),
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SMBUS),
|
||||||
.driver_data = 0 },
|
.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),
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4),
|
||||||
.driver_data = 0 },
|
.driver_data = 0 },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5),
|
||||||
|
|
|
@ -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)
|
if (rc)
|
||||||
goto bail;
|
goto bail;
|
||||||
rc = pmac_i2c_xfer(bus, addrdir, 1, command,
|
rc = pmac_i2c_xfer(bus, addrdir, 1, command,
|
||||||
read ? data->block : &data->block[1],
|
&data->block[1], data->block[0]);
|
||||||
data->block[0]);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -921,7 +921,14 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
||||||
i2c->adap.class = plat->class;
|
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) {
|
if (ret < 0) {
|
||||||
printk(KERN_INFO "I2C: Failed to add bus\n");
|
printk(KERN_INFO "I2C: Failed to add bus\n");
|
||||||
goto eadapt;
|
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
|
/* 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
|
the BT869 and possibly other I2C devices. The DDC bus is not
|
||||||
yet supported because its register is not memory-mapped.
|
yet supported because its register is not memory-mapped.
|
||||||
However we leave the DDC code here, commented out, to make
|
|
||||||
it easier to add later.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -37,36 +35,19 @@
|
||||||
#include <linux/i2c-algo-bit.h>
|
#include <linux/i2c-algo-bit.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
/* 3DFX defines */
|
/* device IDs */
|
||||||
#define PCI_CHIP_SAVAGE3D 0x8A20
|
|
||||||
#define PCI_CHIP_SAVAGE3D_MV 0x8A21
|
|
||||||
#define PCI_CHIP_SAVAGE4 0x8A22
|
#define PCI_CHIP_SAVAGE4 0x8A22
|
||||||
#define PCI_CHIP_SAVAGE2000 0x9102
|
#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 */
|
#define REG 0xff20 /* Serial Port 1 Register */
|
||||||
|
|
||||||
/* bit locations in the 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_ENAB 0x00000020
|
||||||
#define I2C_SCL_OUT 0x00000001
|
#define I2C_SCL_OUT 0x00000001
|
||||||
#define I2C_SDA_OUT 0x00000002
|
#define I2C_SDA_OUT 0x00000002
|
||||||
#define I2C_SCL_IN 0x00000008
|
#define I2C_SCL_IN 0x00000008
|
||||||
#define I2C_SDA_IN 0x00000010
|
#define I2C_SDA_IN 0x00000010
|
||||||
|
|
||||||
/* initialization states */
|
|
||||||
#define INIT2 0x20
|
|
||||||
#define INIT3 0x04
|
|
||||||
|
|
||||||
/* delays */
|
/* delays */
|
||||||
#define CYCLE_DELAY 10
|
#define CYCLE_DELAY 10
|
||||||
#define TIMEOUT (HZ / 2)
|
#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 struct pci_driver sis5595_driver;
|
||||||
static unsigned short sis5595_base;
|
static unsigned short sis5595_base;
|
||||||
|
static struct pci_dev *sis5595_pdev;
|
||||||
|
|
||||||
static u8 sis5595_read(u8 reg)
|
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)
|
static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
if (sis5595_setup(dev)) {
|
if (sis5595_setup(dev)) {
|
||||||
dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
|
dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n");
|
||||||
return -ENODEV;
|
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",
|
sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
|
||||||
sis5595_base + SMB_INDEX);
|
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)
|
/* 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
|
||||||
i2c_del_adapter(&sis5595_adapter);
|
* pci device, we only wanted to read as few register values from it.
|
||||||
release_region(sis5595_base + SMB_INDEX, 2);
|
*/
|
||||||
|
sis5595_pdev = pci_dev_get(dev);
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pci_driver sis5595_driver = {
|
static struct pci_driver sis5595_driver = {
|
||||||
.name = "sis5595_smbus",
|
.name = "sis5595_smbus",
|
||||||
.id_table = sis5595_ids,
|
.id_table = sis5595_ids,
|
||||||
.probe = sis5595_probe,
|
.probe = sis5595_probe,
|
||||||
.remove = __devexit_p(sis5595_remove),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init i2c_sis5595_init(void)
|
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)
|
static void __exit i2c_sis5595_exit(void)
|
||||||
{
|
{
|
||||||
pci_unregister_driver(&sis5595_driver);
|
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>");
|
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||||
|
|
|
@ -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))
|
if (!(vt596_features & FEATURE_I2CBLOCK))
|
||||||
goto exit_unsupported;
|
goto exit_unsupported;
|
||||||
if (read_write == I2C_SMBUS_READ)
|
if (read_write == I2C_SMBUS_READ)
|
||||||
outb_p(I2C_SMBUS_BLOCK_MAX, SMBHSTDAT0);
|
outb_p(data->block[0], SMBHSTDAT0);
|
||||||
/* Fall through */
|
/* Fall through */
|
||||||
case I2C_SMBUS_BLOCK_DATA:
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
outb_p(command, SMBHSTCMD);
|
outb_p(command, SMBHSTCMD);
|
||||||
|
|
|
@ -310,8 +310,6 @@ static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
if (rw == I2C_SMBUS_READ)
|
|
||||||
data->block[0] = I2C_SMBUS_BLOCK_MAX; /* For now */
|
|
||||||
len = data->block[0];
|
len = data->block[0];
|
||||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -388,7 +386,7 @@ static const struct i2c_algorithm scx200_acb_algorithm = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct scx200_acb_iface *scx200_acb_list;
|
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)
|
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;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
down(&scx200_acb_list_mutex);
|
mutex_lock(&scx200_acb_list_mutex);
|
||||||
iface->next = scx200_acb_list;
|
iface->next = scx200_acb_list;
|
||||||
scx200_acb_list = iface;
|
scx200_acb_list = iface;
|
||||||
up(&scx200_acb_list_mutex);
|
mutex_unlock(&scx200_acb_list_mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -633,10 +631,10 @@ static void __exit scx200_acb_cleanup(void)
|
||||||
{
|
{
|
||||||
struct scx200_acb_iface *iface;
|
struct scx200_acb_iface *iface;
|
||||||
|
|
||||||
down(&scx200_acb_list_mutex);
|
mutex_lock(&scx200_acb_list_mutex);
|
||||||
while ((iface = scx200_acb_list) != NULL) {
|
while ((iface = scx200_acb_list) != NULL) {
|
||||||
scx200_acb_list = iface->next;
|
scx200_acb_list = iface->next;
|
||||||
up(&scx200_acb_list_mutex);
|
mutex_unlock(&scx200_acb_list_mutex);
|
||||||
|
|
||||||
i2c_del_adapter(&iface->adapter);
|
i2c_del_adapter(&iface->adapter);
|
||||||
|
|
||||||
|
@ -648,9 +646,9 @@ static void __exit scx200_acb_cleanup(void)
|
||||||
release_region(iface->base, 8);
|
release_region(iface->base, 8);
|
||||||
|
|
||||||
kfree(iface);
|
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);
|
module_init(scx200_acb_init);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
menu "Miscellaneous I2C Chip support"
|
menu "Miscellaneous I2C Chip support"
|
||||||
|
|
||||||
config SENSORS_DS1337
|
config SENSORS_DS1337
|
||||||
tristate "Dallas Semiconductor DS1337 and DS1339 Real Time Clock"
|
tristate "Dallas DS1337 and DS1339 Real Time Clock (DEPRECATED)"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
help
|
help
|
||||||
If you say yes here you get support for Dallas Semiconductor
|
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
|
This driver can also be built as a module. If so, the module
|
||||||
will be called ds1337.
|
will be called ds1337.
|
||||||
|
|
||||||
|
This driver is deprecated and will be dropped soon. Use
|
||||||
|
rtc-ds1307 instead.
|
||||||
|
|
||||||
config SENSORS_DS1374
|
config SENSORS_DS1374
|
||||||
tristate "Maxim/Dallas Semiconductor DS1374 Real Time Clock"
|
tristate "Dallas DS1374 Real Time Clock (DEPRECATED)"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
help
|
help
|
||||||
If you say yes here you get support for Dallas Semiconductor
|
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
|
This driver can also be built as a module. If so, the module
|
||||||
will be called ds1374.
|
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
|
config SENSORS_EEPROM
|
||||||
tristate "EEPROM reader"
|
tristate "EEPROM reader"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
|
@ -101,7 +117,7 @@ config TPS65010
|
||||||
will be called tps65010.
|
will be called tps65010.
|
||||||
|
|
||||||
config SENSORS_M41T00
|
config SENSORS_M41T00
|
||||||
tristate "ST M41T00 RTC chip"
|
tristate "ST M41T00 RTC chip (DEPRECATED)"
|
||||||
depends on PPC32
|
depends on PPC32
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the ST M41T00 RTC chip.
|
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
|
This driver can also be built as a module. If so, the module
|
||||||
will be called m41t00.
|
will be called m41t00.
|
||||||
|
|
||||||
|
This driver is deprecated and will be dropped soon. Use
|
||||||
|
rtc-ds1307 or rtc-m41t80 instead.
|
||||||
|
|
||||||
config SENSORS_MAX6875
|
config SENSORS_MAX6875
|
||||||
tristate "Maxim MAX6875 Power supply supervisor"
|
tristate "Maxim MAX6875 Power supply supervisor"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
|
@ -124,4 +143,14 @@ config SENSORS_MAX6875
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called max6875.
|
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
|
endmenu
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
obj-$(CONFIG_SENSORS_DS1337) += ds1337.o
|
obj-$(CONFIG_SENSORS_DS1337) += ds1337.o
|
||||||
obj-$(CONFIG_SENSORS_DS1374) += ds1374.o
|
obj-$(CONFIG_SENSORS_DS1374) += ds1374.o
|
||||||
|
obj-$(CONFIG_DS1682) += ds1682.o
|
||||||
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
|
obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
|
||||||
obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
|
obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
|
||||||
obj-$(CONFIG_SENSORS_M41T00) += m41t00.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_SENSORS_PCF8591) += pcf8591.o
|
||||||
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
|
||||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||||
|
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
|
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
|
||||||
EXTRA_CFLAGS += -DDEBUG
|
EXTRA_CFLAGS += -DDEBUG
|
||||||
|
|
|
@ -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);
|
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
|
||||||
|
|
||||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
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)
|
for (i = slice << 5; i < (slice + 1) << 5; i += 32)
|
||||||
if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_BLOCK_MAX)
|
if (i2c_smbus_read_i2c_block_data(client, i,
|
||||||
|
32, data->data + i)
|
||||||
|
!= 32)
|
||||||
goto exit;
|
goto exit;
|
||||||
} else {
|
} else {
|
||||||
if (i2c_smbus_write_byte(client, slice << 5)) {
|
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)) {
|
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||||
if (i2c_smbus_read_i2c_block_data(client,
|
if (i2c_smbus_read_i2c_block_data(client,
|
||||||
MAX6875_CMD_BLK_READ,
|
MAX6875_CMD_BLK_READ,
|
||||||
|
SLICE_SIZE,
|
||||||
buf) != SLICE_SIZE) {
|
buf) != SLICE_SIZE) {
|
||||||
goto exit_up;
|
goto exit_up;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* i2c_new_device - instantiate an i2c device for use with a new style driver
|
||||||
* @adap: the adapter managing the device
|
* @adap: the adapter managing the device
|
||||||
* @info: describes one I2C device; bus_num is ignored
|
* @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
|
* 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
|
* 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()
|
* i2c_unregister_device - reverse effect of i2c_new_device()
|
||||||
* @client: value returned from i2c_new_device()
|
* @client: value returned from i2c_new_device()
|
||||||
|
* Context: can sleep
|
||||||
*/
|
*/
|
||||||
void i2c_unregister_device(struct i2c_client *client)
|
void i2c_unregister_device(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
|
@ -379,6 +381,7 @@ out_list:
|
||||||
/**
|
/**
|
||||||
* i2c_add_adapter - declare i2c adapter, use dynamic bus number
|
* i2c_add_adapter - declare i2c adapter, use dynamic bus number
|
||||||
* @adapter: the adapter to add
|
* @adapter: the adapter to add
|
||||||
|
* Context: can sleep
|
||||||
*
|
*
|
||||||
* This routine is used to declare an I2C adapter when its bus number
|
* This routine is used to declare an I2C adapter when its bus number
|
||||||
* doesn't matter. Examples: for I2C adapters dynamically added by
|
* 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
|
* i2c_add_numbered_adapter - declare i2c adapter, use static bus number
|
||||||
* @adap: the adapter to register (with adap->nr initialized)
|
* @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
|
* This routine is used to declare an I2C adapter when its bus number
|
||||||
* matters. Example: for I2C adapters from system-on-chip CPUs, or
|
* matters. Example: for I2C adapters from system-on-chip CPUs, or
|
||||||
|
@ -463,6 +467,14 @@ retry:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
|
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)
|
int i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
struct list_head *item, *_n;
|
struct list_head *item, *_n;
|
||||||
|
@ -598,6 +610,7 @@ EXPORT_SYMBOL(i2c_register_driver);
|
||||||
/**
|
/**
|
||||||
* i2c_del_driver - unregister I2C driver
|
* i2c_del_driver - unregister I2C driver
|
||||||
* @driver: the driver being unregistered
|
* @driver: the driver being unregistered
|
||||||
|
* Context: can sleep
|
||||||
*/
|
*/
|
||||||
void i2c_del_driver(struct i2c_driver *driver)
|
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);
|
EXPORT_SYMBOL(i2c_smbus_write_block_data);
|
||||||
|
|
||||||
/* Returns the number of read bytes */
|
/* 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;
|
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,
|
if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
|
||||||
I2C_SMBUS_READ,command,
|
I2C_SMBUS_READ,command,
|
||||||
I2C_SMBUS_I2C_BLOCK_DATA,&data))
|
I2C_SMBUS_I2C_BLOCK_DATA,&data))
|
||||||
|
@ -1455,7 +1472,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
if (read_write == I2C_SMBUS_READ) {
|
if (read_write == I2C_SMBUS_READ) {
|
||||||
msg[1].len = I2C_SMBUS_BLOCK_MAX;
|
msg[1].len = data->block[0];
|
||||||
} else {
|
} else {
|
||||||
msg[0].len = data->block[0] + 1;
|
msg[0].len = data->block[0] + 1;
|
||||||
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 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);
|
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
/* fixed at 32 for now */
|
for (i = 0; i < data->block[0]; i++)
|
||||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
|
||||||
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
|
|
||||||
data->block[i+1] = msgbuf1[i];
|
data->block[i+1] = msgbuf1[i];
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_BLOCK_DATA:
|
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_WORD_DATA) &&
|
||||||
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
|
(data_arg.size != I2C_SMBUS_PROC_CALL) &&
|
||||||
(data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
|
(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_I2C_BLOCK_DATA) &&
|
||||||
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
|
(data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
|
||||||
dev_dbg(&client->adapter->dev,
|
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) ||
|
if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
|
||||||
(data_arg.size == I2C_SMBUS_BLOCK_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)) {
|
(data_arg.read_write == I2C_SMBUS_WRITE)) {
|
||||||
if (copy_from_user(&temp, data_arg.data, datasize))
|
if (copy_from_user(&temp, data_arg.data, datasize))
|
||||||
return -EFAULT;
|
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,
|
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
|
||||||
data_arg.read_write,
|
data_arg.read_write,
|
||||||
data_arg.command,data_arg.size,&temp);
|
data_arg.command,data_arg.size,&temp);
|
||||||
|
|
|
@ -67,26 +67,6 @@ static struct i2c_driver wf_sat_driver = {
|
||||||
.detach_client = wf_sat_detach,
|
.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,
|
struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
|
||||||
unsigned int *size)
|
unsigned int *size)
|
||||||
{
|
{
|
||||||
|
@ -124,8 +104,8 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 0; i < len; i += 4) {
|
for (i = 0; i < len; i += 4) {
|
||||||
err = sat_read_block(&sat->i2c, 0xa, data, 4);
|
err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0xa, 4, data);
|
||||||
if (err) {
|
if (err < 0) {
|
||||||
printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
|
printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
|
||||||
err);
|
err);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -157,8 +137,8 @@ static int wf_sat_read_cache(struct wf_sat *sat)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = sat_read_block(&sat->i2c, 0x3f, sat->cache, 16);
|
err = i2c_smbus_read_i2c_block_data(&sat->i2c, 0x3f, 16, sat->cache);
|
||||||
if (err)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
sat->last_read = jiffies;
|
sat->last_read = jiffies;
|
||||||
#ifdef LOTSA_DEBUG
|
#ifdef LOTSA_DEBUG
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
*
|
*
|
||||||
* based on a lot of other RTC drivers.
|
* 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
|
* 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
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
|
@ -26,7 +29,7 @@
|
||||||
* Two bytes need to be written to read a single register,
|
* Two bytes need to be written to read a single register,
|
||||||
* while most other chips just require one and take the second
|
* while most other chips just require one and take the second
|
||||||
* one as the data to be written. To prevent corrupting
|
* 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 };
|
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
#define __MATROXFB_CRTC2_H__
|
#define __MATROXFB_CRTC2_H__
|
||||||
|
|
||||||
#include <linux/ioctl.h>
|
#include <linux/ioctl.h>
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/i2c-algo-bit.h>
|
|
||||||
#include "matroxfb_base.h"
|
#include "matroxfb_base.h"
|
||||||
|
|
||||||
struct matroxfb_dh_fb_info {
|
struct matroxfb_dh_fb_info {
|
||||||
|
|
|
@ -90,7 +90,7 @@ extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
|
||||||
const u8 *values);
|
const u8 *values);
|
||||||
/* Returns the number of read bytes */
|
/* Returns the number of read bytes */
|
||||||
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
|
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,
|
extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
|
||||||
u8 command, u8 length,
|
u8 command, u8 length,
|
||||||
const u8 *values);
|
const u8 *values);
|
||||||
|
@ -150,15 +150,20 @@ struct i2c_driver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2c_client - represent an I2C slave device
|
* 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.
|
* @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
|
* @name: Indicates the type of the device, usually a chip name that's
|
||||||
* generic enough to hide second-sourcing and compatible revisions.
|
* 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.
|
* @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
|
* @driver_name: Identifies new-style driver used with this device; also
|
||||||
* used as the module name for hotplug/coldplug modprobe support.
|
* used as the module name for hotplug/coldplug modprobe support.
|
||||||
*
|
*
|
||||||
* An i2c_client identifies a single device (i.e. chip) connected to an
|
* 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 {
|
struct i2c_client {
|
||||||
unsigned short flags; /* div., see below */
|
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)
|
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)
|
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
|
* @addr: stored in i2c_client.addr
|
||||||
* @platform_data: stored in i2c_client.dev.platform_data
|
* @platform_data: stored in i2c_client.dev.platform_data
|
||||||
* @irq: stored in i2c_client.irq
|
* @irq: stored in i2c_client.irq
|
||||||
|
*
|
||||||
* I2C doesn't actually support hardware probing, although controllers and
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* yet available. For add-on boards, i2c_new_device() does this dynamically
|
||||||
* with the adapter already known.
|
* with the adapter already known.
|
||||||
*/
|
*/
|
||||||
|
@ -518,8 +524,9 @@ union i2c_smbus_data {
|
||||||
#define I2C_SMBUS_WORD_DATA 3
|
#define I2C_SMBUS_WORD_DATA 3
|
||||||
#define I2C_SMBUS_PROC_CALL 4
|
#define I2C_SMBUS_PROC_CALL 4
|
||||||
#define I2C_SMBUS_BLOCK_DATA 5
|
#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_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
|
||||||
|
#define I2C_SMBUS_I2C_BLOCK_DATA 8
|
||||||
|
|
||||||
|
|
||||||
/* ----- commands for the ioctl like i2c_command call:
|
/* ----- 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_SMBUS 0x4385
|
||||||
#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c
|
#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c
|
||||||
#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390
|
#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_DEVICE_ID_ATI_IXP700_IDE 0x439c
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_VLSI 0x1004
|
#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_PENMOUNT 0x31
|
||||||
#define SERIO_TOUCHRIGHT 0x32
|
#define SERIO_TOUCHRIGHT 0x32
|
||||||
#define SERIO_TOUCHWIN 0x33
|
#define SERIO_TOUCHWIN 0x33
|
||||||
|
#define SERIO_TAOSEVM 0x34
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче