Merge branch 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: i2c: Clearly mark ACPI drivers as such i2c: Add driver for SMBus Control Method Interface i2c-pnx: Correct use of request_region/request_mem_region MAINTAINERS: Add maintainer for AT24 and PCA9564/PCA9665 i2c-piix4: Add AMD SB900 SMBus device ID i2c/chips: Remove deprecated pcf8574 driver i2c/chips: Remove deprecated pca9539 driver i2c/chips: Remove deprecated pcf8575 driver gpio/pcf857x: Copy i2c_device_id from old pcf8574 driver i2c/scx200_acb: Provide more information on bus errors i2c: Provide compatibility links for i2c adapters i2c: Convert i2c adapters to bus devices i2c: Convert i2c clients to a device type i2c/tsl2550: Use combined SMBus transactions i2c-taos-evm: Switch echo off to improve performance i2c: Drop unused i2c_driver.id field
This commit is contained in:
commit
40aba21896
|
@ -8,6 +8,8 @@ Supported adapters:
|
||||||
Datasheet: Only available via NDA from ServerWorks
|
Datasheet: Only available via NDA from ServerWorks
|
||||||
* ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges
|
* ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges
|
||||||
Datasheet: Not publicly available
|
Datasheet: Not publicly available
|
||||||
|
* AMD SB900
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
Kernel driver pca9539
|
|
||||||
=====================
|
|
||||||
|
|
||||||
NOTE: this driver is deprecated and will be dropped soon, use
|
|
||||||
drivers/gpio/pca9539.c instead.
|
|
||||||
|
|
||||||
Supported chips:
|
|
||||||
* Philips PCA9539
|
|
||||||
Prefix: 'pca9539'
|
|
||||||
Addresses scanned: none
|
|
||||||
Datasheet:
|
|
||||||
http://www.semiconductors.philips.com/acrobat/datasheets/PCA9539_2.pdf
|
|
||||||
|
|
||||||
Author: Ben Gardner <bgardner@wabtec.com>
|
|
||||||
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The Philips PCA9539 is a 16 bit low power I/O device.
|
|
||||||
All 16 lines can be individually configured as an input or output.
|
|
||||||
The input sense can also be inverted.
|
|
||||||
The 16 lines are split between two bytes.
|
|
||||||
|
|
||||||
|
|
||||||
Detection
|
|
||||||
---------
|
|
||||||
|
|
||||||
The PCA9539 is difficult to detect and not commonly found in PC machines,
|
|
||||||
so you have to pass the I2C bus and address of the installed PCA9539
|
|
||||||
devices explicitly to the driver at load time via the force=... parameter.
|
|
||||||
|
|
||||||
|
|
||||||
Sysfs entries
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Each is a byte that maps to the 8 I/O bits.
|
|
||||||
A '0' suffix is for bits 0-7, while '1' is for bits 8-15.
|
|
||||||
|
|
||||||
input[01] - read the current value
|
|
||||||
output[01] - sets the output value
|
|
||||||
direction[01] - direction of each bit: 1=input, 0=output
|
|
||||||
invert[01] - toggle the input bit sense
|
|
||||||
|
|
||||||
input reads the actual state of the line and is always available.
|
|
||||||
The direction defaults to input for all channels.
|
|
||||||
|
|
||||||
|
|
||||||
General Remarks
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Note that each output, direction, and invert entry controls 8 lines.
|
|
||||||
You should use the read, modify, write sequence.
|
|
||||||
For example. to set output bit 0 of 1.
|
|
||||||
val=$(cat output0)
|
|
||||||
val=$(( $val | 1 ))
|
|
||||||
echo $val > output0
|
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
Kernel driver pcf8574
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Supported chips:
|
|
||||||
* Philips PCF8574
|
|
||||||
Prefix: 'pcf8574'
|
|
||||||
Addresses scanned: none
|
|
||||||
Datasheet: Publicly available at the Philips Semiconductors website
|
|
||||||
http://www.semiconductors.philips.com/pip/PCF8574P.html
|
|
||||||
|
|
||||||
* Philips PCF8574A
|
|
||||||
Prefix: 'pcf8574a'
|
|
||||||
Addresses scanned: none
|
|
||||||
Datasheet: Publicly available at the Philips Semiconductors website
|
|
||||||
http://www.semiconductors.philips.com/pip/PCF8574P.html
|
|
||||||
|
|
||||||
Authors:
|
|
||||||
Frodo Looijaard <frodol@dds.nl>,
|
|
||||||
Philip Edelbrock <phil@netroedge.com>,
|
|
||||||
Dan Eaton <dan.eaton@rocketlogix.com>,
|
|
||||||
Aurelien Jarno <aurelien@aurel32.net>,
|
|
||||||
Jean Delvare <khali@linux-fr.org>,
|
|
||||||
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
The PCF8574(A) is an 8-bit I/O expander for the I2C bus produced by Philips
|
|
||||||
Semiconductors. It is designed to provide a byte I2C interface to up to 16
|
|
||||||
separate devices (8 x PCF8574 and 8 x PCF8574A).
|
|
||||||
|
|
||||||
This device consists of a quasi-bidirectional port. Each of the eight I/Os
|
|
||||||
can be independently used as an input or output. To setup an I/O as an
|
|
||||||
input, you have to write a 1 to the corresponding output.
|
|
||||||
|
|
||||||
For more informations see the datasheet.
|
|
||||||
|
|
||||||
|
|
||||||
Accessing PCF8574(A) via /sys interface
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
The PCF8574(A) is plainly impossible to detect ! Stupid chip.
|
|
||||||
So, you have to pass the I2C bus and address of the installed PCF857A
|
|
||||||
and PCF8574A devices explicitly to the driver at load time via the
|
|
||||||
force=... parameter.
|
|
||||||
|
|
||||||
On detection (i.e. insmod, modprobe et al.), directories are being
|
|
||||||
created for each detected PCF8574(A):
|
|
||||||
|
|
||||||
/sys/bus/i2c/devices/<0>-<1>/
|
|
||||||
where <0> is the bus the chip was detected on (e. g. i2c-0)
|
|
||||||
and <1> the chip address ([20..27] or [38..3f]):
|
|
||||||
|
|
||||||
(example: /sys/bus/i2c/devices/1-0020/)
|
|
||||||
|
|
||||||
Inside these directories, there are two files each:
|
|
||||||
read and write (and one file with chip name).
|
|
||||||
|
|
||||||
The read file is read-only. Reading gives you the current I/O input
|
|
||||||
if the corresponding output is set as 1, otherwise the current output
|
|
||||||
value, that is to say 0.
|
|
||||||
|
|
||||||
The write file is read/write. Writing a value outputs it on the I/O
|
|
||||||
port. Reading returns the last written value. As it is not possible
|
|
||||||
to read this value from the chip, you need to write at least once to
|
|
||||||
this file before you can read back from it.
|
|
|
@ -1,69 +0,0 @@
|
||||||
About the PCF8575 chip and the pcf8575 kernel driver
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
The PCF8575 chip is produced by the following manufacturers:
|
|
||||||
|
|
||||||
* Philips NXP
|
|
||||||
http://www.nxp.com/#/pip/cb=[type=product,path=50807/41735/41850,final=PCF8575_3]|pip=[pip=PCF8575_3][0]
|
|
||||||
|
|
||||||
* Texas Instruments
|
|
||||||
http://focus.ti.com/docs/prod/folders/print/pcf8575.html
|
|
||||||
|
|
||||||
|
|
||||||
Some vendors sell small PCB's with the PCF8575 mounted on it. You can connect
|
|
||||||
such a board to a Linux host via e.g. an USB to I2C interface. Examples of
|
|
||||||
PCB boards with a PCF8575:
|
|
||||||
|
|
||||||
* SFE Breakout Board for PCF8575 I2C Expander by RobotShop
|
|
||||||
http://www.robotshop.ca/home/products/robot-parts/electronics/adapters-converters/sfe-pcf8575-i2c-expander-board.html
|
|
||||||
|
|
||||||
* Breakout Board for PCF8575 I2C Expander by Spark Fun Electronics
|
|
||||||
http://www.sparkfun.com/commerce/product_info.php?products_id=8130
|
|
||||||
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
The PCF8575 chip is a 16-bit I/O expander for the I2C bus. Up to eight of
|
|
||||||
these chips can be connected to the same I2C bus. You can find this
|
|
||||||
chip on some custom designed hardware, but you won't find it on PC
|
|
||||||
motherboards.
|
|
||||||
|
|
||||||
The PCF8575 chip consists of a 16-bit quasi-bidirectional port and an I2C-bus
|
|
||||||
interface. Each of the sixteen I/O's can be independently used as an input or
|
|
||||||
an output. To set up an I/O pin as an input, you have to write a 1 to the
|
|
||||||
corresponding output.
|
|
||||||
|
|
||||||
For more information please see the datasheet.
|
|
||||||
|
|
||||||
|
|
||||||
Detection
|
|
||||||
---------
|
|
||||||
|
|
||||||
There is no method known to detect whether a chip on a given I2C address is
|
|
||||||
a PCF8575 or whether it is any other I2C device, so you have to pass the I2C
|
|
||||||
bus and address of the installed PCF8575 devices explicitly to the driver at
|
|
||||||
load time via the force=... parameter.
|
|
||||||
|
|
||||||
/sys interface
|
|
||||||
--------------
|
|
||||||
|
|
||||||
For each address on which a PCF8575 chip was found or forced the following
|
|
||||||
files will be created under /sys:
|
|
||||||
* /sys/bus/i2c/devices/<bus>-<address>/read
|
|
||||||
* /sys/bus/i2c/devices/<bus>-<address>/write
|
|
||||||
where bus is the I2C bus number (0, 1, ...) and address is the four-digit
|
|
||||||
hexadecimal representation of the 7-bit I2C address of the PCF8575
|
|
||||||
(0020 .. 0027).
|
|
||||||
|
|
||||||
The read file is read-only. Reading it will trigger an I2C read and will hence
|
|
||||||
report the current input state for the pins configured as inputs, and the
|
|
||||||
current output value for the pins configured as outputs.
|
|
||||||
|
|
||||||
The write file is read-write. Writing a value to it will configure all pins
|
|
||||||
as output for which the corresponding bit is zero. Reading the write file will
|
|
||||||
return the value last written, or -EAGAIN if no value has yet been written to
|
|
||||||
the write file.
|
|
||||||
|
|
||||||
On module initialization the configuration of the chip is not changed -- the
|
|
||||||
chip is left in the state it was already configured in through either power-up
|
|
||||||
or through previous I2C write actions.
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -895,6 +895,13 @@ F: drivers/dma/
|
||||||
F: include/linux/dmaengine.h
|
F: include/linux/dmaengine.h
|
||||||
F: include/linux/async_tx.h
|
F: include/linux/async_tx.h
|
||||||
|
|
||||||
|
AT24 EEPROM DRIVER
|
||||||
|
M: Wolfram Sang <w.sang@pengutronix.de>
|
||||||
|
L: linux-i2c@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/misc/eeprom/at24.c
|
||||||
|
F: include/linux/i2c/at24.h
|
||||||
|
|
||||||
ATA OVER ETHERNET (AOE) DRIVER
|
ATA OVER ETHERNET (AOE) DRIVER
|
||||||
M: "Ed L. Cashin" <ecashin@coraid.com>
|
M: "Ed L. Cashin" <ecashin@coraid.com>
|
||||||
W: http://www.coraid.com/support/linux
|
W: http://www.coraid.com/support/linux
|
||||||
|
@ -3964,6 +3971,15 @@ S: Maintained
|
||||||
F: drivers/leds/leds-pca9532.c
|
F: drivers/leds/leds-pca9532.c
|
||||||
F: include/linux/leds-pca9532.h
|
F: include/linux/leds-pca9532.h
|
||||||
|
|
||||||
|
PCA9564/PCA9665 I2C BUS DRIVER
|
||||||
|
M: Wolfram Sang <w.sang@pengutronix.de>
|
||||||
|
L: linux-i2c@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/i2c/algos/i2c-algo-pca.c
|
||||||
|
F: drivers/i2c/busses/i2c-pca-*
|
||||||
|
F: include/linux/i2c-algo-pca.h
|
||||||
|
F: include/linux/i2c-pca-platform.h
|
||||||
|
|
||||||
PCI ERROR RECOVERY
|
PCI ERROR RECOVERY
|
||||||
M: Linas Vepstas <linas@austin.ibm.com>
|
M: Linas Vepstas <linas@austin.ibm.com>
|
||||||
L: linux-pci@vger.kernel.org
|
L: linux-pci@vger.kernel.org
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
static const struct i2c_device_id pcf857x_id[] = {
|
static const struct i2c_device_id pcf857x_id[] = {
|
||||||
{ "pcf8574", 8 },
|
{ "pcf8574", 8 },
|
||||||
|
{ "pcf8574a", 8 },
|
||||||
{ "pca8574", 8 },
|
{ "pca8574", 8 },
|
||||||
{ "pca9670", 8 },
|
{ "pca9670", 8 },
|
||||||
{ "pca9672", 8 },
|
{ "pca9672", 8 },
|
||||||
|
|
|
@ -27,6 +27,14 @@ config I2C_BOARDINFO
|
||||||
boolean
|
boolean
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config I2C_COMPAT
|
||||||
|
boolean "Enable compatibility bits for old user-space"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Say Y here if you intend to run lm-sensors 3.1.1 or older, or any
|
||||||
|
other user-space package which expects i2c adapters to be class
|
||||||
|
devices. If you don't know, say Y.
|
||||||
|
|
||||||
config I2C_CHARDEV
|
config I2C_CHARDEV
|
||||||
tristate "I2C device interface"
|
tristate "I2C device interface"
|
||||||
help
|
help
|
||||||
|
|
|
@ -113,7 +113,7 @@ config I2C_ISCH
|
||||||
will be called i2c-isch.
|
will be called i2c-isch.
|
||||||
|
|
||||||
config I2C_PIIX4
|
config I2C_PIIX4
|
||||||
tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)"
|
tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the Intel
|
If you say yes to this option, support will be included for the Intel
|
||||||
|
@ -128,6 +128,7 @@ config I2C_PIIX4
|
||||||
ATI SB600
|
ATI SB600
|
||||||
ATI SB700
|
ATI SB700
|
||||||
ATI SB800
|
ATI SB800
|
||||||
|
AMD SB900
|
||||||
Serverworks OSB4
|
Serverworks OSB4
|
||||||
Serverworks CSB5
|
Serverworks CSB5
|
||||||
Serverworks CSB6
|
Serverworks CSB6
|
||||||
|
@ -231,6 +232,22 @@ config I2C_VIAPRO
|
||||||
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-viapro.
|
will be called i2c-viapro.
|
||||||
|
|
||||||
|
if ACPI
|
||||||
|
|
||||||
|
comment "ACPI drivers"
|
||||||
|
|
||||||
|
config I2C_SCMI
|
||||||
|
tristate "SMBus Control Method Interface"
|
||||||
|
help
|
||||||
|
This driver supports the SMBus Control Method Interface. It needs the
|
||||||
|
BIOS to declare ACPI control methods as described in the SMBus Control
|
||||||
|
Method Interface specification.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here:
|
||||||
|
the module will be called i2c-scmi.
|
||||||
|
|
||||||
|
endif # ACPI
|
||||||
|
|
||||||
comment "Mac SMBus host controller drivers"
|
comment "Mac SMBus host controller drivers"
|
||||||
depends on PPC_CHRP || PPC_PMAC
|
depends on PPC_CHRP || PPC_PMAC
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
# Makefile for the i2c bus drivers.
|
# Makefile for the i2c bus drivers.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# ACPI drivers
|
||||||
|
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
|
||||||
|
|
||||||
# PC SMBus host controller drivers
|
# PC SMBus host controller drivers
|
||||||
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
|
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
|
||||||
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
|
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
Intel PIIX4, 440MX
|
Intel PIIX4, 440MX
|
||||||
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
|
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
|
||||||
ATI IXP200, IXP300, IXP400, SB600, SB700, SB800
|
ATI IXP200, IXP300, IXP400, SB600, SB700, SB800
|
||||||
|
AMD SB900
|
||||||
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.
|
||||||
|
@ -479,6 +480,7 @@ static struct pci_device_id piix4_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
|
@ -499,9 +501,10 @@ static int __devinit piix4_probe(struct pci_dev *dev,
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if ((dev->vendor == PCI_VENDOR_ID_ATI) &&
|
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
||||||
(dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) &&
|
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||||
(dev->revision >= 0x40))
|
dev->revision >= 0x40) ||
|
||||||
|
dev->vendor == PCI_VENDOR_ID_AMD)
|
||||||
/* base address location etc changed in SB800 */
|
/* base address location etc changed in SB800 */
|
||||||
retval = piix4_setup_sb800(dev, id);
|
retval = piix4_setup_sb800(dev, id);
|
||||||
else
|
else
|
||||||
|
|
|
@ -586,7 +586,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
|
||||||
alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;
|
alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter;
|
||||||
|
|
||||||
/* Register I/O resource */
|
/* Register I/O resource */
|
||||||
if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) {
|
if (!request_mem_region(alg_data->base, I2C_PNX_REGION_SIZE,
|
||||||
|
pdev->name)) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"I/O region 0x%08x for I2C already in use.\n",
|
"I/O region 0x%08x for I2C already in use.\n",
|
||||||
alg_data->base);
|
alg_data->base);
|
||||||
|
@ -650,7 +651,7 @@ out_clock:
|
||||||
out_unmap:
|
out_unmap:
|
||||||
iounmap((void *)alg_data->ioaddr);
|
iounmap((void *)alg_data->ioaddr);
|
||||||
out_release:
|
out_release:
|
||||||
release_region(alg_data->base, I2C_PNX_REGION_SIZE);
|
release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
|
||||||
out_drvdata:
|
out_drvdata:
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
out:
|
out:
|
||||||
|
@ -667,7 +668,7 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev)
|
||||||
i2c_del_adapter(adap);
|
i2c_del_adapter(adap);
|
||||||
i2c_pnx->set_clock_stop(pdev);
|
i2c_pnx->set_clock_stop(pdev);
|
||||||
iounmap((void *)alg_data->ioaddr);
|
iounmap((void *)alg_data->ioaddr);
|
||||||
release_region(alg_data->base, I2C_PNX_REGION_SIZE);
|
release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE);
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
* SMBus driver for ACPI SMBus CMI
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Crane Cai <crane.cai@amd.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
|
||||||
|
#define ACPI_SMBUS_HC_CLASS "smbus"
|
||||||
|
#define ACPI_SMBUS_HC_DEVICE_NAME "cmi"
|
||||||
|
|
||||||
|
ACPI_MODULE_NAME("smbus_cmi");
|
||||||
|
|
||||||
|
struct smbus_methods_t {
|
||||||
|
char *mt_info;
|
||||||
|
char *mt_sbr;
|
||||||
|
char *mt_sbw;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct acpi_smbus_cmi {
|
||||||
|
acpi_handle handle;
|
||||||
|
struct i2c_adapter adapter;
|
||||||
|
u8 cap_info:1;
|
||||||
|
u8 cap_read:1;
|
||||||
|
u8 cap_write:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct smbus_methods_t smbus_methods = {
|
||||||
|
.mt_info = "_SBI",
|
||||||
|
.mt_sbr = "_SBR",
|
||||||
|
.mt_sbw = "_SBW",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
|
||||||
|
{"SMBUS01", 0},
|
||||||
|
{"", 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ACPI_SMBUS_STATUS_OK 0x00
|
||||||
|
#define ACPI_SMBUS_STATUS_FAIL 0x07
|
||||||
|
#define ACPI_SMBUS_STATUS_DNAK 0x10
|
||||||
|
#define ACPI_SMBUS_STATUS_DERR 0x11
|
||||||
|
#define ACPI_SMBUS_STATUS_CMD_DENY 0x12
|
||||||
|
#define ACPI_SMBUS_STATUS_UNKNOWN 0x13
|
||||||
|
#define ACPI_SMBUS_STATUS_ACC_DENY 0x17
|
||||||
|
#define ACPI_SMBUS_STATUS_TIMEOUT 0x18
|
||||||
|
#define ACPI_SMBUS_STATUS_NOTSUP 0x19
|
||||||
|
#define ACPI_SMBUS_STATUS_BUSY 0x1a
|
||||||
|
#define ACPI_SMBUS_STATUS_PEC 0x1f
|
||||||
|
|
||||||
|
#define ACPI_SMBUS_PRTCL_WRITE 0x00
|
||||||
|
#define ACPI_SMBUS_PRTCL_READ 0x01
|
||||||
|
#define ACPI_SMBUS_PRTCL_QUICK 0x02
|
||||||
|
#define ACPI_SMBUS_PRTCL_BYTE 0x04
|
||||||
|
#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06
|
||||||
|
#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08
|
||||||
|
#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
||||||
|
char read_write, u8 command, int size,
|
||||||
|
union i2c_smbus_data *data)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;
|
||||||
|
unsigned char protocol;
|
||||||
|
acpi_status status = 0;
|
||||||
|
struct acpi_object_list input;
|
||||||
|
union acpi_object mt_params[5];
|
||||||
|
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
union acpi_object *obj;
|
||||||
|
union acpi_object *pkg;
|
||||||
|
char *method;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
dev_dbg(&adap->dev, "access size: %d %s\n", size,
|
||||||
|
(read_write) ? "READ" : "WRITE");
|
||||||
|
switch (size) {
|
||||||
|
case I2C_SMBUS_QUICK:
|
||||||
|
protocol = ACPI_SMBUS_PRTCL_QUICK;
|
||||||
|
command = 0;
|
||||||
|
if (read_write == I2C_SMBUS_WRITE) {
|
||||||
|
mt_params[3].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[3].integer.value = 0;
|
||||||
|
mt_params[4].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[4].integer.value = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SMBUS_BYTE:
|
||||||
|
protocol = ACPI_SMBUS_PRTCL_BYTE;
|
||||||
|
if (read_write == I2C_SMBUS_WRITE) {
|
||||||
|
mt_params[3].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[3].integer.value = 0;
|
||||||
|
mt_params[4].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[4].integer.value = 0;
|
||||||
|
} else {
|
||||||
|
command = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SMBUS_BYTE_DATA:
|
||||||
|
protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;
|
||||||
|
if (read_write == I2C_SMBUS_WRITE) {
|
||||||
|
mt_params[3].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[3].integer.value = 1;
|
||||||
|
mt_params[4].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[4].integer.value = data->byte;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SMBUS_WORD_DATA:
|
||||||
|
protocol = ACPI_SMBUS_PRTCL_WORD_DATA;
|
||||||
|
if (read_write == I2C_SMBUS_WRITE) {
|
||||||
|
mt_params[3].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[3].integer.value = 2;
|
||||||
|
mt_params[4].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[4].integer.value = data->word;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
|
protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;
|
||||||
|
if (read_write == I2C_SMBUS_WRITE) {
|
||||||
|
len = data->block[0];
|
||||||
|
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
mt_params[3].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[3].integer.value = len;
|
||||||
|
mt_params[4].type = ACPI_TYPE_BUFFER;
|
||||||
|
mt_params[4].buffer.pointer = data->block + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_write == I2C_SMBUS_READ) {
|
||||||
|
protocol |= ACPI_SMBUS_PRTCL_READ;
|
||||||
|
method = smbus_methods.mt_sbr;
|
||||||
|
input.count = 3;
|
||||||
|
} else {
|
||||||
|
protocol |= ACPI_SMBUS_PRTCL_WRITE;
|
||||||
|
method = smbus_methods.mt_sbw;
|
||||||
|
input.count = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.pointer = mt_params;
|
||||||
|
mt_params[0].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[0].integer.value = protocol;
|
||||||
|
mt_params[1].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[1].integer.value = addr;
|
||||||
|
mt_params[2].type = ACPI_TYPE_INTEGER;
|
||||||
|
mt_params[2].integer.value = command;
|
||||||
|
|
||||||
|
status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
|
||||||
|
&buffer);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg = buffer.pointer;
|
||||||
|
if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
|
||||||
|
obj = pkg->package.elements;
|
||||||
|
else {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = obj->integer.value;
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
|
||||||
|
method, result));
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case ACPI_SMBUS_STATUS_OK:
|
||||||
|
result = 0;
|
||||||
|
break;
|
||||||
|
case ACPI_SMBUS_STATUS_BUSY:
|
||||||
|
result = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
case ACPI_SMBUS_STATUS_TIMEOUT:
|
||||||
|
result = -ETIMEDOUT;
|
||||||
|
goto out;
|
||||||
|
case ACPI_SMBUS_STATUS_DNAK:
|
||||||
|
result = -ENXIO;
|
||||||
|
goto out;
|
||||||
|
default:
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
obj = pkg->package.elements + 1;
|
||||||
|
if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = obj->integer.value;
|
||||||
|
obj = pkg->package.elements + 2;
|
||||||
|
switch (size) {
|
||||||
|
case I2C_SMBUS_BYTE:
|
||||||
|
case I2C_SMBUS_BYTE_DATA:
|
||||||
|
case I2C_SMBUS_WORD_DATA:
|
||||||
|
if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (len == 2)
|
||||||
|
data->word = obj->integer.value;
|
||||||
|
else
|
||||||
|
data->byte = obj->integer.value;
|
||||||
|
break;
|
||||||
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
|
if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
result = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
return -EPROTO;
|
||||||
|
data->block[0] = len;
|
||||||
|
memcpy(data->block + 1, obj->buffer.pointer, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(buffer.pointer);
|
||||||
|
dev_dbg(&adap->dev, "Transaction status: %i\n", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?
|
||||||
|
I2C_FUNC_SMBUS_QUICK : 0;
|
||||||
|
|
||||||
|
ret |= smbus_cmi->cap_read ?
|
||||||
|
(I2C_FUNC_SMBUS_READ_BYTE |
|
||||||
|
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||||
|
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||||
|
I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;
|
||||||
|
|
||||||
|
ret |= smbus_cmi->cap_write ?
|
||||||
|
(I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||||
|
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||||
|
I2C_FUNC_SMBUS_WRITE_WORD_DATA |
|
||||||
|
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
|
||||||
|
.smbus_xfer = acpi_smbus_cmi_access,
|
||||||
|
.functionality = acpi_smbus_cmi_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
union acpi_object *obj;
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
if (!strcmp(name, smbus_methods.mt_info)) {
|
||||||
|
status = acpi_evaluate_object(smbus_cmi->handle,
|
||||||
|
smbus_methods.mt_info,
|
||||||
|
NULL, &buffer);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
|
||||||
|
smbus_methods.mt_info, status));
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = buffer.pointer;
|
||||||
|
if (obj && obj->type == ACPI_TYPE_PACKAGE)
|
||||||
|
obj = obj->package.elements;
|
||||||
|
else {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
kfree(buffer.pointer);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||||
|
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||||
|
kfree(buffer.pointer);
|
||||||
|
return -EIO;
|
||||||
|
} else
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
|
||||||
|
"\n", (int)obj->integer.value));
|
||||||
|
|
||||||
|
kfree(buffer.pointer);
|
||||||
|
smbus_cmi->cap_info = 1;
|
||||||
|
} else if (!strcmp(name, smbus_methods.mt_sbr))
|
||||||
|
smbus_cmi->cap_read = 1;
|
||||||
|
else if (!strcmp(name, smbus_methods.mt_sbw))
|
||||||
|
smbus_cmi->cap_write = 1;
|
||||||
|
else
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
|
||||||
|
name));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
|
||||||
|
void *context, void **return_value)
|
||||||
|
{
|
||||||
|
char node_name[5];
|
||||||
|
struct acpi_buffer buffer = { sizeof(node_name), node_name };
|
||||||
|
struct acpi_smbus_cmi *smbus_cmi = context;
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
|
||||||
|
|
||||||
|
if (ACPI_SUCCESS(status))
|
||||||
|
acpi_smbus_cmi_add_cap(smbus_cmi, node_name);
|
||||||
|
|
||||||
|
return AE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acpi_smbus_cmi_add(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
struct acpi_smbus_cmi *smbus_cmi;
|
||||||
|
|
||||||
|
smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
|
||||||
|
if (!smbus_cmi)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
smbus_cmi->handle = device->handle;
|
||||||
|
strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
|
||||||
|
strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
|
||||||
|
device->driver_data = smbus_cmi;
|
||||||
|
smbus_cmi->cap_info = 0;
|
||||||
|
smbus_cmi->cap_read = 0;
|
||||||
|
smbus_cmi->cap_write = 0;
|
||||||
|
|
||||||
|
acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
|
||||||
|
acpi_smbus_cmi_query_methods, smbus_cmi, NULL);
|
||||||
|
|
||||||
|
if (smbus_cmi->cap_info == 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
|
||||||
|
"SMBus CMI adapter %s (%s)",
|
||||||
|
acpi_device_name(device),
|
||||||
|
acpi_device_uid(device));
|
||||||
|
smbus_cmi->adapter.owner = THIS_MODULE;
|
||||||
|
smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
|
||||||
|
smbus_cmi->adapter.algo_data = smbus_cmi;
|
||||||
|
smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||||
|
smbus_cmi->adapter.dev.parent = &device->dev;
|
||||||
|
|
||||||
|
if (i2c_add_adapter(&smbus_cmi->adapter)) {
|
||||||
|
dev_err(&device->dev, "Couldn't register adapter!\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(smbus_cmi);
|
||||||
|
device->driver_data = NULL;
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acpi_smbus_cmi_remove(struct acpi_device *device, int type)
|
||||||
|
{
|
||||||
|
struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device);
|
||||||
|
|
||||||
|
i2c_del_adapter(&smbus_cmi->adapter);
|
||||||
|
kfree(smbus_cmi);
|
||||||
|
device->driver_data = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct acpi_driver acpi_smbus_cmi_driver = {
|
||||||
|
.name = ACPI_SMBUS_HC_DEVICE_NAME,
|
||||||
|
.class = ACPI_SMBUS_HC_CLASS,
|
||||||
|
.ids = acpi_smbus_cmi_ids,
|
||||||
|
.ops = {
|
||||||
|
.add = acpi_smbus_cmi_add,
|
||||||
|
.remove = acpi_smbus_cmi_remove,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init acpi_smbus_cmi_init(void)
|
||||||
|
{
|
||||||
|
return acpi_bus_register_driver(&acpi_smbus_cmi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit acpi_smbus_cmi_exit(void)
|
||||||
|
{
|
||||||
|
acpi_bus_unregister_driver(&acpi_smbus_cmi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(acpi_smbus_cmi_init);
|
||||||
|
module_exit(acpi_smbus_cmi_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
|
||||||
|
MODULE_DESCRIPTION("ACPI SMBus CMI driver");
|
|
@ -32,10 +32,12 @@
|
||||||
|
|
||||||
#define TAOS_STATE_INIT 0
|
#define TAOS_STATE_INIT 0
|
||||||
#define TAOS_STATE_IDLE 1
|
#define TAOS_STATE_IDLE 1
|
||||||
#define TAOS_STATE_SEND 2
|
#define TAOS_STATE_EOFF 2
|
||||||
#define TAOS_STATE_RECV 3
|
#define TAOS_STATE_RECV 3
|
||||||
|
|
||||||
#define TAOS_CMD_RESET 0x12
|
#define TAOS_CMD_RESET 0x12
|
||||||
|
#define TAOS_CMD_ECHO_ON '+'
|
||||||
|
#define TAOS_CMD_ECHO_OFF '-'
|
||||||
|
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(wq);
|
static DECLARE_WAIT_QUEUE_HEAD(wq);
|
||||||
|
|
||||||
|
@ -102,17 +104,9 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
|
|
||||||
/* Send the transaction to the TAOS EVM */
|
/* Send the transaction to the TAOS EVM */
|
||||||
dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer);
|
dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer);
|
||||||
taos->pos = 0;
|
for (p = taos->buffer; *p; p++)
|
||||||
taos->state = TAOS_STATE_SEND;
|
serio_write(serio, *p);
|
||||||
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;
|
taos->addr = addr;
|
||||||
|
|
||||||
/* Start the transaction and read the answer */
|
/* Start the transaction and read the answer */
|
||||||
|
@ -122,7 +116,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
||||||
msecs_to_jiffies(150));
|
msecs_to_jiffies(150));
|
||||||
if (taos->state != TAOS_STATE_IDLE
|
if (taos->state != TAOS_STATE_IDLE
|
||||||
|| taos->pos != 6) {
|
|| taos->pos != 5) {
|
||||||
dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n",
|
dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n",
|
||||||
taos->pos);
|
taos->pos);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -130,7 +124,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer);
|
dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer);
|
||||||
|
|
||||||
/* Interpret the returned string */
|
/* Interpret the returned string */
|
||||||
p = taos->buffer + 2;
|
p = taos->buffer + 1;
|
||||||
p[3] = '\0';
|
p[3] = '\0';
|
||||||
if (!strcmp(p, "NAK"))
|
if (!strcmp(p, "NAK"))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -173,13 +167,9 @@ static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data,
|
||||||
wake_up_interruptible(&wq);
|
wake_up_interruptible(&wq);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TAOS_STATE_SEND:
|
case TAOS_STATE_EOFF:
|
||||||
if (taos->buffer[++taos->pos])
|
taos->state = TAOS_STATE_IDLE;
|
||||||
serio_write(serio, taos->buffer[taos->pos]);
|
wake_up_interruptible(&wq);
|
||||||
else {
|
|
||||||
taos->state = TAOS_STATE_IDLE;
|
|
||||||
wake_up_interruptible(&wq);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TAOS_STATE_RECV:
|
case TAOS_STATE_RECV:
|
||||||
taos->buffer[taos->pos++] = data;
|
taos->buffer[taos->pos++] = data;
|
||||||
|
@ -257,6 +247,19 @@ static int taos_connect(struct serio *serio, struct serio_driver *drv)
|
||||||
}
|
}
|
||||||
strlcpy(adapter->name, name, sizeof(adapter->name));
|
strlcpy(adapter->name, name, sizeof(adapter->name));
|
||||||
|
|
||||||
|
/* Turn echo off for better performance */
|
||||||
|
taos->state = TAOS_STATE_EOFF;
|
||||||
|
serio_write(serio, TAOS_CMD_ECHO_OFF);
|
||||||
|
|
||||||
|
wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE,
|
||||||
|
msecs_to_jiffies(250));
|
||||||
|
if (taos->state != TAOS_STATE_IDLE) {
|
||||||
|
err = -ENODEV;
|
||||||
|
dev_err(&adapter->dev, "Echo off failed "
|
||||||
|
"(state=%d)\n", taos->state);
|
||||||
|
goto exit_close;
|
||||||
|
}
|
||||||
|
|
||||||
err = i2c_add_adapter(adapter);
|
err = i2c_add_adapter(adapter);
|
||||||
if (err)
|
if (err)
|
||||||
goto exit_close;
|
goto exit_close;
|
||||||
|
|
|
@ -217,8 +217,10 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
dev_err(&iface->adapter.dev, "%s in state %s\n", errmsg,
|
dev_err(&iface->adapter.dev,
|
||||||
scx200_acb_state_name[iface->state]);
|
"%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg,
|
||||||
|
scx200_acb_state_name[iface->state], iface->address_byte,
|
||||||
|
iface->len, status);
|
||||||
|
|
||||||
iface->state = state_idle;
|
iface->state = state_idle;
|
||||||
iface->result = -EIO;
|
iface->result = -EIO;
|
||||||
|
|
|
@ -16,54 +16,6 @@ config DS1682
|
||||||
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 ds1682.
|
will be called ds1682.
|
||||||
|
|
||||||
config SENSORS_PCF8574
|
|
||||||
tristate "Philips PCF8574 and PCF8574A (DEPRECATED)"
|
|
||||||
depends on EXPERIMENTAL && GPIO_PCF857X = "n"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If you say yes here you get support for Philips PCF8574 and
|
|
||||||
PCF8574A chips. These chips are 8-bit I/O expanders for the I2C bus.
|
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called pcf8574.
|
|
||||||
|
|
||||||
This driver is deprecated and will be dropped soon. Use
|
|
||||||
drivers/gpio/pcf857x.c instead.
|
|
||||||
|
|
||||||
These devices are hard to detect and rarely found on mainstream
|
|
||||||
hardware. If unsure, say N.
|
|
||||||
|
|
||||||
config PCF8575
|
|
||||||
tristate "Philips PCF8575 (DEPRECATED)"
|
|
||||||
default n
|
|
||||||
depends on GPIO_PCF857X = "n"
|
|
||||||
help
|
|
||||||
If you say yes here you get support for Philips PCF8575 chip.
|
|
||||||
This chip is a 16-bit I/O expander for the I2C bus. Several other
|
|
||||||
chip manufacturers sell equivalent chips, e.g. Texas Instruments.
|
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called pcf8575.
|
|
||||||
|
|
||||||
This driver is deprecated and will be dropped soon. Use
|
|
||||||
drivers/gpio/pcf857x.c instead.
|
|
||||||
|
|
||||||
This device is hard to detect and is rarely found on mainstream
|
|
||||||
hardware. If unsure, say N.
|
|
||||||
|
|
||||||
config SENSORS_PCA9539
|
|
||||||
tristate "Philips PCA9539 16-bit I/O port (DEPRECATED)"
|
|
||||||
depends on EXPERIMENTAL && GPIO_PCA953X = "n"
|
|
||||||
help
|
|
||||||
If you say yes here you get support for the Philips PCA9539
|
|
||||||
16-bit I/O port.
|
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called pca9539.
|
|
||||||
|
|
||||||
This driver is deprecated and will be dropped soon. Use
|
|
||||||
drivers/gpio/pca953x.c instead.
|
|
||||||
|
|
||||||
config SENSORS_TSL2550
|
config SENSORS_TSL2550
|
||||||
tristate "Taos TSL2550 ambient light sensor"
|
tristate "Taos TSL2550 ambient light sensor"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
|
|
|
@ -11,9 +11,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_DS1682) += ds1682.o
|
obj-$(CONFIG_DS1682) += ds1682.o
|
||||||
obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
|
|
||||||
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
|
|
||||||
obj-$(CONFIG_PCF8575) += pcf8575.o
|
|
||||||
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
|
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
|
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
/*
|
|
||||||
pca9539.c - 16-bit I/O port with interrupt and reset
|
|
||||||
|
|
||||||
Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; version 2 of the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/hwmon-sysfs.h>
|
|
||||||
|
|
||||||
/* Addresses to scan: none, device is not autodetected */
|
|
||||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
|
||||||
|
|
||||||
/* Insmod parameters */
|
|
||||||
I2C_CLIENT_INSMOD_1(pca9539);
|
|
||||||
|
|
||||||
enum pca9539_cmd
|
|
||||||
{
|
|
||||||
PCA9539_INPUT_0 = 0,
|
|
||||||
PCA9539_INPUT_1 = 1,
|
|
||||||
PCA9539_OUTPUT_0 = 2,
|
|
||||||
PCA9539_OUTPUT_1 = 3,
|
|
||||||
PCA9539_INVERT_0 = 4,
|
|
||||||
PCA9539_INVERT_1 = 5,
|
|
||||||
PCA9539_DIRECTION_0 = 6,
|
|
||||||
PCA9539_DIRECTION_1 = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* following are the sysfs callback functions */
|
|
||||||
static ssize_t pca9539_show(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
return sprintf(buf, "%d\n", i2c_smbus_read_byte_data(client,
|
|
||||||
psa->index));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t pca9539_store(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
unsigned long val = simple_strtoul(buf, NULL, 0);
|
|
||||||
if (val > 0xff)
|
|
||||||
return -EINVAL;
|
|
||||||
i2c_smbus_write_byte_data(client, psa->index, val);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Define the device attributes */
|
|
||||||
|
|
||||||
#define PCA9539_ENTRY_RO(name, cmd_idx) \
|
|
||||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO, pca9539_show, NULL, cmd_idx)
|
|
||||||
|
|
||||||
#define PCA9539_ENTRY_RW(name, cmd_idx) \
|
|
||||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, pca9539_show, \
|
|
||||||
pca9539_store, cmd_idx)
|
|
||||||
|
|
||||||
PCA9539_ENTRY_RO(input0, PCA9539_INPUT_0);
|
|
||||||
PCA9539_ENTRY_RO(input1, PCA9539_INPUT_1);
|
|
||||||
PCA9539_ENTRY_RW(output0, PCA9539_OUTPUT_0);
|
|
||||||
PCA9539_ENTRY_RW(output1, PCA9539_OUTPUT_1);
|
|
||||||
PCA9539_ENTRY_RW(invert0, PCA9539_INVERT_0);
|
|
||||||
PCA9539_ENTRY_RW(invert1, PCA9539_INVERT_1);
|
|
||||||
PCA9539_ENTRY_RW(direction0, PCA9539_DIRECTION_0);
|
|
||||||
PCA9539_ENTRY_RW(direction1, PCA9539_DIRECTION_1);
|
|
||||||
|
|
||||||
static struct attribute *pca9539_attributes[] = {
|
|
||||||
&sensor_dev_attr_input0.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_input1.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_output0.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_output1.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_invert0.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_invert1.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_direction0.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_direction1.dev_attr.attr,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct attribute_group pca9539_defattr_group = {
|
|
||||||
.attrs = pca9539_attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
||||||
static int pca9539_detect(struct i2c_client *client, int kind,
|
|
||||||
struct i2c_board_info *info)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adapter = client->adapter;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
strlcpy(info->type, "pca9539", I2C_NAME_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pca9539_probe(struct i2c_client *client,
|
|
||||||
const struct i2c_device_id *id)
|
|
||||||
{
|
|
||||||
/* Register sysfs hooks */
|
|
||||||
return sysfs_create_group(&client->dev.kobj,
|
|
||||||
&pca9539_defattr_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pca9539_remove(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id pca9539_id[] = {
|
|
||||||
{ "pca9539", 0 },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct i2c_driver pca9539_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "pca9539",
|
|
||||||
},
|
|
||||||
.probe = pca9539_probe,
|
|
||||||
.remove = pca9539_remove,
|
|
||||||
.id_table = pca9539_id,
|
|
||||||
|
|
||||||
.detect = pca9539_detect,
|
|
||||||
.address_data = &addr_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init pca9539_init(void)
|
|
||||||
{
|
|
||||||
return i2c_add_driver(&pca9539_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit pca9539_exit(void)
|
|
||||||
{
|
|
||||||
i2c_del_driver(&pca9539_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
|
|
||||||
MODULE_DESCRIPTION("PCA9539 driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
module_init(pca9539_init);
|
|
||||||
module_exit(pca9539_exit);
|
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
|
|
||||||
Philip Edelbrock <phil@netroedge.com>,
|
|
||||||
Dan Eaton <dan.eaton@rocketlogix.com>
|
|
||||||
Ported to Linux 2.6 by Aurelien Jarno <aurel32@debian.org> with
|
|
||||||
the help of Jean Delvare <khali@linux-fr.org>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* A few notes about the PCF8574:
|
|
||||||
|
|
||||||
* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by
|
|
||||||
Philips Semiconductors. It is designed to provide a byte I2C
|
|
||||||
interface to up to 8 separate devices.
|
|
||||||
|
|
||||||
* The PCF8574 appears as a very simple SMBus device which can be
|
|
||||||
read from or written to with SMBUS byte read/write accesses.
|
|
||||||
|
|
||||||
--Dan
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
|
|
||||||
/* Addresses to scan: none, device can't be detected */
|
|
||||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
|
||||||
|
|
||||||
/* Insmod parameters */
|
|
||||||
I2C_CLIENT_INSMOD_2(pcf8574, pcf8574a);
|
|
||||||
|
|
||||||
/* Each client has this additional data */
|
|
||||||
struct pcf8574_data {
|
|
||||||
int write; /* Remember last written value */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pcf8574_init_client(struct i2c_client *client);
|
|
||||||
|
|
||||||
/* following are the sysfs callback functions */
|
|
||||||
static ssize_t show_read(struct device *dev, struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
return sprintf(buf, "%u\n", i2c_smbus_read_byte(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
|
|
||||||
|
|
||||||
static ssize_t show_write(struct device *dev, struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct pcf8574_data *data = i2c_get_clientdata(to_i2c_client(dev));
|
|
||||||
|
|
||||||
if (data->write < 0)
|
|
||||||
return data->write;
|
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", data->write);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t set_write(struct device *dev, struct device_attribute *attr, const char *buf,
|
|
||||||
size_t count)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
struct pcf8574_data *data = i2c_get_clientdata(client);
|
|
||||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
|
||||||
|
|
||||||
if (val > 0xff)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
data->write = val;
|
|
||||||
i2c_smbus_write_byte(client, data->write);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
|
|
||||||
|
|
||||||
static struct attribute *pcf8574_attributes[] = {
|
|
||||||
&dev_attr_read.attr,
|
|
||||||
&dev_attr_write.attr,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct attribute_group pcf8574_attr_group = {
|
|
||||||
.attrs = pcf8574_attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Real code
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
||||||
static int pcf8574_detect(struct i2c_client *client, int kind,
|
|
||||||
struct i2c_board_info *info)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adapter = client->adapter;
|
|
||||||
const char *client_name;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* Now, we would do the remaining detection. But the PCF8574 is plainly
|
|
||||||
impossible to detect! Stupid chip. */
|
|
||||||
|
|
||||||
/* Determine the chip type */
|
|
||||||
if (kind <= 0) {
|
|
||||||
if (client->addr >= 0x38 && client->addr <= 0x3f)
|
|
||||||
kind = pcf8574a;
|
|
||||||
else
|
|
||||||
kind = pcf8574;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kind == pcf8574a)
|
|
||||||
client_name = "pcf8574a";
|
|
||||||
else
|
|
||||||
client_name = "pcf8574";
|
|
||||||
strlcpy(info->type, client_name, I2C_NAME_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf8574_probe(struct i2c_client *client,
|
|
||||||
const struct i2c_device_id *id)
|
|
||||||
{
|
|
||||||
struct pcf8574_data *data;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct pcf8574_data), GFP_KERNEL);
|
|
||||||
if (!data) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, data);
|
|
||||||
|
|
||||||
/* Initialize the PCF8574 chip */
|
|
||||||
pcf8574_init_client(client);
|
|
||||||
|
|
||||||
/* Register sysfs hooks */
|
|
||||||
err = sysfs_create_group(&client->dev.kobj, &pcf8574_attr_group);
|
|
||||||
if (err)
|
|
||||||
goto exit_free;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
exit_free:
|
|
||||||
kfree(data);
|
|
||||||
exit:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf8574_remove(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group);
|
|
||||||
kfree(i2c_get_clientdata(client));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called when we have found a new PCF8574. */
|
|
||||||
static void pcf8574_init_client(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
struct pcf8574_data *data = i2c_get_clientdata(client);
|
|
||||||
data->write = -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id pcf8574_id[] = {
|
|
||||||
{ "pcf8574", 0 },
|
|
||||||
{ "pcf8574a", 0 },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct i2c_driver pcf8574_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "pcf8574",
|
|
||||||
},
|
|
||||||
.probe = pcf8574_probe,
|
|
||||||
.remove = pcf8574_remove,
|
|
||||||
.id_table = pcf8574_id,
|
|
||||||
|
|
||||||
.detect = pcf8574_detect,
|
|
||||||
.address_data = &addr_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init pcf8574_init(void)
|
|
||||||
{
|
|
||||||
return i2c_add_driver(&pcf8574_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit pcf8574_exit(void)
|
|
||||||
{
|
|
||||||
i2c_del_driver(&pcf8574_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MODULE_AUTHOR
|
|
||||||
("Frodo Looijaard <frodol@dds.nl>, "
|
|
||||||
"Philip Edelbrock <phil@netroedge.com>, "
|
|
||||||
"Dan Eaton <dan.eaton@rocketlogix.com> "
|
|
||||||
"and Aurelien Jarno <aurelien@aurel32.net>");
|
|
||||||
MODULE_DESCRIPTION("PCF8574 driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
module_init(pcf8574_init);
|
|
||||||
module_exit(pcf8574_exit);
|
|
|
@ -1,198 +0,0 @@
|
||||||
/*
|
|
||||||
pcf8575.c
|
|
||||||
|
|
||||||
About the PCF8575 chip: the PCF8575 is a 16-bit I/O expander for the I2C bus
|
|
||||||
produced by a.o. Philips Semiconductors.
|
|
||||||
|
|
||||||
Copyright (C) 2006 Michael Hennerich, Analog Devices Inc.
|
|
||||||
<hennerich@blackfin.uclinux.org>
|
|
||||||
Based on pcf8574.c.
|
|
||||||
|
|
||||||
Copyright (c) 2007 Bart Van Assche <bart.vanassche@gmail.com>.
|
|
||||||
Ported this driver from ucLinux to the mainstream Linux kernel.
|
|
||||||
|
|
||||||
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/i2c.h>
|
|
||||||
#include <linux/slab.h> /* kzalloc() */
|
|
||||||
#include <linux/sysfs.h> /* sysfs_create_group() */
|
|
||||||
|
|
||||||
/* Addresses to scan: none, device can't be detected */
|
|
||||||
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
|
||||||
|
|
||||||
/* Insmod parameters */
|
|
||||||
I2C_CLIENT_INSMOD;
|
|
||||||
|
|
||||||
|
|
||||||
/* Each client has this additional data */
|
|
||||||
struct pcf8575_data {
|
|
||||||
int write; /* last written value, or error code */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* following are the sysfs callback functions */
|
|
||||||
static ssize_t show_read(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
u16 val;
|
|
||||||
u8 iopin_state[2];
|
|
||||||
|
|
||||||
i2c_master_recv(client, iopin_state, 2);
|
|
||||||
|
|
||||||
val = iopin_state[0];
|
|
||||||
val |= iopin_state[1] << 8;
|
|
||||||
|
|
||||||
return sprintf(buf, "%u\n", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
|
|
||||||
|
|
||||||
static ssize_t show_write(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
struct pcf8575_data *data = dev_get_drvdata(dev);
|
|
||||||
if (data->write < 0)
|
|
||||||
return data->write;
|
|
||||||
return sprintf(buf, "%d\n", data->write);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t set_write(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
struct pcf8575_data *data = i2c_get_clientdata(client);
|
|
||||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
|
||||||
u8 iopin_state[2];
|
|
||||||
|
|
||||||
if (val > 0xffff)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
data->write = val;
|
|
||||||
|
|
||||||
iopin_state[0] = val & 0xFF;
|
|
||||||
iopin_state[1] = val >> 8;
|
|
||||||
|
|
||||||
i2c_master_send(client, iopin_state, 2);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
|
|
||||||
|
|
||||||
static struct attribute *pcf8575_attributes[] = {
|
|
||||||
&dev_attr_read.attr,
|
|
||||||
&dev_attr_write.attr,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct attribute_group pcf8575_attr_group = {
|
|
||||||
.attrs = pcf8575_attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Real code
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
|
||||||
static int pcf8575_detect(struct i2c_client *client, int kind,
|
|
||||||
struct i2c_board_info *info)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adapter = client->adapter;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* This is the place to detect whether the chip at the specified
|
|
||||||
address really is a PCF8575 chip. However, there is no method known
|
|
||||||
to detect whether an I2C chip is a PCF8575 or any other I2C chip. */
|
|
||||||
|
|
||||||
strlcpy(info->type, "pcf8575", I2C_NAME_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf8575_probe(struct i2c_client *client,
|
|
||||||
const struct i2c_device_id *id)
|
|
||||||
{
|
|
||||||
struct pcf8575_data *data;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct pcf8575_data), GFP_KERNEL);
|
|
||||||
if (!data) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, data);
|
|
||||||
data->write = -EAGAIN;
|
|
||||||
|
|
||||||
/* Register sysfs hooks */
|
|
||||||
err = sysfs_create_group(&client->dev.kobj, &pcf8575_attr_group);
|
|
||||||
if (err)
|
|
||||||
goto exit_free;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
exit_free:
|
|
||||||
kfree(data);
|
|
||||||
exit:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pcf8575_remove(struct i2c_client *client)
|
|
||||||
{
|
|
||||||
sysfs_remove_group(&client->dev.kobj, &pcf8575_attr_group);
|
|
||||||
kfree(i2c_get_clientdata(client));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct i2c_device_id pcf8575_id[] = {
|
|
||||||
{ "pcf8575", 0 },
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct i2c_driver pcf8575_driver = {
|
|
||||||
.driver = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "pcf8575",
|
|
||||||
},
|
|
||||||
.probe = pcf8575_probe,
|
|
||||||
.remove = pcf8575_remove,
|
|
||||||
.id_table = pcf8575_id,
|
|
||||||
|
|
||||||
.detect = pcf8575_detect,
|
|
||||||
.address_data = &addr_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init pcf8575_init(void)
|
|
||||||
{
|
|
||||||
return i2c_add_driver(&pcf8575_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit pcf8575_exit(void)
|
|
||||||
{
|
|
||||||
i2c_del_driver(&pcf8575_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>, "
|
|
||||||
"Bart Van Assche <bart.vanassche@gmail.com>");
|
|
||||||
MODULE_DESCRIPTION("pcf8575 driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
module_init(pcf8575_init);
|
|
||||||
module_exit(pcf8575_exit);
|
|
|
@ -24,10 +24,9 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/delay.h>
|
|
||||||
|
|
||||||
#define TSL2550_DRV_NAME "tsl2550"
|
#define TSL2550_DRV_NAME "tsl2550"
|
||||||
#define DRIVER_VERSION "1.1.2"
|
#define DRIVER_VERSION "1.2"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defines
|
* Defines
|
||||||
|
@ -96,32 +95,13 @@ static int tsl2550_set_power_state(struct i2c_client *client, int state)
|
||||||
|
|
||||||
static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
|
static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
|
||||||
{
|
{
|
||||||
unsigned long end;
|
int ret;
|
||||||
int loop = 0, ret = 0;
|
|
||||||
|
|
||||||
/*
|
ret = i2c_smbus_read_byte_data(client, cmd);
|
||||||
* Read ADC channel waiting at most 400ms (see data sheet for further
|
if (ret < 0)
|
||||||
* info).
|
return ret;
|
||||||
* 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))
|
if (!(ret & 0x80))
|
||||||
return -EIO;
|
return -EAGAIN;
|
||||||
return ret & 0x7f; /* remove the "valid" bit */
|
return ret & 0x7f; /* remove the "valid" bit */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,8 +265,6 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
|
||||||
return ret;
|
return ret;
|
||||||
ch0 = ret;
|
ch0 = ret;
|
||||||
|
|
||||||
mdelay(1);
|
|
||||||
|
|
||||||
ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
|
ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -345,11 +323,10 @@ static int tsl2550_init_client(struct i2c_client *client)
|
||||||
* Probe the chip. To do so we try to power up the device and then to
|
* Probe the chip. To do so we try to power up the device and then to
|
||||||
* read back the 0x03 code
|
* read back the 0x03 code
|
||||||
*/
|
*/
|
||||||
err = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
|
err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
mdelay(1);
|
if (err != TSL2550_POWER_UP)
|
||||||
if (i2c_smbus_read_byte(client) != TSL2550_POWER_UP)
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
data->power_state = 1;
|
data->power_state = 1;
|
||||||
|
|
||||||
|
@ -374,7 +351,8 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
|
||||||
struct tsl2550_data *data;
|
struct tsl2550_data *data;
|
||||||
int *opmode, err = 0;
|
int *opmode, err = 0;
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
|
||||||
|
| I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ static DEFINE_MUTEX(core_lock);
|
||||||
static DEFINE_IDR(i2c_adapter_idr);
|
static DEFINE_IDR(i2c_adapter_idr);
|
||||||
static LIST_HEAD(userspace_devices);
|
static LIST_HEAD(userspace_devices);
|
||||||
|
|
||||||
|
static struct device_type i2c_client_type;
|
||||||
static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
|
static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
|
||||||
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
||||||
|
|
||||||
|
@ -64,9 +65,13 @@ static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||||
|
|
||||||
static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver = to_i2c_driver(drv);
|
struct i2c_driver *driver;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
driver = to_i2c_driver(drv);
|
||||||
/* match on an id table if there is one */
|
/* match on an id table if there is one */
|
||||||
if (driver->id_table)
|
if (driver->id_table)
|
||||||
return i2c_match_id(driver->id_table, client) != NULL;
|
return i2c_match_id(driver->id_table, client) != NULL;
|
||||||
|
@ -94,10 +99,14 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
|
|
||||||
static int i2c_device_probe(struct device *dev)
|
static int i2c_device_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver = to_i2c_driver(dev->driver);
|
struct i2c_driver *driver;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
driver = to_i2c_driver(dev->driver);
|
||||||
if (!driver->probe || !driver->id_table)
|
if (!driver->probe || !driver->id_table)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
client->driver = driver;
|
client->driver = driver;
|
||||||
|
@ -114,11 +123,11 @@ static int i2c_device_probe(struct device *dev)
|
||||||
|
|
||||||
static int i2c_device_remove(struct device *dev)
|
static int i2c_device_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver;
|
struct i2c_driver *driver;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (!dev->driver)
|
if (!client || !dev->driver)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
driver = to_i2c_driver(dev->driver);
|
driver = to_i2c_driver(dev->driver);
|
||||||
|
@ -136,37 +145,40 @@ static int i2c_device_remove(struct device *dev)
|
||||||
|
|
||||||
static void i2c_device_shutdown(struct device *dev)
|
static void i2c_device_shutdown(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver;
|
struct i2c_driver *driver;
|
||||||
|
|
||||||
if (!dev->driver)
|
if (!client || !dev->driver)
|
||||||
return;
|
return;
|
||||||
driver = to_i2c_driver(dev->driver);
|
driver = to_i2c_driver(dev->driver);
|
||||||
if (driver->shutdown)
|
if (driver->shutdown)
|
||||||
driver->shutdown(to_i2c_client(dev));
|
driver->shutdown(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
|
static int i2c_device_suspend(struct device *dev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver;
|
struct i2c_driver *driver;
|
||||||
|
|
||||||
if (!dev->driver)
|
if (!client || !dev->driver)
|
||||||
return 0;
|
return 0;
|
||||||
driver = to_i2c_driver(dev->driver);
|
driver = to_i2c_driver(dev->driver);
|
||||||
if (!driver->suspend)
|
if (!driver->suspend)
|
||||||
return 0;
|
return 0;
|
||||||
return driver->suspend(to_i2c_client(dev), mesg);
|
return driver->suspend(client, mesg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_device_resume(struct device *dev)
|
static int i2c_device_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
struct i2c_driver *driver;
|
struct i2c_driver *driver;
|
||||||
|
|
||||||
if (!dev->driver)
|
if (!client || !dev->driver)
|
||||||
return 0;
|
return 0;
|
||||||
driver = to_i2c_driver(dev->driver);
|
driver = to_i2c_driver(dev->driver);
|
||||||
if (!driver->resume)
|
if (!driver->resume)
|
||||||
return 0;
|
return 0;
|
||||||
return driver->resume(to_i2c_client(dev));
|
return driver->resume(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_client_dev_release(struct device *dev)
|
static void i2c_client_dev_release(struct device *dev)
|
||||||
|
@ -175,10 +187,10 @@ static void i2c_client_dev_release(struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
show_client_name(struct device *dev, struct device_attribute *attr, char *buf)
|
show_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
|
||||||
return sprintf(buf, "%s\n", client->name);
|
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
@ -188,18 +200,28 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
|
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device_attribute i2c_dev_attrs[] = {
|
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||||
__ATTR(name, S_IRUGO, show_client_name, NULL),
|
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||||
|
|
||||||
|
static struct attribute *i2c_dev_attrs[] = {
|
||||||
|
&dev_attr_name.attr,
|
||||||
/* modalias helps coldplug: modprobe $(cat .../modalias) */
|
/* modalias helps coldplug: modprobe $(cat .../modalias) */
|
||||||
__ATTR(modalias, S_IRUGO, show_modalias, NULL),
|
&dev_attr_modalias.attr,
|
||||||
{ },
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute_group i2c_dev_attr_group = {
|
||||||
|
.attrs = i2c_dev_attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group *i2c_dev_attr_groups[] = {
|
||||||
|
&i2c_dev_attr_group,
|
||||||
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bus_type i2c_bus_type = {
|
struct bus_type i2c_bus_type = {
|
||||||
.name = "i2c",
|
.name = "i2c",
|
||||||
.dev_attrs = i2c_dev_attrs,
|
|
||||||
.match = i2c_device_match,
|
.match = i2c_device_match,
|
||||||
.uevent = i2c_device_uevent,
|
|
||||||
.probe = i2c_device_probe,
|
.probe = i2c_device_probe,
|
||||||
.remove = i2c_device_remove,
|
.remove = i2c_device_remove,
|
||||||
.shutdown = i2c_device_shutdown,
|
.shutdown = i2c_device_shutdown,
|
||||||
|
@ -208,6 +230,12 @@ struct bus_type i2c_bus_type = {
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
||||||
|
|
||||||
|
static struct device_type i2c_client_type = {
|
||||||
|
.groups = i2c_dev_attr_groups,
|
||||||
|
.uevent = i2c_device_uevent,
|
||||||
|
.release = i2c_client_dev_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_verify_client - return parameter as i2c_client, or NULL
|
* i2c_verify_client - return parameter as i2c_client, or NULL
|
||||||
|
@ -220,7 +248,7 @@ EXPORT_SYMBOL_GPL(i2c_bus_type);
|
||||||
*/
|
*/
|
||||||
struct i2c_client *i2c_verify_client(struct device *dev)
|
struct i2c_client *i2c_verify_client(struct device *dev)
|
||||||
{
|
{
|
||||||
return (dev->bus == &i2c_bus_type)
|
return (dev->type == &i2c_client_type)
|
||||||
? to_i2c_client(dev)
|
? to_i2c_client(dev)
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +301,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
||||||
|
|
||||||
client->dev.parent = &client->adapter->dev;
|
client->dev.parent = &client->adapter->dev;
|
||||||
client->dev.bus = &i2c_bus_type;
|
client->dev.bus = &i2c_bus_type;
|
||||||
client->dev.release = i2c_client_dev_release;
|
client->dev.type = &i2c_client_type;
|
||||||
|
|
||||||
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
||||||
client->addr);
|
client->addr);
|
||||||
|
@ -368,13 +396,6 @@ static void i2c_adapter_dev_release(struct device *dev)
|
||||||
complete(&adap->dev_released);
|
complete(&adap->dev_released);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *adap = to_i2c_adapter(dev);
|
|
||||||
return sprintf(buf, "%s\n", adap->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let users instantiate I2C devices through sysfs. This can be used when
|
* Let users instantiate I2C devices through sysfs. This can be used when
|
||||||
* platform initialization code doesn't contain the proper data for
|
* platform initialization code doesn't contain the proper data for
|
||||||
|
@ -493,19 +514,34 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device_attribute i2c_adapter_attrs[] = {
|
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
|
||||||
__ATTR(name, S_IRUGO, show_adapter_name, NULL),
|
static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);
|
||||||
__ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device),
|
|
||||||
__ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device),
|
static struct attribute *i2c_adapter_attrs[] = {
|
||||||
{ },
|
&dev_attr_name.attr,
|
||||||
|
&dev_attr_new_device.attr,
|
||||||
|
&dev_attr_delete_device.attr,
|
||||||
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct class i2c_adapter_class = {
|
static struct attribute_group i2c_adapter_attr_group = {
|
||||||
.owner = THIS_MODULE,
|
.attrs = i2c_adapter_attrs,
|
||||||
.name = "i2c-adapter",
|
|
||||||
.dev_attrs = i2c_adapter_attrs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group *i2c_adapter_attr_groups[] = {
|
||||||
|
&i2c_adapter_attr_group,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_type i2c_adapter_type = {
|
||||||
|
.groups = i2c_adapter_attr_groups,
|
||||||
|
.release = i2c_adapter_dev_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
|
static struct class_compat *i2c_adapter_compat_class;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
|
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
struct i2c_devinfo *devinfo;
|
struct i2c_devinfo *devinfo;
|
||||||
|
@ -555,14 +591,22 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
adap->timeout = HZ;
|
adap->timeout = HZ;
|
||||||
|
|
||||||
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
|
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
|
||||||
adap->dev.release = &i2c_adapter_dev_release;
|
adap->dev.bus = &i2c_bus_type;
|
||||||
adap->dev.class = &i2c_adapter_class;
|
adap->dev.type = &i2c_adapter_type;
|
||||||
res = device_register(&adap->dev);
|
res = device_register(&adap->dev);
|
||||||
if (res)
|
if (res)
|
||||||
goto out_list;
|
goto out_list;
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
|
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
|
||||||
|
adap->dev.parent);
|
||||||
|
if (res)
|
||||||
|
dev_warn(&adap->dev,
|
||||||
|
"Failed to create compatibility class link\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* create pre-declared device nodes */
|
/* create pre-declared device nodes */
|
||||||
if (adap->nr < __i2c_first_dynamic_bus_num)
|
if (adap->nr < __i2c_first_dynamic_bus_num)
|
||||||
i2c_scan_static_board_info(adap);
|
i2c_scan_static_board_info(adap);
|
||||||
|
@ -741,6 +785,11 @@ int i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
checking the returned value. */
|
checking the returned value. */
|
||||||
res = device_for_each_child(&adap->dev, NULL, __unregister_client);
|
res = device_for_each_child(&adap->dev, NULL, __unregister_client);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
|
class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
|
||||||
|
adap->dev.parent);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* clean up the sysfs representation */
|
/* clean up the sysfs representation */
|
||||||
init_completion(&adap->dev_released);
|
init_completion(&adap->dev_released);
|
||||||
device_unregister(&adap->dev);
|
device_unregister(&adap->dev);
|
||||||
|
@ -768,9 +817,13 @@ EXPORT_SYMBOL(i2c_del_adapter);
|
||||||
|
|
||||||
static int __attach_adapter(struct device *dev, void *data)
|
static int __attach_adapter(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adapter = to_i2c_adapter(dev);
|
struct i2c_adapter *adapter;
|
||||||
struct i2c_driver *driver = data;
|
struct i2c_driver *driver = data;
|
||||||
|
|
||||||
|
if (dev->type != &i2c_adapter_type)
|
||||||
|
return 0;
|
||||||
|
adapter = to_i2c_adapter(dev);
|
||||||
|
|
||||||
i2c_detect(adapter, driver);
|
i2c_detect(adapter, driver);
|
||||||
|
|
||||||
/* Legacy drivers scan i2c busses directly */
|
/* Legacy drivers scan i2c busses directly */
|
||||||
|
@ -809,8 +862,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
||||||
INIT_LIST_HEAD(&driver->clients);
|
INIT_LIST_HEAD(&driver->clients);
|
||||||
/* Walk the adapters that are already present */
|
/* Walk the adapters that are already present */
|
||||||
mutex_lock(&core_lock);
|
mutex_lock(&core_lock);
|
||||||
class_for_each_device(&i2c_adapter_class, NULL, driver,
|
bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
|
||||||
__attach_adapter);
|
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -819,10 +871,14 @@ EXPORT_SYMBOL(i2c_register_driver);
|
||||||
|
|
||||||
static int __detach_adapter(struct device *dev, void *data)
|
static int __detach_adapter(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adapter = to_i2c_adapter(dev);
|
struct i2c_adapter *adapter;
|
||||||
struct i2c_driver *driver = data;
|
struct i2c_driver *driver = data;
|
||||||
struct i2c_client *client, *_n;
|
struct i2c_client *client, *_n;
|
||||||
|
|
||||||
|
if (dev->type != &i2c_adapter_type)
|
||||||
|
return 0;
|
||||||
|
adapter = to_i2c_adapter(dev);
|
||||||
|
|
||||||
/* Remove the devices we created ourselves as the result of hardware
|
/* Remove the devices we created ourselves as the result of hardware
|
||||||
* probing (using a driver's detect method) */
|
* probing (using a driver's detect method) */
|
||||||
list_for_each_entry_safe(client, _n, &driver->clients, detected) {
|
list_for_each_entry_safe(client, _n, &driver->clients, detected) {
|
||||||
|
@ -850,8 +906,7 @@ static int __detach_adapter(struct device *dev, void *data)
|
||||||
void i2c_del_driver(struct i2c_driver *driver)
|
void i2c_del_driver(struct i2c_driver *driver)
|
||||||
{
|
{
|
||||||
mutex_lock(&core_lock);
|
mutex_lock(&core_lock);
|
||||||
class_for_each_device(&i2c_adapter_class, NULL, driver,
|
bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter);
|
||||||
__detach_adapter);
|
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
|
|
||||||
driver_unregister(&driver->driver);
|
driver_unregister(&driver->driver);
|
||||||
|
@ -940,17 +995,23 @@ static int __init i2c_init(void)
|
||||||
retval = bus_register(&i2c_bus_type);
|
retval = bus_register(&i2c_bus_type);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
retval = class_register(&i2c_adapter_class);
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
if (retval)
|
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
|
||||||
|
if (!i2c_adapter_compat_class) {
|
||||||
|
retval = -ENOMEM;
|
||||||
goto bus_err;
|
goto bus_err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
retval = i2c_add_driver(&dummy_driver);
|
retval = i2c_add_driver(&dummy_driver);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto class_err;
|
goto class_err;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
class_err:
|
class_err:
|
||||||
class_unregister(&i2c_adapter_class);
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
|
class_compat_unregister(i2c_adapter_compat_class);
|
||||||
bus_err:
|
bus_err:
|
||||||
|
#endif
|
||||||
bus_unregister(&i2c_bus_type);
|
bus_unregister(&i2c_bus_type);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -958,7 +1019,9 @@ bus_err:
|
||||||
static void __exit i2c_exit(void)
|
static void __exit i2c_exit(void)
|
||||||
{
|
{
|
||||||
i2c_del_driver(&dummy_driver);
|
i2c_del_driver(&dummy_driver);
|
||||||
class_unregister(&i2c_adapter_class);
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
|
class_compat_unregister(i2c_adapter_compat_class);
|
||||||
|
#endif
|
||||||
bus_unregister(&i2c_bus_type);
|
bus_unregister(&i2c_bus_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,17 +27,6 @@
|
||||||
legacy chip driver needs to identify a bus or a bus driver needs to
|
legacy chip driver needs to identify a bus or a bus driver needs to
|
||||||
identify a legacy client. If you don't need them, just don't set them. */
|
identify a legacy client. If you don't need them, just don't set them. */
|
||||||
|
|
||||||
/*
|
|
||||||
* ---- Driver types -----------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define I2C_DRIVERID_MSP3400 1
|
|
||||||
#define I2C_DRIVERID_TUNER 2
|
|
||||||
#define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */
|
|
||||||
#define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */
|
|
||||||
#define I2C_DRIVERID_SAA711X 73 /* saa711x video encoders */
|
|
||||||
#define I2C_DRIVERID_INFRARED 75 /* I2C InfraRed on Video boards */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ---- Adapter types ----------------------------------------------------
|
* ---- Adapter types ----------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -98,7 +98,6 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2c_driver - represent an I2C device driver
|
* struct i2c_driver - represent an I2C device driver
|
||||||
* @id: Unique driver ID (optional)
|
|
||||||
* @class: What kind of i2c device we instantiate (for detect)
|
* @class: What kind of i2c device we instantiate (for detect)
|
||||||
* @attach_adapter: Callback for bus addition (for legacy drivers)
|
* @attach_adapter: Callback for bus addition (for legacy drivers)
|
||||||
* @detach_adapter: Callback for bus removal (for legacy drivers)
|
* @detach_adapter: Callback for bus removal (for legacy drivers)
|
||||||
|
@ -135,7 +134,6 @@ extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client,
|
||||||
* not allowed.
|
* not allowed.
|
||||||
*/
|
*/
|
||||||
struct i2c_driver {
|
struct i2c_driver {
|
||||||
int id;
|
|
||||||
unsigned int class;
|
unsigned int class;
|
||||||
|
|
||||||
/* Notifies the driver that a new bus has appeared or is about to be
|
/* Notifies the driver that a new bus has appeared or is about to be
|
||||||
|
|
|
@ -543,6 +543,7 @@
|
||||||
#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450
|
#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450
|
||||||
#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451
|
#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451
|
||||||
#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458
|
#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458
|
||||||
|
#define PCI_DEVICE_ID_AMD_SB900_SMBUS 0x780b
|
||||||
#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F
|
#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F
|
||||||
#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090
|
#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090
|
||||||
#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091
|
#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091
|
||||||
|
|
Loading…
Reference in New Issue