mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
"Here is the pull request from the i2c subsystem. It got a little
delayed because I needed to wait for a dependency to be included
(commit b424080a9e
: "reset: Add optional resets and stubs"). Plus,
I had some email problems. All done now, the highlights are:
- drivers can now deprecate their use of i2c classes. That shouldn't
be used on embedded platforms anyhow and was often blindly
copy&pasted. This mechanism gives users time to switch away and
ultimately boot faster once the use of classes for those drivers is
gone for good.
- new drivers for QUP, Cadence, efm32
- tracepoint support for I2C and SMBus
- bigger cleanups for the mv64xxx, nomadik, and designware drivers
And the usual bugfixes, cleanups, feature additions. Most stuff has
been in linux-next for a while. Just some hot fixes and new drivers
were added a bit more recently."
* 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (63 commits)
i2c: cadence: fix Kconfig dependency
i2c: Add driver for Cadence I2C controller
i2c: cadence: Document device tree bindings
Documentation: i2c: improve section about flags mangling the protocol
i2c: qup: use proper type fro clk_freq
i2c: qup: off by ones in qup_i2c_probe()
i2c: efm32: fix binding doc
MAINTAINERS: update I2C web resources
i2c: qup: New bus driver for the Qualcomm QUP I2C controller
i2c: qup: Add device tree bindings information
i2c: i2c-xiic: deprecate class based instantiation
i2c: i2c-sirf: deprecate class based instantiation
i2c: i2c-mv64xxx: deprecate class based instantiation
i2c: i2c-designware-platdrv: deprecate class based instantiation
i2c: i2c-davinci: deprecate class based instantiation
i2c: i2c-bcm2835: deprecate class based instantiation
i2c: mv64xxx: Fix reset controller handling
i2c: omap: fix usage of IS_ERR_VALUE with pm_runtime_get_sync
i2c: efm32: new bus driver
i2c: exynos5: remove unnecessary cast of void pointer
...
This commit is contained in:
commit
39de65aa2c
@ -12,6 +12,7 @@ Required properties :
|
||||
- clocks: phandles to input clocks.
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Desired I2C bus frequency in Hz, otherwise defaults to 100000
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Examples :
|
||||
@ -23,6 +24,7 @@ i2c0: i2c@fff84000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&twi0_clk>;
|
||||
clock-frequency = <400000>;
|
||||
|
||||
24c512@50 {
|
||||
compatible = "24c512";
|
||||
|
24
Documentation/devicetree/bindings/i2c/i2c-cadence.txt
Normal file
24
Documentation/devicetree/bindings/i2c/i2c-cadence.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Binding for the Cadence I2C controller
|
||||
|
||||
Required properties:
|
||||
- reg: Physical base address and size of the controller's register area.
|
||||
- compatible: Compatibility string. Must be 'cdns,i2c-r1p10'.
|
||||
- clocks: Input clock specifier. Refer to common clock bindings.
|
||||
- interrupts: Interrupt specifier. Refer to interrupt bindings.
|
||||
- #address-cells: Should be 1.
|
||||
- #size-cells: Should be 0.
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Desired operating frequency, in Hz, of the bus.
|
||||
- clock-names: Input clock name, should be 'pclk'.
|
||||
|
||||
Example:
|
||||
i2c@e0004000 {
|
||||
compatible = "cdns,i2c-r1p10";
|
||||
clocks = <&clkc 38>;
|
||||
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0xe0004000 0x1000>;
|
||||
clock-frequency = <400000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -14,6 +14,12 @@ Optional properties :
|
||||
- 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.
|
||||
|
||||
- i2c-scl-falling-time : should contain the SCL falling time in nanoseconds.
|
||||
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.
|
||||
This value which is by default 300ns is used to compute the tHIGH period.
|
||||
|
||||
Example :
|
||||
|
||||
i2c@f0000 {
|
||||
@ -34,4 +40,6 @@ Example :
|
||||
interrupts = <12 1>;
|
||||
clock-frequency = <400000>;
|
||||
i2c-sda-hold-time-ns = <300>;
|
||||
i2c-sda-falling-time-ns = <300>;
|
||||
i2c-scl-falling-time-ns = <300>;
|
||||
};
|
||||
|
34
Documentation/devicetree/bindings/i2c/i2c-efm32.txt
Normal file
34
Documentation/devicetree/bindings/i2c/i2c-efm32.txt
Normal file
@ -0,0 +1,34 @@
|
||||
* Energymicro efm32 i2c controller
|
||||
|
||||
Required properties :
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
- compatible : should be "energymicro,efm32-i2c"
|
||||
- interrupts : the interrupt number
|
||||
- clocks : reference to the module clock
|
||||
|
||||
Recommended properties :
|
||||
|
||||
- clock-frequency : maximal I2C bus clock frequency in Hz.
|
||||
- efm32,location : Decides the location of the USART I/O pins.
|
||||
Allowed range : [0 .. 6]
|
||||
|
||||
Example:
|
||||
i2c0: i2c@4000a000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "energymicro,efm32-i2c";
|
||||
reg = <0x4000a000 0x400>;
|
||||
interrupts = <9>;
|
||||
clocks = <&cmu clk_HFPERCLKI2C0>;
|
||||
clock-frequency = <100000>;
|
||||
status = "ok";
|
||||
efm32,location = <3>;
|
||||
|
||||
eeprom@50 {
|
||||
compatible = "microchip,24c02";
|
||||
reg = <0x50>;
|
||||
pagesize = <16>;
|
||||
};
|
||||
};
|
||||
|
@ -4,12 +4,16 @@
|
||||
Required properties :
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
- compatible : Should be "marvell,mv64xxx-i2c" or "allwinner,sun4i-i2c"
|
||||
or "marvell,mv78230-i2c" or "marvell,mv78230-a0-i2c"
|
||||
Note: Only use "marvell,mv78230-a0-i2c" for a very rare,
|
||||
initial version of the SoC which had broken offload
|
||||
support. Linux auto-detects this and sets it
|
||||
appropriately.
|
||||
- compatible : Should be either:
|
||||
- "allwinner,sun4i-i2c"
|
||||
- "allwinner,sun6i-a31-i2c"
|
||||
- "marvell,mv64xxx-i2c"
|
||||
- "marvell,mv78230-i2c"
|
||||
- "marvell,mv78230-a0-i2c"
|
||||
* Note: Only use "marvell,mv78230-a0-i2c" for a
|
||||
very rare, initial version of the SoC which
|
||||
had broken offload support. Linux
|
||||
auto-detects this and sets it appropriately.
|
||||
- interrupts : The interrupt number
|
||||
|
||||
Optional properties :
|
||||
@ -17,6 +21,10 @@ Optional properties :
|
||||
- clock-frequency : Desired I2C bus clock frequency in Hz. If not set the
|
||||
default frequency is 100kHz
|
||||
|
||||
- resets : phandle to the parent reset controller. Mandatory
|
||||
whenever you're using the "allwinner,sun6i-a31-i2c"
|
||||
compatible.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c@11000 {
|
||||
|
@ -6,6 +6,7 @@ Required properties:
|
||||
"renesas,i2c-r8a7778"
|
||||
"renesas,i2c-r8a7779"
|
||||
"renesas,i2c-r8a7790"
|
||||
"renesas,i2c-r8a7791"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt specifier.
|
||||
@ -13,11 +14,16 @@ Required properties:
|
||||
Optional properties:
|
||||
- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
|
||||
propoerty indicates the default frequency 100 kHz.
|
||||
- clocks: clock specifier.
|
||||
|
||||
Examples :
|
||||
|
||||
i2c0: i2c@e6500000 {
|
||||
compatible = "renesas,i2c-rcar-h2";
|
||||
reg = <0 0xe6500000 0 0x428>;
|
||||
interrupts = <0 174 0x4>;
|
||||
i2c0: i2c@e6508000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "renesas,i2c-r8a7791";
|
||||
reg = <0 0xe6508000 0 0x40>;
|
||||
interrupts = <0 287 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp9_clks R8A7791_CLK_I2C0>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
||||
|
40
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
Normal file
40
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
Normal file
@ -0,0 +1,40 @@
|
||||
Qualcomm Universal Peripheral (QUP) I2C controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be:
|
||||
* "qcom,i2c-qup-v1.1.1" for 8660, 8960 and 8064.
|
||||
* "qcom,i2c-qup-v2.1.1" for 8974 v1.
|
||||
* "qcom,i2c-qup-v2.2.1" for 8974 v2 and later.
|
||||
- reg: Should contain QUP register address and length.
|
||||
- interrupts: Should contain I2C interrupt.
|
||||
|
||||
- clocks: A list of phandles + clock-specifiers, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: Should contain:
|
||||
* "core" for the core clock
|
||||
* "iface" for the AHB clock
|
||||
|
||||
- #address-cells: Should be <1> Address cells for i2c device address
|
||||
- #size-cells: Should be <0> as i2c addresses have no size component
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Should specify the desired i2c bus clock frequency in Hz,
|
||||
defaults to 100kHz if omitted.
|
||||
|
||||
Child nodes should conform to i2c bus binding.
|
||||
|
||||
Example:
|
||||
|
||||
i2c@f9924000 {
|
||||
compatible = "qcom,i2c-qup-v2.2.1";
|
||||
reg = <0xf9924000 0x1000>;
|
||||
interrupts = <0 96 0>;
|
||||
|
||||
clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
clock-frequency = <355000>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -26,6 +26,7 @@ Supported adapters:
|
||||
* Intel Wellsburg (PCH)
|
||||
* Intel Coleto Creek (PCH)
|
||||
* Intel Wildcat Point-LP (PCH)
|
||||
* Intel BayTrail (SOC)
|
||||
Datasheets: Publicly available at the Intel website
|
||||
|
||||
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
||||
|
@ -46,7 +46,7 @@ A few combinations of the above flags are also defined for your convenience:
|
||||
and write_block_data commands
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK Handles the SMBus read_i2c_block_data
|
||||
and write_i2c_block_data commands
|
||||
I2C_FUNC_SMBUS_EMUL Handles all SMBus commands than can be
|
||||
I2C_FUNC_SMBUS_EMUL Handles all SMBus commands that can be
|
||||
emulated by a real I2C adapter (using
|
||||
the transparent emulation layer)
|
||||
|
||||
|
@ -6,8 +6,8 @@ Key to symbols
|
||||
S (1 bit) : Start bit
|
||||
P (1 bit) : Stop bit
|
||||
Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
|
||||
A, NA (1 bit) : Accept and reverse accept bit.
|
||||
Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to
|
||||
A, NA (1 bit) : Accept and reverse accept bit.
|
||||
Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to
|
||||
get a 10 bit I2C address.
|
||||
Comm (8 bits): Command byte, a data byte which often selects a register on
|
||||
the device.
|
||||
@ -49,11 +49,20 @@ a byte read, followed by a byte write:
|
||||
Modified transactions
|
||||
=====================
|
||||
|
||||
The following modifications to the I2C protocol can also be generated,
|
||||
with the exception of I2C_M_NOSTART these are usually only needed to
|
||||
work around device issues:
|
||||
The following modifications to the I2C protocol can also be generated by
|
||||
setting these flags for i2c messages. With the exception of I2C_M_NOSTART, they
|
||||
are usually only needed to work around device issues:
|
||||
|
||||
Flag I2C_M_NOSTART:
|
||||
I2C_M_IGNORE_NAK:
|
||||
Normally message is interrupted immediately if there is [NA] from the
|
||||
client. Setting this flag treats any [NA] as [A], and all of
|
||||
message is sent.
|
||||
These messages may still fail to SCL lo->hi timeout.
|
||||
|
||||
I2C_M_NO_RD_ACK:
|
||||
In a read message, master A/NA bit is skipped.
|
||||
|
||||
I2C_M_NOSTART:
|
||||
In a combined transaction, no 'S Addr Wr/Rd [A]' is generated at some
|
||||
point. For example, setting I2C_M_NOSTART on the second partial message
|
||||
generates something like:
|
||||
@ -67,17 +76,13 @@ work around device issues:
|
||||
I2C device but may also be used between direction changes by some
|
||||
rare devices.
|
||||
|
||||
Flags I2C_M_REV_DIR_ADDR
|
||||
I2C_M_REV_DIR_ADDR:
|
||||
This toggles the Rd/Wr flag. That is, if you want to do a write, but
|
||||
need to emit an Rd instead of a Wr, or vice versa, you set this
|
||||
flag. For example:
|
||||
S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P
|
||||
|
||||
Flags I2C_M_IGNORE_NAK
|
||||
Normally message is interrupted immediately if there is [NA] from the
|
||||
client. Setting this flag treats any [NA] as [A], and all of
|
||||
message is sent.
|
||||
These messages may still fail to SCL lo->hi timeout.
|
||||
|
||||
Flags I2C_M_NO_RD_ACK
|
||||
In a read message, master A/NA bit is skipped.
|
||||
I2C_M_STOP:
|
||||
Force a stop condition (P) after the message. Some I2C related protocols
|
||||
like SCCB require that. Normally, you really don't want to get interrupted
|
||||
between the messages of one transfer.
|
||||
|
@ -1427,6 +1427,7 @@ F: drivers/cpuidle/cpuidle-zynq.c
|
||||
N: zynq
|
||||
N: xilinx
|
||||
F: drivers/clocksource/cadence_ttc_timer.c
|
||||
F: drivers/i2c/busses/i2c-cadence.c
|
||||
F: drivers/mmc/host/sdhci-of-arasan.c
|
||||
|
||||
ARM SMMU DRIVER
|
||||
@ -4308,7 +4309,8 @@ F: drivers/i2c/i2c-stub.c
|
||||
I2C SUBSYSTEM
|
||||
M: Wolfram Sang <wsa@the-dreams.de>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
W: http://i2c.wiki.kernel.org/
|
||||
W: https://i2c.wiki.kernel.org/
|
||||
Q: https://patchwork.ozlabs.org/project/linux-i2c/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git
|
||||
S: Maintained
|
||||
F: Documentation/i2c/
|
||||
|
@ -9,60 +9,7 @@
|
||||
#ifndef __ASM_BFIN_TWI_H__
|
||||
#define __ASM_BFIN_TWI_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/*
|
||||
* All Blackfin system MMRs are padded to 32bits even if the register
|
||||
* itself is only 16bits. So use a helper macro to streamline this.
|
||||
*/
|
||||
#define __BFP(m) u16 m; u16 __pad_##m
|
||||
|
||||
/*
|
||||
* bfin twi registers layout
|
||||
*/
|
||||
struct bfin_twi_regs {
|
||||
__BFP(clkdiv);
|
||||
__BFP(control);
|
||||
__BFP(slave_ctl);
|
||||
__BFP(slave_stat);
|
||||
__BFP(slave_addr);
|
||||
__BFP(master_ctl);
|
||||
__BFP(master_stat);
|
||||
__BFP(master_addr);
|
||||
__BFP(int_stat);
|
||||
__BFP(int_mask);
|
||||
__BFP(fifo_ctl);
|
||||
__BFP(fifo_stat);
|
||||
u32 __pad[20];
|
||||
__BFP(xmt_data8);
|
||||
__BFP(xmt_data16);
|
||||
__BFP(rcv_data8);
|
||||
__BFP(rcv_data16);
|
||||
};
|
||||
|
||||
#undef __BFP
|
||||
|
||||
struct bfin_twi_iface {
|
||||
int irq;
|
||||
spinlock_t lock;
|
||||
char read_write;
|
||||
u8 command;
|
||||
u8 *transPtr;
|
||||
int readNum;
|
||||
int writeNum;
|
||||
int cur_mode;
|
||||
int manual_stop;
|
||||
int result;
|
||||
struct i2c_adapter adap;
|
||||
struct completion complete;
|
||||
struct i2c_msg *pmsg;
|
||||
int msg_num;
|
||||
int cur_msg;
|
||||
u16 saved_clkdiv;
|
||||
u16 saved_control;
|
||||
struct bfin_twi_regs __iomem *regs_base;
|
||||
};
|
||||
#include <asm/blackfin.h>
|
||||
|
||||
#define DEFINE_TWI_REG(reg_name, reg) \
|
||||
static inline u16 read_##reg_name(struct bfin_twi_iface *iface) \
|
||||
@ -71,7 +18,6 @@ static inline void write_##reg_name(struct bfin_twi_iface *iface, u16 v) \
|
||||
{ bfin_write16(&iface->regs_base->reg, v); }
|
||||
|
||||
DEFINE_TWI_REG(CLKDIV, clkdiv)
|
||||
DEFINE_TWI_REG(CONTROL, control)
|
||||
DEFINE_TWI_REG(SLAVE_CTL, slave_ctl)
|
||||
DEFINE_TWI_REG(SLAVE_STAT, slave_stat)
|
||||
DEFINE_TWI_REG(SLAVE_ADDR, slave_addr)
|
||||
@ -80,7 +26,6 @@ DEFINE_TWI_REG(MASTER_STAT, master_stat)
|
||||
DEFINE_TWI_REG(MASTER_ADDR, master_addr)
|
||||
DEFINE_TWI_REG(INT_STAT, int_stat)
|
||||
DEFINE_TWI_REG(INT_MASK, int_mask)
|
||||
DEFINE_TWI_REG(FIFO_CTL, fifo_ctl)
|
||||
DEFINE_TWI_REG(FIFO_STAT, fifo_stat)
|
||||
DEFINE_TWI_REG(XMT_DATA8, xmt_data8)
|
||||
DEFINE_TWI_REG(XMT_DATA16, xmt_data16)
|
||||
@ -113,75 +58,25 @@ static inline u16 read_RCV_DATA16(struct bfin_twi_iface *iface)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline u16 read_FIFO_CTL(struct bfin_twi_iface *iface)
|
||||
{
|
||||
return bfin_read16(&iface->regs_base->fifo_ctl);
|
||||
}
|
||||
|
||||
/* ******************** TWO-WIRE INTERFACE (TWI) MASKS ***********************/
|
||||
/* TWI_CLKDIV Macros (Use: *pTWI_CLKDIV = CLKLOW(x)|CLKHI(y); ) */
|
||||
#define CLKLOW(x) ((x) & 0xFF) /* Periods Clock Is Held Low */
|
||||
#define CLKHI(y) (((y)&0xFF)<<0x8) /* Periods Before New Clock Low */
|
||||
static inline void write_FIFO_CTL(struct bfin_twi_iface *iface, u16 v)
|
||||
{
|
||||
bfin_write16(&iface->regs_base->fifo_ctl, v);
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
/* TWI_PRESCALE Masks */
|
||||
#define PRESCALE 0x007F /* SCLKs Per Internal Time Reference (10MHz) */
|
||||
#define TWI_ENA 0x0080 /* TWI Enable */
|
||||
#define SCCB 0x0200 /* SCCB Compatibility Enable */
|
||||
|
||||
/* TWI_SLAVE_CTL Masks */
|
||||
#define SEN 0x0001 /* Slave Enable */
|
||||
#define SADD_LEN 0x0002 /* Slave Address Length */
|
||||
#define STDVAL 0x0004 /* Slave Transmit Data Valid */
|
||||
#define NAK 0x0008 /* NAK/ACK* Generated At Conclusion Of Transfer */
|
||||
#define GEN 0x0010 /* General Call Address Matching Enabled */
|
||||
|
||||
/* TWI_SLAVE_STAT Masks */
|
||||
#define SDIR 0x0001 /* Slave Transfer Direction (Transmit/Receive*) */
|
||||
#define GCALL 0x0002 /* General Call Indicator */
|
||||
|
||||
/* TWI_MASTER_CTL Masks */
|
||||
#define MEN 0x0001 /* Master Mode Enable */
|
||||
#define MADD_LEN 0x0002 /* Master Address Length */
|
||||
#define MDIR 0x0004 /* Master Transmit Direction (RX/TX*) */
|
||||
#define FAST 0x0008 /* Use Fast Mode Timing Specs */
|
||||
#define STOP 0x0010 /* Issue Stop Condition */
|
||||
#define RSTART 0x0020 /* Repeat Start or Stop* At End Of Transfer */
|
||||
#define DCNT 0x3FC0 /* Data Bytes To Transfer */
|
||||
#define SDAOVR 0x4000 /* Serial Data Override */
|
||||
#define SCLOVR 0x8000 /* Serial Clock Override */
|
||||
|
||||
/* TWI_MASTER_STAT Masks */
|
||||
#define MPROG 0x0001 /* Master Transfer In Progress */
|
||||
#define LOSTARB 0x0002 /* Lost Arbitration Indicator (Xfer Aborted) */
|
||||
#define ANAK 0x0004 /* Address Not Acknowledged */
|
||||
#define DNAK 0x0008 /* Data Not Acknowledged */
|
||||
#define BUFRDERR 0x0010 /* Buffer Read Error */
|
||||
#define BUFWRERR 0x0020 /* Buffer Write Error */
|
||||
#define SDASEN 0x0040 /* Serial Data Sense */
|
||||
#define SCLSEN 0x0080 /* Serial Clock Sense */
|
||||
#define BUSBUSY 0x0100 /* Bus Busy Indicator */
|
||||
|
||||
/* TWI_INT_SRC and TWI_INT_ENABLE Masks */
|
||||
#define SINIT 0x0001 /* Slave Transfer Initiated */
|
||||
#define SCOMP 0x0002 /* Slave Transfer Complete */
|
||||
#define SERR 0x0004 /* Slave Transfer Error */
|
||||
#define SOVF 0x0008 /* Slave Overflow */
|
||||
#define MCOMP 0x0010 /* Master Transfer Complete */
|
||||
#define MERR 0x0020 /* Master Transfer Error */
|
||||
#define XMTSERV 0x0040 /* Transmit FIFO Service */
|
||||
#define RCVSERV 0x0080 /* Receive FIFO Service */
|
||||
|
||||
/* TWI_FIFO_CTRL Masks */
|
||||
#define XMTFLUSH 0x0001 /* Transmit Buffer Flush */
|
||||
#define RCVFLUSH 0x0002 /* Receive Buffer Flush */
|
||||
#define XMTINTLEN 0x0004 /* Transmit Buffer Interrupt Length */
|
||||
#define RCVINTLEN 0x0008 /* Receive Buffer Interrupt Length */
|
||||
|
||||
/* TWI_FIFO_STAT Masks */
|
||||
#define XMTSTAT 0x0003 /* Transmit FIFO Status */
|
||||
#define XMT_EMPTY 0x0000 /* Transmit FIFO Empty */
|
||||
#define XMT_HALF 0x0001 /* Transmit FIFO Has 1 Byte To Write */
|
||||
#define XMT_FULL 0x0003 /* Transmit FIFO Full (2 Bytes To Write) */
|
||||
|
||||
#define RCVSTAT 0x000C /* Receive FIFO Status */
|
||||
#define RCV_EMPTY 0x0000 /* Receive FIFO Empty */
|
||||
#define RCV_HALF 0x0004 /* Receive FIFO Has 1 Byte To Read */
|
||||
#define RCV_FULL 0x000C /* Receive FIFO Full (2 Bytes To Read) */
|
||||
static inline u16 read_CONTROL(struct bfin_twi_iface *iface)
|
||||
{
|
||||
return bfin_read16(&iface->regs_base->control);
|
||||
}
|
||||
|
||||
static inline void write_CONTROL(struct bfin_twi_iface *iface, u16 v)
|
||||
{
|
||||
SSYNC();
|
||||
bfin_write16(&iface->regs_base->control, v);
|
||||
}
|
||||
#endif
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c/bfin_twi.h>
|
||||
|
||||
#include <asm/blackfin.h>
|
||||
#include <asm/gpio.h>
|
||||
|
@ -110,6 +110,7 @@ config I2C_I801
|
||||
Wellsburg (PCH)
|
||||
Coleto Creek (PCH)
|
||||
Wildcat Point-LP (PCH)
|
||||
BayTrail (SOC)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-i801.
|
||||
@ -375,6 +376,13 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
|
||||
help
|
||||
The unit of the TWI clock is kHz.
|
||||
|
||||
config I2C_CADENCE
|
||||
tristate "Cadence I2C Controller"
|
||||
depends on ARCH_ZYNQ
|
||||
help
|
||||
Say yes here to select Cadence I2C Host Controller. This controller is
|
||||
e.g. used by Xilinx Zynq.
|
||||
|
||||
config I2C_CBUS_GPIO
|
||||
tristate "CBUS I2C driver"
|
||||
depends on GPIOLIB
|
||||
@ -432,6 +440,13 @@ config I2C_DESIGNWARE_PCI
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware-pci.
|
||||
|
||||
config I2C_EFM32
|
||||
tristate "EFM32 I2C controller"
|
||||
depends on ARCH_EFM32 || COMPILE_TEST
|
||||
help
|
||||
This driver supports the i2c block found in Energy Micro's EFM32
|
||||
SoCs.
|
||||
|
||||
config I2C_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
|
||||
depends on PCI
|
||||
@ -527,7 +542,7 @@ config I2C_MPC
|
||||
|
||||
config I2C_MV64XXX
|
||||
tristate "Marvell mv64xxx I2C Controller"
|
||||
depends on (MV64X60 || PLAT_ORION || ARCH_SUNXI)
|
||||
depends on MV64X60 || PLAT_ORION || ARCH_SUNXI
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
built-in I2C interface on the Marvell 64xxx line of host bridges.
|
||||
@ -648,6 +663,16 @@ config I2C_PXA_SLAVE
|
||||
is necessary for systems where the PXA may be a target on the
|
||||
I2C bus.
|
||||
|
||||
config I2C_QUP
|
||||
tristate "Qualcomm QUP based I2C controller"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
built-in I2C interface on the Qualcomm SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-qup.
|
||||
|
||||
config I2C_RIIC
|
||||
tristate "Renesas RIIC adapter"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
|
@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
||||
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
||||
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
||||
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
|
||||
obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
|
||||
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
|
||||
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
|
||||
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
|
||||
@ -41,6 +42,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
||||
i2c-designware-platform-objs := i2c-designware-platdrv.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||
i2c-designware-pci-objs := i2c-designware-pcidrv.o
|
||||
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
||||
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
|
||||
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
|
||||
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||
@ -63,6 +65,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
|
||||
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
|
||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
|
||||
obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
|
||||
|
@ -494,7 +494,7 @@ static struct i2c_adapter ali1535_adapter = {
|
||||
.algo = &smbus_algorithm,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(ali1535_ids) = {
|
||||
static const struct pci_device_id ali1535_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
|
||||
{ },
|
||||
};
|
||||
|
@ -416,7 +416,7 @@ static void ali1563_remove(struct pci_dev *dev)
|
||||
ali1563_shutdown(dev);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(ali1563_id_table) = {
|
||||
static const struct pci_device_id ali1563_id_table[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) },
|
||||
{},
|
||||
};
|
||||
|
@ -476,7 +476,7 @@ static struct i2c_adapter ali15x3_adapter = {
|
||||
.algo = &smbus_algorithm,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(ali15x3_ids) = {
|
||||
static const struct pci_device_id ali15x3_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -307,7 +307,7 @@ static const char* chipname[] = {
|
||||
"nVidia nForce", "AMD8111",
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(amd756_ids) = {
|
||||
static const struct pci_device_id amd756_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B),
|
||||
.driver_data = AMD756 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413),
|
||||
|
@ -414,7 +414,7 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(amd8111_ids) = {
|
||||
static const struct pci_device_id amd8111_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/dma-atmel.h>
|
||||
|
||||
#define 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_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
|
||||
|
||||
@ -711,6 +711,7 @@ static int at91_twi_probe(struct platform_device *pdev)
|
||||
struct resource *mem;
|
||||
int rc;
|
||||
u32 phy_addr;
|
||||
u32 bus_clk_rate;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
@ -756,13 +757,18 @@ static int at91_twi_probe(struct platform_device *pdev)
|
||||
dev->use_dma = true;
|
||||
}
|
||||
|
||||
at91_calc_twi_clock(dev, TWI_CLK_HZ);
|
||||
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
|
||||
&bus_clk_rate);
|
||||
if (rc)
|
||||
bus_clk_rate = DEFAULT_TWI_CLK_HZ;
|
||||
|
||||
at91_calc_twi_clock(dev, bus_clk_rate);
|
||||
at91_init_twi_bus(dev);
|
||||
|
||||
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
|
||||
i2c_set_adapdata(&dev->adapter, dev);
|
||||
dev->adapter.owner = THIS_MODULE;
|
||||
dev->adapter.class = I2C_CLASS_HWMON;
|
||||
dev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
dev->adapter.algo = &at91_twi_algorithm;
|
||||
dev->adapter.dev.parent = dev->dev;
|
||||
dev->adapter.nr = pdev->id;
|
||||
|
@ -219,7 +219,7 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
|
||||
static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev;
|
||||
struct resource *mem, *requested, *irq;
|
||||
struct resource *mem, *irq;
|
||||
u32 bus_clk_rate, divider;
|
||||
int ret;
|
||||
struct i2c_adapter *adap;
|
||||
@ -234,25 +234,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
init_completion(&i2c_dev->completion);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "No mem resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
requested = devm_request_mem_region(&pdev->dev, mem->start,
|
||||
resource_size(mem),
|
||||
dev_name(&pdev->dev));
|
||||
if (!requested) {
|
||||
dev_err(&pdev->dev, "Could not claim register region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
|
||||
resource_size(mem));
|
||||
if (!i2c_dev->regs) {
|
||||
dev_err(&pdev->dev, "Could not map registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(i2c_dev->regs))
|
||||
return PTR_ERR(i2c_dev->regs);
|
||||
|
||||
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_dev->clk)) {
|
||||
@ -295,7 +279,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
adap = &i2c_dev->adapter;
|
||||
i2c_set_adapdata(adap, i2c_dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
|
||||
adap->algo = &bcm2835_i2c_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
@ -21,10 +21,10 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c/bfin_twi.h>
|
||||
|
||||
#include <asm/blackfin.h>
|
||||
#include <asm/portmux.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/portmux.h>
|
||||
#include <asm/bfin_twi.h>
|
||||
|
||||
/* SMBus mode*/
|
||||
@ -65,7 +65,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
|
||||
/* Transmit next data */
|
||||
while (iface->writeNum > 0 &&
|
||||
(read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) {
|
||||
SSYNC();
|
||||
write_XMT_DATA8(iface, *(iface->transPtr++));
|
||||
iface->writeNum--;
|
||||
}
|
||||
@ -248,7 +247,6 @@ static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
|
||||
/* Clear interrupt status */
|
||||
write_INT_STAT(iface, twi_int_status);
|
||||
bfin_twi_handle_interrupt(iface, twi_int_status);
|
||||
SSYNC();
|
||||
}
|
||||
spin_unlock_irqrestore(&iface->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
@ -294,9 +292,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
|
||||
* discarded before start a new operation.
|
||||
*/
|
||||
write_FIFO_CTL(iface, 0x3);
|
||||
SSYNC();
|
||||
write_FIFO_CTL(iface, 0);
|
||||
SSYNC();
|
||||
|
||||
if (pmsg->flags & I2C_M_RD)
|
||||
iface->read_write = I2C_SMBUS_READ;
|
||||
@ -306,7 +302,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
|
||||
if (iface->writeNum > 0) {
|
||||
write_XMT_DATA8(iface, *(iface->transPtr++));
|
||||
iface->writeNum--;
|
||||
SSYNC();
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,7 +310,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
|
||||
|
||||
/* Interrupt mask . Enable XMT, RCV interrupt */
|
||||
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
|
||||
SSYNC();
|
||||
|
||||
if (pmsg->len <= 255)
|
||||
write_MASTER_CTL(iface, pmsg->len << 6);
|
||||
@ -329,7 +323,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
|
||||
(iface->msg_num > 1 ? RSTART : 0) |
|
||||
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
|
||||
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
|
||||
SSYNC();
|
||||
|
||||
while (!iface->result) {
|
||||
if (!wait_for_completion_timeout(&iface->complete,
|
||||
@ -453,7 +446,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
* start a new operation.
|
||||
*/
|
||||
write_FIFO_CTL(iface, 0x3);
|
||||
SSYNC();
|
||||
write_FIFO_CTL(iface, 0);
|
||||
|
||||
/* clear int stat */
|
||||
@ -461,7 +453,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
|
||||
/* Set Transmit device address */
|
||||
write_MASTER_ADDR(iface, addr);
|
||||
SSYNC();
|
||||
|
||||
switch (iface->cur_mode) {
|
||||
case TWI_I2C_MODE_STANDARDSUB:
|
||||
@ -469,7 +460,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
write_INT_MASK(iface, MCOMP | MERR |
|
||||
((iface->read_write == I2C_SMBUS_READ) ?
|
||||
RCVSERV : XMTSERV));
|
||||
SSYNC();
|
||||
|
||||
if (iface->writeNum + 1 <= 255)
|
||||
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
|
||||
@ -484,7 +474,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
case TWI_I2C_MODE_COMBINED:
|
||||
write_XMT_DATA8(iface, iface->command);
|
||||
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
|
||||
SSYNC();
|
||||
|
||||
if (iface->writeNum > 0)
|
||||
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
|
||||
@ -531,7 +520,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
write_INT_MASK(iface, MCOMP | MERR |
|
||||
((iface->read_write == I2C_SMBUS_READ) ?
|
||||
RCVSERV : XMTSERV));
|
||||
SSYNC();
|
||||
|
||||
/* Master enable */
|
||||
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
|
||||
@ -539,7 +527,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
|
||||
break;
|
||||
}
|
||||
SSYNC();
|
||||
|
||||
while (!iface->result) {
|
||||
if (!wait_for_completion_timeout(&iface->complete,
|
||||
@ -669,7 +656,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
|
||||
strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
|
||||
p_adap->algo = &bfin_twi_algorithm;
|
||||
p_adap->algo_data = iface;
|
||||
p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
|
||||
p_adap->dev.parent = &pdev->dev;
|
||||
p_adap->timeout = 5 * HZ;
|
||||
p_adap->retries = 3;
|
||||
@ -704,7 +691,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
|
||||
|
||||
/* Enable TWI */
|
||||
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
|
||||
SSYNC();
|
||||
|
||||
rc = i2c_add_numbered_adapter(p_adap);
|
||||
if (rc < 0) {
|
||||
|
905
drivers/i2c/busses/i2c-cadence.c
Normal file
905
drivers/i2c/busses/i2c-cadence.c
Normal file
@ -0,0 +1,905 @@
|
||||
/*
|
||||
* I2C bus driver for the Cadence I2C controller.
|
||||
*
|
||||
* Copyright (C) 2009 - 2014 Xilinx, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* either version 2 of the License, or (at your option) any
|
||||
* later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* Register offsets for the I2C device. */
|
||||
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
|
||||
#define CDNS_I2C_SR_OFFSET 0x04 /* Status Register, RO */
|
||||
#define CDNS_I2C_ADDR_OFFSET 0x08 /* I2C Address Register, RW */
|
||||
#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */
|
||||
#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */
|
||||
#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */
|
||||
#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */
|
||||
#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */
|
||||
#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */
|
||||
|
||||
/* Control Register Bit mask definitions */
|
||||
#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */
|
||||
#define CDNS_I2C_CR_ACK_EN BIT(3)
|
||||
#define CDNS_I2C_CR_NEA BIT(2)
|
||||
#define CDNS_I2C_CR_MS BIT(1)
|
||||
/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */
|
||||
#define CDNS_I2C_CR_RW BIT(0)
|
||||
/* 1 = Auto init FIFO to zeroes */
|
||||
#define CDNS_I2C_CR_CLR_FIFO BIT(6)
|
||||
#define CDNS_I2C_CR_DIVA_SHIFT 14
|
||||
#define CDNS_I2C_CR_DIVA_MASK (3 << CDNS_I2C_CR_DIVA_SHIFT)
|
||||
#define CDNS_I2C_CR_DIVB_SHIFT 8
|
||||
#define CDNS_I2C_CR_DIVB_MASK (0x3f << CDNS_I2C_CR_DIVB_SHIFT)
|
||||
|
||||
/* Status Register Bit mask definitions */
|
||||
#define CDNS_I2C_SR_BA BIT(8)
|
||||
#define CDNS_I2C_SR_RXDV BIT(5)
|
||||
|
||||
/*
|
||||
* I2C Address Register Bit mask definitions
|
||||
* Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0]
|
||||
* bits. A write access to this register always initiates a transfer if the I2C
|
||||
* is in master mode.
|
||||
*/
|
||||
#define CDNS_I2C_ADDR_MASK 0x000003FF /* I2C Address Mask */
|
||||
|
||||
/*
|
||||
* I2C Interrupt Registers Bit mask definitions
|
||||
* All the four interrupt registers (Status/Mask/Enable/Disable) have the same
|
||||
* bit definitions.
|
||||
*/
|
||||
#define CDNS_I2C_IXR_ARB_LOST BIT(9)
|
||||
#define CDNS_I2C_IXR_RX_UNF BIT(7)
|
||||
#define CDNS_I2C_IXR_TX_OVF BIT(6)
|
||||
#define CDNS_I2C_IXR_RX_OVF BIT(5)
|
||||
#define CDNS_I2C_IXR_SLV_RDY BIT(4)
|
||||
#define CDNS_I2C_IXR_TO BIT(3)
|
||||
#define CDNS_I2C_IXR_NACK BIT(2)
|
||||
#define CDNS_I2C_IXR_DATA BIT(1)
|
||||
#define CDNS_I2C_IXR_COMP BIT(0)
|
||||
|
||||
#define CDNS_I2C_IXR_ALL_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
|
||||
CDNS_I2C_IXR_RX_UNF | \
|
||||
CDNS_I2C_IXR_TX_OVF | \
|
||||
CDNS_I2C_IXR_RX_OVF | \
|
||||
CDNS_I2C_IXR_SLV_RDY | \
|
||||
CDNS_I2C_IXR_TO | \
|
||||
CDNS_I2C_IXR_NACK | \
|
||||
CDNS_I2C_IXR_DATA | \
|
||||
CDNS_I2C_IXR_COMP)
|
||||
|
||||
#define CDNS_I2C_IXR_ERR_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
|
||||
CDNS_I2C_IXR_RX_UNF | \
|
||||
CDNS_I2C_IXR_TX_OVF | \
|
||||
CDNS_I2C_IXR_RX_OVF | \
|
||||
CDNS_I2C_IXR_NACK)
|
||||
|
||||
#define CDNS_I2C_ENABLED_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
|
||||
CDNS_I2C_IXR_RX_UNF | \
|
||||
CDNS_I2C_IXR_TX_OVF | \
|
||||
CDNS_I2C_IXR_RX_OVF | \
|
||||
CDNS_I2C_IXR_NACK | \
|
||||
CDNS_I2C_IXR_DATA | \
|
||||
CDNS_I2C_IXR_COMP)
|
||||
|
||||
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
|
||||
|
||||
#define CDNS_I2C_FIFO_DEPTH 16
|
||||
/* FIFO depth at which the DATA interrupt occurs */
|
||||
#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2)
|
||||
#define CDNS_I2C_MAX_TRANSFER_SIZE 255
|
||||
/* Transfer size in multiples of data interrupt depth */
|
||||
#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
|
||||
|
||||
#define DRIVER_NAME "cdns-i2c"
|
||||
|
||||
#define CDNS_I2C_SPEED_MAX 400000
|
||||
#define CDNS_I2C_SPEED_DEFAULT 100000
|
||||
|
||||
#define CDNS_I2C_DIVA_MAX 4
|
||||
#define CDNS_I2C_DIVB_MAX 64
|
||||
|
||||
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
|
||||
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
|
||||
|
||||
/**
|
||||
* struct cdns_i2c - I2C device private data structure
|
||||
* @membase: Base address of the I2C device
|
||||
* @adap: I2C adapter instance
|
||||
* @p_msg: Message pointer
|
||||
* @err_status: Error status in Interrupt Status Register
|
||||
* @xfer_done: Transfer complete status
|
||||
* @p_send_buf: Pointer to transmit buffer
|
||||
* @p_recv_buf: Pointer to receive buffer
|
||||
* @suspended: Flag holding the device's PM status
|
||||
* @send_count: Number of bytes still expected to send
|
||||
* @recv_count: Number of bytes still expected to receive
|
||||
* @irq: IRQ number
|
||||
* @input_clk: Input clock to I2C controller
|
||||
* @i2c_clk: Maximum I2C clock speed
|
||||
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
|
||||
* @clk: Pointer to struct clk
|
||||
* @clk_rate_change_nb: Notifier block for clock rate changes
|
||||
*/
|
||||
struct cdns_i2c {
|
||||
void __iomem *membase;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *p_msg;
|
||||
int err_status;
|
||||
struct completion xfer_done;
|
||||
unsigned char *p_send_buf;
|
||||
unsigned char *p_recv_buf;
|
||||
u8 suspended;
|
||||
unsigned int send_count;
|
||||
unsigned int recv_count;
|
||||
int irq;
|
||||
unsigned long input_clk;
|
||||
unsigned int i2c_clk;
|
||||
unsigned int bus_hold_flag;
|
||||
struct clk *clk;
|
||||
struct notifier_block clk_rate_change_nb;
|
||||
};
|
||||
|
||||
#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
|
||||
clk_rate_change_nb)
|
||||
|
||||
/**
|
||||
* cdns_i2c_clear_bus_hold() - Clear bus hold bit
|
||||
* @id: Pointer to driver data struct
|
||||
*
|
||||
* Helper to clear the controller's bus hold bit.
|
||||
*/
|
||||
static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
|
||||
{
|
||||
u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
if (reg & CDNS_I2C_CR_HOLD)
|
||||
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_isr - Interrupt handler for the I2C device
|
||||
* @irq: irq number for the I2C device
|
||||
* @ptr: void pointer to cdns_i2c structure
|
||||
*
|
||||
* This function handles the data interrupt, transfer complete interrupt and
|
||||
* the error interrupts of the I2C device.
|
||||
*
|
||||
* Return: IRQ_HANDLED always
|
||||
*/
|
||||
static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
||||
{
|
||||
unsigned int isr_status, avail_bytes;
|
||||
unsigned int bytes_to_recv, bytes_to_send;
|
||||
struct cdns_i2c *id = ptr;
|
||||
/* Signal completion only after everything is updated */
|
||||
int done_flag = 0;
|
||||
irqreturn_t status = IRQ_NONE;
|
||||
|
||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
/* Handling nack and arbitration lost interrupt */
|
||||
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
|
||||
done_flag = 1;
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Handling Data interrupt */
|
||||
if ((isr_status & CDNS_I2C_IXR_DATA) &&
|
||||
(id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) {
|
||||
/* Always read data interrupt threshold bytes */
|
||||
bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH;
|
||||
id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH;
|
||||
avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
|
||||
/*
|
||||
* if the tranfer size register value is zero, then
|
||||
* check for the remaining bytes and update the
|
||||
* transfer size register.
|
||||
*/
|
||||
if (!avail_bytes) {
|
||||
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
|
||||
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
else
|
||||
cdns_i2c_writereg(id->recv_count,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
}
|
||||
|
||||
/* Process the data received */
|
||||
while (bytes_to_recv--)
|
||||
*(id->p_recv_buf)++ =
|
||||
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
||||
|
||||
if (!id->bus_hold_flag &&
|
||||
(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Handling Transfer Complete interrupt */
|
||||
if (isr_status & CDNS_I2C_IXR_COMP) {
|
||||
if (!id->p_recv_buf) {
|
||||
/*
|
||||
* If the device is sending data If there is further
|
||||
* data to be sent. Calculate the available space
|
||||
* in FIFO and fill the FIFO with that many bytes.
|
||||
*/
|
||||
if (id->send_count) {
|
||||
avail_bytes = CDNS_I2C_FIFO_DEPTH -
|
||||
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
if (id->send_count > avail_bytes)
|
||||
bytes_to_send = avail_bytes;
|
||||
else
|
||||
bytes_to_send = id->send_count;
|
||||
|
||||
while (bytes_to_send--) {
|
||||
cdns_i2c_writereg(
|
||||
(*(id->p_send_buf)++),
|
||||
CDNS_I2C_DATA_OFFSET);
|
||||
id->send_count--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Signal the completion of transaction and
|
||||
* clear the hold bus bit if there are no
|
||||
* further messages to be processed.
|
||||
*/
|
||||
done_flag = 1;
|
||||
}
|
||||
if (!id->send_count && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
} else {
|
||||
if (!id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
/*
|
||||
* If the device is receiving data, then signal
|
||||
* the completion of transaction and read the data
|
||||
* present in the FIFO. Signal the completion of
|
||||
* transaction.
|
||||
*/
|
||||
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
|
||||
CDNS_I2C_SR_RXDV) {
|
||||
*(id->p_recv_buf)++ =
|
||||
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
||||
id->recv_count--;
|
||||
}
|
||||
done_flag = 1;
|
||||
}
|
||||
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Update the status for errors */
|
||||
id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
|
||||
if (id->err_status)
|
||||
status = IRQ_HANDLED;
|
||||
|
||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
if (done_flag)
|
||||
complete(&id->xfer_done);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_mrecv - Prepare and start a master receive operation
|
||||
* @id: pointer to the i2c device structure
|
||||
*/
|
||||
static void cdns_i2c_mrecv(struct cdns_i2c *id)
|
||||
{
|
||||
unsigned int ctrl_reg;
|
||||
unsigned int isr_status;
|
||||
|
||||
id->p_recv_buf = id->p_msg->buf;
|
||||
id->recv_count = id->p_msg->len;
|
||||
|
||||
/* Put the controller in master receive mode and clear the FIFO */
|
||||
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
|
||||
|
||||
if (id->p_msg->flags & I2C_M_RECV_LEN)
|
||||
id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
|
||||
|
||||
/*
|
||||
* Check for the message size against FIFO depth and set the
|
||||
* 'hold bus' bit if it is greater than FIFO depth.
|
||||
*/
|
||||
if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
|
||||
ctrl_reg |= CDNS_I2C_CR_HOLD;
|
||||
|
||||
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
|
||||
/* Clear the interrupts in interrupt status register */
|
||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
/*
|
||||
* The no. of bytes to receive is checked against the limit of
|
||||
* max transfer size. Set transfer size register with no of bytes
|
||||
* receive if it is less than transfer size and transfer size if
|
||||
* it is more. Enable the interrupts.
|
||||
*/
|
||||
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
|
||||
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
else
|
||||
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
/* Clear the bus hold flag if bytes to receive is less than FIFO size */
|
||||
if (!id->bus_hold_flag &&
|
||||
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
|
||||
(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
/* Set the slave address in address register - triggers operation */
|
||||
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
|
||||
CDNS_I2C_ADDR_OFFSET);
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_msend - Prepare and start a master send operation
|
||||
* @id: pointer to the i2c device
|
||||
*/
|
||||
static void cdns_i2c_msend(struct cdns_i2c *id)
|
||||
{
|
||||
unsigned int avail_bytes;
|
||||
unsigned int bytes_to_send;
|
||||
unsigned int ctrl_reg;
|
||||
unsigned int isr_status;
|
||||
|
||||
id->p_recv_buf = NULL;
|
||||
id->p_send_buf = id->p_msg->buf;
|
||||
id->send_count = id->p_msg->len;
|
||||
|
||||
/* Set the controller in Master transmit mode and clear the FIFO. */
|
||||
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
ctrl_reg &= ~CDNS_I2C_CR_RW;
|
||||
ctrl_reg |= CDNS_I2C_CR_CLR_FIFO;
|
||||
|
||||
/*
|
||||
* Check for the message size against FIFO depth and set the
|
||||
* 'hold bus' bit if it is greater than FIFO depth.
|
||||
*/
|
||||
if (id->send_count > CDNS_I2C_FIFO_DEPTH)
|
||||
ctrl_reg |= CDNS_I2C_CR_HOLD;
|
||||
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
|
||||
/* Clear the interrupts in interrupt status register. */
|
||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
/*
|
||||
* Calculate the space available in FIFO. Check the message length
|
||||
* against the space available, and fill the FIFO accordingly.
|
||||
* Enable the interrupts.
|
||||
*/
|
||||
avail_bytes = CDNS_I2C_FIFO_DEPTH -
|
||||
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
|
||||
if (id->send_count > avail_bytes)
|
||||
bytes_to_send = avail_bytes;
|
||||
else
|
||||
bytes_to_send = id->send_count;
|
||||
|
||||
while (bytes_to_send--) {
|
||||
cdns_i2c_writereg((*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET);
|
||||
id->send_count--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the bus hold flag if there is no more data
|
||||
* and if it is the last message.
|
||||
*/
|
||||
if (!id->bus_hold_flag && !id->send_count)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
/* Set the slave address in address register - triggers operation. */
|
||||
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
|
||||
CDNS_I2C_ADDR_OFFSET);
|
||||
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_master_reset - Reset the interface
|
||||
* @adap: pointer to the i2c adapter driver instance
|
||||
*
|
||||
* This function cleanup the fifos, clear the hold bit and status
|
||||
* and disable the interrupts.
|
||||
*/
|
||||
static void cdns_i2c_master_reset(struct i2c_adapter *adap)
|
||||
{
|
||||
struct cdns_i2c *id = adap->algo_data;
|
||||
u32 regval;
|
||||
|
||||
/* Disable the interrupts */
|
||||
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
|
||||
/* Clear the hold bit and fifos */
|
||||
regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
regval &= ~CDNS_I2C_CR_HOLD;
|
||||
regval |= CDNS_I2C_CR_CLR_FIFO;
|
||||
cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
|
||||
/* Update the transfercount register to zero */
|
||||
cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
/* Clear the interupt status register */
|
||||
regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET);
|
||||
/* Clear the status register */
|
||||
regval = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
|
||||
cdns_i2c_writereg(regval, CDNS_I2C_SR_OFFSET);
|
||||
}
|
||||
|
||||
static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
|
||||
struct i2c_adapter *adap)
|
||||
{
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
id->p_msg = msg;
|
||||
id->err_status = 0;
|
||||
reinit_completion(&id->xfer_done);
|
||||
|
||||
/* Check for the TEN Bit mode on each msg */
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
if (reg & CDNS_I2C_CR_NEA)
|
||||
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA,
|
||||
CDNS_I2C_CR_OFFSET);
|
||||
} else {
|
||||
if (!(reg & CDNS_I2C_CR_NEA))
|
||||
cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,
|
||||
CDNS_I2C_CR_OFFSET);
|
||||
}
|
||||
|
||||
/* Check for the R/W flag on each msg */
|
||||
if (msg->flags & I2C_M_RD)
|
||||
cdns_i2c_mrecv(id);
|
||||
else
|
||||
cdns_i2c_msend(id);
|
||||
|
||||
/* Wait for the signal of completion */
|
||||
ret = wait_for_completion_timeout(&id->xfer_done, adap->timeout);
|
||||
if (!ret) {
|
||||
cdns_i2c_master_reset(adap);
|
||||
dev_err(id->adap.dev.parent,
|
||||
"timeout waiting on completion\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK,
|
||||
CDNS_I2C_IDR_OFFSET);
|
||||
|
||||
/* If it is bus arbitration error, try again */
|
||||
if (id->err_status & CDNS_I2C_IXR_ARB_LOST)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_master_xfer - The main i2c transfer function
|
||||
* @adap: pointer to the i2c adapter driver instance
|
||||
* @msgs: pointer to the i2c message structure
|
||||
* @num: the number of messages to transfer
|
||||
*
|
||||
* Initiates the send/recv activity based on the transfer message received.
|
||||
*
|
||||
* Return: number of msgs processed on success, negative error otherwise
|
||||
*/
|
||||
static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
int ret, count;
|
||||
u32 reg;
|
||||
struct cdns_i2c *id = adap->algo_data;
|
||||
|
||||
/* Check if the bus is free */
|
||||
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
|
||||
return -EAGAIN;
|
||||
|
||||
/*
|
||||
* Set the flag to one when multiple messages are to be
|
||||
* processed with a repeated start.
|
||||
*/
|
||||
if (num > 1) {
|
||||
id->bus_hold_flag = 1;
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
reg |= CDNS_I2C_CR_HOLD;
|
||||
cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
|
||||
} else {
|
||||
id->bus_hold_flag = 0;
|
||||
}
|
||||
|
||||
/* Process the msg one by one */
|
||||
for (count = 0; count < num; count++, msgs++) {
|
||||
if (count == (num - 1))
|
||||
id->bus_hold_flag = 0;
|
||||
|
||||
ret = cdns_i2c_process_msg(id, msgs, adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Report the other error interrupts to application */
|
||||
if (id->err_status) {
|
||||
cdns_i2c_master_reset(adap);
|
||||
|
||||
if (id->err_status & CDNS_I2C_IXR_NACK)
|
||||
return -ENXIO;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_func - Returns the supported features of the I2C driver
|
||||
* @adap: pointer to the i2c adapter structure
|
||||
*
|
||||
* Return: 32 bit value, each bit corresponding to a feature
|
||||
*/
|
||||
static u32 cdns_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
|
||||
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm cdns_i2c_algo = {
|
||||
.master_xfer = cdns_i2c_master_xfer,
|
||||
.functionality = cdns_i2c_func,
|
||||
};
|
||||
|
||||
/**
|
||||
* cdns_i2c_calc_divs - Calculate clock dividers
|
||||
* @f: I2C clock frequency
|
||||
* @input_clk: Input clock frequency
|
||||
* @a: First divider (return value)
|
||||
* @b: Second divider (return value)
|
||||
*
|
||||
* f is used as input and output variable. As input it is used as target I2C
|
||||
* frequency. On function exit f holds the actually resulting I2C frequency.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
|
||||
unsigned int *a, unsigned int *b)
|
||||
{
|
||||
unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
|
||||
unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
|
||||
unsigned int last_error, current_error;
|
||||
|
||||
/* calculate (divisor_a+1) x (divisor_b+1) */
|
||||
temp = input_clk / (22 * fscl);
|
||||
|
||||
/*
|
||||
* If the calculated value is negative or 0, the fscl input is out of
|
||||
* range. Return error.
|
||||
*/
|
||||
if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
|
||||
return -EINVAL;
|
||||
|
||||
last_error = -1;
|
||||
for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
|
||||
div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
|
||||
|
||||
if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
|
||||
continue;
|
||||
div_b--;
|
||||
|
||||
actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
|
||||
|
||||
if (actual_fscl > fscl)
|
||||
continue;
|
||||
|
||||
current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
|
||||
(fscl - actual_fscl));
|
||||
|
||||
if (last_error > current_error) {
|
||||
calc_div_a = div_a;
|
||||
calc_div_b = div_b;
|
||||
best_fscl = actual_fscl;
|
||||
last_error = current_error;
|
||||
}
|
||||
}
|
||||
|
||||
*a = calc_div_a;
|
||||
*b = calc_div_b;
|
||||
*f = best_fscl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_setclk - This function sets the serial clock rate for the I2C device
|
||||
* @clk_in: I2C clock input frequency in Hz
|
||||
* @id: Pointer to the I2C device structure
|
||||
*
|
||||
* The device must be idle rather than busy transferring data before setting
|
||||
* these device options.
|
||||
* The data rate is set by values in the control register.
|
||||
* The formula for determining the correct register values is
|
||||
* Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
|
||||
* See the hardware data sheet for a full explanation of setting the serial
|
||||
* clock rate. The clock can not be faster than the input clock divide by 22.
|
||||
* The two most common clock rates are 100KHz and 400KHz.
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise
|
||||
*/
|
||||
static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
|
||||
{
|
||||
unsigned int div_a, div_b;
|
||||
unsigned int ctrl_reg;
|
||||
int ret = 0;
|
||||
unsigned long fscl = id->i2c_clk;
|
||||
|
||||
ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
|
||||
ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
|
||||
(div_b << CDNS_I2C_CR_DIVB_SHIFT));
|
||||
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_clk_notifier_cb - Clock rate change callback
|
||||
* @nb: Pointer to notifier block
|
||||
* @event: Notification reason
|
||||
* @data: Pointer to notification data object
|
||||
*
|
||||
* This function is called when the cdns_i2c input clock frequency changes.
|
||||
* 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.
|
||||
*
|
||||
* 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 cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||
event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct cdns_i2c *id = to_cdns_i2c(nb);
|
||||
|
||||
if (id->suspended)
|
||||
return NOTIFY_OK;
|
||||
|
||||
switch (event) {
|
||||
case PRE_RATE_CHANGE:
|
||||
{
|
||||
unsigned long input_clk = ndata->new_rate;
|
||||
unsigned long fscl = id->i2c_clk;
|
||||
unsigned int div_a, div_b;
|
||||
int ret;
|
||||
|
||||
ret = cdns_i2c_calc_divs(&fscl, input_clk, &div_a, &div_b);
|
||||
if (ret) {
|
||||
dev_warn(id->adap.dev.parent,
|
||||
"clock rate change rejected\n");
|
||||
return NOTIFY_STOP;
|
||||
}
|
||||
|
||||
/* scale up */
|
||||
if (ndata->new_rate > ndata->old_rate)
|
||||
cdns_i2c_setclk(ndata->new_rate, id);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
case POST_RATE_CHANGE:
|
||||
id->input_clk = ndata->new_rate;
|
||||
/* scale down */
|
||||
if (ndata->new_rate < ndata->old_rate)
|
||||
cdns_i2c_setclk(ndata->new_rate, id);
|
||||
return NOTIFY_OK;
|
||||
case ABORT_RATE_CHANGE:
|
||||
/* scale up */
|
||||
if (ndata->new_rate > ndata->old_rate)
|
||||
cdns_i2c_setclk(ndata->old_rate, id);
|
||||
return NOTIFY_OK;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_suspend - Suspend method for the driver
|
||||
* @_dev: Address of the platform_device structure
|
||||
*
|
||||
* Put the driver into low power mode.
|
||||
*
|
||||
* Return: 0 always
|
||||
*/
|
||||
static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
|
||||
{
|
||||
struct platform_device *pdev = container_of(_dev,
|
||||
struct platform_device, dev);
|
||||
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable(xi2c->clk);
|
||||
xi2c->suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_resume - Resume from suspend
|
||||
* @_dev: Address of the platform_device structure
|
||||
*
|
||||
* Resume operation after suspend.
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused cdns_i2c_resume(struct device *_dev)
|
||||
{
|
||||
struct platform_device *pdev = container_of(_dev,
|
||||
struct platform_device, dev);
|
||||
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(xi2c->clk);
|
||||
if (ret) {
|
||||
dev_err(_dev, "Cannot enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
xi2c->suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
|
||||
cdns_i2c_resume);
|
||||
|
||||
/**
|
||||
* cdns_i2c_probe - Platform registration call
|
||||
* @pdev: Handle to the platform device structure
|
||||
*
|
||||
* This function does all the memory allocation and registration for the i2c
|
||||
* device. User can modify the address mode to 10 bit address mode using the
|
||||
* ioctl call with option I2C_TENBIT.
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise
|
||||
*/
|
||||
static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *r_mem;
|
||||
struct cdns_i2c *id;
|
||||
int ret;
|
||||
|
||||
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
|
||||
if (!id)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, id);
|
||||
|
||||
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
|
||||
if (IS_ERR(id->membase))
|
||||
return PTR_ERR(id->membase);
|
||||
|
||||
id->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
id->adap.dev.of_node = pdev->dev.of_node;
|
||||
id->adap.algo = &cdns_i2c_algo;
|
||||
id->adap.timeout = CDNS_I2C_TIMEOUT;
|
||||
id->adap.retries = 3; /* Default retry value. */
|
||||
id->adap.algo_data = id;
|
||||
id->adap.dev.parent = &pdev->dev;
|
||||
init_completion(&id->xfer_done);
|
||||
snprintf(id->adap.name, sizeof(id->adap.name),
|
||||
"Cadence I2C at %08lx", (unsigned long)r_mem->start);
|
||||
|
||||
id->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(id->clk)) {
|
||||
dev_err(&pdev->dev, "input clock not found.\n");
|
||||
return PTR_ERR(id->clk);
|
||||
}
|
||||
ret = clk_prepare_enable(id->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
|
||||
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
|
||||
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
|
||||
id->input_clk = clk_get_rate(id->clk);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&id->i2c_clk);
|
||||
if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))
|
||||
id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
|
||||
|
||||
cdns_i2c_writereg(CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS,
|
||||
CDNS_I2C_CR_OFFSET);
|
||||
|
||||
ret = cdns_i2c_setclk(id->input_clk, id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
|
||||
ret = -EINVAL;
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
|
||||
DRIVER_NAME, id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&id->adap);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "reg adap failed: %d\n", ret);
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
|
||||
id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_dis:
|
||||
clk_disable_unprepare(id->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_remove - Unregister the device after releasing the resources
|
||||
* @pdev: Handle to the platform device structure
|
||||
*
|
||||
* This function frees all the resources allocated to the device.
|
||||
*
|
||||
* Return: 0 always
|
||||
*/
|
||||
static int cdns_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_i2c *id = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&id->adap);
|
||||
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
|
||||
clk_disable_unprepare(id->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cdns_i2c_of_match[] = {
|
||||
{ .compatible = "cdns,i2c-r1p10", },
|
||||
{ /* end of table */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
|
||||
|
||||
static struct platform_driver cdns_i2c_drv = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cdns_i2c_of_match,
|
||||
.pm = &cdns_i2c_dev_pm_ops,
|
||||
},
|
||||
.probe = cdns_i2c_probe,
|
||||
.remove = cdns_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(cdns_i2c_drv);
|
||||
|
||||
MODULE_AUTHOR("Xilinx Inc.");
|
||||
MODULE_DESCRIPTION("Cadence I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -712,7 +712,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
|
||||
adap->algo = &i2c_davinci_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
@ -218,7 +218,7 @@ i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
*
|
||||
* If your hardware is free from tHD;STA issue, try this one.
|
||||
*/
|
||||
return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset;
|
||||
return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
|
||||
else
|
||||
/*
|
||||
* Conditional expression:
|
||||
@ -234,7 +234,8 @@ i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
* The reason why we need to take into account "tf" here,
|
||||
* is the same as described in i2c_dw_scl_lcnt().
|
||||
*/
|
||||
return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset;
|
||||
return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000
|
||||
- 3 + offset;
|
||||
}
|
||||
|
||||
static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
@ -250,7 +251,7 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
* account the fall time of SCL signal (tf). Default tf value
|
||||
* should be 0.3 us, for safety.
|
||||
*/
|
||||
return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
|
||||
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
|
||||
}
|
||||
|
||||
static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
|
||||
@ -287,6 +288,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||
u32 input_clock_khz;
|
||||
u32 hcnt, lcnt;
|
||||
u32 reg;
|
||||
u32 sda_falling_time, scl_falling_time;
|
||||
|
||||
input_clock_khz = dev->get_clk_rate_khz(dev);
|
||||
|
||||
@ -308,15 +310,18 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||
|
||||
/* set standard and fast speed deviders for high/low periods */
|
||||
|
||||
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
|
||||
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
|
||||
|
||||
/* Standard-mode */
|
||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
||||
40, /* tHD;STA = tHIGH = 4.0 us */
|
||||
3, /* tf = 0.3 us */
|
||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
||||
47, /* tLOW = 4.7 us */
|
||||
3, /* tf = 0.3 us */
|
||||
4700, /* tLOW = 4.7 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
||||
/* Allow platforms to specify the ideal HCNT and LCNT values */
|
||||
@ -330,13 +335,13 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||
|
||||
/* Fast-mode */
|
||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
||||
6, /* tHD;STA = tHIGH = 0.6 us */
|
||||
3, /* tf = 0.3 us */
|
||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
||||
13, /* tLOW = 1.3 us */
|
||||
3, /* tf = 0.3 us */
|
||||
1300, /* tLOW = 1.3 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
||||
if (dev->fs_hcnt && dev->fs_lcnt) {
|
||||
|
@ -99,6 +99,8 @@ struct dw_i2c_dev {
|
||||
unsigned int rx_fifo_depth;
|
||||
int rx_outstanding;
|
||||
u32 sda_hold_time;
|
||||
u32 sda_falling_time;
|
||||
u32 scl_falling_time;
|
||||
u16 ss_hcnt;
|
||||
u16 ss_lcnt;
|
||||
u16 fs_hcnt;
|
||||
|
@ -54,6 +54,16 @@ enum dw_pci_ctl_id_t {
|
||||
medfield_3,
|
||||
medfield_4,
|
||||
medfield_5,
|
||||
|
||||
baytrail,
|
||||
};
|
||||
|
||||
struct dw_scl_sda_cfg {
|
||||
u32 ss_hcnt;
|
||||
u32 fs_hcnt;
|
||||
u32 ss_lcnt;
|
||||
u32 fs_lcnt;
|
||||
u32 sda_hold;
|
||||
};
|
||||
|
||||
struct dw_pci_controller {
|
||||
@ -62,12 +72,29 @@ struct dw_pci_controller {
|
||||
u32 tx_fifo_depth;
|
||||
u32 rx_fifo_depth;
|
||||
u32 clk_khz;
|
||||
u32 functionality;
|
||||
struct dw_scl_sda_cfg *scl_sda_cfg;
|
||||
};
|
||||
|
||||
#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
|
||||
DW_IC_CON_SLAVE_DISABLE | \
|
||||
DW_IC_CON_RESTART_EN)
|
||||
|
||||
#define DW_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
|
||||
I2C_FUNC_SMBUS_BYTE | \
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | \
|
||||
I2C_FUNC_SMBUS_WORD_DATA | \
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)
|
||||
|
||||
/* BayTrail HCNT/LCNT/SDA hold time */
|
||||
static struct dw_scl_sda_cfg byt_config = {
|
||||
.ss_hcnt = 0x200,
|
||||
.fs_hcnt = 0x55,
|
||||
.ss_lcnt = 0x200,
|
||||
.fs_lcnt = 0x99,
|
||||
.sda_hold = 0x6,
|
||||
};
|
||||
|
||||
static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
[moorestown_0] = {
|
||||
.bus_num = 0,
|
||||
@ -132,75 +159,40 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[baytrail] = {
|
||||
.bus_num = -1,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 100000,
|
||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||
.scl_sda_cfg = &byt_config,
|
||||
},
|
||||
};
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i2c_dw_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||
struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
|
||||
i2c_dw_disable(i2c);
|
||||
|
||||
err = pci_save_state(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_save_state failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_set_power_state(pdev, PCI_D3hot);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_set_power_state failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
i2c_dw_disable(pci_get_drvdata(pdev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_pci_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||
struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
|
||||
int err;
|
||||
u32 enabled;
|
||||
|
||||
enabled = i2c_dw_is_enabled(i2c);
|
||||
if (enabled)
|
||||
return 0;
|
||||
|
||||
err = pci_set_power_state(pdev, PCI_D0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_set_power_state() failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_restore_state(pdev);
|
||||
|
||||
i2c_dw_init(i2c);
|
||||
return 0;
|
||||
return i2c_dw_init(pci_get_drvdata(pdev));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int i2c_dw_pci_runtime_idle(struct device *dev)
|
||||
{
|
||||
int err = pm_schedule_suspend(dev, 500);
|
||||
dev_dbg(dev, "runtime_idle called\n");
|
||||
|
||||
if (err != 0)
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_dw_pm_ops = {
|
||||
.resume = i2c_dw_pci_resume,
|
||||
.suspend = i2c_dw_pci_suspend,
|
||||
SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume,
|
||||
i2c_dw_pci_runtime_idle)
|
||||
};
|
||||
static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
|
||||
i2c_dw_pci_resume, NULL);
|
||||
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -214,6 +206,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
struct i2c_adapter *adap;
|
||||
int r;
|
||||
struct dw_pci_controller *controller;
|
||||
struct dw_scl_sda_cfg *cfg;
|
||||
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
|
||||
dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__,
|
||||
@ -247,13 +240,18 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||
dev->base = pcim_iomap_table(pdev)[0];
|
||||
dev->dev = &pdev->dev;
|
||||
dev->functionality =
|
||||
I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
dev->functionality = controller->functionality |
|
||||
DW_DEFAULT_FUNCTIONALITY;
|
||||
|
||||
dev->master_cfg = controller->bus_cfg;
|
||||
if (controller->scl_sda_cfg) {
|
||||
cfg = controller->scl_sda_cfg;
|
||||
dev->ss_hcnt = cfg->ss_hcnt;
|
||||
dev->fs_hcnt = cfg->fs_hcnt;
|
||||
dev->ss_lcnt = cfg->ss_lcnt;
|
||||
dev->fs_lcnt = cfg->fs_lcnt;
|
||||
dev->sda_hold_time = cfg->sda_hold;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
@ -270,8 +268,8 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->nr = controller->bus_num;
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
|
||||
adap->nr);
|
||||
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci");
|
||||
|
||||
r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, IRQF_SHARED,
|
||||
adap->name, dev);
|
||||
@ -290,6 +288,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
@ -309,7 +308,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("i2c_designware-pci");
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
|
||||
static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||
/* Moorestown */
|
||||
{ PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
|
||||
@ -321,6 +320,14 @@ static DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
|
||||
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
|
||||
{ PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
|
||||
{ PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
|
||||
/* Baytrail */
|
||||
{ PCI_VDEVICE(INTEL, 0x0F41), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F42), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F43), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F44), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F45), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F46), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x0F47), baytrail },
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
|
||||
|
@ -159,6 +159,13 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
"i2c-sda-hold-time-ns", &ht);
|
||||
dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000,
|
||||
1000000);
|
||||
|
||||
of_property_read_u32(pdev->dev.of_node,
|
||||
"i2c-sda-falling-time-ns",
|
||||
&dev->sda_falling_time);
|
||||
of_property_read_u32(pdev->dev.of_node,
|
||||
"i2c-scl-falling-time-ns",
|
||||
&dev->scl_falling_time);
|
||||
}
|
||||
|
||||
dev->functionality =
|
||||
@ -195,7 +202,7 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
|
||||
sizeof(adap->name));
|
||||
adap->algo = &i2c_dw_algo;
|
||||
|
481
drivers/i2c/busses/i2c-efm32.c
Normal file
481
drivers/i2c/busses/i2c-efm32.c
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Uwe Kleine-Koenig for Pengutronix
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define DRIVER_NAME "efm32-i2c"
|
||||
|
||||
#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
|
||||
|
||||
#define REG_CTRL 0x00
|
||||
#define REG_CTRL_EN 0x00001
|
||||
#define REG_CTRL_SLAVE 0x00002
|
||||
#define REG_CTRL_AUTOACK 0x00004
|
||||
#define REG_CTRL_AUTOSE 0x00008
|
||||
#define REG_CTRL_AUTOSN 0x00010
|
||||
#define REG_CTRL_ARBDIS 0x00020
|
||||
#define REG_CTRL_GCAMEN 0x00040
|
||||
#define REG_CTRL_CLHR__MASK 0x00300
|
||||
#define REG_CTRL_BITO__MASK 0x03000
|
||||
#define REG_CTRL_BITO_OFF 0x00000
|
||||
#define REG_CTRL_BITO_40PCC 0x01000
|
||||
#define REG_CTRL_BITO_80PCC 0x02000
|
||||
#define REG_CTRL_BITO_160PCC 0x03000
|
||||
#define REG_CTRL_GIBITO 0x08000
|
||||
#define REG_CTRL_CLTO__MASK 0x70000
|
||||
#define REG_CTRL_CLTO_OFF 0x00000
|
||||
|
||||
#define REG_CMD 0x04
|
||||
#define REG_CMD_START 0x00001
|
||||
#define REG_CMD_STOP 0x00002
|
||||
#define REG_CMD_ACK 0x00004
|
||||
#define REG_CMD_NACK 0x00008
|
||||
#define REG_CMD_CONT 0x00010
|
||||
#define REG_CMD_ABORT 0x00020
|
||||
#define REG_CMD_CLEARTX 0x00040
|
||||
#define REG_CMD_CLEARPC 0x00080
|
||||
|
||||
#define REG_STATE 0x08
|
||||
#define REG_STATE_BUSY 0x00001
|
||||
#define REG_STATE_MASTER 0x00002
|
||||
#define REG_STATE_TRANSMITTER 0x00004
|
||||
#define REG_STATE_NACKED 0x00008
|
||||
#define REG_STATE_BUSHOLD 0x00010
|
||||
#define REG_STATE_STATE__MASK 0x000e0
|
||||
#define REG_STATE_STATE_IDLE 0x00000
|
||||
#define REG_STATE_STATE_WAIT 0x00020
|
||||
#define REG_STATE_STATE_START 0x00040
|
||||
#define REG_STATE_STATE_ADDR 0x00060
|
||||
#define REG_STATE_STATE_ADDRACK 0x00080
|
||||
#define REG_STATE_STATE_DATA 0x000a0
|
||||
#define REG_STATE_STATE_DATAACK 0x000c0
|
||||
|
||||
#define REG_STATUS 0x0c
|
||||
#define REG_STATUS_PSTART 0x00001
|
||||
#define REG_STATUS_PSTOP 0x00002
|
||||
#define REG_STATUS_PACK 0x00004
|
||||
#define REG_STATUS_PNACK 0x00008
|
||||
#define REG_STATUS_PCONT 0x00010
|
||||
#define REG_STATUS_PABORT 0x00020
|
||||
#define REG_STATUS_TXC 0x00040
|
||||
#define REG_STATUS_TXBL 0x00080
|
||||
#define REG_STATUS_RXDATAV 0x00100
|
||||
|
||||
#define REG_CLKDIV 0x10
|
||||
#define REG_CLKDIV_DIV__MASK 0x001ff
|
||||
#define REG_CLKDIV_DIV(div) MASK_VAL(REG_CLKDIV_DIV__MASK, (div))
|
||||
|
||||
#define REG_SADDR 0x14
|
||||
#define REG_SADDRMASK 0x18
|
||||
#define REG_RXDATA 0x1c
|
||||
#define REG_RXDATAP 0x20
|
||||
#define REG_TXDATA 0x24
|
||||
#define REG_IF 0x28
|
||||
#define REG_IF_START 0x00001
|
||||
#define REG_IF_RSTART 0x00002
|
||||
#define REG_IF_ADDR 0x00004
|
||||
#define REG_IF_TXC 0x00008
|
||||
#define REG_IF_TXBL 0x00010
|
||||
#define REG_IF_RXDATAV 0x00020
|
||||
#define REG_IF_ACK 0x00040
|
||||
#define REG_IF_NACK 0x00080
|
||||
#define REG_IF_MSTOP 0x00100
|
||||
#define REG_IF_ARBLOST 0x00200
|
||||
#define REG_IF_BUSERR 0x00400
|
||||
#define REG_IF_BUSHOLD 0x00800
|
||||
#define REG_IF_TXOF 0x01000
|
||||
#define REG_IF_RXUF 0x02000
|
||||
#define REG_IF_BITO 0x04000
|
||||
#define REG_IF_CLTO 0x08000
|
||||
#define REG_IF_SSTOP 0x10000
|
||||
|
||||
#define REG_IFS 0x2c
|
||||
#define REG_IFC 0x30
|
||||
#define REG_IFC__MASK 0x1ffcf
|
||||
|
||||
#define REG_IEN 0x34
|
||||
|
||||
#define REG_ROUTE 0x38
|
||||
#define REG_ROUTE_SDAPEN 0x00001
|
||||
#define REG_ROUTE_SCLPEN 0x00002
|
||||
#define REG_ROUTE_LOCATION__MASK 0x00700
|
||||
#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
|
||||
|
||||
struct efm32_i2c_ddata {
|
||||
struct i2c_adapter adapter;
|
||||
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
u8 location;
|
||||
unsigned long frequency;
|
||||
|
||||
/* transfer data */
|
||||
struct completion done;
|
||||
struct i2c_msg *msgs;
|
||||
size_t num_msgs;
|
||||
size_t current_word, current_msg;
|
||||
int retval;
|
||||
};
|
||||
|
||||
static u32 efm32_i2c_read32(struct efm32_i2c_ddata *ddata, unsigned offset)
|
||||
{
|
||||
return readl(ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_i2c_write32(struct efm32_i2c_ddata *ddata,
|
||||
unsigned offset, u32 value)
|
||||
{
|
||||
writel(value, ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_i2c_send_next_msg(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
|
||||
efm32_i2c_write32(ddata, REG_TXDATA, cur_msg->addr << 1 |
|
||||
(cur_msg->flags & I2C_M_RD ? 1 : 0));
|
||||
}
|
||||
|
||||
static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
if (ddata->current_word >= cur_msg->len) {
|
||||
/* cur_msg completely transferred */
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg += 1;
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
}
|
||||
} else {
|
||||
efm32_i2c_write32(ddata, REG_TXDATA,
|
||||
cur_msg->buf[ddata->current_word++]);
|
||||
}
|
||||
}
|
||||
|
||||
static void efm32_i2c_recv_next_byte(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
cur_msg->buf[ddata->current_word] = efm32_i2c_read32(ddata, REG_RXDATA);
|
||||
ddata->current_word += 1;
|
||||
if (ddata->current_word >= cur_msg->len) {
|
||||
/* cur_msg completely transferred */
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg += 1;
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_NACK);
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
}
|
||||
} else {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t efm32_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = dev_id;
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
u32 irqflag = efm32_i2c_read32(ddata, REG_IF);
|
||||
u32 state = efm32_i2c_read32(ddata, REG_STATE);
|
||||
|
||||
efm32_i2c_write32(ddata, REG_IFC, irqflag & REG_IFC__MASK);
|
||||
|
||||
switch (state & REG_STATE_STATE__MASK) {
|
||||
case REG_STATE_STATE_IDLE:
|
||||
/* arbitration lost? */
|
||||
ddata->retval = -EAGAIN;
|
||||
complete(&ddata->done);
|
||||
break;
|
||||
case REG_STATE_STATE_WAIT:
|
||||
/*
|
||||
* huh, this shouldn't happen.
|
||||
* Reset hardware state and get out
|
||||
*/
|
||||
ddata->retval = -EIO;
|
||||
efm32_i2c_write32(ddata, REG_CMD,
|
||||
REG_CMD_STOP | REG_CMD_ABORT |
|
||||
REG_CMD_CLEARTX | REG_CMD_CLEARPC);
|
||||
complete(&ddata->done);
|
||||
break;
|
||||
case REG_STATE_STATE_START:
|
||||
/* "caller" is expected to send an address */
|
||||
break;
|
||||
case REG_STATE_STATE_ADDR:
|
||||
/* wait for Ack or NAck of slave */
|
||||
break;
|
||||
case REG_STATE_STATE_ADDRACK:
|
||||
if (state & REG_STATE_NACKED) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
ddata->retval = -ENXIO;
|
||||
complete(&ddata->done);
|
||||
} else if (cur_msg->flags & I2C_M_RD) {
|
||||
/* wait for slave to send first data byte */
|
||||
} else {
|
||||
efm32_i2c_send_next_byte(ddata);
|
||||
}
|
||||
break;
|
||||
case REG_STATE_STATE_DATA:
|
||||
if (cur_msg->flags & I2C_M_RD) {
|
||||
efm32_i2c_recv_next_byte(ddata);
|
||||
} else {
|
||||
/* wait for Ack or Nack of slave */
|
||||
}
|
||||
break;
|
||||
case REG_STATE_STATE_DATAACK:
|
||||
if (state & REG_STATE_NACKED) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_byte(ddata);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int efm32_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
|
||||
if (ddata->msgs)
|
||||
return -EBUSY;
|
||||
|
||||
ddata->msgs = msgs;
|
||||
ddata->num_msgs = num;
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg = 0;
|
||||
ddata->retval = -EIO;
|
||||
|
||||
reinit_completion(&ddata->done);
|
||||
|
||||
dev_dbg(&ddata->adapter.dev, "state: %08x, status: %08x\n",
|
||||
efm32_i2c_read32(ddata, REG_STATE),
|
||||
efm32_i2c_read32(ddata, REG_STATUS));
|
||||
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
|
||||
wait_for_completion(&ddata->done);
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs)
|
||||
ret = ddata->num_msgs;
|
||||
else
|
||||
ret = ddata->retval;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 efm32_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm efm32_i2c_algo = {
|
||||
.master_xfer = efm32_i2c_master_xfer,
|
||||
.functionality = efm32_i2c_functionality,
|
||||
};
|
||||
|
||||
static u32 efm32_i2c_get_configured_location(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
u32 reg = efm32_i2c_read32(ddata, REG_ROUTE);
|
||||
|
||||
return (reg & REG_ROUTE_LOCATION__MASK) >>
|
||||
__ffs(REG_ROUTE_LOCATION__MASK);
|
||||
}
|
||||
|
||||
static int efm32_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata;
|
||||
struct resource *res;
|
||||
unsigned long rate;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 location, frequency;
|
||||
int ret;
|
||||
u32 clkdiv;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata) {
|
||||
dev_dbg(&pdev->dev, "failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
init_completion(&ddata->done);
|
||||
strlcpy(ddata->adapter.name, pdev->name, sizeof(ddata->adapter.name));
|
||||
ddata->adapter.owner = THIS_MODULE;
|
||||
ddata->adapter.algo = &efm32_i2c_algo;
|
||||
ddata->adapter.dev.parent = &pdev->dev;
|
||||
ddata->adapter.dev.of_node = pdev->dev.of_node;
|
||||
i2c_set_adapdata(&ddata->adapter, ddata);
|
||||
|
||||
ddata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ddata->clk)) {
|
||||
ret = PTR_ERR(ddata->clk);
|
||||
dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to determine base address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (resource_size(res) < 0x42) {
|
||||
dev_err(&pdev->dev, "memory resource too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq (%d)\n", ret);
|
||||
if (!ret)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ddata->irq = ret;
|
||||
|
||||
ret = clk_prepare_enable(ddata->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "efm32,location", &location);
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||
} else {
|
||||
/* default to location configured in hardware */
|
||||
location = efm32_i2c_get_configured_location(ddata);
|
||||
|
||||
dev_info(&pdev->dev, "fall back to location %u\n", location);
|
||||
}
|
||||
|
||||
ddata->location = location;
|
||||
|
||||
ret = of_property_read_u32(np, "clock-frequency", &frequency);
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using frequency %u\n", frequency);
|
||||
} else {
|
||||
frequency = 100000;
|
||||
dev_info(&pdev->dev, "defaulting to 100 kHz\n");
|
||||
}
|
||||
ddata->frequency = frequency;
|
||||
|
||||
rate = clk_get_rate(ddata->clk);
|
||||
if (!rate) {
|
||||
dev_err(&pdev->dev, "there is no input clock available\n");
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
clkdiv = DIV_ROUND_UP(rate, 8 * ddata->frequency) - 1;
|
||||
if (clkdiv >= 0x200) {
|
||||
dev_err(&pdev->dev,
|
||||
"input clock too fast (%lu) to divide down to bus freq (%lu)",
|
||||
rate, ddata->frequency);
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "input clock = %lu, bus freq = %lu, clkdiv = %lu\n",
|
||||
rate, ddata->frequency, (unsigned long)clkdiv);
|
||||
efm32_i2c_write32(ddata, REG_CLKDIV, REG_CLKDIV_DIV(clkdiv));
|
||||
|
||||
efm32_i2c_write32(ddata, REG_ROUTE, REG_ROUTE_SDAPEN |
|
||||
REG_ROUTE_SCLPEN |
|
||||
REG_ROUTE_LOCATION(ddata->location));
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CTRL, REG_CTRL_EN |
|
||||
REG_CTRL_BITO_160PCC | 0 * REG_CTRL_GIBITO);
|
||||
|
||||
efm32_i2c_write32(ddata, REG_IFC, REG_IFC__MASK);
|
||||
efm32_i2c_write32(ddata, REG_IEN, REG_IF_TXC | REG_IF_ACK | REG_IF_NACK
|
||||
| REG_IF_ARBLOST | REG_IF_BUSERR | REG_IF_RXDATAV);
|
||||
|
||||
/* to make bus idle */
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ABORT);
|
||||
|
||||
ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&ddata->adapter);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add i2c adapter (%d)\n", ret);
|
||||
free_irq(ddata->irq, ddata);
|
||||
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int efm32_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&ddata->adapter);
|
||||
free_irq(ddata->irq, ddata);
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id efm32_i2c_dt_ids[] = {
|
||||
{
|
||||
.compatible = "energymicro,efm32-i2c",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, efm32_i2c_dt_ids);
|
||||
|
||||
static struct platform_driver efm32_i2c_driver = {
|
||||
.probe = efm32_i2c_probe,
|
||||
.remove = efm32_i2c_remove,
|
||||
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = efm32_i2c_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(efm32_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("EFM32 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
@ -186,7 +186,7 @@ static DEFINE_MUTEX(pch_mutex);
|
||||
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
|
||||
#define PCI_DEVICE_ID_ML7831_I2C 0x8817
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(pch_pcidev_id) = {
|
||||
static const struct pci_device_id pch_pcidev_id[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
|
||||
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
|
||||
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, },
|
||||
|
@ -566,7 +566,7 @@ static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
|
||||
static int exynos5_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
|
||||
struct exynos5_i2c *i2c = adap->algo_data;
|
||||
int i = 0, ret = 0, stop = 0;
|
||||
|
||||
if (i2c->suspended) {
|
||||
@ -715,6 +715,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos5_i2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
@ -745,6 +746,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(exynos5_i2c_dev_pm_ops, exynos5_i2c_suspend_noirq,
|
||||
exynos5_i2c_resume_noirq);
|
||||
|
@ -94,6 +94,9 @@ static int of_i2c_gpio_get_pins(struct device_node *np,
|
||||
*sda_pin = of_get_gpio(np, 0);
|
||||
*scl_pin = of_get_gpio(np, 1);
|
||||
|
||||
if (*sda_pin == -EPROBE_DEFER || *scl_pin == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) {
|
||||
pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n",
|
||||
np->full_name, *sda_pin, *scl_pin);
|
||||
|
@ -104,7 +104,7 @@ static struct i2c_adapter hydra_adap = {
|
||||
.algo_data = &hydra_bit_data,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(hydra_ids) = {
|
||||
static const struct pci_device_id hydra_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_HYDRA) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -60,6 +60,7 @@
|
||||
Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes
|
||||
Coleto Creek (PCH) 0x23b0 32 hard yes yes yes
|
||||
Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
|
||||
BayTrail (SOC) 0x0f12 32 hard yes yes yes
|
||||
|
||||
Features supported by this driver:
|
||||
Software PEC no
|
||||
@ -161,6 +162,7 @@
|
||||
STATUS_ERROR_FLAGS)
|
||||
|
||||
/* Older devices have their ID defined in <linux/pci_ids.h> */
|
||||
#define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12
|
||||
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
|
||||
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
|
||||
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
|
||||
@ -789,7 +791,7 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||
.functionality = i801_func,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
|
||||
static const struct pci_device_id i801_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) },
|
||||
@ -822,6 +824,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -182,7 +182,7 @@ struct ismt_priv {
|
||||
/**
|
||||
* ismt_ids - PCI device IDs supported by this driver
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {
|
||||
static const struct pci_device_id ismt_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mv643xx_i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -97,7 +98,6 @@ enum {
|
||||
enum {
|
||||
MV64XXX_I2C_ACTION_INVALID,
|
||||
MV64XXX_I2C_ACTION_CONTINUE,
|
||||
MV64XXX_I2C_ACTION_SEND_START,
|
||||
MV64XXX_I2C_ACTION_SEND_RESTART,
|
||||
MV64XXX_I2C_ACTION_OFFLOAD_RESTART,
|
||||
MV64XXX_I2C_ACTION_SEND_ADDR_1,
|
||||
@ -148,6 +148,8 @@ struct mv64xxx_i2c_data {
|
||||
bool offload_enabled;
|
||||
/* 5us delay in order to avoid repeated start timing violation */
|
||||
bool errata_delay;
|
||||
struct reset_control *rstc;
|
||||
bool irq_clear_inverted;
|
||||
};
|
||||
|
||||
static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
|
||||
@ -176,11 +178,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
|
||||
{
|
||||
u32 dir = 0;
|
||||
|
||||
drv_data->msg = msg;
|
||||
drv_data->byte_posn = 0;
|
||||
drv_data->bytes_left = msg->len;
|
||||
drv_data->aborting = 0;
|
||||
drv_data->rc = 0;
|
||||
drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
|
||||
MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
|
||||
|
||||
@ -206,11 +203,6 @@ static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
|
||||
if (!drv_data->offload_enabled)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
drv_data->msg = msg;
|
||||
drv_data->byte_posn = 0;
|
||||
drv_data->bytes_left = msg->len;
|
||||
drv_data->aborting = 0;
|
||||
drv_data->rc = 0;
|
||||
/* Only regular transactions can be offloaded */
|
||||
if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0)
|
||||
return -EINVAL;
|
||||
@ -419,6 +411,23 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
|
||||
}
|
||||
}
|
||||
|
||||
static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data)
|
||||
{
|
||||
drv_data->msg = drv_data->msgs;
|
||||
drv_data->byte_posn = 0;
|
||||
drv_data->bytes_left = drv_data->msg->len;
|
||||
drv_data->aborting = 0;
|
||||
drv_data->rc = 0;
|
||||
|
||||
/* Can we offload this msg ? */
|
||||
if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
|
||||
/* No, switch to standard path */
|
||||
mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
|
||||
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
|
||||
drv_data->reg_base + drv_data->reg_offsets.control);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
|
||||
{
|
||||
@ -435,14 +444,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
|
||||
|
||||
drv_data->msgs++;
|
||||
drv_data->num_msgs--;
|
||||
if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
|
||||
drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START;
|
||||
writel(drv_data->cntl_bits,
|
||||
drv_data->reg_base + drv_data->reg_offsets.control);
|
||||
mv64xxx_i2c_send_start(drv_data);
|
||||
|
||||
/* Setup for the next message */
|
||||
mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
|
||||
}
|
||||
if (drv_data->errata_delay)
|
||||
udelay(5);
|
||||
|
||||
@ -459,16 +462,6 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
|
||||
drv_data->reg_base + drv_data->reg_offsets.control);
|
||||
break;
|
||||
|
||||
case MV64XXX_I2C_ACTION_SEND_START:
|
||||
/* Can we offload this msg ? */
|
||||
if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
|
||||
/* No, switch to standard path */
|
||||
mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
|
||||
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
|
||||
drv_data->reg_base + drv_data->reg_offsets.control);
|
||||
}
|
||||
break;
|
||||
|
||||
case MV64XXX_I2C_ACTION_SEND_ADDR_1:
|
||||
writel(drv_data->addr1,
|
||||
drv_data->reg_base + drv_data->reg_offsets.data);
|
||||
@ -566,6 +559,11 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
|
||||
status = readl(drv_data->reg_base + drv_data->reg_offsets.status);
|
||||
mv64xxx_i2c_fsm(drv_data, status);
|
||||
mv64xxx_i2c_do_action(drv_data);
|
||||
|
||||
if (drv_data->irq_clear_inverted)
|
||||
writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_IFLG,
|
||||
drv_data->reg_base + drv_data->reg_offsets.control);
|
||||
|
||||
rc = IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||
@ -626,12 +624,11 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
|
||||
|
||||
spin_lock_irqsave(&drv_data->lock, flags);
|
||||
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
|
||||
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
|
||||
|
||||
drv_data->send_stop = is_last;
|
||||
drv_data->block = 1;
|
||||
mv64xxx_i2c_do_action(drv_data);
|
||||
mv64xxx_i2c_send_start(drv_data);
|
||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||
|
||||
mv64xxx_i2c_wait_for_completion(drv_data);
|
||||
@ -685,6 +682,7 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {
|
||||
*/
|
||||
static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
|
||||
{ .compatible = "allwinner,sun4i-i2c", .data = &mv64xxx_i2c_regs_sun4i},
|
||||
{ .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i},
|
||||
{ .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
|
||||
{ .compatible = "marvell,mv78230-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
|
||||
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
|
||||
@ -759,6 +757,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
||||
}
|
||||
drv_data->irq = irq_of_parse_and_map(np, 0);
|
||||
|
||||
drv_data->rstc = devm_reset_control_get_optional(dev, NULL);
|
||||
if (IS_ERR(drv_data->rstc)) {
|
||||
if (PTR_ERR(drv_data->rstc) == -EPROBE_DEFER) {
|
||||
rc = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
reset_control_deassert(drv_data->rstc);
|
||||
}
|
||||
|
||||
/* Its not yet defined how timeouts will be specified in device tree.
|
||||
* So hard code the value to 1 second.
|
||||
*/
|
||||
@ -783,6 +791,10 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
||||
drv_data->offload_enabled = false;
|
||||
drv_data->errata_delay = true;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
|
||||
drv_data->irq_clear_inverted = true;
|
||||
|
||||
out:
|
||||
return rc;
|
||||
#endif
|
||||
@ -845,13 +857,13 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
}
|
||||
if (drv_data->irq < 0) {
|
||||
rc = -ENXIO;
|
||||
goto exit_clk;
|
||||
goto exit_reset;
|
||||
}
|
||||
|
||||
drv_data->adapter.dev.parent = &pd->dev;
|
||||
drv_data->adapter.algo = &mv64xxx_i2c_algo;
|
||||
drv_data->adapter.owner = THIS_MODULE;
|
||||
drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
|
||||
drv_data->adapter.nr = pd->id;
|
||||
drv_data->adapter.dev.of_node = pd->dev.of_node;
|
||||
platform_set_drvdata(pd, drv_data);
|
||||
@ -865,7 +877,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
dev_err(&drv_data->adapter.dev,
|
||||
"mv64xxx: Can't register intr handler irq%d: %d\n",
|
||||
drv_data->irq, rc);
|
||||
goto exit_clk;
|
||||
goto exit_reset;
|
||||
} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
|
||||
dev_err(&drv_data->adapter.dev,
|
||||
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
|
||||
@ -876,6 +888,9 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
|
||||
exit_free_irq:
|
||||
free_irq(drv_data->irq, drv_data);
|
||||
exit_reset:
|
||||
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
||||
reset_control_assert(drv_data->rstc);
|
||||
exit_clk:
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
/* Not all platforms have a clk */
|
||||
@ -894,6 +909,8 @@ mv64xxx_i2c_remove(struct platform_device *dev)
|
||||
|
||||
i2c_del_adapter(&drv_data->adapter);
|
||||
free_irq(drv_data->irq, drv_data);
|
||||
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
||||
reset_control_assert(drv_data->rstc);
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
/* Not all platforms have a clk */
|
||||
if (!IS_ERR(drv_data->clk)) {
|
||||
|
@ -806,7 +806,6 @@ static int mxs_i2c_probe(struct platform_device *pdev)
|
||||
struct mxs_i2c_dev *i2c;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
resource_size_t res_size;
|
||||
int err, irq;
|
||||
|
||||
i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL);
|
||||
@ -819,18 +818,13 @@ static int mxs_i2c_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(i2c->regs))
|
||||
return PTR_ERR(i2c->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!res || irq < 0)
|
||||
return -ENOENT;
|
||||
|
||||
res_size = resource_size(res);
|
||||
if (!devm_request_mem_region(dev, res->start, res_size, res->name))
|
||||
return -EBUSY;
|
||||
|
||||
i2c->regs = devm_ioremap_nocache(dev, res->start, res_size);
|
||||
if (!i2c->regs)
|
||||
return -EBUSY;
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c);
|
||||
if (err)
|
||||
|
@ -306,7 +306,7 @@ static struct i2c_algorithm smbus_algorithm = {
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
|
||||
static const struct pci_device_id nforce2_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) },
|
||||
|
@ -110,22 +110,6 @@ enum i2c_freq_mode {
|
||||
I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nmk_i2c_controller - client specific controller configuration
|
||||
* @clk_freq: clock frequency for the operation mode
|
||||
* @tft: Tx FIFO Threshold in bytes
|
||||
* @rft: Rx FIFO Threshold in bytes
|
||||
* @timeout Slave response timeout(ms)
|
||||
* @sm: speed mode
|
||||
*/
|
||||
struct nmk_i2c_controller {
|
||||
u32 clk_freq;
|
||||
unsigned char tft;
|
||||
unsigned char rft;
|
||||
int timeout;
|
||||
enum i2c_freq_mode sm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct i2c_vendor_data - per-vendor variations
|
||||
* @has_mtdws: variant has the MTDWS bit
|
||||
@ -174,12 +158,15 @@ struct i2c_nmk_client {
|
||||
* @irq: interrupt line for the controller.
|
||||
* @virtbase: virtual io memory area.
|
||||
* @clk: hardware i2c block clock.
|
||||
* @cfg: machine provided controller configuration.
|
||||
* @cli: holder of client specific data.
|
||||
* @clk_freq: clock frequency for the operation mode
|
||||
* @tft: Tx FIFO Threshold in bytes
|
||||
* @rft: Rx FIFO Threshold in bytes
|
||||
* @timeout Slave response timeout (ms)
|
||||
* @sm: speed mode
|
||||
* @stop: stop condition.
|
||||
* @xfer_complete: acknowledge completion for a I2C message.
|
||||
* @result: controller propogated result.
|
||||
* @busy: Busy doing transfer.
|
||||
*/
|
||||
struct nmk_i2c_dev {
|
||||
struct i2c_vendor_data *vendor;
|
||||
@ -188,12 +175,15 @@ struct nmk_i2c_dev {
|
||||
int irq;
|
||||
void __iomem *virtbase;
|
||||
struct clk *clk;
|
||||
struct nmk_i2c_controller cfg;
|
||||
struct i2c_nmk_client cli;
|
||||
u32 clk_freq;
|
||||
unsigned char tft;
|
||||
unsigned char rft;
|
||||
int timeout;
|
||||
enum i2c_freq_mode sm;
|
||||
int stop;
|
||||
struct completion xfer_complete;
|
||||
int result;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
/* controller's abort causes */
|
||||
@ -386,7 +376,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
|
||||
* slsu = cycles / (1000000000 / f) + 1
|
||||
*/
|
||||
ns = DIV_ROUND_UP_ULL(1000000000ULL, i2c_clk);
|
||||
switch (dev->cfg.sm) {
|
||||
switch (dev->sm) {
|
||||
case I2C_FREQ_MODE_FAST:
|
||||
case I2C_FREQ_MODE_FAST_PLUS:
|
||||
slsu = DIV_ROUND_UP(100, ns); /* Fast */
|
||||
@ -409,7 +399,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
|
||||
* 2 whereas it is 3 for fast and fastplus mode of
|
||||
* operation. TODO - high speed support.
|
||||
*/
|
||||
div = (dev->cfg.clk_freq > 100000) ? 3 : 2;
|
||||
div = (dev->clk_freq > 100000) ? 3 : 2;
|
||||
|
||||
/*
|
||||
* generate the mask for baud rate counters. The controller
|
||||
@ -419,7 +409,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
|
||||
* so set brcr1 to 0.
|
||||
*/
|
||||
brcr1 = 0 << 16;
|
||||
brcr2 = (i2c_clk/(dev->cfg.clk_freq * div)) & 0xffff;
|
||||
brcr2 = (i2c_clk/(dev->clk_freq * div)) & 0xffff;
|
||||
|
||||
/* set the baud rate counter register */
|
||||
writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
|
||||
@ -430,7 +420,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
|
||||
* TODO - support for fast mode plus (up to 1Mb/s)
|
||||
* and high speed (up to 3.4 Mb/s)
|
||||
*/
|
||||
if (dev->cfg.sm > I2C_FREQ_MODE_FAST) {
|
||||
if (dev->sm > I2C_FREQ_MODE_FAST) {
|
||||
dev_err(&dev->adev->dev,
|
||||
"do not support this mode defaulting to std. mode\n");
|
||||
brcr2 = i2c_clk/(100000 * 2) & 0xffff;
|
||||
@ -438,11 +428,11 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
|
||||
writel(I2C_FREQ_MODE_STANDARD << 4,
|
||||
dev->virtbase + I2C_CR);
|
||||
}
|
||||
writel(dev->cfg.sm << 4, dev->virtbase + I2C_CR);
|
||||
writel(dev->sm << 4, dev->virtbase + I2C_CR);
|
||||
|
||||
/* set the Tx and Rx FIFO threshold */
|
||||
writel(dev->cfg.tft, dev->virtbase + I2C_TFTR);
|
||||
writel(dev->cfg.rft, dev->virtbase + I2C_RFTR);
|
||||
writel(dev->tft, dev->virtbase + I2C_TFTR);
|
||||
writel(dev->rft, dev->virtbase + I2C_RFTR);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -674,28 +664,13 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
|
||||
static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg msgs[], int num_msgs)
|
||||
{
|
||||
int status;
|
||||
int status = 0;
|
||||
int i;
|
||||
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
|
||||
int j;
|
||||
|
||||
dev->busy = true;
|
||||
|
||||
pm_runtime_get_sync(&dev->adev->dev);
|
||||
|
||||
status = clk_prepare_enable(dev->clk);
|
||||
if (status) {
|
||||
dev_err(&dev->adev->dev, "can't prepare_enable clock\n");
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
/* Optionaly enable pins to be muxed in and configured */
|
||||
pinctrl_pm_select_default_state(&dev->adev->dev);
|
||||
|
||||
status = init_hw(dev);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* Attempt three times to send the message queue */
|
||||
for (j = 0; j < 3; j++) {
|
||||
/* setup the i2c controller */
|
||||
@ -716,16 +691,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
clk_disable_unprepare(dev->clk);
|
||||
out_clk:
|
||||
/* Optionally let pins go into idle state */
|
||||
pinctrl_pm_select_idle_state(&dev->adev->dev);
|
||||
|
||||
pm_runtime_put_sync(&dev->adev->dev);
|
||||
|
||||
dev->busy = false;
|
||||
|
||||
/* return the no. messages processed */
|
||||
if (status)
|
||||
return status;
|
||||
@ -909,22 +876,15 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int nmk_i2c_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int nmk_i2c_suspend_late(struct device *dev)
|
||||
{
|
||||
struct amba_device *adev = to_amba_device(dev);
|
||||
struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
|
||||
|
||||
if (nmk_i2c->busy)
|
||||
return -EBUSY;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nmk_i2c_resume(struct device *dev)
|
||||
static int nmk_i2c_resume_early(struct device *dev)
|
||||
{
|
||||
/* First go to the default state */
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
@ -933,19 +893,48 @@ static int nmk_i2c_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define nmk_i2c_suspend NULL
|
||||
#define nmk_i2c_resume NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We use noirq so that we suspend late and resume before the wakeup interrupt
|
||||
* to ensure that we do the !pm_runtime_suspended() check in resume before
|
||||
* there has been a regular pm runtime resume (via pm_runtime_get_sync()).
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
static int nmk_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct amba_device *adev = to_amba_device(dev);
|
||||
struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
|
||||
|
||||
clk_disable_unprepare(nmk_i2c->clk);
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nmk_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct amba_device *adev = to_amba_device(dev);
|
||||
struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(nmk_i2c->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't prepare_enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = init_hw(nmk_i2c);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(nmk_i2c->clk);
|
||||
pinctrl_pm_select_idle_state(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops nmk_i2c_pm = {
|
||||
.suspend_noirq = nmk_i2c_suspend,
|
||||
.resume_noirq = nmk_i2c_resume,
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early)
|
||||
SET_PM_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,
|
||||
nmk_i2c_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
|
||||
@ -958,118 +947,98 @@ static const struct i2c_algorithm nmk_i2c_algo = {
|
||||
.functionality = nmk_i2c_functionality
|
||||
};
|
||||
|
||||
static struct nmk_i2c_controller u8500_i2c = {
|
||||
.tft = 1, /* Tx FIFO threshold */
|
||||
.rft = 8, /* Rx FIFO threshold */
|
||||
.clk_freq = 400000, /* fast mode operation */
|
||||
.timeout = 200, /* Slave response timeout(ms) */
|
||||
.sm = I2C_FREQ_MODE_FAST,
|
||||
};
|
||||
|
||||
static void nmk_i2c_of_probe(struct device_node *np,
|
||||
struct nmk_i2c_controller *pdata)
|
||||
struct nmk_i2c_dev *nmk)
|
||||
{
|
||||
of_property_read_u32(np, "clock-frequency", &pdata->clk_freq);
|
||||
/* Default to 100 kHz if no frequency is given in the node */
|
||||
if (of_property_read_u32(np, "clock-frequency", &nmk->clk_freq))
|
||||
nmk->clk_freq = 100000;
|
||||
|
||||
/* This driver only supports 'standard' and 'fast' modes of operation. */
|
||||
if (pdata->clk_freq <= 100000)
|
||||
pdata->sm = I2C_FREQ_MODE_STANDARD;
|
||||
if (nmk->clk_freq <= 100000)
|
||||
nmk->sm = I2C_FREQ_MODE_STANDARD;
|
||||
else
|
||||
pdata->sm = I2C_FREQ_MODE_FAST;
|
||||
nmk->sm = I2C_FREQ_MODE_FAST;
|
||||
nmk->tft = 1; /* Tx FIFO threshold */
|
||||
nmk->rft = 8; /* Rx FIFO threshold */
|
||||
nmk->timeout = 200; /* Slave response timeout(ms) */
|
||||
}
|
||||
|
||||
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nmk_i2c_controller *pdata = dev_get_platdata(&adev->dev);
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
struct nmk_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct i2c_vendor_data *vendor = id->data;
|
||||
u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1;
|
||||
|
||||
if (!pdata) {
|
||||
if (np) {
|
||||
pdata = devm_kzalloc(&adev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
ret = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
/* Provide the default configuration as a base. */
|
||||
memcpy(pdata, &u8500_i2c, sizeof(struct nmk_i2c_controller));
|
||||
nmk_i2c_of_probe(np, pdata);
|
||||
} else
|
||||
/* No i2c configuration found, using the default. */
|
||||
pdata = &u8500_i2c;
|
||||
}
|
||||
|
||||
if (pdata->tft > max_fifo_threshold) {
|
||||
dev_warn(&adev->dev, "requested TX FIFO threshold %u, adjusted down to %u\n",
|
||||
pdata->tft, max_fifo_threshold);
|
||||
pdata->tft = max_fifo_threshold;
|
||||
}
|
||||
|
||||
if (pdata->rft > max_fifo_threshold) {
|
||||
dev_warn(&adev->dev, "requested RX FIFO threshold %u, adjusted down to %u\n",
|
||||
pdata->rft, max_fifo_threshold);
|
||||
pdata->rft = max_fifo_threshold;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
|
||||
dev = devm_kzalloc(&adev->dev, sizeof(struct nmk_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
dev_err(&adev->dev, "cannot allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
dev->vendor = vendor;
|
||||
dev->busy = false;
|
||||
dev->adev = adev;
|
||||
nmk_i2c_of_probe(np, dev);
|
||||
|
||||
if (dev->tft > max_fifo_threshold) {
|
||||
dev_warn(&adev->dev, "requested TX FIFO threshold %u, adjusted down to %u\n",
|
||||
dev->tft, max_fifo_threshold);
|
||||
dev->tft = max_fifo_threshold;
|
||||
}
|
||||
|
||||
if (dev->rft > max_fifo_threshold) {
|
||||
dev_warn(&adev->dev, "requested RX FIFO threshold %u, adjusted down to %u\n",
|
||||
dev->rft, max_fifo_threshold);
|
||||
dev->rft = max_fifo_threshold;
|
||||
}
|
||||
|
||||
amba_set_drvdata(adev, dev);
|
||||
|
||||
/* Select default pin state */
|
||||
pinctrl_pm_select_default_state(&adev->dev);
|
||||
/* If possible, let's go to idle until the first transfer */
|
||||
pinctrl_pm_select_idle_state(&adev->dev);
|
||||
|
||||
dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
|
||||
if (!dev->virtbase) {
|
||||
dev->virtbase = devm_ioremap(&adev->dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (IS_ERR(dev->virtbase)) {
|
||||
ret = -ENOMEM;
|
||||
goto err_no_ioremap;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
dev->irq = adev->irq[0];
|
||||
ret = request_irq(dev->irq, i2c_irq_handler, 0,
|
||||
ret = devm_request_irq(&adev->dev, dev->irq, i2c_irq_handler, 0,
|
||||
DRIVER_NAME, dev);
|
||||
if (ret) {
|
||||
dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq);
|
||||
goto err_irq;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
pm_suspend_ignore_children(&adev->dev, true);
|
||||
|
||||
dev->clk = clk_get(&adev->dev, NULL);
|
||||
dev->clk = devm_clk_get(&adev->dev, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
dev_err(&adev->dev, "could not get i2c clock\n");
|
||||
ret = PTR_ERR(dev->clk);
|
||||
goto err_no_clk;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dev->clk);
|
||||
if (ret) {
|
||||
dev_err(&adev->dev, "can't prepare_enable clock\n");
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
init_hw(dev);
|
||||
|
||||
adap = &dev->adap;
|
||||
adap->dev.of_node = np;
|
||||
adap->dev.parent = &adev->dev;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
|
||||
adap->algo = &nmk_i2c_algo;
|
||||
adap->timeout = msecs_to_jiffies(pdata->timeout);
|
||||
adap->timeout = msecs_to_jiffies(dev->timeout);
|
||||
snprintf(adap->name, sizeof(adap->name),
|
||||
"Nomadik I2C at %pR", &adev->res);
|
||||
|
||||
/* fetch the controller configuration from machine */
|
||||
dev->cfg.clk_freq = pdata->clk_freq;
|
||||
dev->cfg.tft = pdata->tft;
|
||||
dev->cfg.rft = pdata->rft;
|
||||
dev->cfg.sm = pdata->sm;
|
||||
|
||||
i2c_set_adapdata(adap, dev);
|
||||
|
||||
dev_info(&adev->dev,
|
||||
@ -1079,21 +1048,15 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret) {
|
||||
dev_err(&adev->dev, "failed to add adapter\n");
|
||||
goto err_add_adap;
|
||||
goto err_no_adap;
|
||||
}
|
||||
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_adap:
|
||||
clk_put(dev->clk);
|
||||
err_no_clk:
|
||||
free_irq(dev->irq, dev);
|
||||
err_irq:
|
||||
iounmap(dev->virtbase);
|
||||
err_no_ioremap:
|
||||
kfree(dev);
|
||||
err_no_adap:
|
||||
clk_disable_unprepare(dev->clk);
|
||||
err_no_mem:
|
||||
|
||||
return ret;
|
||||
@ -1110,13 +1073,9 @@ static int nmk_i2c_remove(struct amba_device *adev)
|
||||
clear_all_interrupts(dev);
|
||||
/* disable the controller */
|
||||
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(dev->virtbase);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
clk_put(dev->clk);
|
||||
pm_runtime_disable(&adev->dev);
|
||||
kfree(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ static const struct i2c_algorithm ocores_algorithm = {
|
||||
static struct i2c_adapter ocores_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "i2c-ocores",
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED,
|
||||
.algo = &ocores_algorithm,
|
||||
};
|
||||
|
||||
|
@ -636,7 +636,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
int r;
|
||||
|
||||
r = pm_runtime_get_sync(dev->dev);
|
||||
if (IS_ERR_VALUE(r))
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = omap_i2c_wait_for_bb(dev);
|
||||
@ -1155,7 +1155,7 @@ omap_i2c_probe(struct platform_device *pdev)
|
||||
pm_runtime_use_autosuspend(dev->dev);
|
||||
|
||||
r = pm_runtime_get_sync(dev->dev);
|
||||
if (IS_ERR_VALUE(r))
|
||||
if (r < 0)
|
||||
goto err_free_mem;
|
||||
|
||||
/*
|
||||
@ -1238,7 +1238,7 @@ omap_i2c_probe(struct platform_device *pdev)
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
|
||||
adap->algo = &omap_i2c_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
@ -1276,7 +1276,7 @@ static int omap_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
||||
|
@ -401,7 +401,7 @@ static void pasemi_smb_remove(struct pci_dev *dev)
|
||||
kfree(smbus);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(pasemi_smb_ids) = {
|
||||
static const struct pci_device_id pasemi_smb_ids[] = {
|
||||
{ PCI_DEVICE(0x1959, 0xa003) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -540,7 +540,7 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||
.functionality = piix4_func,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = {
|
||||
static const struct pci_device_id piix4_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) },
|
||||
|
@ -148,7 +148,7 @@ static void ce4100_i2c_remove(struct pci_dev *dev)
|
||||
kfree(sds);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(ce4100_i2c_devices) = {
|
||||
static const struct pci_device_id ce4100_i2c_devices[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
|
||||
{ },
|
||||
};
|
||||
|
768
drivers/i2c/busses/i2c-qup.c
Normal file
768
drivers/i2c/busses/i2c-qup.c
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014, Sony Mobile Communications AB.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* QUP Registers */
|
||||
#define QUP_CONFIG 0x000
|
||||
#define QUP_STATE 0x004
|
||||
#define QUP_IO_MODE 0x008
|
||||
#define QUP_SW_RESET 0x00c
|
||||
#define QUP_OPERATIONAL 0x018
|
||||
#define QUP_ERROR_FLAGS 0x01c
|
||||
#define QUP_ERROR_FLAGS_EN 0x020
|
||||
#define QUP_HW_VERSION 0x030
|
||||
#define QUP_MX_OUTPUT_CNT 0x100
|
||||
#define QUP_OUT_FIFO_BASE 0x110
|
||||
#define QUP_MX_WRITE_CNT 0x150
|
||||
#define QUP_MX_INPUT_CNT 0x200
|
||||
#define QUP_MX_READ_CNT 0x208
|
||||
#define QUP_IN_FIFO_BASE 0x218
|
||||
#define QUP_I2C_CLK_CTL 0x400
|
||||
#define QUP_I2C_STATUS 0x404
|
||||
|
||||
/* QUP States and reset values */
|
||||
#define QUP_RESET_STATE 0
|
||||
#define QUP_RUN_STATE 1
|
||||
#define QUP_PAUSE_STATE 3
|
||||
#define QUP_STATE_MASK 3
|
||||
|
||||
#define QUP_STATE_VALID BIT(2)
|
||||
#define QUP_I2C_MAST_GEN BIT(4)
|
||||
|
||||
#define QUP_OPERATIONAL_RESET 0x000ff0
|
||||
#define QUP_I2C_STATUS_RESET 0xfffffc
|
||||
|
||||
/* QUP OPERATIONAL FLAGS */
|
||||
#define QUP_I2C_NACK_FLAG BIT(3)
|
||||
#define QUP_OUT_NOT_EMPTY BIT(4)
|
||||
#define QUP_IN_NOT_EMPTY BIT(5)
|
||||
#define QUP_OUT_FULL BIT(6)
|
||||
#define QUP_OUT_SVC_FLAG BIT(8)
|
||||
#define QUP_IN_SVC_FLAG BIT(9)
|
||||
#define QUP_MX_OUTPUT_DONE BIT(10)
|
||||
#define QUP_MX_INPUT_DONE BIT(11)
|
||||
|
||||
/* I2C mini core related values */
|
||||
#define QUP_CLOCK_AUTO_GATE BIT(13)
|
||||
#define I2C_MINI_CORE (2 << 8)
|
||||
#define I2C_N_VAL 15
|
||||
/* Most significant word offset in FIFO port */
|
||||
#define QUP_MSW_SHIFT (I2C_N_VAL + 1)
|
||||
|
||||
/* Packing/Unpacking words in FIFOs, and IO modes */
|
||||
#define QUP_OUTPUT_BLK_MODE (1 << 10)
|
||||
#define QUP_INPUT_BLK_MODE (1 << 12)
|
||||
#define QUP_UNPACK_EN BIT(14)
|
||||
#define QUP_PACK_EN BIT(15)
|
||||
|
||||
#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN)
|
||||
|
||||
#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03)
|
||||
#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07)
|
||||
#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03)
|
||||
#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07)
|
||||
|
||||
/* QUP tags */
|
||||
#define QUP_TAG_START (1 << 8)
|
||||
#define QUP_TAG_DATA (2 << 8)
|
||||
#define QUP_TAG_STOP (3 << 8)
|
||||
#define QUP_TAG_REC (4 << 8)
|
||||
|
||||
/* Status, Error flags */
|
||||
#define I2C_STATUS_WR_BUFFER_FULL BIT(0)
|
||||
#define I2C_STATUS_BUS_ACTIVE BIT(8)
|
||||
#define I2C_STATUS_ERROR_MASK 0x38000fc
|
||||
#define QUP_STATUS_ERROR_FLAGS 0x7c
|
||||
|
||||
#define QUP_READ_LIMIT 256
|
||||
|
||||
struct qup_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct i2c_adapter adap;
|
||||
|
||||
int clk_ctl;
|
||||
int out_fifo_sz;
|
||||
int in_fifo_sz;
|
||||
int out_blk_sz;
|
||||
int in_blk_sz;
|
||||
|
||||
unsigned long one_byte_t;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
/* Current posion in user message buffer */
|
||||
int pos;
|
||||
/* I2C protocol errors */
|
||||
u32 bus_err;
|
||||
/* QUP core errors */
|
||||
u32 qup_err;
|
||||
|
||||
struct completion xfer;
|
||||
};
|
||||
|
||||
static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct qup_i2c_dev *qup = dev;
|
||||
u32 bus_err;
|
||||
u32 qup_err;
|
||||
u32 opflags;
|
||||
|
||||
bus_err = readl(qup->base + QUP_I2C_STATUS);
|
||||
qup_err = readl(qup->base + QUP_ERROR_FLAGS);
|
||||
opflags = readl(qup->base + QUP_OPERATIONAL);
|
||||
|
||||
if (!qup->msg) {
|
||||
/* Clear Error interrupt */
|
||||
writel(QUP_RESET_STATE, qup->base + QUP_STATE);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
bus_err &= I2C_STATUS_ERROR_MASK;
|
||||
qup_err &= QUP_STATUS_ERROR_FLAGS;
|
||||
|
||||
if (qup_err) {
|
||||
/* Clear Error interrupt */
|
||||
writel(qup_err, qup->base + QUP_ERROR_FLAGS);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (bus_err) {
|
||||
/* Clear Error interrupt */
|
||||
writel(QUP_RESET_STATE, qup->base + QUP_STATE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (opflags & QUP_IN_SVC_FLAG)
|
||||
writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
|
||||
|
||||
if (opflags & QUP_OUT_SVC_FLAG)
|
||||
writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
|
||||
|
||||
done:
|
||||
qup->qup_err = qup_err;
|
||||
qup->bus_err = bus_err;
|
||||
complete(&qup->xfer);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup,
|
||||
u32 req_state, u32 req_mask)
|
||||
{
|
||||
int retries = 1;
|
||||
u32 state;
|
||||
|
||||
/*
|
||||
* State transition takes 3 AHB clocks cycles + 3 I2C master clock
|
||||
* cycles. So retry once after a 1uS delay.
|
||||
*/
|
||||
do {
|
||||
state = readl(qup->base + QUP_STATE);
|
||||
|
||||
if (state & QUP_STATE_VALID &&
|
||||
(state & req_mask) == req_state)
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
} while (retries--);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state)
|
||||
{
|
||||
return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
|
||||
}
|
||||
|
||||
static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup)
|
||||
{
|
||||
return qup_i2c_poll_state_mask(qup, 0, 0);
|
||||
}
|
||||
|
||||
static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup)
|
||||
{
|
||||
return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
|
||||
}
|
||||
|
||||
static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
|
||||
{
|
||||
if (qup_i2c_poll_state_valid(qup) != 0)
|
||||
return -EIO;
|
||||
|
||||
writel(state, qup->base + QUP_STATE);
|
||||
|
||||
if (qup_i2c_poll_state(qup, state) != 0)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 opflags;
|
||||
u32 status;
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
|
||||
for (;;) {
|
||||
opflags = readl(qup->base + QUP_OPERATIONAL);
|
||||
status = readl(qup->base + QUP_I2C_STATUS);
|
||||
|
||||
if (!(opflags & QUP_OUT_NOT_EMPTY) &&
|
||||
!(status & I2C_STATUS_BUS_ACTIVE))
|
||||
return 0;
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
usleep_range(qup->one_byte_t, qup->one_byte_t * 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
/* Number of entries to shift out, including the start */
|
||||
int total = msg->len + 1;
|
||||
|
||||
if (total < qup->out_fifo_sz) {
|
||||
/* FIFO mode */
|
||||
writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
|
||||
writel(total, qup->base + QUP_MX_WRITE_CNT);
|
||||
} else {
|
||||
/* BLOCK mode (transfer data on chunks) */
|
||||
writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
|
||||
qup->base + QUP_IO_MODE);
|
||||
writel(total, qup->base + QUP_MX_OUTPUT_CNT);
|
||||
}
|
||||
}
|
||||
|
||||
static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
u32 addr = msg->addr << 1;
|
||||
u32 qup_tag;
|
||||
u32 opflags;
|
||||
int idx;
|
||||
u32 val;
|
||||
|
||||
if (qup->pos == 0) {
|
||||
val = QUP_TAG_START | addr;
|
||||
idx = 1;
|
||||
} else {
|
||||
val = 0;
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
while (qup->pos < msg->len) {
|
||||
/* Check that there's space in the FIFO for our pair */
|
||||
opflags = readl(qup->base + QUP_OPERATIONAL);
|
||||
if (opflags & QUP_OUT_FULL)
|
||||
break;
|
||||
|
||||
if (qup->pos == msg->len - 1)
|
||||
qup_tag = QUP_TAG_STOP;
|
||||
else
|
||||
qup_tag = QUP_TAG_DATA;
|
||||
|
||||
if (idx & 1)
|
||||
val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT;
|
||||
else
|
||||
val = qup_tag | msg->buf[qup->pos];
|
||||
|
||||
/* Write out the pair and the last odd value */
|
||||
if (idx & 1 || qup->pos == msg->len - 1)
|
||||
writel(val, qup->base + QUP_OUT_FIFO_BASE);
|
||||
|
||||
qup->pos++;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
unsigned long left;
|
||||
int ret;
|
||||
|
||||
qup->msg = msg;
|
||||
qup->pos = 0;
|
||||
|
||||
enable_irq(qup->irq);
|
||||
|
||||
qup_i2c_set_write_mode(qup, msg);
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
|
||||
|
||||
do {
|
||||
ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
qup_i2c_issue_write(qup, msg);
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
left = wait_for_completion_timeout(&qup->xfer, HZ);
|
||||
if (!left) {
|
||||
writel(1, qup->base + QUP_SW_RESET);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (qup->bus_err || qup->qup_err) {
|
||||
if (qup->bus_err & QUP_I2C_NACK_FLAG)
|
||||
dev_err(qup->dev, "NACK from %x\n", msg->addr);
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
} while (qup->pos < msg->len);
|
||||
|
||||
/* Wait for the outstanding data in the fifo to drain */
|
||||
ret = qup_i2c_wait_writeready(qup);
|
||||
|
||||
err:
|
||||
disable_irq(qup->irq);
|
||||
qup->msg = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
|
||||
{
|
||||
if (len < qup->in_fifo_sz) {
|
||||
/* FIFO mode */
|
||||
writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
|
||||
writel(len, qup->base + QUP_MX_READ_CNT);
|
||||
} else {
|
||||
/* BLOCK mode (transfer data on chunks) */
|
||||
writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
|
||||
qup->base + QUP_IO_MODE);
|
||||
writel(len, qup->base + QUP_MX_INPUT_CNT);
|
||||
}
|
||||
}
|
||||
|
||||
static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
u32 addr, len, val;
|
||||
|
||||
addr = (msg->addr << 1) | 1;
|
||||
|
||||
/* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
|
||||
len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
|
||||
|
||||
val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
|
||||
writel(val, qup->base + QUP_OUT_FIFO_BASE);
|
||||
}
|
||||
|
||||
|
||||
static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
u32 opflags;
|
||||
u32 val = 0;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; qup->pos < msg->len; idx++) {
|
||||
if ((idx & 1) == 0) {
|
||||
/* Check that FIFO have data */
|
||||
opflags = readl(qup->base + QUP_OPERATIONAL);
|
||||
if (!(opflags & QUP_IN_NOT_EMPTY))
|
||||
break;
|
||||
|
||||
/* Reading 2 words at time */
|
||||
val = readl(qup->base + QUP_IN_FIFO_BASE);
|
||||
|
||||
msg->buf[qup->pos++] = val & 0xFF;
|
||||
} else {
|
||||
msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||
{
|
||||
unsigned long left;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The QUP block will issue a NACK and STOP on the bus when reaching
|
||||
* the end of the read, the length of the read is specified as one byte
|
||||
* which limits the possible read to 256 (QUP_READ_LIMIT) bytes.
|
||||
*/
|
||||
if (msg->len > QUP_READ_LIMIT) {
|
||||
dev_err(qup->dev, "HW not capable of reads over %d bytes\n",
|
||||
QUP_READ_LIMIT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qup->msg = msg;
|
||||
qup->pos = 0;
|
||||
|
||||
enable_irq(qup->irq);
|
||||
|
||||
qup_i2c_set_read_mode(qup, msg->len);
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
qup_i2c_issue_read(qup, msg);
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
do {
|
||||
left = wait_for_completion_timeout(&qup->xfer, HZ);
|
||||
if (!left) {
|
||||
writel(1, qup->base + QUP_SW_RESET);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (qup->bus_err || qup->qup_err) {
|
||||
if (qup->bus_err & QUP_I2C_NACK_FLAG)
|
||||
dev_err(qup->dev, "NACK from %x\n", msg->addr);
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
qup_i2c_read_fifo(qup, msg);
|
||||
} while (qup->pos < msg->len);
|
||||
|
||||
err:
|
||||
disable_irq(qup->irq);
|
||||
qup->msg = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qup_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg msgs[],
|
||||
int num)
|
||||
{
|
||||
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
|
||||
int ret, idx;
|
||||
|
||||
ret = pm_runtime_get_sync(qup->dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
writel(1, qup->base + QUP_SW_RESET);
|
||||
ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Configure QUP as I2C mini core */
|
||||
writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
|
||||
|
||||
for (idx = 0; idx < num; idx++) {
|
||||
if (msgs[idx].len == 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qup_i2c_poll_state_i2c_master(qup)) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (msgs[idx].flags & I2C_M_RD)
|
||||
ret = qup_i2c_read_one(qup, &msgs[idx]);
|
||||
else
|
||||
ret = qup_i2c_write_one(qup, &msgs[idx]);
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = num;
|
||||
out:
|
||||
|
||||
pm_runtime_mark_last_busy(qup->dev);
|
||||
pm_runtime_put_autosuspend(qup->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 qup_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm qup_i2c_algo = {
|
||||
.master_xfer = qup_i2c_xfer,
|
||||
.functionality = qup_i2c_func,
|
||||
};
|
||||
|
||||
static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
|
||||
{
|
||||
clk_prepare_enable(qup->clk);
|
||||
clk_prepare_enable(qup->pclk);
|
||||
}
|
||||
|
||||
static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
|
||||
{
|
||||
u32 config;
|
||||
|
||||
qup_i2c_change_state(qup, QUP_RESET_STATE);
|
||||
clk_disable_unprepare(qup->clk);
|
||||
config = readl(qup->base + QUP_CONFIG);
|
||||
config |= QUP_CLOCK_AUTO_GATE;
|
||||
writel(config, qup->base + QUP_CONFIG);
|
||||
clk_disable_unprepare(qup->pclk);
|
||||
}
|
||||
|
||||
static int qup_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
static const int blk_sizes[] = {4, 16, 32};
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct qup_i2c_dev *qup;
|
||||
unsigned long one_bit_t;
|
||||
struct resource *res;
|
||||
u32 io_mode, hw_ver, size;
|
||||
int ret, fs_div, hs_div;
|
||||
int src_clk_freq;
|
||||
u32 clk_freq = 100000;
|
||||
|
||||
qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
|
||||
if (!qup)
|
||||
return -ENOMEM;
|
||||
|
||||
qup->dev = &pdev->dev;
|
||||
init_completion(&qup->xfer);
|
||||
platform_set_drvdata(pdev, qup);
|
||||
|
||||
of_property_read_u32(node, "clock-frequency", &clk_freq);
|
||||
|
||||
/* We support frequencies up to FAST Mode (400KHz) */
|
||||
if (!clk_freq || clk_freq > 400000) {
|
||||
dev_err(qup->dev, "clock frequency not supported %d\n",
|
||||
clk_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
qup->base = devm_ioremap_resource(qup->dev, res);
|
||||
if (IS_ERR(qup->base))
|
||||
return PTR_ERR(qup->base);
|
||||
|
||||
qup->irq = platform_get_irq(pdev, 0);
|
||||
if (qup->irq < 0) {
|
||||
dev_err(qup->dev, "No IRQ defined\n");
|
||||
return qup->irq;
|
||||
}
|
||||
|
||||
qup->clk = devm_clk_get(qup->dev, "core");
|
||||
if (IS_ERR(qup->clk)) {
|
||||
dev_err(qup->dev, "Could not get core clock\n");
|
||||
return PTR_ERR(qup->clk);
|
||||
}
|
||||
|
||||
qup->pclk = devm_clk_get(qup->dev, "iface");
|
||||
if (IS_ERR(qup->pclk)) {
|
||||
dev_err(qup->dev, "Could not get iface clock\n");
|
||||
return PTR_ERR(qup->pclk);
|
||||
}
|
||||
|
||||
qup_i2c_enable_clocks(qup);
|
||||
|
||||
/*
|
||||
* Bootloaders might leave a pending interrupt on certain QUP's,
|
||||
* so we reset the core before registering for interrupts.
|
||||
*/
|
||||
writel(1, qup->base + QUP_SW_RESET);
|
||||
ret = qup_i2c_poll_state_valid(qup);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
|
||||
IRQF_TRIGGER_HIGH, "i2c_qup", qup);
|
||||
if (ret) {
|
||||
dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
|
||||
goto fail;
|
||||
}
|
||||
disable_irq(qup->irq);
|
||||
|
||||
hw_ver = readl(qup->base + QUP_HW_VERSION);
|
||||
dev_dbg(qup->dev, "Revision %x\n", hw_ver);
|
||||
|
||||
io_mode = readl(qup->base + QUP_IO_MODE);
|
||||
|
||||
/*
|
||||
* The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
|
||||
* associated with each byte written/received
|
||||
*/
|
||||
size = QUP_OUTPUT_BLOCK_SIZE(io_mode);
|
||||
if (size >= ARRAY_SIZE(blk_sizes))
|
||||
return -EIO;
|
||||
qup->out_blk_sz = blk_sizes[size] / 2;
|
||||
|
||||
size = QUP_INPUT_BLOCK_SIZE(io_mode);
|
||||
if (size >= ARRAY_SIZE(blk_sizes))
|
||||
return -EIO;
|
||||
qup->in_blk_sz = blk_sizes[size] / 2;
|
||||
|
||||
size = QUP_OUTPUT_FIFO_SIZE(io_mode);
|
||||
qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
|
||||
|
||||
size = QUP_INPUT_FIFO_SIZE(io_mode);
|
||||
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
|
||||
|
||||
src_clk_freq = clk_get_rate(qup->clk);
|
||||
fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
|
||||
hs_div = 3;
|
||||
qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
|
||||
|
||||
/*
|
||||
* Time it takes for a byte to be clocked out on the bus.
|
||||
* Each byte takes 9 clock cycles (8 bits + 1 ack).
|
||||
*/
|
||||
one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
|
||||
qup->one_byte_t = one_bit_t * 9;
|
||||
|
||||
dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
||||
qup->in_blk_sz, qup->in_fifo_sz,
|
||||
qup->out_blk_sz, qup->out_fifo_sz);
|
||||
|
||||
i2c_set_adapdata(&qup->adap, qup);
|
||||
qup->adap.algo = &qup_i2c_algo;
|
||||
qup->adap.dev.parent = qup->dev;
|
||||
qup->adap.dev.of_node = pdev->dev.of_node;
|
||||
strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
|
||||
|
||||
ret = i2c_add_adapter(&qup->adap);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
|
||||
pm_runtime_use_autosuspend(qup->dev);
|
||||
pm_runtime_set_active(qup->dev);
|
||||
pm_runtime_enable(qup->dev);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
qup_i2c_disable_clocks(qup);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qup_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
|
||||
|
||||
disable_irq(qup->irq);
|
||||
qup_i2c_disable_clocks(qup);
|
||||
i2c_del_adapter(&qup->adap);
|
||||
pm_runtime_disable(qup->dev);
|
||||
pm_runtime_set_suspended(qup->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int qup_i2c_pm_suspend_runtime(struct device *device)
|
||||
{
|
||||
struct qup_i2c_dev *qup = dev_get_drvdata(device);
|
||||
|
||||
dev_dbg(device, "pm_runtime: suspending...\n");
|
||||
qup_i2c_disable_clocks(qup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qup_i2c_pm_resume_runtime(struct device *device)
|
||||
{
|
||||
struct qup_i2c_dev *qup = dev_get_drvdata(device);
|
||||
|
||||
dev_dbg(device, "pm_runtime: resuming...\n");
|
||||
qup_i2c_enable_clocks(qup);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int qup_i2c_suspend(struct device *device)
|
||||
{
|
||||
qup_i2c_pm_suspend_runtime(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qup_i2c_resume(struct device *device)
|
||||
{
|
||||
qup_i2c_pm_resume_runtime(device);
|
||||
pm_runtime_mark_last_busy(device);
|
||||
pm_request_autosuspend(device);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(
|
||||
qup_i2c_suspend,
|
||||
qup_i2c_resume)
|
||||
SET_RUNTIME_PM_OPS(
|
||||
qup_i2c_pm_suspend_runtime,
|
||||
qup_i2c_pm_resume_runtime,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id qup_i2c_dt_match[] = {
|
||||
{ .compatible = "qcom,i2c-qup-v1.1.1" },
|
||||
{ .compatible = "qcom,i2c-qup-v2.1.1" },
|
||||
{ .compatible = "qcom,i2c-qup-v2.2.1" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
|
||||
|
||||
static struct platform_driver qup_i2c_driver = {
|
||||
.probe = qup_i2c_probe,
|
||||
.remove = qup_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c_qup",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &qup_i2c_qup_pm_ops,
|
||||
.of_match_table = qup_i2c_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qup_i2c_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:i2c_qup");
|
@ -638,6 +638,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,i2c-r8a7790", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,i2c-r8a7791", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
|
||||
@ -691,7 +692,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
adap = &priv->adap;
|
||||
adap->nr = pdev->id;
|
||||
adap->algo = &rcar_i2c_algo;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
|
||||
adap->retries = 3;
|
||||
adap->dev.parent = dev;
|
||||
adap->dev.of_node = dev->of_node;
|
||||
|
@ -601,6 +601,31 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the bus so that we won't get any interrupts from now on, or try
|
||||
* to drive any lines. This is the default state when we don't have
|
||||
* anything to send/receive.
|
||||
*
|
||||
* If there is an event on the bus, or we have a pre-existing event at
|
||||
* kernel boot time, we may not notice the event and the I2C controller
|
||||
* will lock the bus with the I2C clock line low indefinitely.
|
||||
*/
|
||||
static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/* Stop driving the I2C pins */
|
||||
tmp = readl(i2c->regs + S3C2410_IICSTAT);
|
||||
tmp &= ~S3C2410_IICSTAT_TXRXEN;
|
||||
writel(tmp, i2c->regs + S3C2410_IICSTAT);
|
||||
|
||||
/* We don't expect any interrupts now, and don't want send acks */
|
||||
tmp = readl(i2c->regs + S3C2410_IICCON);
|
||||
tmp &= ~(S3C2410_IICCON_IRQEN | S3C2410_IICCON_IRQPEND |
|
||||
S3C2410_IICCON_ACKEN);
|
||||
writel(tmp, i2c->regs + S3C2410_IICCON);
|
||||
}
|
||||
|
||||
|
||||
/* s3c24xx_i2c_set_master
|
||||
*
|
||||
@ -735,7 +760,11 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
||||
|
||||
s3c24xx_i2c_wait_idle(i2c);
|
||||
|
||||
s3c24xx_i2c_disable_bus(i2c);
|
||||
|
||||
out:
|
||||
i2c->state = STATE_IDLE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1004,7 +1033,6 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
|
||||
|
||||
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
||||
{
|
||||
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
|
||||
struct s3c2410_platform_i2c *pdata;
|
||||
unsigned int freq;
|
||||
|
||||
@ -1018,12 +1046,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
||||
|
||||
dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
|
||||
|
||||
writel(iicon, i2c->regs + S3C2410_IICCON);
|
||||
writel(0, i2c->regs + S3C2410_IICCON);
|
||||
writel(0, i2c->regs + S3C2410_IICSTAT);
|
||||
|
||||
/* we need to work out the divisors for the clock... */
|
||||
|
||||
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
|
||||
writel(0, i2c->regs + S3C2410_IICCON);
|
||||
dev_err(i2c->dev, "cannot meet bus frequency required\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1031,7 +1059,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
||||
/* todo - check that the i2c lines aren't being dragged anywhere */
|
||||
|
||||
dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
|
||||
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
|
||||
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02x\n",
|
||||
readl(i2c->regs + S3C2410_IICCON));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1106,7 +1135,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
||||
i2c->adap.owner = THIS_MODULE;
|
||||
i2c->adap.algo = &s3c24xx_i2c_algorithm;
|
||||
i2c->adap.retries = 2;
|
||||
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
|
||||
i2c->tx_setup = 50;
|
||||
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
|
@ -312,7 +312,7 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
|
||||
goto out;
|
||||
}
|
||||
adap = &siic->adapter;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
siic->base = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||
|
@ -369,7 +369,7 @@ static struct i2c_adapter sis5595_adapter = {
|
||||
.algo = &smbus_algorithm,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(sis5595_ids) = {
|
||||
static const struct pci_device_id sis5595_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -510,7 +510,7 @@ static struct i2c_adapter sis630_adapter = {
|
||||
.retries = 3
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
|
||||
static const struct pci_device_id sis630_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
|
||||
|
@ -244,7 +244,7 @@ static struct i2c_adapter sis96x_adapter = {
|
||||
.algo = &smbus_algorithm,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(sis96x_ids) = {
|
||||
static const struct pci_device_id sis96x_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -574,7 +574,7 @@ static irqreturn_t st_i2c_isr_thread(int irq, void *data)
|
||||
writel_relaxed(it, i2c_dev->base + SSC_IEN);
|
||||
|
||||
st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG);
|
||||
c->result = -EIO;
|
||||
c->result = -EAGAIN;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -911,7 +911,7 @@ static int stu300_probe(struct platform_device *pdev)
|
||||
adap = &dev->adapter;
|
||||
adap->owner = THIS_MODULE;
|
||||
/* DDC class but actually often used for more generic I2C */
|
||||
adap->class = I2C_CLASS_DDC;
|
||||
adap->class = I2C_CLASS_DDC | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "ST Microelectronics DDC I2C adapter",
|
||||
sizeof(adap->name));
|
||||
adap->nr = bus_nr;
|
||||
|
@ -794,7 +794,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
|
||||
i2c_dev->adapter.owner = THIS_MODULE;
|
||||
i2c_dev->adapter.class = I2C_CLASS_HWMON;
|
||||
i2c_dev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
|
||||
strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
|
||||
sizeof(i2c_dev->adapter.name));
|
||||
i2c_dev->adapter.algo = &tegra_i2c_algo;
|
||||
|
@ -88,7 +88,7 @@ static struct i2c_adapter vt586b_adapter = {
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(vt586b_ids) = {
|
||||
static const struct pci_device_id vt586b_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) },
|
||||
{ 0, }
|
||||
};
|
||||
|
@ -442,7 +442,7 @@ release_region:
|
||||
return error;
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(vt596_ids) = {
|
||||
static const struct pci_device_id vt596_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3),
|
||||
.driver_data = SMBBA1 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3),
|
||||
|
@ -684,7 +684,7 @@ static const struct i2c_algorithm xiic_algorithm = {
|
||||
static struct i2c_adapter xiic_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED,
|
||||
.algo = &xiic_algorithm,
|
||||
};
|
||||
|
||||
|
@ -556,7 +556,7 @@ static struct platform_driver scx200_pci_driver = {
|
||||
.remove = scx200_remove,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(scx200_isa) = {
|
||||
static const struct pci_device_id scx200_isa[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
|
||||
{ 0, }
|
||||
|
@ -48,10 +48,13 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/i2c.h>
|
||||
|
||||
/* core_lock protects i2c_adapter_idr, and guarantees
|
||||
that device detection, deletion of detected devices, and attach_adapter
|
||||
@ -62,6 +65,18 @@ static DEFINE_IDR(i2c_adapter_idr);
|
||||
static struct device_type i2c_client_type;
|
||||
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
||||
|
||||
static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
|
||||
|
||||
void i2c_transfer_trace_reg(void)
|
||||
{
|
||||
static_key_slow_inc(&i2c_trace_msg);
|
||||
}
|
||||
|
||||
void i2c_transfer_trace_unreg(void)
|
||||
{
|
||||
static_key_slow_dec(&i2c_trace_msg);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
@ -1686,6 +1701,7 @@ static void __exit i2c_exit(void)
|
||||
class_compat_unregister(i2c_adapter_compat_class);
|
||||
#endif
|
||||
bus_unregister(&i2c_bus_type);
|
||||
tracepoint_synchronize_unregister();
|
||||
}
|
||||
|
||||
/* We must initialize early, because some subsystems register i2c drivers
|
||||
@ -1716,6 +1732,19 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
unsigned long orig_jiffies;
|
||||
int ret, try;
|
||||
|
||||
/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
|
||||
* enabled. This is an efficient way of keeping the for-loop from
|
||||
* being executed when not needed.
|
||||
*/
|
||||
if (static_key_false(&i2c_trace_msg)) {
|
||||
int i;
|
||||
for (i = 0; i < num; i++)
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
trace_i2c_read(adap, &msgs[i], i);
|
||||
else
|
||||
trace_i2c_write(adap, &msgs[i], i);
|
||||
}
|
||||
|
||||
/* Retry automatically on arbitration loss */
|
||||
orig_jiffies = jiffies;
|
||||
for (ret = 0, try = 0; try <= adap->retries; try++) {
|
||||
@ -1726,6 +1755,14 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
break;
|
||||
}
|
||||
|
||||
if (static_key_false(&i2c_trace_msg)) {
|
||||
int i;
|
||||
for (i = 0; i < ret; i++)
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
trace_i2c_reply(adap, &msgs[i], i);
|
||||
trace_i2c_result(adap, i, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(__i2c_transfer);
|
||||
@ -1941,6 +1978,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
|
||||
struct i2c_client *client;
|
||||
|
||||
/* Detection succeeded, instantiate the device */
|
||||
if (adapter->class & I2C_CLASS_DEPRECATED)
|
||||
dev_warn(&adapter->dev,
|
||||
"This adapter will soon drop class based instantiation of devices. "
|
||||
"Please make sure client 0x%02x gets instantiated by other means. "
|
||||
"Check 'Documentation/i2c/instantiating-devices' for details.\n",
|
||||
info.addr);
|
||||
|
||||
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
|
||||
info.type, info.addr);
|
||||
client = i2c_new_device(adapter, &info);
|
||||
@ -2521,6 +2565,14 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
|
||||
int try;
|
||||
s32 res;
|
||||
|
||||
/* If enabled, the following two tracepoints are conditional on
|
||||
* read_write and protocol.
|
||||
*/
|
||||
trace_smbus_write(adapter, addr, flags, read_write,
|
||||
command, protocol, data);
|
||||
trace_smbus_read(adapter, addr, flags, read_write,
|
||||
command, protocol);
|
||||
|
||||
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
|
||||
|
||||
if (adapter->algo->smbus_xfer) {
|
||||
@ -2541,15 +2593,24 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
|
||||
i2c_unlock_adapter(adapter);
|
||||
|
||||
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
|
||||
return res;
|
||||
goto trace;
|
||||
/*
|
||||
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
|
||||
* implement native support for the SMBus operation.
|
||||
*/
|
||||
}
|
||||
|
||||
return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
|
||||
command, protocol, data);
|
||||
res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
|
||||
command, protocol, data);
|
||||
|
||||
trace:
|
||||
/* If enabled, the reply tracepoint is conditional on read_write. */
|
||||
trace_smbus_reply(adapter, addr, flags, read_write,
|
||||
command, protocol, data);
|
||||
trace_smbus_result(adapter, addr, flags, read_write,
|
||||
command, protocol, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_smbus_xfer);
|
||||
|
||||
|
@ -487,6 +487,7 @@ void i2c_unlock_adapter(struct i2c_adapter *);
|
||||
#define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */
|
||||
#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */
|
||||
#define I2C_CLASS_SPD (1<<7) /* Memory modules */
|
||||
#define I2C_CLASS_DEPRECATED (1<<8) /* Warn users that adapter will stop using classes */
|
||||
|
||||
/* Internal numbers to terminate lists */
|
||||
#define I2C_CLIENT_END 0xfffeU
|
||||
|
145
include/linux/i2c/bfin_twi.h
Normal file
145
include/linux/i2c/bfin_twi.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* i2c-bfin-twi.h - interface to ADI TWI controller
|
||||
*
|
||||
* Copyright 2005-2014 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef __I2C_BFIN_TWI_H__
|
||||
#define __I2C_BFIN_TWI_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/*
|
||||
* ADI twi registers layout
|
||||
*/
|
||||
struct bfin_twi_regs {
|
||||
u16 clkdiv;
|
||||
u16 dummy1;
|
||||
u16 control;
|
||||
u16 dummy2;
|
||||
u16 slave_ctl;
|
||||
u16 dummy3;
|
||||
u16 slave_stat;
|
||||
u16 dummy4;
|
||||
u16 slave_addr;
|
||||
u16 dummy5;
|
||||
u16 master_ctl;
|
||||
u16 dummy6;
|
||||
u16 master_stat;
|
||||
u16 dummy7;
|
||||
u16 master_addr;
|
||||
u16 dummy8;
|
||||
u16 int_stat;
|
||||
u16 dummy9;
|
||||
u16 int_mask;
|
||||
u16 dummy10;
|
||||
u16 fifo_ctl;
|
||||
u16 dummy11;
|
||||
u16 fifo_stat;
|
||||
u16 dummy12;
|
||||
u32 __pad[20];
|
||||
u16 xmt_data8;
|
||||
u16 dummy13;
|
||||
u16 xmt_data16;
|
||||
u16 dummy14;
|
||||
u16 rcv_data8;
|
||||
u16 dummy15;
|
||||
u16 rcv_data16;
|
||||
u16 dummy16;
|
||||
};
|
||||
|
||||
struct bfin_twi_iface {
|
||||
int irq;
|
||||
spinlock_t lock;
|
||||
char read_write;
|
||||
u8 command;
|
||||
u8 *transPtr;
|
||||
int readNum;
|
||||
int writeNum;
|
||||
int cur_mode;
|
||||
int manual_stop;
|
||||
int result;
|
||||
struct i2c_adapter adap;
|
||||
struct completion complete;
|
||||
struct i2c_msg *pmsg;
|
||||
int msg_num;
|
||||
int cur_msg;
|
||||
u16 saved_clkdiv;
|
||||
u16 saved_control;
|
||||
struct bfin_twi_regs __iomem *regs_base;
|
||||
};
|
||||
|
||||
/* ******************** TWO-WIRE INTERFACE (TWI) MASKS ********************/
|
||||
/* TWI_CLKDIV Macros (Use: *pTWI_CLKDIV = CLKLOW(x)|CLKHI(y); ) */
|
||||
#define CLKLOW(x) ((x) & 0xFF) /* Periods Clock Is Held Low */
|
||||
#define CLKHI(y) (((y)&0xFF)<<0x8) /* Periods Before New Clock Low */
|
||||
|
||||
/* TWI_PRESCALE Masks */
|
||||
#define PRESCALE 0x007F /* SCLKs Per Internal Time Reference (10MHz) */
|
||||
#define TWI_ENA 0x0080 /* TWI Enable */
|
||||
#define SCCB 0x0200 /* SCCB Compatibility Enable */
|
||||
|
||||
/* TWI_SLAVE_CTL Masks */
|
||||
#define SEN 0x0001 /* Slave Enable */
|
||||
#define SADD_LEN 0x0002 /* Slave Address Length */
|
||||
#define STDVAL 0x0004 /* Slave Transmit Data Valid */
|
||||
#define NAK 0x0008 /* NAK Generated At Conclusion Of Transfer */
|
||||
#define GEN 0x0010 /* General Call Address Matching Enabled */
|
||||
|
||||
/* TWI_SLAVE_STAT Masks */
|
||||
#define SDIR 0x0001 /* Slave Transfer Direction (RX/TX*) */
|
||||
#define GCALL 0x0002 /* General Call Indicator */
|
||||
|
||||
/* TWI_MASTER_CTL Masks */
|
||||
#define MEN 0x0001 /* Master Mode Enable */
|
||||
#define MADD_LEN 0x0002 /* Master Address Length */
|
||||
#define MDIR 0x0004 /* Master Transmit Direction (RX/TX*) */
|
||||
#define FAST 0x0008 /* Use Fast Mode Timing Specs */
|
||||
#define STOP 0x0010 /* Issue Stop Condition */
|
||||
#define RSTART 0x0020 /* Repeat Start or Stop* At End Of Transfer */
|
||||
#define DCNT 0x3FC0 /* Data Bytes To Transfer */
|
||||
#define SDAOVR 0x4000 /* Serial Data Override */
|
||||
#define SCLOVR 0x8000 /* Serial Clock Override */
|
||||
|
||||
/* TWI_MASTER_STAT Masks */
|
||||
#define MPROG 0x0001 /* Master Transfer In Progress */
|
||||
#define LOSTARB 0x0002 /* Lost Arbitration Indicator (Xfer Aborted) */
|
||||
#define ANAK 0x0004 /* Address Not Acknowledged */
|
||||
#define DNAK 0x0008 /* Data Not Acknowledged */
|
||||
#define BUFRDERR 0x0010 /* Buffer Read Error */
|
||||
#define BUFWRERR 0x0020 /* Buffer Write Error */
|
||||
#define SDASEN 0x0040 /* Serial Data Sense */
|
||||
#define SCLSEN 0x0080 /* Serial Clock Sense */
|
||||
#define BUSBUSY 0x0100 /* Bus Busy Indicator */
|
||||
|
||||
/* TWI_INT_SRC and TWI_INT_ENABLE Masks */
|
||||
#define SINIT 0x0001 /* Slave Transfer Initiated */
|
||||
#define SCOMP 0x0002 /* Slave Transfer Complete */
|
||||
#define SERR 0x0004 /* Slave Transfer Error */
|
||||
#define SOVF 0x0008 /* Slave Overflow */
|
||||
#define MCOMP 0x0010 /* Master Transfer Complete */
|
||||
#define MERR 0x0020 /* Master Transfer Error */
|
||||
#define XMTSERV 0x0040 /* Transmit FIFO Service */
|
||||
#define RCVSERV 0x0080 /* Receive FIFO Service */
|
||||
|
||||
/* TWI_FIFO_CTRL Masks */
|
||||
#define XMTFLUSH 0x0001 /* Transmit Buffer Flush */
|
||||
#define RCVFLUSH 0x0002 /* Receive Buffer Flush */
|
||||
#define XMTINTLEN 0x0004 /* Transmit Buffer Interrupt Length */
|
||||
#define RCVINTLEN 0x0008 /* Receive Buffer Interrupt Length */
|
||||
|
||||
/* TWI_FIFO_STAT Masks */
|
||||
#define XMTSTAT 0x0003 /* Transmit FIFO Status */
|
||||
#define XMT_EMPTY 0x0000 /* Transmit FIFO Empty */
|
||||
#define XMT_HALF 0x0001 /* Transmit FIFO Has 1 Byte To Write */
|
||||
#define XMT_FULL 0x0003 /* Transmit FIFO Full (2 Bytes To Write) */
|
||||
|
||||
#define RCVSTAT 0x000C /* Receive FIFO Status */
|
||||
#define RCV_EMPTY 0x0000 /* Receive FIFO Empty */
|
||||
#define RCV_HALF 0x0004 /* Receive FIFO Has 1 Byte To Read */
|
||||
#define RCV_FULL 0x000C /* Receive FIFO Full (2 Bytes To Read) */
|
||||
|
||||
#endif
|
@ -1,5 +1,4 @@
|
||||
/* arch/arm/plat-s3c/include/plat/iic.h
|
||||
*
|
||||
/*
|
||||
* Copyright 2004-2009 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
@ -10,8 +9,8 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_IIC_H
|
||||
#define __ASM_ARCH_IIC_H __FILE__
|
||||
#ifndef __I2C_S3C2410_H
|
||||
#define __I2C_S3C2410_H __FILE__
|
||||
|
||||
#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */
|
||||
|
||||
@ -76,4 +75,4 @@ extern void s3c_i2c7_cfg_gpio(struct platform_device *dev);
|
||||
|
||||
extern struct s3c2410_platform_i2c default_i2c_data;
|
||||
|
||||
#endif /* __ASM_ARCH_IIC_H */
|
||||
#endif /* __I2C_S3C2410_H */
|
||||
|
372
include/trace/events/i2c.h
Normal file
372
include/trace/events/i2c.h
Normal file
@ -0,0 +1,372 @@
|
||||
/* I2C and SMBUS message transfer tracepoints
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM i2c
|
||||
|
||||
#if !defined(_TRACE_I2C_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_I2C_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
/*
|
||||
* drivers/i2c/i2c-core.c
|
||||
*/
|
||||
extern void i2c_transfer_trace_reg(void);
|
||||
extern void i2c_transfer_trace_unreg(void);
|
||||
|
||||
/*
|
||||
* __i2c_transfer() write request
|
||||
*/
|
||||
TRACE_EVENT_FN(i2c_write,
|
||||
TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg,
|
||||
int num),
|
||||
TP_ARGS(adap, msg, num),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, msg_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u16, len )
|
||||
__dynamic_array(__u8, buf, msg->len) ),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->msg_nr = num;
|
||||
__entry->addr = msg->addr;
|
||||
__entry->flags = msg->flags;
|
||||
__entry->len = msg->len;
|
||||
memcpy(__get_dynamic_array(buf), msg->buf, msg->len);
|
||||
),
|
||||
TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]",
|
||||
__entry->adapter_nr,
|
||||
__entry->msg_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->len,
|
||||
__entry->len, __get_dynamic_array(buf)
|
||||
),
|
||||
i2c_transfer_trace_reg,
|
||||
i2c_transfer_trace_unreg);
|
||||
|
||||
/*
|
||||
* __i2c_transfer() read request
|
||||
*/
|
||||
TRACE_EVENT_FN(i2c_read,
|
||||
TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg,
|
||||
int num),
|
||||
TP_ARGS(adap, msg, num),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, msg_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u16, len )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->msg_nr = num;
|
||||
__entry->addr = msg->addr;
|
||||
__entry->flags = msg->flags;
|
||||
__entry->len = msg->len;
|
||||
),
|
||||
TP_printk("i2c-%d #%u a=%03x f=%04x l=%u",
|
||||
__entry->adapter_nr,
|
||||
__entry->msg_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->len
|
||||
),
|
||||
i2c_transfer_trace_reg,
|
||||
i2c_transfer_trace_unreg);
|
||||
|
||||
/*
|
||||
* __i2c_transfer() read reply
|
||||
*/
|
||||
TRACE_EVENT_FN(i2c_reply,
|
||||
TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg,
|
||||
int num),
|
||||
TP_ARGS(adap, msg, num),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, msg_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u16, len )
|
||||
__dynamic_array(__u8, buf, msg->len) ),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->msg_nr = num;
|
||||
__entry->addr = msg->addr;
|
||||
__entry->flags = msg->flags;
|
||||
__entry->len = msg->len;
|
||||
memcpy(__get_dynamic_array(buf), msg->buf, msg->len);
|
||||
),
|
||||
TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]",
|
||||
__entry->adapter_nr,
|
||||
__entry->msg_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->len,
|
||||
__entry->len, __get_dynamic_array(buf)
|
||||
),
|
||||
i2c_transfer_trace_reg,
|
||||
i2c_transfer_trace_unreg);
|
||||
|
||||
/*
|
||||
* __i2c_transfer() result
|
||||
*/
|
||||
TRACE_EVENT_FN(i2c_result,
|
||||
TP_PROTO(const struct i2c_adapter *adap, int num, int ret),
|
||||
TP_ARGS(adap, num, ret),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, nr_msgs )
|
||||
__field(__s16, ret )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->nr_msgs = num;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("i2c-%d n=%u ret=%d",
|
||||
__entry->adapter_nr,
|
||||
__entry->nr_msgs,
|
||||
__entry->ret
|
||||
),
|
||||
i2c_transfer_trace_reg,
|
||||
i2c_transfer_trace_unreg);
|
||||
|
||||
/*
|
||||
* i2c_smbus_xfer() write data or procedure call request
|
||||
*/
|
||||
TRACE_EVENT_CONDITION(smbus_write,
|
||||
TP_PROTO(const struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int protocol,
|
||||
const union i2c_smbus_data *data),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol, data),
|
||||
TP_CONDITION(read_write == I2C_SMBUS_WRITE ||
|
||||
protocol == I2C_SMBUS_PROC_CALL ||
|
||||
protocol == I2C_SMBUS_BLOCK_PROC_CALL),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u8, command )
|
||||
__field(__u8, len )
|
||||
__field(__u32, protocol )
|
||||
__array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->addr = addr;
|
||||
__entry->flags = flags;
|
||||
__entry->command = command;
|
||||
__entry->protocol = protocol;
|
||||
|
||||
switch (protocol) {
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
__entry->len = 1;
|
||||
goto copy;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
__entry->len = 2;
|
||||
goto copy;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
__entry->len = data->block[0] + 1;
|
||||
copy:
|
||||
memcpy(__entry->buf, data->block, __entry->len);
|
||||
break;
|
||||
case I2C_SMBUS_QUICK:
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_I2C_BLOCK_BROKEN:
|
||||
default:
|
||||
__entry->len = 0;
|
||||
}
|
||||
),
|
||||
TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]",
|
||||
__entry->adapter_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->command,
|
||||
__print_symbolic(__entry->protocol,
|
||||
{ I2C_SMBUS_QUICK, "QUICK" },
|
||||
{ I2C_SMBUS_BYTE, "BYTE" },
|
||||
{ I2C_SMBUS_BYTE_DATA, "BYTE_DATA" },
|
||||
{ I2C_SMBUS_WORD_DATA, "WORD_DATA" },
|
||||
{ I2C_SMBUS_PROC_CALL, "PROC_CALL" },
|
||||
{ I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" },
|
||||
{ I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }),
|
||||
__entry->len,
|
||||
__entry->len, __entry->buf
|
||||
));
|
||||
|
||||
/*
|
||||
* i2c_smbus_xfer() read data request
|
||||
*/
|
||||
TRACE_EVENT_CONDITION(smbus_read,
|
||||
TP_PROTO(const struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int protocol),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol),
|
||||
TP_CONDITION(!(read_write == I2C_SMBUS_WRITE ||
|
||||
protocol == I2C_SMBUS_PROC_CALL ||
|
||||
protocol == I2C_SMBUS_BLOCK_PROC_CALL)),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, flags )
|
||||
__field(__u16, addr )
|
||||
__field(__u8, command )
|
||||
__field(__u32, protocol )
|
||||
__array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->addr = addr;
|
||||
__entry->flags = flags;
|
||||
__entry->command = command;
|
||||
__entry->protocol = protocol;
|
||||
),
|
||||
TP_printk("i2c-%d a=%03x f=%04x c=%x %s",
|
||||
__entry->adapter_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->command,
|
||||
__print_symbolic(__entry->protocol,
|
||||
{ I2C_SMBUS_QUICK, "QUICK" },
|
||||
{ I2C_SMBUS_BYTE, "BYTE" },
|
||||
{ I2C_SMBUS_BYTE_DATA, "BYTE_DATA" },
|
||||
{ I2C_SMBUS_WORD_DATA, "WORD_DATA" },
|
||||
{ I2C_SMBUS_PROC_CALL, "PROC_CALL" },
|
||||
{ I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" },
|
||||
{ I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" })
|
||||
));
|
||||
|
||||
/*
|
||||
* i2c_smbus_xfer() read data or procedure call reply
|
||||
*/
|
||||
TRACE_EVENT_CONDITION(smbus_reply,
|
||||
TP_PROTO(const struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int protocol,
|
||||
const union i2c_smbus_data *data),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol, data),
|
||||
TP_CONDITION(read_write == I2C_SMBUS_READ),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u8, command )
|
||||
__field(__u8, len )
|
||||
__field(__u32, protocol )
|
||||
__array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2) ),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->addr = addr;
|
||||
__entry->flags = flags;
|
||||
__entry->command = command;
|
||||
__entry->protocol = protocol;
|
||||
|
||||
switch (protocol) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
__entry->len = 1;
|
||||
goto copy;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
__entry->len = 2;
|
||||
goto copy;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
__entry->len = data->block[0] + 1;
|
||||
copy:
|
||||
memcpy(__entry->buf, data->block, __entry->len);
|
||||
break;
|
||||
case I2C_SMBUS_QUICK:
|
||||
case I2C_SMBUS_I2C_BLOCK_BROKEN:
|
||||
default:
|
||||
__entry->len = 0;
|
||||
}
|
||||
),
|
||||
TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]",
|
||||
__entry->adapter_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->command,
|
||||
__print_symbolic(__entry->protocol,
|
||||
{ I2C_SMBUS_QUICK, "QUICK" },
|
||||
{ I2C_SMBUS_BYTE, "BYTE" },
|
||||
{ I2C_SMBUS_BYTE_DATA, "BYTE_DATA" },
|
||||
{ I2C_SMBUS_WORD_DATA, "WORD_DATA" },
|
||||
{ I2C_SMBUS_PROC_CALL, "PROC_CALL" },
|
||||
{ I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" },
|
||||
{ I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }),
|
||||
__entry->len,
|
||||
__entry->len, __entry->buf
|
||||
));
|
||||
|
||||
/*
|
||||
* i2c_smbus_xfer() result
|
||||
*/
|
||||
TRACE_EVENT(smbus_result,
|
||||
TP_PROTO(const struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int protocol,
|
||||
int res),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol, res),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, addr )
|
||||
__field(__u16, flags )
|
||||
__field(__u8, read_write )
|
||||
__field(__u8, command )
|
||||
__field(__s16, res )
|
||||
__field(__u32, protocol )
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->adapter_nr = adap->nr;
|
||||
__entry->addr = addr;
|
||||
__entry->flags = flags;
|
||||
__entry->read_write = read_write;
|
||||
__entry->command = command;
|
||||
__entry->protocol = protocol;
|
||||
__entry->res = res;
|
||||
),
|
||||
TP_printk("i2c-%d a=%03x f=%04x c=%x %s %s res=%d",
|
||||
__entry->adapter_nr,
|
||||
__entry->addr,
|
||||
__entry->flags,
|
||||
__entry->command,
|
||||
__print_symbolic(__entry->protocol,
|
||||
{ I2C_SMBUS_QUICK, "QUICK" },
|
||||
{ I2C_SMBUS_BYTE, "BYTE" },
|
||||
{ I2C_SMBUS_BYTE_DATA, "BYTE_DATA" },
|
||||
{ I2C_SMBUS_WORD_DATA, "WORD_DATA" },
|
||||
{ I2C_SMBUS_PROC_CALL, "PROC_CALL" },
|
||||
{ I2C_SMBUS_BLOCK_DATA, "BLOCK_DATA" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_BROKEN, "I2C_BLOCK_BROKEN" },
|
||||
{ I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
|
||||
{ I2C_SMBUS_I2C_BLOCK_DATA, "I2C_BLOCK_DATA" }),
|
||||
__entry->read_write == I2C_SMBUS_WRITE ? "wr" : "rd",
|
||||
__entry->res
|
||||
));
|
||||
|
||||
#endif /* _TRACE_I2C_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
Loading…
Reference in New Issue
Block a user