Merge branch 'i2c/for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "For 3.19, the I2C subsystem has to offer special candy this time. Right in time for Christmas :) - I2C slave framework: finally, a generic mechanism for Linux being an I2C slave (if the bus driver supports that). Docs are still missing but will come later this cycle, the code is good enough to go. - I2C muxes represent their topology in sysfs much more detailed. This will help users to navigate around much easier. - irq population of i2c clients is now done at probe time, not device creation time, to have better support for deferred probing. - new drivers for Imagination SCB, Amlogic Meson - DMA support added for Freescale IMX, Renesas SHMobile - slightly bigger driver updates to OMAP, i801, AT91, and rk3x (mostly quirk handling, timing updates, and using better kernel interfaces) - eeprom driver can now write with byte-access (very slow, but OK to have) - and the bunch of smaller fixes, cleanups, ID updates..." * 'i2c/for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (56 commits) i2c: sh_mobile: remove unneeded DMA mask i2c: rcar: add slave support i2c: slave-eeprom: add eeprom simulator driver i2c: core changes for slave support MAINTAINERS: add I2C dt bindings also to I2C realm i2c: designware: Fix falling time bindings doc i2c: davinci: switch to use platform_get_irq Documentation: i2c: Use PM ops instead of legacy suspend/resume i2c: sh_mobile: optimize irq entry i2c: pxa: add support for SCCB devices omap: i2c: don't check bus state IP rev3.3 and earlier i2c: s3c2410: Handle i2c sys_cfg register in i2c driver i2c: rk3x: add Kconfig dependency on COMMON_CLK i2c: omap: add notes related to i2c multimaster mode i2c: omap: don't reset controller if Arbitration Lost detected i2c: omap: implement workaround for handling invalid BB-bit values i2c: omap: cleanup register definitions i2c: rk3x: handle dynamic clock rate changes correctly i2c: at91: enable probe deferring on dma channel request i2c: at91: remove legacy DMA support ...
This commit is contained in:
Коммит
96895199c8
|
@ -14,10 +14,10 @@ Optional properties :
|
||||||
- i2c-sda-hold-time-ns : should contain the SDA hold time in nanoseconds.
|
- i2c-sda-hold-time-ns : should contain the SDA hold time in nanoseconds.
|
||||||
This option is only supported in hardware blocks version 1.11a or newer.
|
This option is only supported in hardware blocks version 1.11a or newer.
|
||||||
|
|
||||||
- i2c-scl-falling-time : should contain the SCL falling time in nanoseconds.
|
- i2c-scl-falling-time-ns : should contain the SCL falling time in nanoseconds.
|
||||||
This value which is by default 300ns is used to compute the tLOW period.
|
This value which is by default 300ns is used to compute the tLOW period.
|
||||||
|
|
||||||
- i2c-sda-falling-time : should contain the SDA falling time in nanoseconds.
|
- i2c-sda-falling-time-ns : should contain the SDA falling time in nanoseconds.
|
||||||
This value which is by default 300ns is used to compute the tHIGH period.
|
This value which is by default 300ns is used to compute the tHIGH period.
|
||||||
|
|
||||||
Example :
|
Example :
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
IMG Serial Control Bus (SCB) I2C Controller
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
- compatible: "img,scb-i2c"
|
||||||
|
- reg: Physical base address and length of controller registers
|
||||||
|
- interrupts: Interrupt number used by the controller
|
||||||
|
- clocks : Should contain a clock specifier for each entry in clock-names
|
||||||
|
- clock-names : Should contain the following entries:
|
||||||
|
"scb", for the SCB core clock.
|
||||||
|
"sys", for the system clock.
|
||||||
|
- clock-frequency: The I2C bus frequency in Hz
|
||||||
|
- #address-cells: Should be <1>
|
||||||
|
- #size-cells: Should be <0>
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
i2c@18100000 {
|
||||||
|
compatible = "img,scb-i2c";
|
||||||
|
reg = <0x18100000 0x200>;
|
||||||
|
interrupts = <GIC_SHARED 2 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&i2c0_clk>, <&system_clk>;
|
||||||
|
clock-names = "scb", "sys";
|
||||||
|
clock-frequency = <400000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
|
@ -11,6 +11,8 @@ Required properties:
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
||||||
The absence of the propoerty indicates the default frequency 100 kHz.
|
The absence of the propoerty indicates the default frequency 100 kHz.
|
||||||
|
- dmas: A list of two dma specifiers, one for each entry in dma-names.
|
||||||
|
- dma-names: should contain "tx" and "rx".
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
@ -26,3 +28,12 @@ i2c@70038000 { /* HS-I2C on i.MX51 */
|
||||||
interrupts = <64>;
|
interrupts = <64>;
|
||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
i2c0: i2c@40066000 { /* i2c0 on vf610 */
|
||||||
|
compatible = "fsl,vf610-i2c";
|
||||||
|
reg = <0x40066000 0x1000>;
|
||||||
|
interrupts =<0 71 0x04>;
|
||||||
|
dmas = <&edma0 0 50>,
|
||||||
|
<&edma0 0 51>;
|
||||||
|
dma-names = "rx","tx";
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
Amlogic Meson I2C controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: must be "amlogic,meson6-i2c"
|
||||||
|
- reg: physical address and length of the device registers
|
||||||
|
- interrupts: a single interrupt specifier
|
||||||
|
- clocks: clock for the device
|
||||||
|
- #address-cells: should be <1>
|
||||||
|
- #size-cells: should be <0>
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clock-frequency: the desired I2C bus clock frequency in Hz; in
|
||||||
|
absence of this property the default value is used (100 kHz).
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
i2c@c8100500 {
|
||||||
|
compatible = "amlogic,meson6-i2c";
|
||||||
|
reg = <0xc8100500 0x20>;
|
||||||
|
interrupts = <0 92 1>;
|
||||||
|
clocks = <&clk81>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
|
@ -2,6 +2,15 @@ Device tree configuration for Renesas IIC (sh_mobile) driver
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,iic-<soctype>". "renesas,rmobile-iic" as fallback
|
- compatible : "renesas,iic-<soctype>". "renesas,rmobile-iic" as fallback
|
||||||
|
Examples with soctypes are:
|
||||||
|
- "renesas,iic-r8a73a4" (R-Mobile APE6)
|
||||||
|
- "renesas,iic-r8a7740" (R-Mobile A1)
|
||||||
|
- "renesas,iic-r8a7790" (R-Car H2)
|
||||||
|
- "renesas,iic-r8a7791" (R-Car M2-W)
|
||||||
|
- "renesas,iic-r8a7792" (R-Car V2H)
|
||||||
|
- "renesas,iic-r8a7793" (R-Car M2-N)
|
||||||
|
- "renesas,iic-r8a7794" (R-Car E2)
|
||||||
|
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||||
- reg : address start and address range size of device
|
- reg : address start and address range size of device
|
||||||
- interrupts : interrupt of device
|
- interrupts : interrupt of device
|
||||||
- clocks : clock for device
|
- clocks : clock for device
|
||||||
|
@ -10,6 +19,11 @@ Required properties:
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- clock-frequency : frequency of bus clock in Hz. Default 100kHz if unset.
|
- clock-frequency : frequency of bus clock in Hz. Default 100kHz if unset.
|
||||||
|
- dmas : Must contain a list of two references to DMA
|
||||||
|
specifiers, one for transmission, and one for
|
||||||
|
reception.
|
||||||
|
- dma-names : Must contain a list of two DMA names, "tx" and "rx".
|
||||||
|
|
||||||
|
|
||||||
Pinctrl properties might be needed, too. See there.
|
Pinctrl properties might be needed, too. See there.
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ adi,adt7473 +/-1C TDM Extended Temp Range I.C
|
||||||
adi,adt7475 +/-1C TDM Extended Temp Range I.C
|
adi,adt7475 +/-1C TDM Extended Temp Range I.C
|
||||||
adi,adt7476 +/-1C TDM Extended Temp Range I.C
|
adi,adt7476 +/-1C TDM Extended Temp Range I.C
|
||||||
adi,adt7490 +/-1C TDM Extended Temp Range I.C
|
adi,adt7490 +/-1C TDM Extended Temp Range I.C
|
||||||
|
adi,adxl345 Three-Axis Digital Accelerometer
|
||||||
|
adi,adxl346 Three-Axis Digital Accelerometer
|
||||||
|
adi,adxl34x Three-Axis Digital Accelerometer
|
||||||
at,24c08 i2c serial eeprom (24cxx)
|
at,24c08 i2c serial eeprom (24cxx)
|
||||||
atmel,24c00 i2c serial eeprom (24cxx)
|
atmel,24c00 i2c serial eeprom (24cxx)
|
||||||
atmel,24c01 i2c serial eeprom (24cxx)
|
atmel,24c01 i2c serial eeprom (24cxx)
|
||||||
|
@ -76,7 +79,12 @@ ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI an
|
||||||
pericom,pt7c4338 Real-time Clock Module
|
pericom,pt7c4338 Real-time Clock Module
|
||||||
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
|
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
|
||||||
ramtron,24c64 i2c serial eeprom (24cxx)
|
ramtron,24c64 i2c serial eeprom (24cxx)
|
||||||
|
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
|
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
|
ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
|
ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
|
ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
|
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
|
||||||
sii,s35390a 2-wire CMOS real-time clock
|
sii,s35390a 2-wire CMOS real-time clock
|
||||||
st-micro,24c256 i2c serial eeprom (24cxx)
|
st-micro,24c256 i2c serial eeprom (24cxx)
|
||||||
|
|
|
@ -29,6 +29,7 @@ Supported adapters:
|
||||||
* Intel Wildcat Point-LP (PCH)
|
* Intel Wildcat Point-LP (PCH)
|
||||||
* Intel BayTrail (SOC)
|
* Intel BayTrail (SOC)
|
||||||
* Intel Sunrise Point-H (PCH)
|
* Intel Sunrise Point-H (PCH)
|
||||||
|
* Intel Sunrise Point-LP (PCH)
|
||||||
Datasheets: Publicly available at the Intel website
|
Datasheets: Publicly available at the Intel website
|
||||||
|
|
||||||
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
||||||
|
|
|
@ -79,11 +79,10 @@ static struct i2c_driver example_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "example",
|
.name = "example",
|
||||||
|
.pm = &example_pm_ops,
|
||||||
},
|
},
|
||||||
.attach_adapter = example_attach_adapter,
|
.attach_adapter = example_attach_adapter,
|
||||||
.detach_client = example_detach,
|
.detach_client = example_detach,
|
||||||
.suspend = example_suspend,
|
|
||||||
.resume = example_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,10 +271,9 @@ static struct i2c_driver example_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "example",
|
.name = "example",
|
||||||
|
.pm = &example_pm_ops,
|
||||||
},
|
},
|
||||||
.id_table = example_idtable,
|
.id_table = example_idtable,
|
||||||
.probe = example_probe,
|
.probe = example_probe,
|
||||||
.remove = example_remove,
|
.remove = example_remove,
|
||||||
.suspend = example_suspend,
|
|
||||||
.resume = example_resume,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,7 @@ MODULE_DEVICE_TABLE(i2c, foo_idtable);
|
||||||
static struct i2c_driver foo_driver = {
|
static struct i2c_driver foo_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "foo",
|
.name = "foo",
|
||||||
|
.pm = &foo_pm_ops, /* optional */
|
||||||
},
|
},
|
||||||
|
|
||||||
.id_table = foo_idtable,
|
.id_table = foo_idtable,
|
||||||
|
@ -47,8 +48,6 @@ static struct i2c_driver foo_driver = {
|
||||||
.address_list = normal_i2c,
|
.address_list = normal_i2c,
|
||||||
|
|
||||||
.shutdown = foo_shutdown, /* optional */
|
.shutdown = foo_shutdown, /* optional */
|
||||||
.suspend = foo_suspend, /* optional */
|
|
||||||
.resume = foo_resume, /* optional */
|
|
||||||
.command = foo_command, /* optional, deprecated */
|
.command = foo_command, /* optional, deprecated */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +278,9 @@ Power Management
|
||||||
|
|
||||||
If your I2C device needs special handling when entering a system low
|
If your I2C device needs special handling when entering a system low
|
||||||
power state -- like putting a transceiver into a low power mode, or
|
power state -- like putting a transceiver into a low power mode, or
|
||||||
activating a system wakeup mechanism -- do that in the suspend() method.
|
activating a system wakeup mechanism -- do that by implementing the
|
||||||
The resume() method should reverse what the suspend() method does.
|
appropriate callbacks for the dev_pm_ops of the driver (like suspend
|
||||||
|
and resume).
|
||||||
|
|
||||||
These are standard driver model calls, and they work just like they
|
These are standard driver model calls, and they work just like they
|
||||||
would for any other driver stack. The calls can sleep, and can use
|
would for any other driver stack. The calls can sleep, and can use
|
||||||
|
|
|
@ -4657,6 +4657,7 @@ W: https://i2c.wiki.kernel.org/
|
||||||
Q: https://patchwork.ozlabs.org/project/linux-i2c/list/
|
Q: https://patchwork.ozlabs.org/project/linux-i2c/list/
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/i2c/
|
||||||
F: Documentation/i2c/
|
F: Documentation/i2c/
|
||||||
F: drivers/i2c/
|
F: drivers/i2c/
|
||||||
F: include/linux/i2c.h
|
F: include/linux/i2c.h
|
||||||
|
|
|
@ -110,6 +110,16 @@ config I2C_STUB
|
||||||
|
|
||||||
If you don't know what to do here, definitely say N.
|
If you don't know what to do here, definitely say N.
|
||||||
|
|
||||||
|
config I2C_SLAVE
|
||||||
|
bool "I2C slave support"
|
||||||
|
|
||||||
|
if I2C_SLAVE
|
||||||
|
|
||||||
|
config I2C_SLAVE_EEPROM
|
||||||
|
tristate "I2C eeprom slave driver"
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
config I2C_DEBUG_CORE
|
config I2C_DEBUG_CORE
|
||||||
bool "I2C Core debugging messages"
|
bool "I2C Core debugging messages"
|
||||||
help
|
help
|
||||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
|
||||||
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
|
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
|
||||||
obj-y += algos/ busses/ muxes/
|
obj-y += algos/ busses/ muxes/
|
||||||
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
|
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
|
||||||
|
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
|
||||||
|
|
||||||
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
|
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
|
||||||
CFLAGS_i2c-core.o := -Wno-deprecated-declarations
|
CFLAGS_i2c-core.o := -Wno-deprecated-declarations
|
||||||
|
|
|
@ -123,6 +123,7 @@ config I2C_I801
|
||||||
Wildcat Point-LP (PCH)
|
Wildcat Point-LP (PCH)
|
||||||
BayTrail (SOC)
|
BayTrail (SOC)
|
||||||
Sunrise Point-H (PCH)
|
Sunrise Point-H (PCH)
|
||||||
|
Sunrise Point-LP (PCH)
|
||||||
|
|
||||||
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-i801.
|
will be called i2c-i801.
|
||||||
|
@ -523,6 +524,16 @@ config I2C_IBM_IIC
|
||||||
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-ibm_iic.
|
will be called i2c-ibm_iic.
|
||||||
|
|
||||||
|
config I2C_IMG
|
||||||
|
tristate "Imagination Technologies I2C SCB Controller"
|
||||||
|
depends on MIPS || METAG || COMPILE_TEST
|
||||||
|
help
|
||||||
|
Say Y here if you want to use the IMG I2C SCB controller,
|
||||||
|
available on the TZ1090 and other IMG SoCs.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called i2c-img-scb.
|
||||||
|
|
||||||
config I2C_IMX
|
config I2C_IMX
|
||||||
tristate "IMX I2C interface"
|
tristate "IMX I2C interface"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC
|
||||||
|
@ -553,6 +564,13 @@ config I2C_KEMPLD
|
||||||
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-kempld.
|
will be called i2c-kempld.
|
||||||
|
|
||||||
|
config I2C_MESON
|
||||||
|
tristate "Amlogic Meson I2C controller"
|
||||||
|
depends on ARCH_MESON
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for the
|
||||||
|
I2C interface on the Amlogic Meson family of SoCs.
|
||||||
|
|
||||||
config I2C_MPC
|
config I2C_MPC
|
||||||
tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
|
tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
|
||||||
depends on PPC
|
depends on PPC
|
||||||
|
@ -702,7 +720,7 @@ config I2C_RIIC
|
||||||
|
|
||||||
config I2C_RK3X
|
config I2C_RK3X
|
||||||
tristate "Rockchip RK3xxx I2C adapter"
|
tristate "Rockchip RK3xxx I2C adapter"
|
||||||
depends on OF
|
depends on OF && COMMON_CLK
|
||||||
help
|
help
|
||||||
Say Y here to include support for the I2C adapter in Rockchip RK3xxx
|
Say Y here to include support for the I2C adapter in Rockchip RK3xxx
|
||||||
SoCs.
|
SoCs.
|
||||||
|
|
|
@ -50,9 +50,11 @@ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||||
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
||||||
obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
|
obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
|
||||||
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
||||||
|
obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
|
||||||
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
||||||
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||||
|
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
|
||||||
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
||||||
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
|
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
|
||||||
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
|
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
|
||||||
|
|
|
@ -31,10 +31,13 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/platform_data/dma-atmel.h>
|
#include <linux/platform_data/dma-atmel.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
|
#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
|
||||||
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
|
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
|
||||||
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
|
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
|
||||||
|
#define AUTOSUSPEND_TIMEOUT 2000
|
||||||
|
|
||||||
/* AT91 TWI register definitions */
|
/* AT91 TWI register definitions */
|
||||||
#define AT91_TWI_CR 0x0000 /* Control Register */
|
#define AT91_TWI_CR 0x0000 /* Control Register */
|
||||||
|
@ -72,7 +75,6 @@ struct at91_twi_pdata {
|
||||||
unsigned clk_max_div;
|
unsigned clk_max_div;
|
||||||
unsigned clk_offset;
|
unsigned clk_offset;
|
||||||
bool has_unre_flag;
|
bool has_unre_flag;
|
||||||
bool has_dma_support;
|
|
||||||
struct at_dma_slave dma_slave;
|
struct at_dma_slave dma_slave;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -481,6 +483,10 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
|
dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num);
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(dev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The hardware can handle at most two messages concatenated by a
|
* The hardware can handle at most two messages concatenated by a
|
||||||
* repeated start via it's internal address feature.
|
* repeated start via it's internal address feature.
|
||||||
|
@ -488,18 +494,21 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
|
||||||
if (num > 2) {
|
if (num > 2) {
|
||||||
dev_err(dev->dev,
|
dev_err(dev->dev,
|
||||||
"cannot handle more than two concatenated messages.\n");
|
"cannot handle more than two concatenated messages.\n");
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto out;
|
||||||
} else if (num == 2) {
|
} else if (num == 2) {
|
||||||
int internal_address = 0;
|
int internal_address = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (msg->flags & I2C_M_RD) {
|
if (msg->flags & I2C_M_RD) {
|
||||||
dev_err(dev->dev, "first transfer must be write.\n");
|
dev_err(dev->dev, "first transfer must be write.\n");
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
if (msg->len > 3) {
|
if (msg->len > 3) {
|
||||||
dev_err(dev->dev, "first message size must be <= 3.\n");
|
dev_err(dev->dev, "first message size must be <= 3.\n");
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1st msg is put into the internal address, start with 2nd */
|
/* 1st msg is put into the internal address, start with 2nd */
|
||||||
|
@ -523,7 +532,12 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
|
||||||
|
|
||||||
ret = at91_do_twi_transfer(dev);
|
ret = at91_do_twi_transfer(dev);
|
||||||
|
|
||||||
return (ret < 0) ? ret : num;
|
ret = (ret < 0) ? ret : num;
|
||||||
|
out:
|
||||||
|
pm_runtime_mark_last_busy(dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(dev->dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 at91_twi_func(struct i2c_adapter *adapter)
|
static u32 at91_twi_func(struct i2c_adapter *adapter)
|
||||||
|
@ -541,35 +555,30 @@ static struct at91_twi_pdata at91rm9200_config = {
|
||||||
.clk_max_div = 5,
|
.clk_max_div = 5,
|
||||||
.clk_offset = 3,
|
.clk_offset = 3,
|
||||||
.has_unre_flag = true,
|
.has_unre_flag = true,
|
||||||
.has_dma_support = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9261_config = {
|
static struct at91_twi_pdata at91sam9261_config = {
|
||||||
.clk_max_div = 5,
|
.clk_max_div = 5,
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_dma_support = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9260_config = {
|
static struct at91_twi_pdata at91sam9260_config = {
|
||||||
.clk_max_div = 7,
|
.clk_max_div = 7,
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_dma_support = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g20_config = {
|
static struct at91_twi_pdata at91sam9g20_config = {
|
||||||
.clk_max_div = 7,
|
.clk_max_div = 7,
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_dma_support = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g10_config = {
|
static struct at91_twi_pdata at91sam9g10_config = {
|
||||||
.clk_max_div = 7,
|
.clk_max_div = 7,
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_dma_support = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct platform_device_id at91_twi_devtypes[] = {
|
static const struct platform_device_id at91_twi_devtypes[] = {
|
||||||
|
@ -598,7 +607,6 @@ static struct at91_twi_pdata at91sam9x5_config = {
|
||||||
.clk_max_div = 7,
|
.clk_max_div = 7,
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_dma_support = true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id atmel_twi_dt_ids[] = {
|
static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||||
|
@ -627,30 +635,11 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||||
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
|
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool filter(struct dma_chan *chan, void *pdata)
|
|
||||||
{
|
|
||||||
struct at91_twi_pdata *sl_pdata = pdata;
|
|
||||||
struct at_dma_slave *sl;
|
|
||||||
|
|
||||||
if (!sl_pdata)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sl = &sl_pdata->dma_slave;
|
|
||||||
if (sl && (sl->dma_dev == chan->device->dev)) {
|
|
||||||
chan->private = sl;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct at91_twi_pdata *pdata = dev->pdata;
|
|
||||||
struct dma_slave_config slave_config;
|
struct dma_slave_config slave_config;
|
||||||
struct at91_twi_dma *dma = &dev->dma;
|
struct at91_twi_dma *dma = &dev->dma;
|
||||||
dma_cap_mask_t mask;
|
|
||||||
|
|
||||||
memset(&slave_config, 0, sizeof(slave_config));
|
memset(&slave_config, 0, sizeof(slave_config));
|
||||||
slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR;
|
slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR;
|
||||||
|
@ -661,22 +650,17 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
||||||
slave_config.dst_maxburst = 1;
|
slave_config.dst_maxburst = 1;
|
||||||
slave_config.device_fc = false;
|
slave_config.device_fc = false;
|
||||||
|
|
||||||
dma_cap_zero(mask);
|
dma->chan_tx = dma_request_slave_channel_reason(dev->dev, "tx");
|
||||||
dma_cap_set(DMA_SLAVE, mask);
|
if (IS_ERR(dma->chan_tx)) {
|
||||||
|
ret = PTR_ERR(dma->chan_tx);
|
||||||
dma->chan_tx = dma_request_slave_channel_compat(mask, filter, pdata,
|
dma->chan_tx = NULL;
|
||||||
dev->dev, "tx");
|
|
||||||
if (!dma->chan_tx) {
|
|
||||||
dev_err(dev->dev, "can't get a DMA channel for tx\n");
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
dma->chan_rx = dma_request_slave_channel_compat(mask, filter, pdata,
|
dma->chan_rx = dma_request_slave_channel_reason(dev->dev, "rx");
|
||||||
dev->dev, "rx");
|
if (IS_ERR(dma->chan_rx)) {
|
||||||
if (!dma->chan_rx) {
|
ret = PTR_ERR(dma->chan_rx);
|
||||||
dev_err(dev->dev, "can't get a DMA channel for rx\n");
|
dma->chan_rx = NULL;
|
||||||
ret = -EBUSY;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,6 +681,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
||||||
sg_init_table(&dma->sg, 1);
|
sg_init_table(&dma->sg, 1);
|
||||||
dma->buf_mapped = false;
|
dma->buf_mapped = false;
|
||||||
dma->xfer_in_progress = false;
|
dma->xfer_in_progress = false;
|
||||||
|
dev->use_dma = true;
|
||||||
|
|
||||||
dev_info(dev->dev, "using %s (tx) and %s (rx) for DMA transfers\n",
|
dev_info(dev->dev, "using %s (tx) and %s (rx) for DMA transfers\n",
|
||||||
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
|
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
|
||||||
|
@ -704,7 +689,8 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
dev_info(dev->dev, "can't use DMA\n");
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_info(dev->dev, "can't use DMA, error %d\n", ret);
|
||||||
if (dma->chan_rx)
|
if (dma->chan_rx)
|
||||||
dma_release_channel(dma->chan_rx);
|
dma_release_channel(dma->chan_rx);
|
||||||
if (dma->chan_tx)
|
if (dma->chan_tx)
|
||||||
|
@ -772,9 +758,10 @@ static int at91_twi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
clk_prepare_enable(dev->clk);
|
clk_prepare_enable(dev->clk);
|
||||||
|
|
||||||
if (dev->pdata->has_dma_support) {
|
if (dev->dev->of_node) {
|
||||||
if (at91_twi_configure_dma(dev, phy_addr) == 0)
|
rc = at91_twi_configure_dma(dev, phy_addr);
|
||||||
dev->use_dma = true;
|
if (rc == -EPROBE_DEFER)
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
|
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
|
||||||
|
@ -795,11 +782,20 @@ static int at91_twi_probe(struct platform_device *pdev)
|
||||||
dev->adapter.timeout = AT91_I2C_TIMEOUT;
|
dev->adapter.timeout = AT91_I2C_TIMEOUT;
|
||||||
dev->adapter.dev.of_node = pdev->dev.of_node;
|
dev->adapter.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(dev->dev);
|
||||||
|
pm_runtime_set_active(dev->dev);
|
||||||
|
pm_runtime_enable(dev->dev);
|
||||||
|
|
||||||
rc = i2c_add_numbered_adapter(&dev->adapter);
|
rc = i2c_add_numbered_adapter(&dev->adapter);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(dev->dev, "Adapter %s registration failed\n",
|
dev_err(dev->dev, "Adapter %s registration failed\n",
|
||||||
dev->adapter.name);
|
dev->adapter.name);
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev->dev);
|
||||||
|
pm_runtime_set_suspended(dev->dev);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,6 +810,9 @@ static int at91_twi_remove(struct platform_device *pdev)
|
||||||
i2c_del_adapter(&dev->adapter);
|
i2c_del_adapter(&dev->adapter);
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev->dev);
|
||||||
|
pm_runtime_set_suspended(dev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,7 +822,9 @@ static int at91_twi_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
clk_disable(twi_dev->clk);
|
clk_disable_unprepare(twi_dev->clk);
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -832,10 +833,38 @@ static int at91_twi_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return clk_enable(twi_dev->clk);
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
|
return clk_prepare_enable(twi_dev->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_suspend_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
if (!pm_runtime_status_suspended(dev))
|
||||||
|
at91_twi_runtime_suspend(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_resume_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pm_runtime_status_suspended(dev)) {
|
||||||
|
ret = at91_twi_runtime_resume(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_request_autosuspend(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops at91_twi_pm = {
|
static const struct dev_pm_ops at91_twi_pm = {
|
||||||
|
.suspend_noirq = at91_twi_suspend_noirq,
|
||||||
|
.resume_noirq = at91_twi_resume_noirq,
|
||||||
.runtime_suspend = at91_twi_runtime_suspend,
|
.runtime_suspend = at91_twi_runtime_suspend,
|
||||||
.runtime_resume = at91_twi_runtime_resume,
|
.runtime_resume = at91_twi_runtime_resume,
|
||||||
};
|
};
|
||||||
|
|
|
@ -368,8 +368,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
|
||||||
flag |= DAVINCI_I2C_MDR_STP;
|
flag |= DAVINCI_I2C_MDR_STP;
|
||||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
|
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
|
||||||
|
|
||||||
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
|
r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout);
|
||||||
dev->adapter.timeout);
|
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
dev_err(dev->dev, "controller timed out\n");
|
dev_err(dev->dev, "controller timed out\n");
|
||||||
davinci_i2c_recover_bus(dev);
|
davinci_i2c_recover_bus(dev);
|
||||||
|
@ -380,7 +379,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
|
||||||
if (dev->buf_len) {
|
if (dev->buf_len) {
|
||||||
/* This should be 0 if all bytes were transferred
|
/* This should be 0 if all bytes were transferred
|
||||||
* or dev->cmd_err denotes an error.
|
* or dev->cmd_err denotes an error.
|
||||||
* A signal may have aborted the transfer.
|
|
||||||
*/
|
*/
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
dev_err(dev->dev, "abnormal termination buf_len=%i\n",
|
dev_err(dev->dev, "abnormal termination buf_len=%i\n",
|
||||||
|
@ -634,13 +632,17 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct davinci_i2c_dev *dev;
|
struct davinci_i2c_dev *dev;
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct resource *mem, *irq;
|
struct resource *mem;
|
||||||
int r;
|
int r, irq;
|
||||||
|
|
||||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (!irq) {
|
if (irq <= 0) {
|
||||||
dev_err(&pdev->dev, "no irq resource?\n");
|
if (!irq)
|
||||||
return -ENODEV;
|
irq = -ENXIO;
|
||||||
|
if (irq != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"can't get irq resource ret=%d\n", irq);
|
||||||
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev),
|
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_i2c_dev),
|
||||||
|
@ -655,7 +657,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
||||||
init_completion(&dev->xfr_complete);
|
init_completion(&dev->xfr_complete);
|
||||||
#endif
|
#endif
|
||||||
dev->dev = &pdev->dev;
|
dev->dev = &pdev->dev;
|
||||||
dev->irq = irq->start;
|
dev->irq = irq;
|
||||||
dev->pdata = dev_get_platdata(&pdev->dev);
|
dev->pdata = dev_get_platdata(&pdev->dev);
|
||||||
platform_set_drvdata(pdev, dev);
|
platform_set_drvdata(pdev, dev);
|
||||||
|
|
||||||
|
|
|
@ -457,7 +457,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
goto stop;
|
goto stop;
|
||||||
} else if (int_status & HSI2C_INT_TIMEOUT) {
|
} else if (int_status & HSI2C_INT_TIMEOUT) {
|
||||||
dev_dbg(i2c->dev, "Accessing device timed out\n");
|
dev_dbg(i2c->dev, "Accessing device timed out\n");
|
||||||
i2c->state = -EAGAIN;
|
i2c->state = -ETIMEDOUT;
|
||||||
goto stop;
|
goto stop;
|
||||||
}
|
}
|
||||||
} else if (int_status & HSI2C_INT_I2C) {
|
} else if (int_status & HSI2C_INT_I2C) {
|
||||||
|
@ -476,7 +476,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
goto stop;
|
goto stop;
|
||||||
} else if (trans_status & HSI2C_TIMEOUT_AUTO) {
|
} else if (trans_status & HSI2C_TIMEOUT_AUTO) {
|
||||||
dev_dbg(i2c->dev, "Accessing device timed out\n");
|
dev_dbg(i2c->dev, "Accessing device timed out\n");
|
||||||
i2c->state = -EAGAIN;
|
i2c->state = -ETIMEDOUT;
|
||||||
goto stop;
|
goto stop;
|
||||||
} else if (trans_status & HSI2C_TRANS_DONE) {
|
} else if (trans_status & HSI2C_TRANS_DONE) {
|
||||||
i2c->trans_done = 1;
|
i2c->trans_done = 1;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
|
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
|
||||||
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
|
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
|
||||||
<mdsxyz123@yahoo.com>
|
<mdsxyz123@yahoo.com>
|
||||||
Copyright (C) 2007 - 2012 Jean Delvare <jdelvare@suse.de>
|
Copyright (C) 2007 - 2014 Jean Delvare <jdelvare@suse.de>
|
||||||
Copyright (C) 2010 Intel Corporation,
|
Copyright (C) 2010 Intel Corporation,
|
||||||
David Woodhouse <dwmw2@infradead.org>
|
David Woodhouse <dwmw2@infradead.org>
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@
|
||||||
* Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
|
* Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
|
||||||
* BayTrail (SOC) 0x0f12 32 hard yes yes yes
|
* BayTrail (SOC) 0x0f12 32 hard yes yes yes
|
||||||
* Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
|
* Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
|
||||||
|
* Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes
|
||||||
*
|
*
|
||||||
* Features supported by this driver:
|
* Features supported by this driver:
|
||||||
* Software PEC no
|
* Software PEC no
|
||||||
|
@ -109,12 +110,16 @@
|
||||||
|
|
||||||
/* PCI Address Constants */
|
/* PCI Address Constants */
|
||||||
#define SMBBAR 4
|
#define SMBBAR 4
|
||||||
|
#define SMBPCICTL 0x004
|
||||||
#define SMBPCISTS 0x006
|
#define SMBPCISTS 0x006
|
||||||
#define SMBHSTCFG 0x040
|
#define SMBHSTCFG 0x040
|
||||||
|
|
||||||
/* Host status bits for SMBPCISTS */
|
/* Host status bits for SMBPCISTS */
|
||||||
#define SMBPCISTS_INTS 0x08
|
#define SMBPCISTS_INTS 0x08
|
||||||
|
|
||||||
|
/* Control bits for SMBPCICTL */
|
||||||
|
#define SMBPCICTL_INTDIS 0x0400
|
||||||
|
|
||||||
/* Host configuration bits for SMBHSTCFG */
|
/* Host configuration bits for SMBHSTCFG */
|
||||||
#define SMBHSTCFG_HST_EN 1
|
#define SMBHSTCFG_HST_EN 1
|
||||||
#define SMBHSTCFG_SMB_SMI_EN 2
|
#define SMBHSTCFG_SMB_SMI_EN 2
|
||||||
|
@ -182,6 +187,7 @@
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
|
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
|
||||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
|
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
|
||||||
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
|
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
|
||||||
|
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23
|
||||||
|
|
||||||
struct i801_mux_config {
|
struct i801_mux_config {
|
||||||
char *gpio_chip;
|
char *gpio_chip;
|
||||||
|
@ -371,6 +377,7 @@ static int i801_transaction(struct i801_priv *priv, int xact)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
int result;
|
int result;
|
||||||
|
const struct i2c_adapter *adap = &priv->adapter;
|
||||||
|
|
||||||
result = i801_check_pre(priv);
|
result = i801_check_pre(priv);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
|
@ -379,7 +386,14 @@ static int i801_transaction(struct i801_priv *priv, int xact)
|
||||||
if (priv->features & FEATURE_IRQ) {
|
if (priv->features & FEATURE_IRQ) {
|
||||||
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
|
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
|
||||||
SMBHSTCNT(priv));
|
SMBHSTCNT(priv));
|
||||||
wait_event(priv->waitq, (status = priv->status));
|
result = wait_event_timeout(priv->waitq,
|
||||||
|
(status = priv->status),
|
||||||
|
adap->timeout);
|
||||||
|
if (!result) {
|
||||||
|
status = -ETIMEDOUT;
|
||||||
|
dev_warn(&priv->pci_dev->dev,
|
||||||
|
"Timeout waiting for interrupt!\n");
|
||||||
|
}
|
||||||
priv->status = 0;
|
priv->status = 0;
|
||||||
return i801_check_post(priv, status);
|
return i801_check_post(priv, status);
|
||||||
}
|
}
|
||||||
|
@ -493,9 +507,6 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
status = inb_p(SMBHSTSTS(priv));
|
status = inb_p(SMBHSTSTS(priv));
|
||||||
if (status != 0x42)
|
|
||||||
dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status);
|
|
||||||
|
|
||||||
if (status & SMBHSTSTS_BYTE_DONE)
|
if (status & SMBHSTSTS_BYTE_DONE)
|
||||||
i801_isr_byte_done(priv);
|
i801_isr_byte_done(priv);
|
||||||
|
|
||||||
|
@ -527,6 +538,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||||
int smbcmd;
|
int smbcmd;
|
||||||
int status;
|
int status;
|
||||||
int result;
|
int result;
|
||||||
|
const struct i2c_adapter *adap = &priv->adapter;
|
||||||
|
|
||||||
result = i801_check_pre(priv);
|
result = i801_check_pre(priv);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
|
@ -555,7 +567,14 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||||
priv->data = &data->block[1];
|
priv->data = &data->block[1];
|
||||||
|
|
||||||
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
|
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||||
wait_event(priv->waitq, (status = priv->status));
|
result = wait_event_timeout(priv->waitq,
|
||||||
|
(status = priv->status),
|
||||||
|
adap->timeout);
|
||||||
|
if (!result) {
|
||||||
|
status = -ETIMEDOUT;
|
||||||
|
dev_warn(&priv->pci_dev->dev,
|
||||||
|
"Timeout waiting for interrupt!\n");
|
||||||
|
}
|
||||||
priv->status = 0;
|
priv->status = 0;
|
||||||
return i801_check_post(priv, status);
|
return i801_check_post(priv, status);
|
||||||
}
|
}
|
||||||
|
@ -829,6 +848,7 @@ static const struct pci_device_id i801_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) },
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) },
|
||||||
{ 0, }
|
{ 0, }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1212,6 +1232,25 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
outb_p(inb_p(SMBAUXCTL(priv)) &
|
outb_p(inb_p(SMBAUXCTL(priv)) &
|
||||||
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
||||||
|
|
||||||
|
/* Default timeout in interrupt mode: 200 ms */
|
||||||
|
priv->adapter.timeout = HZ / 5;
|
||||||
|
|
||||||
|
if (priv->features & FEATURE_IRQ) {
|
||||||
|
u16 pcictl, pcists;
|
||||||
|
|
||||||
|
/* Complain if an interrupt is already pending */
|
||||||
|
pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
|
||||||
|
if (pcists & SMBPCISTS_INTS)
|
||||||
|
dev_warn(&dev->dev, "An interrupt is pending!\n");
|
||||||
|
|
||||||
|
/* Check if interrupts have been disabled */
|
||||||
|
pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
|
||||||
|
if (pcictl & SMBPCICTL_INTDIS) {
|
||||||
|
dev_info(&dev->dev, "Interrupts are disabled\n");
|
||||||
|
priv->features &= ~FEATURE_IRQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (priv->features & FEATURE_IRQ) {
|
if (priv->features & FEATURE_IRQ) {
|
||||||
init_waitqueue_head(&priv->waitq);
|
init_waitqueue_head(&priv->waitq);
|
||||||
|
|
||||||
|
@ -1220,10 +1259,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
|
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
|
||||||
dev->irq, err);
|
dev->irq, err);
|
||||||
goto exit_release;
|
priv->features &= ~FEATURE_IRQ;
|
||||||
}
|
}
|
||||||
dev_info(&dev->dev, "SMBus using PCI Interrupt\n");
|
|
||||||
}
|
}
|
||||||
|
dev_info(&dev->dev, "SMBus using %s\n",
|
||||||
|
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
|
||||||
|
|
||||||
/* set up the sysfs linkage to our parent device */
|
/* set up the sysfs linkage to our parent device */
|
||||||
priv->adapter.dev.parent = &dev->dev;
|
priv->adapter.dev.parent = &dev->dev;
|
||||||
|
@ -1250,7 +1290,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
exit_free_irq:
|
exit_free_irq:
|
||||||
if (priv->features & FEATURE_IRQ)
|
if (priv->features & FEATURE_IRQ)
|
||||||
free_irq(dev->irq, priv);
|
free_irq(dev->irq, priv);
|
||||||
exit_release:
|
|
||||||
pci_release_region(dev, SMBBAR);
|
pci_release_region(dev, SMBBAR);
|
||||||
exit:
|
exit:
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -32,22 +32,27 @@
|
||||||
/** Includes *******************************************************************
|
/** Includes *******************************************************************
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dmapool.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_dma.h>
|
||||||
#include <linux/platform_data/i2c-imx.h>
|
#include <linux/platform_data/i2c-imx.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/** Defines ********************************************************************
|
/** Defines ********************************************************************
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -58,6 +63,15 @@
|
||||||
/* Default value */
|
/* Default value */
|
||||||
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
|
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable DMA if transfer byte size is bigger than this threshold.
|
||||||
|
* As the hardware request, it must bigger than 4 bytes.\
|
||||||
|
* I have set '16' here, maybe it's not the best but I think it's
|
||||||
|
* the appropriate.
|
||||||
|
*/
|
||||||
|
#define DMA_THRESHOLD 16
|
||||||
|
#define DMA_TIMEOUT 1000
|
||||||
|
|
||||||
/* IMX I2C registers:
|
/* IMX I2C registers:
|
||||||
* the I2C register offset is different between SoCs,
|
* the I2C register offset is different between SoCs,
|
||||||
* to provid support for all these chips, split the
|
* to provid support for all these chips, split the
|
||||||
|
@ -83,6 +97,7 @@
|
||||||
#define I2SR_IBB 0x20
|
#define I2SR_IBB 0x20
|
||||||
#define I2SR_IAAS 0x40
|
#define I2SR_IAAS 0x40
|
||||||
#define I2SR_ICF 0x80
|
#define I2SR_ICF 0x80
|
||||||
|
#define I2CR_DMAEN 0x02
|
||||||
#define I2CR_RSTA 0x04
|
#define I2CR_RSTA 0x04
|
||||||
#define I2CR_TXAK 0x08
|
#define I2CR_TXAK 0x08
|
||||||
#define I2CR_MTX 0x10
|
#define I2CR_MTX 0x10
|
||||||
|
@ -169,6 +184,17 @@ struct imx_i2c_hwdata {
|
||||||
unsigned i2cr_ien_opcode;
|
unsigned i2cr_ien_opcode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct imx_i2c_dma {
|
||||||
|
struct dma_chan *chan_tx;
|
||||||
|
struct dma_chan *chan_rx;
|
||||||
|
struct dma_chan *chan_using;
|
||||||
|
struct completion cmd_complete;
|
||||||
|
dma_addr_t dma_buf;
|
||||||
|
unsigned int dma_len;
|
||||||
|
enum dma_transfer_direction dma_transfer_dir;
|
||||||
|
enum dma_data_direction dma_data_dir;
|
||||||
|
};
|
||||||
|
|
||||||
struct imx_i2c_struct {
|
struct imx_i2c_struct {
|
||||||
struct i2c_adapter adapter;
|
struct i2c_adapter adapter;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
@ -181,6 +207,8 @@ struct imx_i2c_struct {
|
||||||
unsigned int cur_clk;
|
unsigned int cur_clk;
|
||||||
unsigned int bitrate;
|
unsigned int bitrate;
|
||||||
const struct imx_i2c_hwdata *hwdata;
|
const struct imx_i2c_hwdata *hwdata;
|
||||||
|
|
||||||
|
struct imx_i2c_dma *dma;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||||
|
@ -251,6 +279,138 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
|
||||||
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
|
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Functions for DMA support */
|
||||||
|
static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||||
|
dma_addr_t phy_addr)
|
||||||
|
{
|
||||||
|
struct imx_i2c_dma *dma;
|
||||||
|
struct dma_slave_config dma_sconfig;
|
||||||
|
struct device *dev = &i2c_imx->adapter.dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||||
|
if (!dma)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dma->chan_tx = dma_request_slave_channel(dev, "tx");
|
||||||
|
if (!dma->chan_tx) {
|
||||||
|
dev_dbg(dev, "can't request DMA tx channel\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto fail_al;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_sconfig.dst_addr = phy_addr +
|
||||||
|
(IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
|
||||||
|
dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
dma_sconfig.dst_maxburst = 1;
|
||||||
|
dma_sconfig.direction = DMA_MEM_TO_DEV;
|
||||||
|
ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_dbg(dev, "can't configure tx channel\n");
|
||||||
|
goto fail_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma->chan_rx = dma_request_slave_channel(dev, "rx");
|
||||||
|
if (!dma->chan_rx) {
|
||||||
|
dev_dbg(dev, "can't request DMA rx channel\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto fail_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_sconfig.src_addr = phy_addr +
|
||||||
|
(IMX_I2C_I2DR << i2c_imx->hwdata->regshift);
|
||||||
|
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
dma_sconfig.src_maxburst = 1;
|
||||||
|
dma_sconfig.direction = DMA_DEV_TO_MEM;
|
||||||
|
ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_dbg(dev, "can't configure rx channel\n");
|
||||||
|
goto fail_rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_imx->dma = dma;
|
||||||
|
init_completion(&dma->cmd_complete);
|
||||||
|
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
|
||||||
|
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail_rx:
|
||||||
|
dma_release_channel(dma->chan_rx);
|
||||||
|
fail_tx:
|
||||||
|
dma_release_channel(dma->chan_tx);
|
||||||
|
fail_al:
|
||||||
|
devm_kfree(dev, dma);
|
||||||
|
dev_info(dev, "can't use DMA\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_imx_dma_callback(void *arg)
|
||||||
|
{
|
||||||
|
struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *)arg;
|
||||||
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
|
|
||||||
|
dma_unmap_single(dma->chan_using->device->dev, dma->dma_buf,
|
||||||
|
dma->dma_len, dma->dma_data_dir);
|
||||||
|
complete(&dma->cmd_complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
|
||||||
|
struct i2c_msg *msgs)
|
||||||
|
{
|
||||||
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
|
struct dma_async_tx_descriptor *txdesc;
|
||||||
|
struct device *dev = &i2c_imx->adapter.dev;
|
||||||
|
struct device *chan_dev = dma->chan_using->device->dev;
|
||||||
|
|
||||||
|
dma->dma_buf = dma_map_single(chan_dev, msgs->buf,
|
||||||
|
dma->dma_len, dma->dma_data_dir);
|
||||||
|
if (dma_mapping_error(chan_dev, dma->dma_buf)) {
|
||||||
|
dev_err(dev, "DMA mapping failed\n");
|
||||||
|
goto err_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
txdesc = dmaengine_prep_slave_single(dma->chan_using, dma->dma_buf,
|
||||||
|
dma->dma_len, dma->dma_transfer_dir,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!txdesc) {
|
||||||
|
dev_err(dev, "Not able to get desc for DMA xfer\n");
|
||||||
|
goto err_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
txdesc->callback = i2c_imx_dma_callback;
|
||||||
|
txdesc->callback_param = i2c_imx;
|
||||||
|
if (dma_submit_error(dmaengine_submit(txdesc))) {
|
||||||
|
dev_err(dev, "DMA submit failed\n");
|
||||||
|
goto err_submit;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_async_issue_pending(dma->chan_using);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_submit:
|
||||||
|
err_desc:
|
||||||
|
dma_unmap_single(chan_dev, dma->dma_buf,
|
||||||
|
dma->dma_len, dma->dma_data_dir);
|
||||||
|
err_map:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
||||||
|
{
|
||||||
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
|
|
||||||
|
dma->dma_buf = 0;
|
||||||
|
dma->dma_len = 0;
|
||||||
|
|
||||||
|
dma_release_channel(dma->chan_tx);
|
||||||
|
dma->chan_tx = NULL;
|
||||||
|
|
||||||
|
dma_release_channel(dma->chan_rx);
|
||||||
|
dma->chan_rx = NULL;
|
||||||
|
|
||||||
|
dma->chan_using = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/** Functions for IMX I2C adapter driver ***************************************
|
/** Functions for IMX I2C adapter driver ***************************************
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
@ -382,6 +542,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
||||||
i2c_imx->stopped = 0;
|
i2c_imx->stopped = 0;
|
||||||
|
|
||||||
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
|
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
|
||||||
|
temp &= ~I2CR_DMAEN;
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -395,6 +556,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
|
if (i2c_imx->dma)
|
||||||
|
temp &= ~I2CR_DMAEN;
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
}
|
}
|
||||||
if (is_imx1_i2c(i2c_imx)) {
|
if (is_imx1_i2c(i2c_imx)) {
|
||||||
|
@ -435,6 +598,155 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||||
|
struct i2c_msg *msgs)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
unsigned int temp = 0;
|
||||||
|
unsigned long orig_jiffies = jiffies;
|
||||||
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
|
struct device *dev = &i2c_imx->adapter.dev;
|
||||||
|
|
||||||
|
dma->chan_using = dma->chan_tx;
|
||||||
|
dma->dma_transfer_dir = DMA_MEM_TO_DEV;
|
||||||
|
dma->dma_data_dir = DMA_TO_DEVICE;
|
||||||
|
dma->dma_len = msgs->len - 1;
|
||||||
|
result = i2c_imx_dma_xfer(i2c_imx, msgs);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp |= I2CR_DMAEN;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write slave address.
|
||||||
|
* The first byte must be transmitted by the CPU.
|
||||||
|
*/
|
||||||
|
imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);
|
||||||
|
reinit_completion(&i2c_imx->dma->cmd_complete);
|
||||||
|
result = wait_for_completion_timeout(
|
||||||
|
&i2c_imx->dma->cmd_complete,
|
||||||
|
msecs_to_jiffies(DMA_TIMEOUT));
|
||||||
|
if (result <= 0) {
|
||||||
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
|
return result ?: -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Waiting for transfer complete. */
|
||||||
|
while (1) {
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||||
|
if (temp & I2SR_ICF)
|
||||||
|
break;
|
||||||
|
if (time_after(jiffies, orig_jiffies +
|
||||||
|
msecs_to_jiffies(DMA_TIMEOUT))) {
|
||||||
|
dev_dbg(dev, "<%s> Timeout\n", __func__);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp &= ~I2CR_DMAEN;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
|
/* The last data byte must be transferred by the CPU. */
|
||||||
|
imx_i2c_write_reg(msgs->buf[msgs->len-1],
|
||||||
|
i2c_imx, IMX_I2C_I2DR);
|
||||||
|
result = i2c_imx_trx_complete(i2c_imx);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return i2c_imx_acked(i2c_imx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||||
|
struct i2c_msg *msgs, bool is_lastmsg)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
unsigned int temp;
|
||||||
|
unsigned long orig_jiffies = jiffies;
|
||||||
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
|
struct device *dev = &i2c_imx->adapter.dev;
|
||||||
|
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp |= I2CR_DMAEN;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
|
dma->chan_using = dma->chan_rx;
|
||||||
|
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
|
||||||
|
dma->dma_data_dir = DMA_FROM_DEVICE;
|
||||||
|
/* The last two data bytes must be transferred by the CPU. */
|
||||||
|
dma->dma_len = msgs->len - 2;
|
||||||
|
result = i2c_imx_dma_xfer(i2c_imx, msgs);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
reinit_completion(&i2c_imx->dma->cmd_complete);
|
||||||
|
result = wait_for_completion_timeout(
|
||||||
|
&i2c_imx->dma->cmd_complete,
|
||||||
|
msecs_to_jiffies(DMA_TIMEOUT));
|
||||||
|
if (result <= 0) {
|
||||||
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
|
return result ?: -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* waiting for transfer complete. */
|
||||||
|
while (1) {
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||||
|
if (temp & I2SR_ICF)
|
||||||
|
break;
|
||||||
|
if (time_after(jiffies, orig_jiffies +
|
||||||
|
msecs_to_jiffies(DMA_TIMEOUT))) {
|
||||||
|
dev_dbg(dev, "<%s> Timeout\n", __func__);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp &= ~I2CR_DMAEN;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
|
/* read n-1 byte data */
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp |= I2CR_TXAK;
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
|
msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||||
|
/* read n byte data */
|
||||||
|
result = i2c_imx_trx_complete(i2c_imx);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (is_lastmsg) {
|
||||||
|
/*
|
||||||
|
* It must generate STOP before read I2DR to prevent
|
||||||
|
* controller from generating another clock cycle
|
||||||
|
*/
|
||||||
|
dev_dbg(dev, "<%s> clear MSTA\n", __func__);
|
||||||
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
i2c_imx_bus_busy(i2c_imx, 0);
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
* read -> repeat MSTA -> read/write
|
||||||
|
* The controller must set MTX before read the last byte in
|
||||||
|
* the first read operation, otherwise the first read cost
|
||||||
|
* one extra clock cycle.
|
||||||
|
*/
|
||||||
|
temp = readb(i2c_imx->base + IMX_I2C_I2CR);
|
||||||
|
temp |= I2CR_MTX;
|
||||||
|
writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
|
||||||
|
}
|
||||||
|
msgs->buf[msgs->len-1] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
||||||
{
|
{
|
||||||
int i, result;
|
int i, result;
|
||||||
|
@ -504,6 +816,9 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
||||||
|
|
||||||
|
if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data)
|
||||||
|
return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
|
||||||
|
|
||||||
/* read data */
|
/* read data */
|
||||||
for (i = 0; i < msgs->len; i++) {
|
for (i = 0; i < msgs->len; i++) {
|
||||||
u8 len = 0;
|
u8 len = 0;
|
||||||
|
@ -618,8 +933,12 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||||
#endif
|
#endif
|
||||||
if (msgs[i].flags & I2C_M_RD)
|
if (msgs[i].flags & I2C_M_RD)
|
||||||
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
|
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
|
||||||
else
|
else {
|
||||||
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
|
||||||
|
result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
|
||||||
|
else
|
||||||
|
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
||||||
|
}
|
||||||
if (result)
|
if (result)
|
||||||
goto fail0;
|
goto fail0;
|
||||||
}
|
}
|
||||||
|
@ -654,6 +973,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
|
dma_addr_t phy_addr;
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "<%s>\n", __func__);
|
dev_dbg(&pdev->dev, "<%s>\n", __func__);
|
||||||
|
|
||||||
|
@ -668,8 +988,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(base))
|
if (IS_ERR(base))
|
||||||
return PTR_ERR(base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(struct imx_i2c_struct),
|
phy_addr = (dma_addr_t)res->start;
|
||||||
GFP_KERNEL);
|
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
|
||||||
if (!i2c_imx)
|
if (!i2c_imx)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -743,6 +1063,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
i2c_imx->adapter.name);
|
i2c_imx->adapter.name);
|
||||||
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||||
|
|
||||||
|
/* Init DMA config if support*/
|
||||||
|
i2c_imx_dma_request(i2c_imx, phy_addr);
|
||||||
|
|
||||||
return 0; /* Return OK */
|
return 0; /* Return OK */
|
||||||
|
|
||||||
clk_disable:
|
clk_disable:
|
||||||
|
@ -758,6 +1081,9 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
||||||
i2c_del_adapter(&i2c_imx->adapter);
|
i2c_del_adapter(&i2c_imx->adapter);
|
||||||
|
|
||||||
|
if (i2c_imx->dma)
|
||||||
|
i2c_imx_dma_free(i2c_imx);
|
||||||
|
|
||||||
/* setup chip registers to defaults */
|
/* setup chip registers to defaults */
|
||||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
|
||||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR);
|
||||||
|
|
|
@ -0,0 +1,492 @@
|
||||||
|
/*
|
||||||
|
* I2C bus driver for Amlogic Meson SoCs
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Meson I2C register map */
|
||||||
|
#define REG_CTRL 0x00
|
||||||
|
#define REG_SLAVE_ADDR 0x04
|
||||||
|
#define REG_TOK_LIST0 0x08
|
||||||
|
#define REG_TOK_LIST1 0x0c
|
||||||
|
#define REG_TOK_WDATA0 0x10
|
||||||
|
#define REG_TOK_WDATA1 0x14
|
||||||
|
#define REG_TOK_RDATA0 0x18
|
||||||
|
#define REG_TOK_RDATA1 0x1c
|
||||||
|
|
||||||
|
/* Control register fields */
|
||||||
|
#define REG_CTRL_START BIT(0)
|
||||||
|
#define REG_CTRL_ACK_IGNORE BIT(1)
|
||||||
|
#define REG_CTRL_STATUS BIT(2)
|
||||||
|
#define REG_CTRL_ERROR BIT(3)
|
||||||
|
#define REG_CTRL_CLKDIV_SHIFT 12
|
||||||
|
#define REG_CTRL_CLKDIV_MASK ((BIT(10) - 1) << REG_CTRL_CLKDIV_SHIFT)
|
||||||
|
|
||||||
|
#define I2C_TIMEOUT_MS 500
|
||||||
|
#define DEFAULT_FREQ 100000
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TOKEN_END = 0,
|
||||||
|
TOKEN_START,
|
||||||
|
TOKEN_SLAVE_ADDR_WRITE,
|
||||||
|
TOKEN_SLAVE_ADDR_READ,
|
||||||
|
TOKEN_DATA,
|
||||||
|
TOKEN_DATA_LAST,
|
||||||
|
TOKEN_STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATE_IDLE,
|
||||||
|
STATE_READ,
|
||||||
|
STATE_WRITE,
|
||||||
|
STATE_STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct meson_i2c - Meson I2C device private data
|
||||||
|
*
|
||||||
|
* @adap: I2C adapter instance
|
||||||
|
* @dev: Pointer to device structure
|
||||||
|
* @regs: Base address of the device memory mapped registers
|
||||||
|
* @clk: Pointer to clock structure
|
||||||
|
* @irq: IRQ number
|
||||||
|
* @msg: Pointer to the current I2C message
|
||||||
|
* @state: Current state in the driver state machine
|
||||||
|
* @last: Flag set for the last message in the transfer
|
||||||
|
* @count: Number of bytes to be sent/received in current transfer
|
||||||
|
* @pos: Current position in the send/receive buffer
|
||||||
|
* @error: Flag set when an error is received
|
||||||
|
* @lock: To avoid race conditions between irq handler and xfer code
|
||||||
|
* @done: Completion used to wait for transfer termination
|
||||||
|
* @frequency: Operating frequency of I2C bus clock
|
||||||
|
* @tokens: Sequence of tokens to be written to the device
|
||||||
|
* @num_tokens: Number of tokens
|
||||||
|
*/
|
||||||
|
struct meson_i2c {
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *regs;
|
||||||
|
struct clk *clk;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
int state;
|
||||||
|
bool last;
|
||||||
|
int count;
|
||||||
|
int pos;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
spinlock_t lock;
|
||||||
|
struct completion done;
|
||||||
|
unsigned int frequency;
|
||||||
|
u32 tokens[2];
|
||||||
|
int num_tokens;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void meson_i2c_set_mask(struct meson_i2c *i2c, int reg, u32 mask,
|
||||||
|
u32 val)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
|
||||||
|
data = readl(i2c->regs + reg);
|
||||||
|
data &= ~mask;
|
||||||
|
data |= val & mask;
|
||||||
|
writel(data, i2c->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_reset_tokens(struct meson_i2c *i2c)
|
||||||
|
{
|
||||||
|
i2c->tokens[0] = 0;
|
||||||
|
i2c->tokens[1] = 0;
|
||||||
|
i2c->num_tokens = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_add_token(struct meson_i2c *i2c, int token)
|
||||||
|
{
|
||||||
|
if (i2c->num_tokens < 8)
|
||||||
|
i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4);
|
||||||
|
else
|
||||||
|
i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4);
|
||||||
|
|
||||||
|
i2c->num_tokens++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_write_tokens(struct meson_i2c *i2c)
|
||||||
|
{
|
||||||
|
writel(i2c->tokens[0], i2c->regs + REG_TOK_LIST0);
|
||||||
|
writel(i2c->tokens[1], i2c->regs + REG_TOK_LIST1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_set_clk_div(struct meson_i2c *i2c)
|
||||||
|
{
|
||||||
|
unsigned long clk_rate = clk_get_rate(i2c->clk);
|
||||||
|
unsigned int div;
|
||||||
|
|
||||||
|
div = DIV_ROUND_UP(clk_rate, i2c->frequency * 4);
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIV_MASK,
|
||||||
|
div << REG_CTRL_CLKDIV_SHIFT);
|
||||||
|
|
||||||
|
dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__,
|
||||||
|
clk_rate, i2c->frequency, div);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_get_data(struct meson_i2c *i2c, char *buf, int len)
|
||||||
|
{
|
||||||
|
u32 rdata0, rdata1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
rdata0 = readl(i2c->regs + REG_TOK_RDATA0);
|
||||||
|
rdata1 = readl(i2c->regs + REG_TOK_RDATA1);
|
||||||
|
|
||||||
|
dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
|
||||||
|
rdata0, rdata1, len);
|
||||||
|
|
||||||
|
for (i = 0; i < min_t(int, 4, len); i++)
|
||||||
|
*buf++ = (rdata0 >> i * 8) & 0xff;
|
||||||
|
|
||||||
|
for (i = 4; i < min_t(int, 8, len); i++)
|
||||||
|
*buf++ = (rdata1 >> (i - 4) * 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_put_data(struct meson_i2c *i2c, char *buf, int len)
|
||||||
|
{
|
||||||
|
u32 wdata0 = 0, wdata1 = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < min_t(int, 4, len); i++)
|
||||||
|
wdata0 |= *buf++ << (i * 8);
|
||||||
|
|
||||||
|
for (i = 4; i < min_t(int, 8, len); i++)
|
||||||
|
wdata1 |= *buf++ << ((i - 4) * 8);
|
||||||
|
|
||||||
|
writel(wdata0, i2c->regs + REG_TOK_WDATA0);
|
||||||
|
writel(wdata0, i2c->regs + REG_TOK_WDATA1);
|
||||||
|
|
||||||
|
dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
|
||||||
|
wdata0, wdata1, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_prepare_xfer(struct meson_i2c *i2c)
|
||||||
|
{
|
||||||
|
bool write = !(i2c->msg->flags & I2C_M_RD);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i2c->count = min_t(int, i2c->msg->len - i2c->pos, 8);
|
||||||
|
|
||||||
|
for (i = 0; i < i2c->count - 1; i++)
|
||||||
|
meson_i2c_add_token(i2c, TOKEN_DATA);
|
||||||
|
|
||||||
|
if (i2c->count) {
|
||||||
|
if (write || i2c->pos + i2c->count < i2c->msg->len)
|
||||||
|
meson_i2c_add_token(i2c, TOKEN_DATA);
|
||||||
|
else
|
||||||
|
meson_i2c_add_token(i2c, TOKEN_DATA_LAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write)
|
||||||
|
meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_stop(struct meson_i2c *i2c)
|
||||||
|
{
|
||||||
|
dev_dbg(i2c->dev, "%s: last %d\n", __func__, i2c->last);
|
||||||
|
|
||||||
|
if (i2c->last) {
|
||||||
|
i2c->state = STATE_STOP;
|
||||||
|
meson_i2c_add_token(i2c, TOKEN_STOP);
|
||||||
|
} else {
|
||||||
|
i2c->state = STATE_IDLE;
|
||||||
|
complete_all(&i2c->done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t meson_i2c_irq(int irqno, void *dev_id)
|
||||||
|
{
|
||||||
|
struct meson_i2c *i2c = dev_id;
|
||||||
|
unsigned int ctrl;
|
||||||
|
|
||||||
|
spin_lock(&i2c->lock);
|
||||||
|
|
||||||
|
meson_i2c_reset_tokens(i2c);
|
||||||
|
ctrl = readl(i2c->regs + REG_CTRL);
|
||||||
|
|
||||||
|
dev_dbg(i2c->dev, "irq: state %d, pos %d, count %d, ctrl %08x\n",
|
||||||
|
i2c->state, i2c->pos, i2c->count, ctrl);
|
||||||
|
|
||||||
|
if (ctrl & REG_CTRL_ERROR && i2c->state != STATE_IDLE) {
|
||||||
|
/*
|
||||||
|
* The bit is set when the IGNORE_NAK bit is cleared
|
||||||
|
* and the device didn't respond. In this case, the
|
||||||
|
* I2C controller automatically generates a STOP
|
||||||
|
* condition.
|
||||||
|
*/
|
||||||
|
dev_dbg(i2c->dev, "error bit set\n");
|
||||||
|
i2c->error = -ENXIO;
|
||||||
|
i2c->state = STATE_IDLE;
|
||||||
|
complete_all(&i2c->done);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i2c->state) {
|
||||||
|
case STATE_READ:
|
||||||
|
if (i2c->count > 0) {
|
||||||
|
meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos,
|
||||||
|
i2c->count);
|
||||||
|
i2c->pos += i2c->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c->pos >= i2c->msg->len) {
|
||||||
|
meson_i2c_stop(i2c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
meson_i2c_prepare_xfer(i2c);
|
||||||
|
break;
|
||||||
|
case STATE_WRITE:
|
||||||
|
i2c->pos += i2c->count;
|
||||||
|
|
||||||
|
if (i2c->pos >= i2c->msg->len) {
|
||||||
|
meson_i2c_stop(i2c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
meson_i2c_prepare_xfer(i2c);
|
||||||
|
break;
|
||||||
|
case STATE_STOP:
|
||||||
|
i2c->state = STATE_IDLE;
|
||||||
|
complete_all(&i2c->done);
|
||||||
|
break;
|
||||||
|
case STATE_IDLE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (i2c->state != STATE_IDLE) {
|
||||||
|
/* Restart the processing */
|
||||||
|
meson_i2c_write_tokens(i2c);
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START,
|
||||||
|
REG_CTRL_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&i2c->lock);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg)
|
||||||
|
{
|
||||||
|
int token;
|
||||||
|
|
||||||
|
token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ :
|
||||||
|
TOKEN_SLAVE_ADDR_WRITE;
|
||||||
|
|
||||||
|
writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR);
|
||||||
|
meson_i2c_add_token(i2c, TOKEN_START);
|
||||||
|
meson_i2c_add_token(i2c, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
|
||||||
|
int last)
|
||||||
|
{
|
||||||
|
unsigned long time_left, flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
i2c->msg = msg;
|
||||||
|
i2c->last = last;
|
||||||
|
i2c->pos = 0;
|
||||||
|
i2c->count = 0;
|
||||||
|
i2c->error = 0;
|
||||||
|
|
||||||
|
meson_i2c_reset_tokens(i2c);
|
||||||
|
|
||||||
|
flags = (msg->flags & I2C_M_IGNORE_NAK) ? REG_CTRL_ACK_IGNORE : 0;
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_ACK_IGNORE, flags);
|
||||||
|
|
||||||
|
if (!(msg->flags & I2C_M_NOSTART))
|
||||||
|
meson_i2c_do_start(i2c, msg);
|
||||||
|
|
||||||
|
i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
|
||||||
|
meson_i2c_prepare_xfer(i2c);
|
||||||
|
meson_i2c_write_tokens(i2c);
|
||||||
|
reinit_completion(&i2c->done);
|
||||||
|
|
||||||
|
/* Start the transfer */
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, REG_CTRL_START);
|
||||||
|
|
||||||
|
time_left = msecs_to_jiffies(I2C_TIMEOUT_MS);
|
||||||
|
time_left = wait_for_completion_timeout(&i2c->done, time_left);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protect access to i2c struct and registers from interrupt
|
||||||
|
* handlers triggered by a transfer terminated after the
|
||||||
|
* timeout period
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&i2c->lock, flags);
|
||||||
|
|
||||||
|
/* Abort any active operation */
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
|
||||||
|
|
||||||
|
if (!time_left) {
|
||||||
|
i2c->state = STATE_IDLE;
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c->error)
|
||||||
|
ret = i2c->error;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meson_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct meson_i2c *i2c = adap->algo_data;
|
||||||
|
int i, ret = 0, count = 0;
|
||||||
|
|
||||||
|
clk_enable(i2c->clk);
|
||||||
|
meson_i2c_set_clk_div(i2c);
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
ret = meson_i2c_xfer_msg(i2c, msgs + i, i == num - 1);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_disable(i2c->clk);
|
||||||
|
|
||||||
|
return ret ? ret : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 meson_i2c_func(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm meson_i2c_algorithm = {
|
||||||
|
.master_xfer = meson_i2c_xfer,
|
||||||
|
.functionality = meson_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int meson_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct meson_i2c *i2c;
|
||||||
|
struct resource *mem;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
i2c = devm_kzalloc(&pdev->dev, sizeof(struct meson_i2c), GFP_KERNEL);
|
||||||
|
if (!i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||||
|
&i2c->frequency))
|
||||||
|
i2c->frequency = DEFAULT_FREQ;
|
||||||
|
|
||||||
|
i2c->dev = &pdev->dev;
|
||||||
|
platform_set_drvdata(pdev, i2c);
|
||||||
|
|
||||||
|
spin_lock_init(&i2c->lock);
|
||||||
|
init_completion(&i2c->done);
|
||||||
|
|
||||||
|
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(i2c->clk)) {
|
||||||
|
dev_err(&pdev->dev, "can't get device clock\n");
|
||||||
|
return PTR_ERR(i2c->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(i2c->regs))
|
||||||
|
return PTR_ERR(i2c->regs);
|
||||||
|
|
||||||
|
i2c->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (i2c->irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't find IRQ\n");
|
||||||
|
return i2c->irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, i2c->irq, meson_i2c_irq,
|
||||||
|
0, dev_name(&pdev->dev), i2c);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't request IRQ\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare(i2c->clk);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't prepare clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(i2c->adap.name, "Meson I2C adapter",
|
||||||
|
sizeof(i2c->adap.name));
|
||||||
|
i2c->adap.owner = THIS_MODULE;
|
||||||
|
i2c->adap.algo = &meson_i2c_algorithm;
|
||||||
|
i2c->adap.dev.parent = &pdev->dev;
|
||||||
|
i2c->adap.dev.of_node = np;
|
||||||
|
i2c->adap.algo_data = i2c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A transfer is triggered when START bit changes from 0 to 1.
|
||||||
|
* Ensure that the bit is set to 0 after probe
|
||||||
|
*/
|
||||||
|
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(&i2c->adap);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't register adapter\n");
|
||||||
|
clk_unprepare(i2c->clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meson_i2c_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct meson_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&i2c->adap);
|
||||||
|
clk_unprepare(i2c->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id meson_i2c_match[] = {
|
||||||
|
{ .compatible = "amlogic,meson6-i2c" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver meson_i2c_driver = {
|
||||||
|
.probe = meson_i2c_probe,
|
||||||
|
.remove = meson_i2c_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "meson-i2c",
|
||||||
|
.of_match_table = meson_i2c_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(meson_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Amlogic Meson I2C Bus driver");
|
||||||
|
MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -124,7 +124,7 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
|
||||||
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;
|
||||||
u32 x;
|
u32 cmd_err;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
if (!i2c->irq) {
|
if (!i2c->irq) {
|
||||||
|
@ -133,11 +133,11 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
|
||||||
if (time_after(jiffies, orig_jiffies + timeout)) {
|
if (time_after(jiffies, orig_jiffies + timeout)) {
|
||||||
dev_dbg(i2c->dev, "timeout\n");
|
dev_dbg(i2c->dev, "timeout\n");
|
||||||
writeccr(i2c, 0);
|
writeccr(i2c, 0);
|
||||||
result = -EIO;
|
result = -ETIMEDOUT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x = readb(i2c->base + MPC_I2C_SR);
|
cmd_err = readb(i2c->base + MPC_I2C_SR);
|
||||||
writeb(0, i2c->base + MPC_I2C_SR);
|
writeb(0, i2c->base + MPC_I2C_SR);
|
||||||
} else {
|
} else {
|
||||||
/* Interrupt mode */
|
/* Interrupt mode */
|
||||||
|
@ -150,28 +150,28 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
|
||||||
result = -ETIMEDOUT;
|
result = -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = i2c->interrupt;
|
cmd_err = i2c->interrupt;
|
||||||
i2c->interrupt = 0;
|
i2c->interrupt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (!(x & CSR_MCF)) {
|
if (!(cmd_err & CSR_MCF)) {
|
||||||
dev_dbg(i2c->dev, "unfinished\n");
|
dev_dbg(i2c->dev, "unfinished\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x & CSR_MAL) {
|
if (cmd_err & CSR_MAL) {
|
||||||
dev_dbg(i2c->dev, "MAL\n");
|
dev_dbg(i2c->dev, "MAL\n");
|
||||||
return -EIO;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writing && (x & CSR_RXAK)) {
|
if (writing && (cmd_err & CSR_RXAK)) {
|
||||||
dev_dbg(i2c->dev, "No RXAK\n");
|
dev_dbg(i2c->dev, "No RXAK\n");
|
||||||
/* generate stop */
|
/* generate stop */
|
||||||
writeccr(i2c, CCR_MEN);
|
writeccr(i2c, CCR_MEN);
|
||||||
return -EIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -811,7 +811,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int err, irq;
|
int err, irq;
|
||||||
|
|
||||||
i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL);
|
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
if (!i2c)
|
if (!i2c)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@
|
||||||
/* timeout for pm runtime autosuspend */
|
/* timeout for pm runtime autosuspend */
|
||||||
#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */
|
#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */
|
||||||
|
|
||||||
|
/* timeout for making decision on bus free status */
|
||||||
|
#define OMAP_I2C_BUS_FREE_TIMEOUT (msecs_to_jiffies(10))
|
||||||
|
|
||||||
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
|
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
|
||||||
enum {
|
enum {
|
||||||
OMAP_I2C_REV_REG = 0,
|
OMAP_I2C_REV_REG = 0,
|
||||||
|
@ -98,7 +101,7 @@ enum {
|
||||||
#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
|
#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
|
||||||
#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
|
#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
|
||||||
#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
|
#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
|
||||||
#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */
|
#define OMAP_I2C_STAT_BF (1 << 8) /* Bus Free */
|
||||||
#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
|
#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
|
||||||
#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
|
#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
|
||||||
#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
|
#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
|
||||||
|
@ -146,16 +149,20 @@ enum {
|
||||||
#define OMAP_I2C_SCLH_HSSCLH 8
|
#define OMAP_I2C_SCLH_HSSCLH 8
|
||||||
|
|
||||||
/* I2C System Test Register (OMAP_I2C_SYSTEST): */
|
/* I2C System Test Register (OMAP_I2C_SYSTEST): */
|
||||||
#ifdef DEBUG
|
|
||||||
#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
|
#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
|
||||||
#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
|
#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
|
||||||
#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
|
#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
|
||||||
#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
|
#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
|
||||||
|
/* Functional mode */
|
||||||
|
#define OMAP_I2C_SYSTEST_SCL_I_FUNC (1 << 8) /* SCL line input value */
|
||||||
|
#define OMAP_I2C_SYSTEST_SCL_O_FUNC (1 << 7) /* SCL line output value */
|
||||||
|
#define OMAP_I2C_SYSTEST_SDA_I_FUNC (1 << 6) /* SDA line input value */
|
||||||
|
#define OMAP_I2C_SYSTEST_SDA_O_FUNC (1 << 5) /* SDA line output value */
|
||||||
|
/* SDA/SCL IO mode */
|
||||||
#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
|
#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
|
||||||
#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
|
#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
|
||||||
#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
|
#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
|
||||||
#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
|
#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
|
||||||
#endif
|
|
||||||
|
|
||||||
/* OCP_SYSSTATUS bit definitions */
|
/* OCP_SYSSTATUS bit definitions */
|
||||||
#define SYSS_RESETDONE_MASK (1 << 0)
|
#define SYSS_RESETDONE_MASK (1 << 0)
|
||||||
|
@ -202,6 +209,9 @@ struct omap_i2c_dev {
|
||||||
*/
|
*/
|
||||||
u32 rev;
|
u32 rev;
|
||||||
unsigned b_hw:1; /* bad h/w fixes */
|
unsigned b_hw:1; /* bad h/w fixes */
|
||||||
|
unsigned bb_valid:1; /* true when BB-bit reflects
|
||||||
|
* the I2C bus state
|
||||||
|
*/
|
||||||
unsigned receiver:1; /* true when we're in receiver mode */
|
unsigned receiver:1; /* true when we're in receiver mode */
|
||||||
u16 iestate; /* Saved interrupt register */
|
u16 iestate; /* Saved interrupt register */
|
||||||
u16 pscstate;
|
u16 pscstate;
|
||||||
|
@ -289,6 +299,12 @@ static void __omap_i2c_init(struct omap_i2c_dev *dev)
|
||||||
/* Take the I2C module out of reset: */
|
/* Take the I2C module out of reset: */
|
||||||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: right after setting CON_EN, STAT_BB could be 0 while the
|
||||||
|
* bus is busy. It will be changed to 1 on the next IP FCLK clock.
|
||||||
|
* udelay(1) will be enough to fix that.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't write to this register if the IE state is 0 as it can
|
* Don't write to this register if the IE state is 0 as it can
|
||||||
* cause deadlock.
|
* cause deadlock.
|
||||||
|
@ -328,7 +344,12 @@ static int omap_i2c_reset(struct omap_i2c_dev *dev)
|
||||||
/* SYSC register is cleared by the reset; rewrite it */
|
/* SYSC register is cleared by the reset; rewrite it */
|
||||||
omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, sysc);
|
omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, sysc);
|
||||||
|
|
||||||
|
if (dev->rev > OMAP_I2C_REV_ON_3430_3530) {
|
||||||
|
/* Schedule I2C-bus monitoring on the next transfer */
|
||||||
|
dev->bb_valid = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,6 +462,11 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
|
||||||
dev->scllstate = scll;
|
dev->scllstate = scll;
|
||||||
dev->sclhstate = sclh;
|
dev->sclhstate = sclh;
|
||||||
|
|
||||||
|
if (dev->rev <= OMAP_I2C_REV_ON_3430_3530) {
|
||||||
|
/* Not implemented */
|
||||||
|
dev->bb_valid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
__omap_i2c_init(dev);
|
__omap_i2c_init(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -465,6 +491,91 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait while BB-bit doesn't reflect the I2C bus state
|
||||||
|
*
|
||||||
|
* In a multimaster environment, after IP software reset, BB-bit value doesn't
|
||||||
|
* correspond to the current bus state. It may happen what BB-bit will be 0,
|
||||||
|
* while the bus is busy due to another I2C master activity.
|
||||||
|
* Here are BB-bit values after reset:
|
||||||
|
* SDA SCL BB NOTES
|
||||||
|
* 0 0 0 1, 2
|
||||||
|
* 1 0 0 1, 2
|
||||||
|
* 0 1 1
|
||||||
|
* 1 1 0 3
|
||||||
|
* Later, if IP detect SDA=0 and SCL=1 (ACK) or SDA 1->0 while SCL=1 (START)
|
||||||
|
* combinations on the bus, it set BB-bit to 1.
|
||||||
|
* If IP detect SDA 0->1 while SCL=1 (STOP) combination on the bus,
|
||||||
|
* it set BB-bit to 0 and BF to 1.
|
||||||
|
* BB and BF bits correctly tracks the bus state while IP is suspended
|
||||||
|
* BB bit became valid on the next FCLK clock after CON_EN bit set
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
* 1. Any transfer started when BB=0 and bus is busy wouldn't be
|
||||||
|
* completed by IP and results in controller timeout.
|
||||||
|
* 2. Any transfer started when BB=0 and SCL=0 results in IP
|
||||||
|
* starting to drive SDA low. In that case IP corrupt data
|
||||||
|
* on the bus.
|
||||||
|
* 3. Any transfer started in the middle of another master's transfer
|
||||||
|
* results in unpredictable results and data corruption
|
||||||
|
*/
|
||||||
|
static int omap_i2c_wait_for_bb_valid(struct omap_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
unsigned long bus_free_timeout = 0;
|
||||||
|
unsigned long timeout;
|
||||||
|
int bus_free = 0;
|
||||||
|
u16 stat, systest;
|
||||||
|
|
||||||
|
if (dev->bb_valid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
timeout = jiffies + OMAP_I2C_TIMEOUT;
|
||||||
|
while (1) {
|
||||||
|
stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
||||||
|
/*
|
||||||
|
* We will see BB or BF event in a case IP had detected any
|
||||||
|
* activity on the I2C bus. Now IP correctly tracks the bus
|
||||||
|
* state. BB-bit value is valid.
|
||||||
|
*/
|
||||||
|
if (stat & (OMAP_I2C_STAT_BB | OMAP_I2C_STAT_BF))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, we must look signals on the bus to make
|
||||||
|
* the right decision.
|
||||||
|
*/
|
||||||
|
systest = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG);
|
||||||
|
if ((systest & OMAP_I2C_SYSTEST_SCL_I_FUNC) &&
|
||||||
|
(systest & OMAP_I2C_SYSTEST_SDA_I_FUNC)) {
|
||||||
|
if (!bus_free) {
|
||||||
|
bus_free_timeout = jiffies +
|
||||||
|
OMAP_I2C_BUS_FREE_TIMEOUT;
|
||||||
|
bus_free = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SDA and SCL lines was high for 10 ms without bus
|
||||||
|
* activity detected. The bus is free. Consider
|
||||||
|
* BB-bit value is valid.
|
||||||
|
*/
|
||||||
|
if (time_after(jiffies, bus_free_timeout))
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
bus_free = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->bb_valid = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
|
static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
|
||||||
{
|
{
|
||||||
u16 buf;
|
u16 buf;
|
||||||
|
@ -557,7 +668,11 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
|
|
||||||
if (!dev->b_hw && stop)
|
if (!dev->b_hw && stop)
|
||||||
w |= OMAP_I2C_CON_STP;
|
w |= OMAP_I2C_CON_STP;
|
||||||
|
/*
|
||||||
|
* NOTE: STAT_BB bit could became 1 here if another master occupy
|
||||||
|
* the bus. IP successfully complete transfer when the bus will be
|
||||||
|
* free again (BB reset to 0).
|
||||||
|
*/
|
||||||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -600,13 +715,15 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* We have an error */
|
/* We have an error */
|
||||||
if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
|
if (dev->cmd_err & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF)) {
|
||||||
OMAP_I2C_STAT_XUDF)) {
|
|
||||||
omap_i2c_reset(dev);
|
omap_i2c_reset(dev);
|
||||||
__omap_i2c_init(dev);
|
__omap_i2c_init(dev);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev->cmd_err & OMAP_I2C_STAT_AL)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
|
if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
|
||||||
if (msg->flags & I2C_M_IGNORE_NAK)
|
if (msg->flags & I2C_M_IGNORE_NAK)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -635,6 +752,10 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
r = omap_i2c_wait_for_bb_valid(dev);
|
||||||
|
if (r < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
r = omap_i2c_wait_for_bb(dev);
|
r = omap_i2c_wait_for_bb(dev);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -885,7 +885,9 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
|
||||||
return; /* ignore */
|
return; /* ignore */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isr & ISR_BED) {
|
if ((isr & ISR_BED) &&
|
||||||
|
(!((i2c->msg->flags & I2C_M_IGNORE_NAK) &&
|
||||||
|
(isr & ISR_ACKNAK)))) {
|
||||||
int ret = BUS_ERROR;
|
int ret = BUS_ERROR;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -919,12 +921,14 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
|
||||||
icr |= ICR_ALDIE | ICR_TB;
|
icr |= ICR_ALDIE | ICR_TB;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the last byte of the last message, send
|
* If this is the last byte of the last message or last byte
|
||||||
* a STOP.
|
* of any message with I2C_M_STOP (e.g. SCCB), send a STOP.
|
||||||
*/
|
*/
|
||||||
if (i2c->msg_ptr == i2c->msg->len &&
|
if ((i2c->msg_ptr == i2c->msg->len) &&
|
||||||
i2c->msg_idx == i2c->msg_num - 1)
|
((i2c->msg->flags & I2C_M_STOP) ||
|
||||||
icr |= ICR_STOP;
|
(i2c->msg_idx == i2c->msg_num - 1)))
|
||||||
|
icr |= ICR_STOP;
|
||||||
|
|
||||||
} else if (i2c->msg_idx < i2c->msg_num - 1) {
|
} else if (i2c->msg_idx < i2c->msg_num - 1) {
|
||||||
/*
|
/*
|
||||||
* Next segment of the message.
|
* Next segment of the message.
|
||||||
|
@ -1071,7 +1075,8 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num
|
||||||
|
|
||||||
static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
|
static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
||||||
|
I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm i2c_pxa_algorithm = {
|
static const struct i2c_algorithm i2c_pxa_algorithm = {
|
||||||
|
|
|
@ -48,6 +48,12 @@
|
||||||
#define ICMAR 0x20 /* master address */
|
#define ICMAR 0x20 /* master address */
|
||||||
#define ICRXTX 0x24 /* data port */
|
#define ICRXTX 0x24 /* data port */
|
||||||
|
|
||||||
|
/* ICSCR */
|
||||||
|
#define SDBS (1 << 3) /* slave data buffer select */
|
||||||
|
#define SIE (1 << 2) /* slave interface enable */
|
||||||
|
#define GCAE (1 << 1) /* general call address enable */
|
||||||
|
#define FNA (1 << 0) /* forced non acknowledgment */
|
||||||
|
|
||||||
/* ICMCR */
|
/* ICMCR */
|
||||||
#define MDBS (1 << 7) /* non-fifo mode switch */
|
#define MDBS (1 << 7) /* non-fifo mode switch */
|
||||||
#define FSCL (1 << 6) /* override SCL pin */
|
#define FSCL (1 << 6) /* override SCL pin */
|
||||||
|
@ -58,6 +64,15 @@
|
||||||
#define FSB (1 << 1) /* force stop bit */
|
#define FSB (1 << 1) /* force stop bit */
|
||||||
#define ESG (1 << 0) /* en startbit gen */
|
#define ESG (1 << 0) /* en startbit gen */
|
||||||
|
|
||||||
|
/* ICSSR (also for ICSIER) */
|
||||||
|
#define GCAR (1 << 6) /* general call received */
|
||||||
|
#define STM (1 << 5) /* slave transmit mode */
|
||||||
|
#define SSR (1 << 4) /* stop received */
|
||||||
|
#define SDE (1 << 3) /* slave data empty */
|
||||||
|
#define SDT (1 << 2) /* slave data transmitted */
|
||||||
|
#define SDR (1 << 1) /* slave data received */
|
||||||
|
#define SAR (1 << 0) /* slave addr received */
|
||||||
|
|
||||||
/* ICMSR (also for ICMIE) */
|
/* ICMSR (also for ICMIE) */
|
||||||
#define MNR (1 << 6) /* nack received */
|
#define MNR (1 << 6) /* nack received */
|
||||||
#define MAL (1 << 5) /* arbitration lost */
|
#define MAL (1 << 5) /* arbitration lost */
|
||||||
|
@ -103,6 +118,7 @@ struct rcar_i2c_priv {
|
||||||
u32 icccr;
|
u32 icccr;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
enum rcar_i2c_type devtype;
|
enum rcar_i2c_type devtype;
|
||||||
|
struct i2c_client *slave;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||||
|
@ -126,15 +142,6 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
|
||||||
|
|
||||||
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* reset slave mode.
|
|
||||||
* slave mode is not used on this driver
|
|
||||||
*/
|
|
||||||
rcar_i2c_write(priv, ICSIER, 0);
|
|
||||||
rcar_i2c_write(priv, ICSAR, 0);
|
|
||||||
rcar_i2c_write(priv, ICSCR, 0);
|
|
||||||
rcar_i2c_write(priv, ICSSR, 0);
|
|
||||||
|
|
||||||
/* reset master mode */
|
/* reset master mode */
|
||||||
rcar_i2c_write(priv, ICMIER, 0);
|
rcar_i2c_write(priv, ICMIER, 0);
|
||||||
rcar_i2c_write(priv, ICMCR, 0);
|
rcar_i2c_write(priv, ICMCR, 0);
|
||||||
|
@ -360,6 +367,63 @@ static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
u32 ssr_raw, ssr_filtered;
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff;
|
||||||
|
ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER);
|
||||||
|
|
||||||
|
if (!ssr_filtered)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* address detected */
|
||||||
|
if (ssr_filtered & SAR) {
|
||||||
|
/* read or write request */
|
||||||
|
if (ssr_raw & STM) {
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value);
|
||||||
|
rcar_i2c_write(priv, ICRXTX, value);
|
||||||
|
rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR);
|
||||||
|
} else {
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value);
|
||||||
|
rcar_i2c_read(priv, ICRXTX); /* dummy read */
|
||||||
|
rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
rcar_i2c_write(priv, ICSSR, ~SAR & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* master sent stop */
|
||||||
|
if (ssr_filtered & SSR) {
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
|
||||||
|
rcar_i2c_write(priv, ICSIER, SAR | SSR);
|
||||||
|
rcar_i2c_write(priv, ICSSR, ~SSR & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* master wants to write to us */
|
||||||
|
if (ssr_filtered & SDR) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
value = rcar_i2c_read(priv, ICRXTX);
|
||||||
|
ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value);
|
||||||
|
/* Send NACK in case of error */
|
||||||
|
rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0));
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value);
|
||||||
|
rcar_i2c_write(priv, ICSSR, ~SDR & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* master wants to read from us */
|
||||||
|
if (ssr_filtered & SDE) {
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, &value);
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value);
|
||||||
|
rcar_i2c_write(priv, ICRXTX, value);
|
||||||
|
rcar_i2c_write(priv, ICSSR, ~SDE & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||||
{
|
{
|
||||||
struct rcar_i2c_priv *priv = ptr;
|
struct rcar_i2c_priv *priv = ptr;
|
||||||
|
@ -369,6 +433,9 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||||
/*-------------- spin lock -----------------*/
|
/*-------------- spin lock -----------------*/
|
||||||
spin_lock(&priv->lock);
|
spin_lock(&priv->lock);
|
||||||
|
|
||||||
|
if (rcar_i2c_slave_irq(priv))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
msr = rcar_i2c_read(priv, ICMSR);
|
msr = rcar_i2c_read(priv, ICMSR);
|
||||||
|
|
||||||
/* Only handle interrupts that are currently enabled */
|
/* Only handle interrupts that are currently enabled */
|
||||||
|
@ -499,6 +566,43 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rcar_reg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
if (priv->slave)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (slave->flags & I2C_CLIENT_TEN)
|
||||||
|
return -EAFNOSUPPORT;
|
||||||
|
|
||||||
|
pm_runtime_forbid(rcar_i2c_priv_to_dev(priv));
|
||||||
|
|
||||||
|
priv->slave = slave;
|
||||||
|
rcar_i2c_write(priv, ICSAR, slave->addr);
|
||||||
|
rcar_i2c_write(priv, ICSSR, 0);
|
||||||
|
rcar_i2c_write(priv, ICSIER, SAR | SSR);
|
||||||
|
rcar_i2c_write(priv, ICSCR, SIE | SDBS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rcar_unreg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
WARN_ON(!priv->slave);
|
||||||
|
|
||||||
|
rcar_i2c_write(priv, ICSIER, 0);
|
||||||
|
rcar_i2c_write(priv, ICSCR, 0);
|
||||||
|
|
||||||
|
priv->slave = NULL;
|
||||||
|
|
||||||
|
pm_runtime_allow(rcar_i2c_priv_to_dev(priv));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
/* This HW can't do SMBUS_QUICK and NOSTART */
|
/* This HW can't do SMBUS_QUICK and NOSTART */
|
||||||
|
@ -508,6 +612,8 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||||
static const struct i2c_algorithm rcar_i2c_algo = {
|
static const struct i2c_algorithm rcar_i2c_algo = {
|
||||||
.master_xfer = rcar_i2c_master_xfer,
|
.master_xfer = rcar_i2c_master_xfer,
|
||||||
.functionality = rcar_i2c_func,
|
.functionality = rcar_i2c_func,
|
||||||
|
.reg_slave = rcar_reg_slave,
|
||||||
|
.unreg_slave = rcar_unreg_slave,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id rcar_i2c_dt_ids[] = {
|
static const struct of_device_id rcar_i2c_dt_ids[] = {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/math64.h>
|
||||||
|
|
||||||
|
|
||||||
/* Register Map */
|
/* Register Map */
|
||||||
|
@ -97,6 +98,7 @@ struct rk3x_i2c {
|
||||||
/* Hardware resources */
|
/* Hardware resources */
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct notifier_block clk_rate_nb;
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
unsigned int scl_frequency;
|
unsigned int scl_frequency;
|
||||||
|
@ -428,18 +430,231 @@ out:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate)
|
/**
|
||||||
|
* Calculate divider values for desired SCL frequency
|
||||||
|
*
|
||||||
|
* @clk_rate: I2C input clock rate
|
||||||
|
* @scl_rate: Desired SCL rate
|
||||||
|
* @div_low: Divider output for low
|
||||||
|
* @div_high: Divider output for high
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
|
||||||
|
* a best-effort divider value is returned in divs. If the target rate is
|
||||||
|
* too high, we silently use the highest possible rate.
|
||||||
|
*/
|
||||||
|
static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
||||||
|
unsigned long *div_low, unsigned long *div_high)
|
||||||
{
|
{
|
||||||
unsigned long i2c_rate = clk_get_rate(i2c->clk);
|
unsigned long min_low_ns, min_high_ns;
|
||||||
unsigned int div;
|
unsigned long max_data_hold_ns;
|
||||||
|
unsigned long data_hold_buffer_ns;
|
||||||
|
unsigned long max_low_ns, min_total_ns;
|
||||||
|
|
||||||
/* set DIV = DIVH = DIVL
|
unsigned long clk_rate_khz, scl_rate_khz;
|
||||||
* SCL rate = (clk rate) / (8 * (DIVH + 1 + DIVL + 1))
|
|
||||||
* = (clk rate) / (16 * (DIV + 1))
|
unsigned long min_low_div, min_high_div;
|
||||||
|
unsigned long max_low_div;
|
||||||
|
|
||||||
|
unsigned long min_div_for_hold, min_total_div;
|
||||||
|
unsigned long extra_div, extra_low_div, ideal_low_div;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Only support standard-mode and fast-mode */
|
||||||
|
if (WARN_ON(scl_rate > 400000))
|
||||||
|
scl_rate = 400000;
|
||||||
|
|
||||||
|
/* prevent scl_rate_khz from becoming 0 */
|
||||||
|
if (WARN_ON(scl_rate < 1000))
|
||||||
|
scl_rate = 1000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* min_low_ns: The minimum number of ns we need to hold low
|
||||||
|
* to meet i2c spec
|
||||||
|
* min_high_ns: The minimum number of ns we need to hold high
|
||||||
|
* to meet i2c spec
|
||||||
|
* max_low_ns: The maximum number of ns we can hold low
|
||||||
|
* to meet i2c spec
|
||||||
|
*
|
||||||
|
* Note: max_low_ns should be (max data hold time * 2 - buffer)
|
||||||
|
* This is because the i2c host on Rockchip holds the data line
|
||||||
|
* for half the low time.
|
||||||
*/
|
*/
|
||||||
div = DIV_ROUND_UP(i2c_rate, scl_rate * 16) - 1;
|
if (scl_rate <= 100000) {
|
||||||
|
min_low_ns = 4700;
|
||||||
|
min_high_ns = 4000;
|
||||||
|
max_data_hold_ns = 3450;
|
||||||
|
data_hold_buffer_ns = 50;
|
||||||
|
} else {
|
||||||
|
min_low_ns = 1300;
|
||||||
|
min_high_ns = 600;
|
||||||
|
max_data_hold_ns = 900;
|
||||||
|
data_hold_buffer_ns = 50;
|
||||||
|
}
|
||||||
|
max_low_ns = max_data_hold_ns * 2 - data_hold_buffer_ns;
|
||||||
|
min_total_ns = min_low_ns + min_high_ns;
|
||||||
|
|
||||||
i2c_writel(i2c, (div << 16) | (div & 0xffff), REG_CLKDIV);
|
/* Adjust to avoid overflow */
|
||||||
|
clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
|
||||||
|
scl_rate_khz = scl_rate / 1000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need the total div to be >= this number
|
||||||
|
* so we don't clock too fast.
|
||||||
|
*/
|
||||||
|
min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8);
|
||||||
|
|
||||||
|
/* These are the min dividers needed for min hold times. */
|
||||||
|
min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000);
|
||||||
|
min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000);
|
||||||
|
min_div_for_hold = (min_low_div + min_high_div);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the maximum divider so we don't go over the max.
|
||||||
|
* We don't round up here (we round down) since this is a max.
|
||||||
|
*/
|
||||||
|
max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000);
|
||||||
|
|
||||||
|
if (min_low_div > max_low_div) {
|
||||||
|
WARN_ONCE(true,
|
||||||
|
"Conflicting, min_low_div %lu, max_low_div %lu\n",
|
||||||
|
min_low_div, max_low_div);
|
||||||
|
max_low_div = min_low_div;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_div_for_hold > min_total_div) {
|
||||||
|
/*
|
||||||
|
* Time needed to meet hold requirements is important.
|
||||||
|
* Just use that.
|
||||||
|
*/
|
||||||
|
*div_low = min_low_div;
|
||||||
|
*div_high = min_high_div;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We've got to distribute some time among the low and high
|
||||||
|
* so we don't run too fast.
|
||||||
|
*/
|
||||||
|
extra_div = min_total_div - min_div_for_hold;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We'll try to split things up perfectly evenly,
|
||||||
|
* biasing slightly towards having a higher div
|
||||||
|
* for low (spend more time low).
|
||||||
|
*/
|
||||||
|
ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns,
|
||||||
|
scl_rate_khz * 8 * min_total_ns);
|
||||||
|
|
||||||
|
/* Don't allow it to go over the max */
|
||||||
|
if (ideal_low_div > max_low_div)
|
||||||
|
ideal_low_div = max_low_div;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle when the ideal low div is going to take up
|
||||||
|
* more than we have.
|
||||||
|
*/
|
||||||
|
if (ideal_low_div > min_low_div + extra_div)
|
||||||
|
ideal_low_div = min_low_div + extra_div;
|
||||||
|
|
||||||
|
/* Give low the "ideal" and give high whatever extra is left */
|
||||||
|
extra_low_div = ideal_low_div - min_low_div;
|
||||||
|
*div_low = ideal_low_div;
|
||||||
|
*div_high = min_high_div + (extra_div - extra_low_div);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust to the fact that the hardware has an implicit "+1".
|
||||||
|
* NOTE: Above calculations always produce div_low > 0 and div_high > 0.
|
||||||
|
*/
|
||||||
|
*div_low = *div_low - 1;
|
||||||
|
*div_high = *div_high - 1;
|
||||||
|
|
||||||
|
/* Maximum divider supported by hw is 0xffff */
|
||||||
|
if (*div_low > 0xffff) {
|
||||||
|
*div_low = 0xffff;
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*div_high > 0xffff) {
|
||||||
|
*div_high = 0xffff;
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
||||||
|
{
|
||||||
|
unsigned long div_low, div_high;
|
||||||
|
u64 t_low_ns, t_high_ns;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, &div_low,
|
||||||
|
&div_high);
|
||||||
|
|
||||||
|
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
|
||||||
|
|
||||||
|
clk_enable(i2c->clk);
|
||||||
|
i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
|
||||||
|
clk_disable(i2c->clk);
|
||||||
|
|
||||||
|
t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
|
||||||
|
t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
|
||||||
|
dev_dbg(i2c->dev,
|
||||||
|
"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
|
||||||
|
clk_rate / 1000,
|
||||||
|
1000000000 / i2c->scl_frequency,
|
||||||
|
t_low_ns, t_high_ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rk3x_i2c_clk_notifier_cb - Clock rate change callback
|
||||||
|
* @nb: Pointer to notifier block
|
||||||
|
* @event: Notification reason
|
||||||
|
* @data: Pointer to notification data object
|
||||||
|
*
|
||||||
|
* The callback checks whether a valid bus frequency can be generated after the
|
||||||
|
* change. If so, the change is acknowledged, otherwise the change is aborted.
|
||||||
|
* New dividers are written to the HW in the pre- or post change notification
|
||||||
|
* depending on the scaling direction.
|
||||||
|
*
|
||||||
|
* Code adapted from i2c-cadence.c.
|
||||||
|
*
|
||||||
|
* Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK
|
||||||
|
* to acknowedge the change, NOTIFY_DONE if the notification is
|
||||||
|
* considered irrelevant.
|
||||||
|
*/
|
||||||
|
static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||||
|
event, void *data)
|
||||||
|
{
|
||||||
|
struct clk_notifier_data *ndata = data;
|
||||||
|
struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
|
||||||
|
unsigned long div_low, div_high;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case PRE_RATE_CHANGE:
|
||||||
|
if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
|
||||||
|
&div_low, &div_high) != 0) {
|
||||||
|
return NOTIFY_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scale up */
|
||||||
|
if (ndata->new_rate > ndata->old_rate)
|
||||||
|
rk3x_i2c_adapt_div(i2c, ndata->new_rate);
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
case POST_RATE_CHANGE:
|
||||||
|
/* scale down */
|
||||||
|
if (ndata->new_rate < ndata->old_rate)
|
||||||
|
rk3x_i2c_adapt_div(i2c, ndata->new_rate);
|
||||||
|
return NOTIFY_OK;
|
||||||
|
case ABORT_RATE_CHANGE:
|
||||||
|
/* scale up */
|
||||||
|
if (ndata->new_rate > ndata->old_rate)
|
||||||
|
rk3x_i2c_adapt_div(i2c, ndata->old_rate);
|
||||||
|
return NOTIFY_OK;
|
||||||
|
default:
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -536,9 +751,6 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
|
||||||
|
|
||||||
clk_enable(i2c->clk);
|
clk_enable(i2c->clk);
|
||||||
|
|
||||||
/* The clock rate might have changed, so setup the divider again */
|
|
||||||
rk3x_i2c_set_scl_rate(i2c, i2c->scl_frequency);
|
|
||||||
|
|
||||||
i2c->is_last_msg = false;
|
i2c->is_last_msg = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -624,6 +836,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
int bus_nr;
|
int bus_nr;
|
||||||
u32 value;
|
u32 value;
|
||||||
int irq;
|
int irq;
|
||||||
|
unsigned long clk_rate;
|
||||||
|
|
||||||
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
|
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
|
||||||
if (!i2c)
|
if (!i2c)
|
||||||
|
@ -724,16 +937,28 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
|
||||||
|
ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(&pdev->dev, "Unable to register clock notifier\n");
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_rate = clk_get_rate(i2c->clk);
|
||||||
|
rk3x_i2c_adapt_div(i2c, clk_rate);
|
||||||
|
|
||||||
ret = i2c_add_adapter(&i2c->adap);
|
ret = i2c_add_adapter(&i2c->adap);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Could not register adapter\n");
|
dev_err(&pdev->dev, "Could not register adapter\n");
|
||||||
goto err_clk;
|
goto err_clk_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);
|
dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_clk_notifier:
|
||||||
|
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||||
err_clk:
|
err_clk:
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -744,6 +969,8 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
|
||||||
struct rk3x_i2c *i2c = platform_get_drvdata(pdev);
|
struct rk3x_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_del_adapter(&i2c->adap);
|
i2c_del_adapter(&i2c->adap);
|
||||||
|
|
||||||
|
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
|
||||||
|
@ -87,6 +89,9 @@
|
||||||
/* Max time to wait for bus to become idle after a xfer (in us) */
|
/* Max time to wait for bus to become idle after a xfer (in us) */
|
||||||
#define S3C2410_IDLE_TIMEOUT 5000
|
#define S3C2410_IDLE_TIMEOUT 5000
|
||||||
|
|
||||||
|
/* Exynos5 Sysreg offset */
|
||||||
|
#define EXYNOS5_SYS_I2C_CFG 0x0234
|
||||||
|
|
||||||
/* i2c controller state */
|
/* i2c controller state */
|
||||||
enum s3c24xx_i2c_state {
|
enum s3c24xx_i2c_state {
|
||||||
STATE_IDLE,
|
STATE_IDLE,
|
||||||
|
@ -123,6 +128,8 @@ struct s3c24xx_i2c {
|
||||||
#if defined(CONFIG_ARM_S3C24XX_CPUFREQ)
|
#if defined(CONFIG_ARM_S3C24XX_CPUFREQ)
|
||||||
struct notifier_block freq_transition;
|
struct notifier_block freq_transition;
|
||||||
#endif
|
#endif
|
||||||
|
struct regmap *sysreg;
|
||||||
|
unsigned int sys_i2c_cfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device_id s3c24xx_driver_ids[] = {
|
static struct platform_device_id s3c24xx_driver_ids[] = {
|
||||||
|
@ -1071,6 +1078,7 @@ static void
|
||||||
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
struct s3c2410_platform_i2c *pdata = i2c->pdata;
|
struct s3c2410_platform_i2c *pdata = i2c->pdata;
|
||||||
|
int id;
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
return;
|
return;
|
||||||
|
@ -1080,6 +1088,21 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
||||||
of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
|
of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);
|
||||||
of_property_read_u32(np, "samsung,i2c-max-bus-freq",
|
of_property_read_u32(np, "samsung,i2c-max-bus-freq",
|
||||||
(u32 *)&pdata->frequency);
|
(u32 *)&pdata->frequency);
|
||||||
|
/*
|
||||||
|
* Exynos5's legacy i2c controller and new high speed i2c
|
||||||
|
* controller have muxed interrupt sources. By default the
|
||||||
|
* interrupts for 4-channel HS-I2C controller are enabled.
|
||||||
|
* If nodes for first four channels of legacy i2c controller
|
||||||
|
* are available then re-configure the interrupts via the
|
||||||
|
* system register.
|
||||||
|
*/
|
||||||
|
id = of_alias_get_id(np, "i2c");
|
||||||
|
i2c->sysreg = syscon_regmap_lookup_by_phandle(np,
|
||||||
|
"samsung,sysreg-phandle");
|
||||||
|
if (IS_ERR(i2c->sysreg))
|
||||||
|
return;
|
||||||
|
|
||||||
|
regmap_update_bits(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, BIT(id), 0);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static void
|
static void
|
||||||
|
@ -1260,6 +1283,9 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
|
||||||
|
|
||||||
i2c->suspended = 1;
|
i2c->suspended = 1;
|
||||||
|
|
||||||
|
if (!IS_ERR(i2c->sysreg))
|
||||||
|
regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1268,6 +1294,9 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (!IS_ERR(i2c->sysreg))
|
||||||
|
regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg);
|
||||||
|
|
||||||
clk_prepare_enable(i2c->clk);
|
clk_prepare_enable(i2c->clk);
|
||||||
s3c24xx_i2c_init(i2c);
|
s3c24xx_i2c_init(i2c);
|
||||||
clk_disable_unprepare(i2c->clk);
|
clk_disable_unprepare(i2c->clk);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* SuperH Mobile I2C Controller
|
* SuperH Mobile I2C Controller
|
||||||
*
|
*
|
||||||
|
* Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
|
||||||
|
*
|
||||||
* Copyright (C) 2008 Magnus Damm
|
* Copyright (C) 2008 Magnus Damm
|
||||||
*
|
*
|
||||||
* Portions of the code based on out-of-tree driver i2c-sh7343.c
|
* Portions of the code based on out-of-tree driver i2c-sh7343.c
|
||||||
|
@ -16,20 +18,22 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c/i2c-sh_mobile.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/i2c/i2c-sh_mobile.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/* Transmit operation: */
|
/* Transmit operation: */
|
||||||
/* */
|
/* */
|
||||||
|
@ -110,6 +114,7 @@ enum sh_mobile_i2c_op {
|
||||||
OP_TX_FIRST,
|
OP_TX_FIRST,
|
||||||
OP_TX,
|
OP_TX,
|
||||||
OP_TX_STOP,
|
OP_TX_STOP,
|
||||||
|
OP_TX_STOP_DATA,
|
||||||
OP_TX_TO_RX,
|
OP_TX_TO_RX,
|
||||||
OP_RX,
|
OP_RX,
|
||||||
OP_RX_STOP,
|
OP_RX_STOP,
|
||||||
|
@ -134,6 +139,11 @@ struct sh_mobile_i2c_data {
|
||||||
int pos;
|
int pos;
|
||||||
int sr;
|
int sr;
|
||||||
bool send_stop;
|
bool send_stop;
|
||||||
|
|
||||||
|
struct dma_chan *dma_tx;
|
||||||
|
struct dma_chan *dma_rx;
|
||||||
|
struct scatterlist sg;
|
||||||
|
enum dma_data_direction dma_direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sh_mobile_dt_config {
|
struct sh_mobile_dt_config {
|
||||||
|
@ -171,6 +181,8 @@ struct sh_mobile_dt_config {
|
||||||
|
|
||||||
#define ICIC_ICCLB8 0x80
|
#define ICIC_ICCLB8 0x80
|
||||||
#define ICIC_ICCHB8 0x40
|
#define ICIC_ICCHB8 0x40
|
||||||
|
#define ICIC_TDMAE 0x20
|
||||||
|
#define ICIC_RDMAE 0x10
|
||||||
#define ICIC_ALE 0x08
|
#define ICIC_ALE 0x08
|
||||||
#define ICIC_TACKE 0x04
|
#define ICIC_TACKE 0x04
|
||||||
#define ICIC_WAITE 0x02
|
#define ICIC_WAITE 0x02
|
||||||
|
@ -277,6 +289,7 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
|
||||||
else
|
else
|
||||||
pd->icic &= ~ICIC_ICCHB8;
|
pd->icic &= ~ICIC_ICCHB8;
|
||||||
|
|
||||||
|
dev_dbg(pd->dev, "timing values: L/H=0x%x/0x%x\n", pd->iccl, pd->icch);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,8 +345,10 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
|
||||||
case OP_TX: /* write data */
|
case OP_TX: /* write data */
|
||||||
iic_wr(pd, ICDR, data);
|
iic_wr(pd, ICDR, data);
|
||||||
break;
|
break;
|
||||||
case OP_TX_STOP: /* write data and issue a stop afterwards */
|
case OP_TX_STOP_DATA: /* write data and issue a stop afterwards */
|
||||||
iic_wr(pd, ICDR, data);
|
iic_wr(pd, ICDR, data);
|
||||||
|
/* fallthrough */
|
||||||
|
case OP_TX_STOP: /* issue a stop */
|
||||||
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
|
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
|
||||||
: ICCR_ICE | ICCR_TRS | ICCR_BBSY);
|
: ICCR_ICE | ICCR_TRS | ICCR_BBSY);
|
||||||
break;
|
break;
|
||||||
|
@ -389,13 +404,17 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
|
||||||
{
|
{
|
||||||
unsigned char data;
|
unsigned char data;
|
||||||
|
|
||||||
if (pd->pos == pd->msg->len)
|
if (pd->pos == pd->msg->len) {
|
||||||
|
/* Send stop if we haven't yet (DMA case) */
|
||||||
|
if (pd->send_stop && (iic_rd(pd, ICCR) & ICCR_BBSY))
|
||||||
|
i2c_op(pd, OP_TX_STOP, 0);
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
sh_mobile_i2c_get_data(pd, &data);
|
sh_mobile_i2c_get_data(pd, &data);
|
||||||
|
|
||||||
if (sh_mobile_i2c_is_last_byte(pd))
|
if (sh_mobile_i2c_is_last_byte(pd))
|
||||||
i2c_op(pd, OP_TX_STOP, data);
|
i2c_op(pd, OP_TX_STOP_DATA, data);
|
||||||
else if (sh_mobile_i2c_is_first_byte(pd))
|
else if (sh_mobile_i2c_is_first_byte(pd))
|
||||||
i2c_op(pd, OP_TX_FIRST, data);
|
i2c_op(pd, OP_TX_FIRST, data);
|
||||||
else
|
else
|
||||||
|
@ -447,10 +466,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
|
||||||
|
|
||||||
static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct platform_device *dev = dev_id;
|
struct sh_mobile_i2c_data *pd = dev_id;
|
||||||
struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
|
|
||||||
unsigned char sr;
|
unsigned char sr;
|
||||||
int wakeup;
|
int wakeup = 0;
|
||||||
|
|
||||||
sr = iic_rd(pd, ICSR);
|
sr = iic_rd(pd, ICSR);
|
||||||
pd->sr |= sr; /* remember state */
|
pd->sr |= sr; /* remember state */
|
||||||
|
@ -459,15 +477,21 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
||||||
(pd->msg->flags & I2C_M_RD) ? "read" : "write",
|
(pd->msg->flags & I2C_M_RD) ? "read" : "write",
|
||||||
pd->pos, pd->msg->len);
|
pd->pos, pd->msg->len);
|
||||||
|
|
||||||
if (sr & (ICSR_AL | ICSR_TACK)) {
|
/* Kick off TxDMA after preface was done */
|
||||||
|
if (pd->dma_direction == DMA_TO_DEVICE && pd->pos == 0)
|
||||||
|
iic_set_clr(pd, ICIC, ICIC_TDMAE, 0);
|
||||||
|
else if (sr & (ICSR_AL | ICSR_TACK))
|
||||||
/* don't interrupt transaction - continue to issue stop */
|
/* don't interrupt transaction - continue to issue stop */
|
||||||
iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
|
iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
|
||||||
wakeup = 0;
|
else if (pd->msg->flags & I2C_M_RD)
|
||||||
} else if (pd->msg->flags & I2C_M_RD)
|
|
||||||
wakeup = sh_mobile_i2c_isr_rx(pd);
|
wakeup = sh_mobile_i2c_isr_rx(pd);
|
||||||
else
|
else
|
||||||
wakeup = sh_mobile_i2c_isr_tx(pd);
|
wakeup = sh_mobile_i2c_isr_tx(pd);
|
||||||
|
|
||||||
|
/* Kick off RxDMA after preface was done */
|
||||||
|
if (pd->dma_direction == DMA_FROM_DEVICE && pd->pos == 1)
|
||||||
|
iic_set_clr(pd, ICIC, ICIC_RDMAE, 0);
|
||||||
|
|
||||||
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
|
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
|
||||||
iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
|
iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
|
||||||
|
|
||||||
|
@ -482,6 +506,84 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd)
|
||||||
|
{
|
||||||
|
struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE
|
||||||
|
? pd->dma_rx : pd->dma_tx;
|
||||||
|
|
||||||
|
dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg),
|
||||||
|
pd->msg->len, pd->dma_direction);
|
||||||
|
|
||||||
|
pd->dma_direction = DMA_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd)
|
||||||
|
{
|
||||||
|
if (pd->dma_direction == DMA_NONE)
|
||||||
|
return;
|
||||||
|
else if (pd->dma_direction == DMA_FROM_DEVICE)
|
||||||
|
dmaengine_terminate_all(pd->dma_rx);
|
||||||
|
else if (pd->dma_direction == DMA_TO_DEVICE)
|
||||||
|
dmaengine_terminate_all(pd->dma_tx);
|
||||||
|
|
||||||
|
sh_mobile_i2c_dma_unmap(pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sh_mobile_i2c_dma_callback(void *data)
|
||||||
|
{
|
||||||
|
struct sh_mobile_i2c_data *pd = data;
|
||||||
|
|
||||||
|
sh_mobile_i2c_dma_unmap(pd);
|
||||||
|
pd->pos = pd->msg->len;
|
||||||
|
|
||||||
|
iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
|
||||||
|
{
|
||||||
|
bool read = pd->msg->flags & I2C_M_RD;
|
||||||
|
enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||||
|
struct dma_chan *chan = read ? pd->dma_rx : pd->dma_tx;
|
||||||
|
struct dma_async_tx_descriptor *txdesc;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
|
dma_cookie_t cookie;
|
||||||
|
|
||||||
|
if (!chan)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir);
|
||||||
|
if (dma_mapping_error(pd->dev, dma_addr)) {
|
||||||
|
dev_dbg(pd->dev, "dma map failed, using PIO\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_dma_len(&pd->sg) = pd->msg->len;
|
||||||
|
sg_dma_address(&pd->sg) = dma_addr;
|
||||||
|
|
||||||
|
pd->dma_direction = dir;
|
||||||
|
|
||||||
|
txdesc = dmaengine_prep_slave_sg(chan, &pd->sg, 1,
|
||||||
|
read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!txdesc) {
|
||||||
|
dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n");
|
||||||
|
sh_mobile_i2c_cleanup_dma(pd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
txdesc->callback = sh_mobile_i2c_dma_callback;
|
||||||
|
txdesc->callback_param = pd;
|
||||||
|
|
||||||
|
cookie = dmaengine_submit(txdesc);
|
||||||
|
if (dma_submit_error(cookie)) {
|
||||||
|
dev_dbg(pd->dev, "submitting dma failed, using PIO\n");
|
||||||
|
sh_mobile_i2c_cleanup_dma(pd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_async_issue_pending(chan);
|
||||||
|
}
|
||||||
|
|
||||||
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
|
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
|
||||||
bool do_init)
|
bool do_init)
|
||||||
{
|
{
|
||||||
|
@ -506,6 +608,9 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
|
||||||
pd->pos = -1;
|
pd->pos = -1;
|
||||||
pd->sr = 0;
|
pd->sr = 0;
|
||||||
|
|
||||||
|
if (pd->msg->len > 8)
|
||||||
|
sh_mobile_i2c_xfer_dma(pd);
|
||||||
|
|
||||||
/* Enable all interrupts to begin with */
|
/* Enable all interrupts to begin with */
|
||||||
iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -589,6 +694,9 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
5 * HZ);
|
5 * HZ);
|
||||||
if (!k) {
|
if (!k) {
|
||||||
dev_err(pd->dev, "Transfer request timed out\n");
|
dev_err(pd->dev, "Transfer request timed out\n");
|
||||||
|
if (pd->dma_direction != DMA_NONE)
|
||||||
|
sh_mobile_i2c_cleanup_dma(pd);
|
||||||
|
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -622,22 +730,77 @@ static const struct sh_mobile_dt_config default_dt_config = {
|
||||||
.clks_per_count = 1,
|
.clks_per_count = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sh_mobile_dt_config rcar_gen2_dt_config = {
|
static const struct sh_mobile_dt_config fast_clock_dt_config = {
|
||||||
.clks_per_count = 2,
|
.clks_per_count = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
||||||
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
|
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
|
||||||
{ .compatible = "renesas,iic-r8a7790", .data = &rcar_gen2_dt_config },
|
{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
|
||||||
{ .compatible = "renesas,iic-r8a7791", .data = &rcar_gen2_dt_config },
|
{ .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
|
||||||
{ .compatible = "renesas,iic-r8a7792", .data = &rcar_gen2_dt_config },
|
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
|
||||||
{ .compatible = "renesas,iic-r8a7793", .data = &rcar_gen2_dt_config },
|
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
|
||||||
{ .compatible = "renesas,iic-r8a7794", .data = &rcar_gen2_dt_config },
|
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
|
||||||
|
{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
|
||||||
|
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids);
|
MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids);
|
||||||
|
|
||||||
static int sh_mobile_i2c_hook_irqs(struct platform_device *dev)
|
static int sh_mobile_i2c_request_dma_chan(struct device *dev, enum dma_transfer_direction dir,
|
||||||
|
dma_addr_t port_addr, struct dma_chan **chan_ptr)
|
||||||
|
{
|
||||||
|
struct dma_chan *chan;
|
||||||
|
struct dma_slave_config cfg;
|
||||||
|
char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*chan_ptr = NULL;
|
||||||
|
|
||||||
|
chan = dma_request_slave_channel_reason(dev, chan_name);
|
||||||
|
if (IS_ERR(chan)) {
|
||||||
|
ret = PTR_ERR(chan);
|
||||||
|
dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
cfg.direction = dir;
|
||||||
|
if (dir == DMA_MEM_TO_DEV) {
|
||||||
|
cfg.dst_addr = port_addr;
|
||||||
|
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
} else {
|
||||||
|
cfg.src_addr = port_addr;
|
||||||
|
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dmaengine_slave_config(chan, &cfg);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret);
|
||||||
|
dma_release_channel(chan);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*chan_ptr = chan;
|
||||||
|
|
||||||
|
dev_dbg(dev, "got DMA channel for %s\n", chan_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd)
|
||||||
|
{
|
||||||
|
if (pd->dma_tx) {
|
||||||
|
dma_release_channel(pd->dma_tx);
|
||||||
|
pd->dma_tx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pd->dma_rx) {
|
||||||
|
dma_release_channel(pd->dma_rx);
|
||||||
|
pd->dma_rx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
resource_size_t n;
|
resource_size_t n;
|
||||||
|
@ -646,7 +809,7 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev)
|
||||||
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
|
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
|
||||||
for (n = res->start; n <= res->end; n++) {
|
for (n = res->start; n <= res->end; n++) {
|
||||||
ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
|
ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
|
||||||
0, dev_name(&dev->dev), dev);
|
0, dev_name(&dev->dev), pd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
|
dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -677,7 +840,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||||
return PTR_ERR(pd->clk);
|
return PTR_ERR(pd->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sh_mobile_i2c_hook_irqs(dev);
|
ret = sh_mobile_i2c_hook_irqs(dev, pd);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -723,6 +886,21 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
/* Init DMA */
|
||||||
|
sg_init_table(&pd->sg, 1);
|
||||||
|
pd->dma_direction = DMA_NONE;
|
||||||
|
ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM,
|
||||||
|
res->start + ICDR, &pd->dma_rx);
|
||||||
|
if (ret == -EPROBE_DEFER)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV,
|
||||||
|
res->start + ICDR, &pd->dma_tx);
|
||||||
|
if (ret == -EPROBE_DEFER) {
|
||||||
|
sh_mobile_i2c_release_dma(pd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable Runtime PM for this device.
|
/* Enable Runtime PM for this device.
|
||||||
*
|
*
|
||||||
* Also tell the Runtime PM core to ignore children
|
* Also tell the Runtime PM core to ignore children
|
||||||
|
@ -754,13 +932,13 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||||
|
|
||||||
ret = i2c_add_numbered_adapter(adap);
|
ret = i2c_add_numbered_adapter(adap);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
sh_mobile_i2c_release_dma(pd);
|
||||||
dev_err(&dev->dev, "cannot add numbered adapter\n");
|
dev_err(&dev->dev, "cannot add numbered adapter\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&dev->dev,
|
dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz, DMA=%c\n",
|
||||||
"I2C adapter %d with bus speed %lu Hz (L/H=0x%x/0x%x)\n",
|
adap->nr, pd->bus_speed, (pd->dma_rx || pd->dma_tx) ? 'y' : 'n');
|
||||||
adap->nr, pd->bus_speed, pd->iccl, pd->icch);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -770,6 +948,7 @@ static int sh_mobile_i2c_remove(struct platform_device *dev)
|
||||||
struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
|
struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
|
||||||
|
|
||||||
i2c_del_adapter(&pd->adap);
|
i2c_del_adapter(&pd->adap);
|
||||||
|
sh_mobile_i2c_release_dma(pd);
|
||||||
pm_runtime_disable(&dev->dev);
|
pm_runtime_disable(&dev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -806,16 +985,15 @@ static int __init sh_mobile_i2c_adap_init(void)
|
||||||
{
|
{
|
||||||
return platform_driver_register(&sh_mobile_i2c_driver);
|
return platform_driver_register(&sh_mobile_i2c_driver);
|
||||||
}
|
}
|
||||||
|
subsys_initcall(sh_mobile_i2c_adap_init);
|
||||||
|
|
||||||
static void __exit sh_mobile_i2c_adap_exit(void)
|
static void __exit sh_mobile_i2c_adap_exit(void)
|
||||||
{
|
{
|
||||||
platform_driver_unregister(&sh_mobile_i2c_driver);
|
platform_driver_unregister(&sh_mobile_i2c_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
subsys_initcall(sh_mobile_i2c_adap_init);
|
|
||||||
module_exit(sh_mobile_i2c_adap_exit);
|
module_exit(sh_mobile_i2c_adap_exit);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver");
|
MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver");
|
||||||
MODULE_AUTHOR("Magnus Damm");
|
MODULE_AUTHOR("Magnus Damm and Wolfram Sang");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:i2c-sh_mobile");
|
MODULE_ALIAS("platform:i2c-sh_mobile");
|
||||||
|
|
|
@ -46,6 +46,11 @@ enum xilinx_i2c_state {
|
||||||
STATE_START
|
STATE_START
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum xiic_endian {
|
||||||
|
LITTLE,
|
||||||
|
BIG
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
||||||
* @base: Memory base of the HW registers
|
* @base: Memory base of the HW registers
|
||||||
|
@ -70,6 +75,7 @@ struct xiic_i2c {
|
||||||
enum xilinx_i2c_state state;
|
enum xilinx_i2c_state state;
|
||||||
struct i2c_msg *rx_msg;
|
struct i2c_msg *rx_msg;
|
||||||
int rx_pos;
|
int rx_pos;
|
||||||
|
enum xiic_endian endianness;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,29 +176,58 @@ struct xiic_i2c {
|
||||||
static void xiic_start_xfer(struct xiic_i2c *i2c);
|
static void xiic_start_xfer(struct xiic_i2c *i2c);
|
||||||
static void __xiic_start_xfer(struct xiic_i2c *i2c);
|
static void __xiic_start_xfer(struct xiic_i2c *i2c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the register read and write functions, a little-endian and big-endian
|
||||||
|
* version are necessary. Endianness is detected during the probe function.
|
||||||
|
* Only the least significant byte [doublet] of the register are ever
|
||||||
|
* accessed. This requires an offset of 3 [2] from the base address for
|
||||||
|
* big-endian systems.
|
||||||
|
*/
|
||||||
|
|
||||||
static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value)
|
static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value)
|
||||||
{
|
{
|
||||||
iowrite8(value, i2c->base + reg);
|
if (i2c->endianness == LITTLE)
|
||||||
|
iowrite8(value, i2c->base + reg);
|
||||||
|
else
|
||||||
|
iowrite8(value, i2c->base + reg + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg)
|
static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg)
|
||||||
{
|
{
|
||||||
return ioread8(i2c->base + reg);
|
u8 ret;
|
||||||
|
|
||||||
|
if (i2c->endianness == LITTLE)
|
||||||
|
ret = ioread8(i2c->base + reg);
|
||||||
|
else
|
||||||
|
ret = ioread8(i2c->base + reg + 3);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value)
|
static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value)
|
||||||
{
|
{
|
||||||
iowrite16(value, i2c->base + reg);
|
if (i2c->endianness == LITTLE)
|
||||||
|
iowrite16(value, i2c->base + reg);
|
||||||
|
else
|
||||||
|
iowrite16be(value, i2c->base + reg + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value)
|
static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value)
|
||||||
{
|
{
|
||||||
iowrite32(value, i2c->base + reg);
|
if (i2c->endianness == LITTLE)
|
||||||
|
iowrite32(value, i2c->base + reg);
|
||||||
|
else
|
||||||
|
iowrite32be(value, i2c->base + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg)
|
static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg)
|
||||||
{
|
{
|
||||||
return ioread32(i2c->base + reg);
|
u32 ret;
|
||||||
|
|
||||||
|
if (i2c->endianness == LITTLE)
|
||||||
|
ret = ioread32(i2c->base + reg);
|
||||||
|
else
|
||||||
|
ret = ioread32be(i2c->base + reg);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask)
|
static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask)
|
||||||
|
@ -692,6 +727,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
u8 i;
|
u8 i;
|
||||||
|
u32 sr;
|
||||||
|
|
||||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
if (!i2c)
|
if (!i2c)
|
||||||
|
@ -724,6 +760,18 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect endianness
|
||||||
|
* Try to reset the TX FIFO. Then check the EMPTY flag. If it is not
|
||||||
|
* set, assume that the endianness was wrong and swap.
|
||||||
|
*/
|
||||||
|
i2c->endianness = LITTLE;
|
||||||
|
xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK);
|
||||||
|
/* Reset is cleared in xiic_reinit */
|
||||||
|
sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET);
|
||||||
|
if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK))
|
||||||
|
i2c->endianness = BIG;
|
||||||
|
|
||||||
xiic_reinit(i2c);
|
xiic_reinit(i2c);
|
||||||
|
|
||||||
/* add i2c adapter to i2c tree */
|
/* add i2c adapter to i2c tree */
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
(c) 2013 Wolfram Sang <wsa@the-dreams.de>
|
(c) 2013 Wolfram Sang <wsa@the-dreams.de>
|
||||||
I2C ACPI code Copyright (C) 2014 Intel Corp
|
I2C ACPI code Copyright (C) 2014 Intel Corp
|
||||||
Author: Lan Tianyu <tianyu.lan@intel.com>
|
Author: Lan Tianyu <tianyu.lan@intel.com>
|
||||||
|
I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -261,7 +262,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
|
||||||
struct acpi_resource *ares;
|
struct acpi_resource *ares;
|
||||||
u32 accessor_type = function >> 16;
|
u32 accessor_type = function >> 16;
|
||||||
u8 action = function & ACPI_IO_MASK;
|
u8 action = function & ACPI_IO_MASK;
|
||||||
acpi_status ret = AE_OK;
|
acpi_status ret;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
|
ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
|
||||||
|
@ -628,6 +629,17 @@ static int i2c_device_probe(struct device *dev)
|
||||||
if (!client)
|
if (!client)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!client->irq && dev->of_node) {
|
||||||
|
int irq = of_irq_get(dev->of_node, 0);
|
||||||
|
|
||||||
|
if (irq == -EPROBE_DEFER)
|
||||||
|
return irq;
|
||||||
|
if (irq < 0)
|
||||||
|
irq = 0;
|
||||||
|
|
||||||
|
client->irq = irq;
|
||||||
|
}
|
||||||
|
|
||||||
driver = to_i2c_driver(dev->driver);
|
driver = to_i2c_driver(dev->driver);
|
||||||
if (!driver->probe || !driver->id_table)
|
if (!driver->probe || !driver->id_table)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1401,7 +1413,6 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
info.irq = irq_of_parse_and_map(node, 0);
|
|
||||||
info.of_node = of_node_get(node);
|
info.of_node = of_node_get(node);
|
||||||
info.archdata = &dev_ad;
|
info.archdata = &dev_ad;
|
||||||
|
|
||||||
|
@ -1415,7 +1426,6 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
||||||
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
||||||
node->full_name);
|
node->full_name);
|
||||||
of_node_put(node);
|
of_node_put(node);
|
||||||
irq_dispose_mapping(info.irq);
|
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -2962,6 +2972,54 @@ trace:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_smbus_xfer);
|
EXPORT_SYMBOL(i2c_smbus_xfer);
|
||||||
|
|
||||||
|
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!client || !slave_cb)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!(client->flags & I2C_CLIENT_TEN)) {
|
||||||
|
/* Enforce stricter address checking */
|
||||||
|
ret = i2c_check_addr_validity(client->addr);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client->adapter->algo->reg_slave)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
client->slave_cb = slave_cb;
|
||||||
|
|
||||||
|
i2c_lock_adapter(client->adapter);
|
||||||
|
ret = client->adapter->algo->reg_slave(client);
|
||||||
|
i2c_unlock_adapter(client->adapter);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
client->slave_cb = NULL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_slave_register);
|
||||||
|
|
||||||
|
int i2c_slave_unregister(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!client->adapter->algo->unreg_slave)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
i2c_lock_adapter(client->adapter);
|
||||||
|
ret = client->adapter->algo->unreg_slave(client);
|
||||||
|
i2c_unlock_adapter(client->adapter);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
client->slave_cb = NULL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
|
||||||
|
|
||||||
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
|
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
|
||||||
MODULE_DESCRIPTION("I2C-Bus main module");
|
MODULE_DESCRIPTION("I2C-Bus main module");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -110,6 +110,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
||||||
void *, u32))
|
void *, u32))
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv;
|
struct i2c_mux_priv *priv;
|
||||||
|
char symlink_name[20];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
|
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
|
||||||
|
@ -183,6 +184,12 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"),
|
||||||
|
"can't create symlink to mux device\n");
|
||||||
|
|
||||||
|
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
|
||||||
|
WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name),
|
||||||
|
"can't create symlink for channel %u\n", chan_id);
|
||||||
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
|
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
|
||||||
i2c_adapter_id(&priv->adap));
|
i2c_adapter_id(&priv->adap));
|
||||||
|
|
||||||
|
@ -193,7 +200,12 @@ EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
|
||||||
void i2c_del_mux_adapter(struct i2c_adapter *adap)
|
void i2c_del_mux_adapter(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv = adap->algo_data;
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
|
char symlink_name[20];
|
||||||
|
|
||||||
|
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id);
|
||||||
|
sysfs_remove_link(&adap->dev.parent->kobj, symlink_name);
|
||||||
|
|
||||||
|
sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
|
||||||
i2c_del_adapter(adap);
|
i2c_del_adapter(adap);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* I2C slave mode EEPROM simulator
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||||
|
* Copyright (C) 2014 by Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Because most IP blocks can only detect one I2C slave address anyhow, this
|
||||||
|
* driver does not support simulating EEPROM types which take more than one
|
||||||
|
* address. It is prepared to simulate bigger EEPROMs with an internal 16 bit
|
||||||
|
* pointer, yet implementation is deferred until the need actually arises.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
|
||||||
|
struct eeprom_data {
|
||||||
|
struct bin_attribute bin;
|
||||||
|
bool first_write;
|
||||||
|
spinlock_t buffer_lock;
|
||||||
|
u8 buffer_idx;
|
||||||
|
u8 buffer[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
|
||||||
|
enum i2c_slave_event event, u8 *val)
|
||||||
|
{
|
||||||
|
struct eeprom_data *eeprom = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case I2C_SLAVE_REQ_WRITE_END:
|
||||||
|
if (eeprom->first_write) {
|
||||||
|
eeprom->buffer_idx = *val;
|
||||||
|
eeprom->first_write = false;
|
||||||
|
} else {
|
||||||
|
spin_lock(&eeprom->buffer_lock);
|
||||||
|
eeprom->buffer[eeprom->buffer_idx++] = *val;
|
||||||
|
spin_unlock(&eeprom->buffer_lock);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SLAVE_REQ_READ_START:
|
||||||
|
spin_lock(&eeprom->buffer_lock);
|
||||||
|
*val = eeprom->buffer[eeprom->buffer_idx];
|
||||||
|
spin_unlock(&eeprom->buffer_lock);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SLAVE_REQ_READ_END:
|
||||||
|
eeprom->buffer_idx++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case I2C_SLAVE_STOP:
|
||||||
|
eeprom->first_write = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr, char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct eeprom_data *eeprom;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (off + count >= attr->size)
|
||||||
|
return -EFBIG;
|
||||||
|
|
||||||
|
eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
|
||||||
|
|
||||||
|
spin_lock_irqsave(&eeprom->buffer_lock, flags);
|
||||||
|
memcpy(buf, &eeprom->buffer[off], count);
|
||||||
|
spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr, char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct eeprom_data *eeprom;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (off + count >= attr->size)
|
||||||
|
return -EFBIG;
|
||||||
|
|
||||||
|
eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
|
||||||
|
|
||||||
|
spin_lock_irqsave(&eeprom->buffer_lock, flags);
|
||||||
|
memcpy(&eeprom->buffer[off], buf, count);
|
||||||
|
spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct eeprom_data *eeprom;
|
||||||
|
int ret;
|
||||||
|
unsigned size = id->driver_data;
|
||||||
|
|
||||||
|
eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL);
|
||||||
|
if (!eeprom)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
eeprom->first_write = true;
|
||||||
|
spin_lock_init(&eeprom->buffer_lock);
|
||||||
|
i2c_set_clientdata(client, eeprom);
|
||||||
|
|
||||||
|
sysfs_bin_attr_init(&eeprom->bin);
|
||||||
|
eeprom->bin.attr.name = "slave-eeprom";
|
||||||
|
eeprom->bin.attr.mode = S_IRUSR | S_IWUSR;
|
||||||
|
eeprom->bin.read = i2c_slave_eeprom_bin_read;
|
||||||
|
eeprom->bin.write = i2c_slave_eeprom_bin_write;
|
||||||
|
eeprom->bin.size = size;
|
||||||
|
|
||||||
|
ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = i2c_slave_register(client, i2c_slave_eeprom_slave_cb);
|
||||||
|
if (ret) {
|
||||||
|
sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_slave_eeprom_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct eeprom_data *eeprom = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
i2c_slave_unregister(client);
|
||||||
|
sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id i2c_slave_eeprom_id[] = {
|
||||||
|
{ "slave-24c02", 2048 / 8 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id);
|
||||||
|
|
||||||
|
static struct i2c_driver i2c_slave_eeprom_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "i2c-slave-eeprom",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = i2c_slave_eeprom_probe,
|
||||||
|
.remove = i2c_slave_eeprom_remove,
|
||||||
|
.id_table = i2c_slave_eeprom_id,
|
||||||
|
};
|
||||||
|
module_i2c_driver(i2c_slave_eeprom_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
|
||||||
|
MODULE_DESCRIPTION("I2C slave mode EEPROM simulator");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -56,6 +56,7 @@ struct at24_data {
|
||||||
struct at24_platform_data chip;
|
struct at24_platform_data chip;
|
||||||
struct memory_accessor macc;
|
struct memory_accessor macc;
|
||||||
int use_smbus;
|
int use_smbus;
|
||||||
|
int use_smbus_write;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock protects against activities from other Linux tasks,
|
* Lock protects against activities from other Linux tasks,
|
||||||
|
@ -324,7 +325,7 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
||||||
{
|
{
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct i2c_msg msg;
|
struct i2c_msg msg;
|
||||||
ssize_t status;
|
ssize_t status = 0;
|
||||||
unsigned long timeout, write_time;
|
unsigned long timeout, write_time;
|
||||||
unsigned next_page;
|
unsigned next_page;
|
||||||
|
|
||||||
|
@ -365,9 +366,18 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
||||||
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
||||||
do {
|
do {
|
||||||
write_time = jiffies;
|
write_time = jiffies;
|
||||||
if (at24->use_smbus) {
|
if (at24->use_smbus_write) {
|
||||||
status = i2c_smbus_write_i2c_block_data(client,
|
switch (at24->use_smbus_write) {
|
||||||
offset, count, buf);
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
|
status = i2c_smbus_write_i2c_block_data(client,
|
||||||
|
offset, count, buf);
|
||||||
|
break;
|
||||||
|
case I2C_SMBUS_BYTE_DATA:
|
||||||
|
status = i2c_smbus_write_byte_data(client,
|
||||||
|
offset, buf[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
status = count;
|
status = count;
|
||||||
} else {
|
} else {
|
||||||
|
@ -487,6 +497,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
struct at24_platform_data chip;
|
struct at24_platform_data chip;
|
||||||
bool writable;
|
bool writable;
|
||||||
int use_smbus = 0;
|
int use_smbus = 0;
|
||||||
|
int use_smbus_write = 0;
|
||||||
struct at24_data *at24;
|
struct at24_data *at24;
|
||||||
int err;
|
int err;
|
||||||
unsigned i, num_addresses;
|
unsigned i, num_addresses;
|
||||||
|
@ -546,6 +557,18 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Use I2C operations unless we're stuck with SMBus extensions. */
|
||||||
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||||
|
if (i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
||||||
|
use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
|
||||||
|
} else if (i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
|
||||||
|
use_smbus_write = I2C_SMBUS_BYTE_DATA;
|
||||||
|
chip.page_size = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (chip.flags & AT24_FLAG_TAKE8ADDR)
|
if (chip.flags & AT24_FLAG_TAKE8ADDR)
|
||||||
num_addresses = 8;
|
num_addresses = 8;
|
||||||
else
|
else
|
||||||
|
@ -559,6 +582,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
|
|
||||||
mutex_init(&at24->lock);
|
mutex_init(&at24->lock);
|
||||||
at24->use_smbus = use_smbus;
|
at24->use_smbus = use_smbus;
|
||||||
|
at24->use_smbus_write = use_smbus_write;
|
||||||
at24->chip = chip;
|
at24->chip = chip;
|
||||||
at24->num_addresses = num_addresses;
|
at24->num_addresses = num_addresses;
|
||||||
|
|
||||||
|
@ -576,8 +600,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
|
|
||||||
writable = !(chip.flags & AT24_FLAG_READONLY);
|
writable = !(chip.flags & AT24_FLAG_READONLY);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
if (!use_smbus || i2c_check_functionality(client->adapter,
|
if (!use_smbus || use_smbus_write) {
|
||||||
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
|
||||||
|
|
||||||
unsigned write_max = chip.page_size;
|
unsigned write_max = chip.page_size;
|
||||||
|
|
||||||
|
|
|
@ -405,6 +405,7 @@ int of_irq_get(struct device_node *dev, int index)
|
||||||
|
|
||||||
return irq_create_of_mapping(&oirq);
|
return irq_create_of_mapping(&oirq);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_irq_get);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
|
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
|
||||||
|
|
|
@ -46,6 +46,8 @@ struct i2c_client;
|
||||||
struct i2c_driver;
|
struct i2c_driver;
|
||||||
union i2c_smbus_data;
|
union i2c_smbus_data;
|
||||||
struct i2c_board_info;
|
struct i2c_board_info;
|
||||||
|
enum i2c_slave_event;
|
||||||
|
typedef int (*i2c_slave_cb_t)(struct i2c_client *, enum i2c_slave_event, u8 *);
|
||||||
|
|
||||||
struct module;
|
struct module;
|
||||||
|
|
||||||
|
@ -209,6 +211,8 @@ struct i2c_driver {
|
||||||
* @irq: indicates the IRQ generated by this device (if any)
|
* @irq: indicates the IRQ generated by this device (if any)
|
||||||
* @detected: member of an i2c_driver.clients list or i2c-core's
|
* @detected: member of an i2c_driver.clients list or i2c-core's
|
||||||
* userspace_devices list
|
* userspace_devices list
|
||||||
|
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
|
||||||
|
* calls it to pass on slave events to the slave driver.
|
||||||
*
|
*
|
||||||
* 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 exposed to Linux is defined by the driver
|
* i2c bus. The behaviour exposed to Linux is defined by the driver
|
||||||
|
@ -224,6 +228,7 @@ struct i2c_client {
|
||||||
struct device dev; /* the device structure */
|
struct device dev; /* the device structure */
|
||||||
int irq; /* irq issued by device */
|
int irq; /* irq issued by device */
|
||||||
struct list_head detected;
|
struct list_head detected;
|
||||||
|
i2c_slave_cb_t slave_cb; /* callback for slave mode */
|
||||||
};
|
};
|
||||||
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
|
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
|
||||||
|
|
||||||
|
@ -246,6 +251,25 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
|
||||||
dev_set_drvdata(&dev->dev, data);
|
dev_set_drvdata(&dev->dev, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* I2C slave support */
|
||||||
|
|
||||||
|
enum i2c_slave_event {
|
||||||
|
I2C_SLAVE_REQ_READ_START,
|
||||||
|
I2C_SLAVE_REQ_READ_END,
|
||||||
|
I2C_SLAVE_REQ_WRITE_START,
|
||||||
|
I2C_SLAVE_REQ_WRITE_END,
|
||||||
|
I2C_SLAVE_STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
|
||||||
|
extern int i2c_slave_unregister(struct i2c_client *client);
|
||||||
|
|
||||||
|
static inline int i2c_slave_event(struct i2c_client *client,
|
||||||
|
enum i2c_slave_event event, u8 *val)
|
||||||
|
{
|
||||||
|
return client->slave_cb(client, event, val);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2c_board_info - template for device creation
|
* struct i2c_board_info - template for device creation
|
||||||
* @type: chip type, to initialize i2c_client.name
|
* @type: chip type, to initialize i2c_client.name
|
||||||
|
@ -352,6 +376,8 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
|
||||||
* into I2C transfers instead.
|
* into I2C transfers instead.
|
||||||
* @functionality: Return the flags that this algorithm/adapter pair supports
|
* @functionality: Return the flags that this algorithm/adapter pair supports
|
||||||
* from the I2C_FUNC_* flags.
|
* from the I2C_FUNC_* flags.
|
||||||
|
* @reg_slave: Register given client to I2C slave mode of this adapter
|
||||||
|
* @unreg_slave: Unregister given client from I2C slave mode of this adapter
|
||||||
*
|
*
|
||||||
* The following structs are for those who like to implement new bus drivers:
|
* The following structs are for those who like to implement new bus drivers:
|
||||||
* i2c_algorithm is the interface to a class of hardware solutions which can
|
* i2c_algorithm is the interface to a class of hardware solutions which can
|
||||||
|
@ -377,6 +403,9 @@ struct i2c_algorithm {
|
||||||
|
|
||||||
/* To determine what the adapter supports */
|
/* To determine what the adapter supports */
|
||||||
u32 (*functionality) (struct i2c_adapter *);
|
u32 (*functionality) (struct i2c_adapter *);
|
||||||
|
|
||||||
|
int (*reg_slave)(struct i2c_client *client);
|
||||||
|
int (*unreg_slave)(struct i2c_client *client);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче